From 1516de1feeeeff19a8b204a9b273f83c3390c99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Mon, 24 Jul 2023 02:48:01 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=A1=E3=83=87=E3=82=A3=E3=82=A2?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E3=82=92?= =?UTF-8?q?=E8=BC=B8=E5=85=A5=20(#103)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * メディアタイムラインの実装 * refactor(stream): ストリーミングにwithFilesオプションを実装 --------- Co-authored-by: tar_bin --- locales/en-US.yml | 1 + locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + misskey-assets | 2 +- .../api/stream/channels/global-timeline.ts | 5 ++++ .../api/stream/channels/home-timeline.ts | 5 ++++ .../api/stream/channels/hybrid-timeline.ts | 5 ++++ .../api/stream/channels/local-timeline.ts | 5 ++++ .../frontend/src/components/MkTimeline.vue | 11 ++++++++ packages/frontend/src/pages/timeline.vue | 27 +++++-------------- packages/frontend/src/ui/deck/deck-store.ts | 2 +- packages/frontend/src/ui/deck/tl-column.vue | 3 +++ .../frontend/src/widgets/WidgetTimeline.vue | 4 +++ 13 files changed, 50 insertions(+), 22 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index dfbab848fa..46d77611b5 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1933,6 +1933,7 @@ _instanceCharts: _timelines: home: "Home" local: "Local" + media: "Media" social: "Social" global: "Global" _play: diff --git a/locales/index.d.ts b/locales/index.d.ts index 88183b810a..a1285d21c4 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2067,6 +2067,7 @@ export interface Locale { "_timelines": { "home": string; "local": string; + "media": string; "social": string; "global": string; }; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 13c2498a81..591fefb061 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1982,6 +1982,7 @@ _instanceCharts: _timelines: home: "ホーム" local: "ローカル" + media: "メディア" social: "ソーシャル" global: "グローバル" diff --git a/misskey-assets b/misskey-assets index 0179793ec8..cf3ce27b2e 160000 --- a/misskey-assets +++ b/misskey-assets @@ -1 +1 @@ -Subproject commit 0179793ec891856d6f37a3be16ba4c22f67a81b5 +Subproject commit cf3ce27b2eb8417233072e3d6d2fb7c5356c2364 diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index a33f1a956a..3fb2f81fac 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -19,6 +19,7 @@ class GlobalTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = false; private withReplies: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -38,6 +39,7 @@ class GlobalTimelineChannel extends Channel { if (!policies.gtlAvailable) return; this.withReplies = params.withReplies as boolean; + this.withFiles = params.withFiles as boolean; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -48,6 +50,9 @@ class GlobalTimelineChannel extends Channel { if (note.visibility !== 'public') return; if (note.channelId != null) return; + // ファイルを含まない投稿は除外 + if (this.withFiles && (note.files === undefined || note.files.length === 0)) return; + // リプライなら再pack if (note.replyId != null) { note.reply = await this.noteEntityService.pack(note.replyId, this.user, { diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index bd8888f679..f4971d832b 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -17,6 +17,7 @@ class HomeTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = true; private withReplies: boolean; + private withFiles: boolean; constructor( private noteEntityService: NoteEntityService, @@ -31,6 +32,7 @@ class HomeTimelineChannel extends Channel { @bindThis public async init(params: any) { this.withReplies = params.withReplies as boolean; + this.withFiles = params.withFiles as boolean; this.subscriber.on('notesStream', this.onNote); } @@ -47,6 +49,9 @@ class HomeTimelineChannel extends Channel { // Ignore notes from instances the user has muted if (isInstanceMuted(note, new Set(this.userProfile!.mutedInstances ?? []))) return; + // ファイルを含まない投稿は除外 + if (this.withFiles && (note.files === undefined || note.files.length === 0)) return; + if (['followers', 'specified'].includes(note.visibility)) { note = await this.noteEntityService.pack(note.id, this.user!, { detail: true, diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 760fb8d19f..36fc23cb16 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -19,6 +19,7 @@ class HybridTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = true; private withReplies: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -38,6 +39,7 @@ class HybridTimelineChannel extends Channel { if (!policies.ltlAvailable) return; this.withReplies = params.withReplies as boolean; + this.withFiles = params.withFiles as boolean; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -56,6 +58,9 @@ class HybridTimelineChannel extends Channel { (note.channelId != null && this.followingChannels.has(note.channelId)) )) return; + // ファイルを含まない投稿は除外 + if (this.withFiles && (note.files === undefined || note.files.length === 0)) return; + if (['followers', 'specified'].includes(note.visibility)) { note = await this.noteEntityService.pack(note.id, this.user!, { detail: true, diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index f32f8c5cec..6fb1f456a6 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -18,6 +18,7 @@ class LocalTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = false; private withReplies: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -37,6 +38,7 @@ class LocalTimelineChannel extends Channel { if (!policies.ltlAvailable) return; this.withReplies = params.withReplies as boolean; + this.withFiles = params.withFiles as boolean; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -48,6 +50,9 @@ class LocalTimelineChannel extends Channel { if (note.visibility !== 'public') return; if (note.channelId != null && !this.followingChannels.has(note.channelId)) return; + // ファイルを含まない投稿は除外 + if (this.withFiles && (note.files === undefined || note.files.length === 0)) return; + // リプライなら再pack if (note.replyId != null) { note.reply = await this.noteEntityService.pack(note.replyId, this.user, { diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index e7a595b7d8..8c6029c053 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -79,6 +79,17 @@ if (props.src === 'antenna') { withReplies: defaultStore.state.showTimelineReplies, }); connection.on('note', prepend); +} else if (props.src === 'media') { + endpoint = 'notes/hybrid-timeline'; + query = { + withFiles: true, + withReplies: defaultStore.state.showTimelineReplies, + }; + connection = stream.useChannel('hybridTimeline', { + withFiles: true, + withReplies: defaultStore.state.showTimelineReplies, + }); + connection.on('note', prepend); } else if (props.src === 'social') { endpoint = 'notes/hybrid-timeline'; query = { diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 0b4f826919..d085443290 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -1,8 +1,3 @@ - - @@ -61,6 +62,8 @@ async function setType() { value: 'home' as const, text: i18n.ts._timelines.home, }, { value: 'local' as const, text: i18n.ts._timelines.local, + }, { + value: 'media' as const, text: i18n.ts._timelines.media, }, { value: 'social' as const, text: i18n.ts._timelines.social, }, { diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue index 51623023c7..3eede6e7be 100644 --- a/packages/frontend/src/widgets/WidgetTimeline.vue +++ b/packages/frontend/src/widgets/WidgetTimeline.vue @@ -122,6 +122,10 @@ const choose = async (ev) => { text: i18n.ts._timelines.local, icon: 'ti ti-planet', action: () => { setSrc('local'); }, + }, { + text: i18n.ts._timelines.media, + icon: 'ti ti-photo', + action: () => { setSrc('media'); }, }, { text: i18n.ts._timelines.social, icon: 'ti ti-rocket',