From 1cac81ad5791b7dc52a643fff296bb7f4c7aeef9 Mon Sep 17 00:00:00 2001 From: Ayo Date: Tue, 10 Jan 2023 16:23:47 +0100 Subject: [PATCH 01/24] feat: totally hide strict filters --- components/status/StatusCard.vue | 5 ----- locales/ar-EG.json | 1 - locales/cs-CZ.json | 1 - locales/de-DE.json | 1 - locales/en-GB.json | 1 - locales/en-US.json | 1 - locales/es-ES.json | 1 - locales/fr-FR.json | 1 - locales/nl-NL.json | 1 - locales/uk-UA.json | 1 - locales/zh-CN.json | 1 - locales/zh-TW.json | 1 - 12 files changed, 16 deletions(-) diff --git a/components/status/StatusCard.vue b/components/status/StatusCard.vue index 9e227114..3517b316 100644 --- a/components/status/StatusCard.vue +++ b/components/status/StatusCard.vue @@ -187,9 +187,4 @@ const showReplyTo = $computed(() => !replyToMain && !directReply) -
-

- {{ filterPhrase && `${$t('status.filter_removed_phrase')}: ${filterPhrase}` }} -

-
diff --git a/locales/ar-EG.json b/locales/ar-EG.json index 7d74fb78..509c00b8 100644 --- a/locales/ar-EG.json +++ b/locales/ar-EG.json @@ -273,7 +273,6 @@ "status": { "edited": "عدل {0}", "filter_hidden_phrase": "تمت تصفيتها بواسطة", - "filter_removed_phrase": "تمت إزالته بواسطة عامل التصفية", "filter_show_anyway": "عرض على أي حال", "img_alt": { "desc": "وصف", diff --git a/locales/cs-CZ.json b/locales/cs-CZ.json index fcae19a0..9648f526 100644 --- a/locales/cs-CZ.json +++ b/locales/cs-CZ.json @@ -158,7 +158,6 @@ "status": { "edited": "Upraveno {0}", "filter_hidden_phrase": "Vyfiltrováno", - "filter_removed_phrase": "Vyfiltrováno", "filter_show_anyway": "Ukázat i tak", "img_alt": { "desc": "Popis", diff --git a/locales/de-DE.json b/locales/de-DE.json index 6e1c26f6..193e2e52 100644 --- a/locales/de-DE.json +++ b/locales/de-DE.json @@ -208,7 +208,6 @@ "status": { "edited": "Zuletzt bearbeitet: {0}", "filter_hidden_phrase": "Versteckt durch", - "filter_removed_phrase": "Entfernt durch Filter", "filter_show_anyway": "Trotzdem zeigen", "img_alt": { "desc": "Beschreibung", diff --git a/locales/en-GB.json b/locales/en-GB.json index e094cbf3..a64c3740 100644 --- a/locales/en-GB.json +++ b/locales/en-GB.json @@ -292,7 +292,6 @@ "status": { "edited": "Edited {0}", "filter_hidden_phrase": "Filtered by", - "filter_removed_phrase": "Removed by filter", "filter_show_anyway": "Show anyway", "img_alt": { "desc": "Description", diff --git a/locales/en-US.json b/locales/en-US.json index 39f614bc..f406b9ad 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -364,7 +364,6 @@ "edited": "Edited {0}", "favourited_by": "Favorited By", "filter_hidden_phrase": "Filtered by", - "filter_removed_phrase": "Removed by filter", "filter_show_anyway": "Show anyway", "img_alt": { "desc": "Description", diff --git a/locales/es-ES.json b/locales/es-ES.json index b8ceb713..bc6122e0 100644 --- a/locales/es-ES.json +++ b/locales/es-ES.json @@ -355,7 +355,6 @@ "edited": "Editado {0}", "favourited_by": "Marcado como favorito por", "filter_hidden_phrase": "Filtrado por", - "filter_removed_phrase": "Eliminado por filtrado", "filter_show_anyway": "Mostrar de todas formas", "img_alt": { "desc": "Descripción", diff --git a/locales/fr-FR.json b/locales/fr-FR.json index 362dd1b5..efdc2743 100644 --- a/locales/fr-FR.json +++ b/locales/fr-FR.json @@ -354,7 +354,6 @@ "edited": "Edité {0}", "favourited_by": "Aimé par", "filter_hidden_phrase": "Filtré par", - "filter_removed_phrase": "Caché par le filtre", "filter_show_anyway": "Montrer coûte que coûte", "img_alt": { "desc": "Description", diff --git a/locales/nl-NL.json b/locales/nl-NL.json index d4c930d6..196c533b 100644 --- a/locales/nl-NL.json +++ b/locales/nl-NL.json @@ -296,7 +296,6 @@ "status": { "edited": "Aangepast {0}", "filter_hidden_phrase": "Gefilterd door", - "filter_removed_phrase": "Verwijderd door filter", "filter_show_anyway": "Laat toch zien", "img_alt": { "desc": "Descriptie", diff --git a/locales/uk-UA.json b/locales/uk-UA.json index 6e6a0239..d9459a13 100644 --- a/locales/uk-UA.json +++ b/locales/uk-UA.json @@ -322,7 +322,6 @@ "status": { "edited": "Редаговано {0}", "filter_hidden_phrase": "Відфільтровано", - "filter_removed_phrase": "Видалено фільтром", "filter_show_anyway": "Показати все одно", "img_alt": { "desc": "Опис зображення", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 17f2a89a..ef963038 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -352,7 +352,6 @@ "edited": "在 {0} 编辑了", "favourited_by": "被喜欢", "filter_hidden_phrase": "筛选依据", - "filter_removed_phrase": "从筛选中移除", "filter_show_anyway": "仍然展示", "img_alt": { "desc": "描述", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 3b158438..22ad5d5e 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -277,7 +277,6 @@ "status": { "edited": "在 {0} 編輯了", "filter_hidden_phrase": "篩選依據", - "filter_removed_phrase": "從篩選中移除", "filter_show_anyway": "仍然展示", "img_alt": { "desc": "描述", From 3f2227ef4ce5700caa6d4f324a40f0ef064934b8 Mon Sep 17 00:00:00 2001 From: Ayo Date: Tue, 10 Jan 2023 17:57:58 +0100 Subject: [PATCH 02/24] feat: filter stream items if filterAction is 'hide' --- composables/timeline.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/composables/timeline.ts b/composables/timeline.ts index 0d0d9b4c..8fb1dc7f 100644 --- a/composables/timeline.ts +++ b/composables/timeline.ts @@ -9,10 +9,16 @@ function areStatusesConsecutive(a: mastodon.v1.Status, b: mastodon.v1.Status) { return !!inReplyToId && (inReplyToId === a.reblog?.id || inReplyToId === a.id) } -export function reorderedTimeline(items: mastodon.v1.Status[]) { +export function reorderedTimeline(items: mastodon.v1.Status[], context: mastodon.v1.FilterContext = 'public') { let steps = 0 - const newItems = [...items] - for (let i = items.length - 1; i > 0; i--) { + + const isStrict = (filter: mastodon.v1.FilterResult) => filter.filter.filterAction === 'hide' && filter.filter.context.includes(context) + const isFiltered = (item: mastodon.v1.Status) => !item.filtered?.find(isStrict) + const isReblogFiltered = (item: mastodon.v1.Status) => !item.reblog?.filtered?.find(isStrict) + + const newItems = [...items].filter(isFiltered).filter(isReblogFiltered) + + for (let i = newItems.length - 1; i > 0; i--) { for (let k = 1; k <= maxDistance && i - k >= 0; k++) { // Prevent infinite loops steps++ From 5de275b505f5651a552e921e72090cc271a229ae Mon Sep 17 00:00:00 2001 From: Ayo Date: Tue, 10 Jan 2023 17:58:23 +0100 Subject: [PATCH 03/24] test: reorderedTimeline filter scenarios --- tests/reorder-timeline.test.ts | 38 +++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/reorder-timeline.test.ts b/tests/reorder-timeline.test.ts index 242b674c..004f4813 100644 --- a/tests/reorder-timeline.test.ts +++ b/tests/reorder-timeline.test.ts @@ -5,7 +5,21 @@ import type { mastodon } from 'masto' import { describe, expect, it } from 'vitest' import { reorderedTimeline } from '~/composables/timeline' -function status(id: string): mastodon.v1.Status { +function status(id: string, filtered?: mastodon.v1.FilterContext): mastodon.v1.Status { + if (filtered) { + return { + id, + filtered: [ + { + filter: { + filterAction: 'hide', + context: [filtered], + }, + } as mastodon.v1.FilterResult, + ], + } as mastodon.v1.Status + } + return { id } as mastodon.v1.Status } function reply(id: string, s: mastodon.v1.Status) { @@ -30,6 +44,11 @@ const r_b1 = reblog('r_b1', p_b1) const r_a2 = reblog('r_a2', p_a2) const r_b2 = reblog('r_b2', p_b2) +const f = status('f', 'public') +const r_f = reply('r_f', f) +const rb_f = reblog('rb_f', f) +const n_f = status('f', 'notifications') + describe('timeline reordering', () => { it('reorder basic', () => { expect(reorderedTimeline([r_a2, r_a1])).toEqual([r_a1, r_a2]) @@ -52,4 +71,21 @@ describe('timeline reordering', () => { expect(reorderedTimeline([p_a3, r_a1, r_a2, r_b2, p_b3, r_b1])).toEqual([r_a1, r_a2, p_a3, r_b1, r_b2, p_b3]) }) + + it('reorder with filtered item', () => { + // should not show filtered status with 'hide' filterAction + expect(reorderedTimeline([p_a3, f, r_a1, r_a2, r_b2, p_b3, r_b1])).toEqual([r_a1, r_a2, p_a3, r_b1, r_b2, p_b3]) + + // should not filter status with 'hide' filterAction but does not matches context + expect(reorderedTimeline([p_a3, n_f, r_a1, r_a2, r_b2, p_b3, r_b1], 'public')).toEqual([r_a1, r_a2, p_a3, n_f, r_b1, r_b2, p_b3]) + + // should filter status with 'hide' filterAction and matches context + expect(reorderedTimeline([p_a3, n_f, r_a1, r_a2, r_b2, p_b3, r_b1], 'notifications')).toEqual([r_a1, r_a2, p_a3, r_b1, r_b2, p_b3]) + + // should show reply to a filtered status + expect(reorderedTimeline([p_a3, f, r_a1, r_f, r_a2, r_b2, p_b3, r_b1])).toEqual([r_a1, r_a2, p_a3, r_f, r_b1, r_b2, p_b3]) + + // should not show reblogged status that is filtered with 'hide' filterAction + expect(reorderedTimeline([p_a3, f, r_a1, rb_f, r_a2, r_b2, p_b3, r_b1])).toEqual([r_a1, r_a2, p_a3, r_b1, r_b2, p_b3]) + }) }) From 1cd6448bc5413af06c832a294d2ca4e2866c6251 Mon Sep 17 00:00:00 2001 From: Ayo Date: Tue, 10 Jan 2023 18:35:34 +0100 Subject: [PATCH 04/24] refactor: test variables and mock IDs --- tests/reorder-timeline.test.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/reorder-timeline.test.ts b/tests/reorder-timeline.test.ts index 004f4813..b8e7aa3d 100644 --- a/tests/reorder-timeline.test.ts +++ b/tests/reorder-timeline.test.ts @@ -44,10 +44,10 @@ const r_b1 = reblog('r_b1', p_b1) const r_a2 = reblog('r_a2', p_a2) const r_b2 = reblog('r_b2', p_b2) -const f = status('f', 'public') -const r_f = reply('r_f', f) -const rb_f = reblog('rb_f', f) -const n_f = status('f', 'notifications') +const f1 = status('f1', 'public') +const r_f1 = reply('r_f1', f1) +const rb_f1 = reblog('rb_f1', f1) +const n_f2 = status('f2', 'notifications') describe('timeline reordering', () => { it('reorder basic', () => { @@ -74,18 +74,18 @@ describe('timeline reordering', () => { it('reorder with filtered item', () => { // should not show filtered status with 'hide' filterAction - expect(reorderedTimeline([p_a3, f, r_a1, r_a2, r_b2, p_b3, r_b1])).toEqual([r_a1, r_a2, p_a3, r_b1, r_b2, p_b3]) + expect(reorderedTimeline([p_a3, f1, r_a1, r_a2, r_b2, p_b3, r_b1])).toEqual([r_a1, r_a2, p_a3, r_b1, r_b2, p_b3]) - // should not filter status with 'hide' filterAction but does not matches context - expect(reorderedTimeline([p_a3, n_f, r_a1, r_a2, r_b2, p_b3, r_b1], 'public')).toEqual([r_a1, r_a2, p_a3, n_f, r_b1, r_b2, p_b3]) + // should not filter status with 'hide' filterAction but does not match context + expect(reorderedTimeline([p_a3, n_f2, r_a1, r_a2, r_b2, p_b3, r_b1], 'public')).toEqual([r_a1, r_a2, p_a3, n_f2, r_b1, r_b2, p_b3]) // should filter status with 'hide' filterAction and matches context - expect(reorderedTimeline([p_a3, n_f, r_a1, r_a2, r_b2, p_b3, r_b1], 'notifications')).toEqual([r_a1, r_a2, p_a3, r_b1, r_b2, p_b3]) + expect(reorderedTimeline([p_a3, n_f2, r_a1, r_a2, r_b2, p_b3, r_b1], 'notifications')).toEqual([r_a1, r_a2, p_a3, r_b1, r_b2, p_b3]) // should show reply to a filtered status - expect(reorderedTimeline([p_a3, f, r_a1, r_f, r_a2, r_b2, p_b3, r_b1])).toEqual([r_a1, r_a2, p_a3, r_f, r_b1, r_b2, p_b3]) + expect(reorderedTimeline([p_a3, f1, r_a1, r_f1, r_a2, r_b2, p_b3, r_b1])).toEqual([r_a1, r_a2, p_a3, r_f1, r_b1, r_b2, p_b3]) // should not show reblogged status that is filtered with 'hide' filterAction - expect(reorderedTimeline([p_a3, f, r_a1, rb_f, r_a2, r_b2, p_b3, r_b1])).toEqual([r_a1, r_a2, p_a3, r_b1, r_b2, p_b3]) + expect(reorderedTimeline([p_a3, f1, r_a1, rb_f1, r_a2, r_b2, p_b3, r_b1])).toEqual([r_a1, r_a2, p_a3, r_b1, r_b2, p_b3]) }) }) From a2a76651a5452275f90da4ac4b268605f29dd5c1 Mon Sep 17 00:00:00 2001 From: Ayo Date: Tue, 10 Jan 2023 20:08:36 +0100 Subject: [PATCH 05/24] feat: implement paginator context --- components/common/CommonPaginator.vue | 8 +++++--- components/timeline/TimelinePaginator.vue | 2 +- composables/paginator.ts | 7 ++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/components/common/CommonPaginator.vue b/components/common/CommonPaginator.vue index 1191f17d..19f14677 100644 --- a/components/common/CommonPaginator.vue +++ b/components/common/CommonPaginator.vue @@ -2,7 +2,7 @@ // @ts-expect-error missing types import { DynamicScroller } from 'vue-virtual-scroller' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' -import type { Paginator, WsEvents } from 'masto' +import type { Paginator, WsEvents, mastodon } from 'masto' const { paginator, @@ -11,13 +11,15 @@ const { virtualScroller = false, eventType = 'update', preprocess, + context = 'public', } = defineProps<{ paginator: Paginator keyProp?: keyof T virtualScroller?: boolean stream?: Promise eventType?: 'notification' | 'update' - preprocess?: (items: (U | T)[]) => U[] + preprocess?: (items: (U | T)[], context?: mastodon.v2.FilterContext) => U[] + context?: mastodon.v2.FilterContext }>() defineSlots<{ @@ -42,7 +44,7 @@ defineSlots<{ const { t } = useI18n() -const { items, prevItems, update, state, endAnchor, error } = usePaginator(paginator, stream, eventType, preprocess) +const { items, prevItems, update, state, endAnchor, error } = usePaginator(paginator, stream, eventType, preprocess, context)