From 6277a5545c746fac15ee6b4fe58de2e354ed7fda Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 3 Oct 2023 20:26:11 +0900 Subject: feat: improve tl performance (#11946) * wip * wip * wip * wip * wip * wip * Update NoteCreateService.ts * wip * wip * wip * wip * Update NoteCreateService.ts * wip * Update NoteCreateService.ts * wip * Update user-notes.ts * wip * wip * wip * Update NoteCreateService.ts * wip * Update timeline.ts * Update timeline.ts * Update timeline.ts * Update timeline.ts * Update timeline.ts * wip * Update timelines.ts * Update timelines.ts * Update timelines.ts * wip * wip * wip * Update timelines.ts * Update misskey-js.api.md * Update timelines.ts * Update timelines.ts * wip * wip * wip * Update timelines.ts * wip * Update timelines.ts * wip * test * Update activitypub.ts * refactor: UserListJoining -> UserListMembership * Update NoteCreateService.ts * wip --- packages/frontend/src/components/MkNote.vue | 4 +- .../frontend/src/components/MkNoteDetailed.vue | 4 +- packages/frontend/src/components/MkNoteSub.vue | 8 +- packages/frontend/src/components/MkTimeline.vue | 12 --- packages/frontend/src/pages/my-lists/list.vue | 94 ++++++++++-------- packages/frontend/src/pages/settings/word-mute.vue | 52 +++------- packages/frontend/src/pages/timeline.vue | 9 +- packages/frontend/src/pages/user/home.vue | 6 +- packages/frontend/src/pages/user/index.files.vue | 97 +++++++++++++++++++ packages/frontend/src/pages/user/index.photos.vue | 105 --------------------- packages/frontend/src/scripts/get-user-menu.ts | 13 +++ packages/frontend/src/store.ts | 6 +- packages/frontend/src/ui/deck/deck-store.ts | 1 - packages/frontend/src/ui/deck/tl-column.vue | 14 +-- 14 files changed, 191 insertions(+), 234 deletions(-) create mode 100644 packages/frontend/src/pages/user/index.files.vue delete mode 100644 packages/frontend/src/pages/user/index.photos.vue (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index b397f3eee9..4860f42cdc 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -165,7 +165,7 @@ import { deepClone } from '@/scripts/clone.js'; import { useTooltip } from '@/scripts/use-tooltip.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { getNoteSummary } from '@/scripts/get-note-summary.js'; -import { MenuItem } from '@/types/menu'; +import { MenuItem } from '@/types/menu.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import { shouldCollapsed } from '@/scripts/collapsed.js'; @@ -211,7 +211,7 @@ const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : n const isLong = shouldCollapsed(appearNote); const collapsed = ref(appearNote.cw == null && isLong); const isDeleted = ref(false); -const muted = ref(checkWordMute(appearNote, $i, defaultStore.state.mutedWords)); +const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false); const translation = ref(null); const translating = ref(false); const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance); diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index ab8886e8ba..a9da5a3a62 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -214,7 +214,7 @@ import { useNoteCapture } from '@/scripts/use-note-capture.js'; import { deepClone } from '@/scripts/clone.js'; import { useTooltip } from '@/scripts/use-tooltip.js'; import { claimAchievement } from '@/scripts/achievements.js'; -import { MenuItem } from '@/types/menu'; +import { MenuItem } from '@/types/menu.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; @@ -258,7 +258,7 @@ let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note const isMyRenote = $i && ($i.id === note.userId); const showContent = ref(false); const isDeleted = ref(false); -const muted = ref(checkWordMute(appearNote, $i, defaultStore.state.mutedWords)); +const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false); const translation = ref(null); const translating = ref(false); const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : null; diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue index 2a3cd9bf02..bc52101f42 100644 --- a/packages/frontend/src/components/MkNoteSub.vue +++ b/packages/frontend/src/components/MkNoteSub.vue @@ -49,9 +49,9 @@ import { notePage } from '@/filters/note.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; -import { userPage } from "@/filters/user"; -import { checkWordMute } from "@/scripts/check-word-mute"; -import { defaultStore } from "@/store"; +import { userPage } from '@/filters/user.js'; +import { checkWordMute } from '@/scripts/check-word-mute.js'; +import { defaultStore } from '@/store.js'; const props = withDefaults(defineProps<{ note: Misskey.entities.Note; @@ -63,7 +63,7 @@ const props = withDefaults(defineProps<{ depth: 1, }); -const muted = ref(checkWordMute(props.note, $i, defaultStore.state.mutedWords)); +const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false); let showContent = $ref(false); let replies: Misskey.entities.Note[] = $ref([]); diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 1dcafd6be1..c4a34667ef 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -23,11 +23,9 @@ const props = withDefaults(defineProps<{ role?: string; sound?: boolean; withRenotes?: boolean; - withReplies?: boolean; onlyFiles?: boolean; }>(), { withRenotes: true, - withReplies: false, onlyFiles: false, }); @@ -70,12 +68,10 @@ if (props.src === 'antenna') { endpoint = 'notes/timeline'; query = { withRenotes: props.withRenotes, - withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }; connection = stream.useChannel('homeTimeline', { withRenotes: props.withRenotes, - withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }); connection.on('note', prepend); @@ -85,12 +81,10 @@ if (props.src === 'antenna') { endpoint = 'notes/local-timeline'; query = { withRenotes: props.withRenotes, - withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }; connection = stream.useChannel('localTimeline', { withRenotes: props.withRenotes, - withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }); connection.on('note', prepend); @@ -98,12 +92,10 @@ if (props.src === 'antenna') { endpoint = 'notes/hybrid-timeline'; query = { withRenotes: props.withRenotes, - withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }; connection = stream.useChannel('hybridTimeline', { withRenotes: props.withRenotes, - withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }); connection.on('note', prepend); @@ -111,12 +103,10 @@ if (props.src === 'antenna') { endpoint = 'notes/global-timeline'; query = { withRenotes: props.withRenotes, - withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }; connection = stream.useChannel('globalTimeline', { withRenotes: props.withRenotes, - withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }); connection.on('note', prepend); @@ -140,13 +130,11 @@ if (props.src === 'antenna') { endpoint = 'notes/user-list-timeline'; query = { withRenotes: props.withRenotes, - withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, listId: props.list, }; connection = stream.useChannel('userList', { withRenotes: props.withRenotes, - withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, listId: props.list, }); diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue index df13998f6b..b600f99fbc 100644 --- a/packages/frontend/src/pages/my-lists/list.vue +++ b/packages/frontend/src/pages/my-lists/list.vue @@ -29,16 +29,22 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.addUser }} -
- - - - -
- - {{ i18n.ts.loadMore }} - - + + + +
@@ -59,9 +65,11 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkInput from '@/components/MkInput.vue'; -import { userListsCache } from '@/cache'; +import { userListsCache } from '@/cache.js'; import { $i } from '@/account.js'; import { defaultStore } from '@/store.js'; +import MkPagination, { Paging } from '@/components/MkPagination.vue'; + const { enableInfiniteScroll, } = defaultStore.reactiveState; @@ -70,40 +78,25 @@ const props = defineProps<{ listId: string; }>(); -const FETCH_USERS_LIMIT = 20; - +const paginationEl = ref>(); let list = $ref(null); -let users = $ref([]); -let queueUserIds = $ref([]); -let fetching = $ref(true); const isPublic = ref(false); const name = ref(''); +const membershipsPagination = { + endpoint: 'users/lists/get-memberships' as const, + limit: 30, + params: computed(() => ({ + listId: props.listId, + })), +}; function fetchList() { - fetching = true; os.api('users/lists/show', { listId: props.listId, }).then(_list => { list = _list; name.value = list.name; isPublic.value = list.isPublic; - queueUserIds = list.userIds; - - return fetchMoreUsers(); - }); -} - -function fetchMoreUsers() { - if (!list) return; - if (fetching && users.length !== 0) return; // fetchingがtrueならやめるが、usersが空なら続行 - fetching = true; - os.api('users/show', { - userIds: queueUserIds.slice(0, FETCH_USERS_LIMIT), - }).then(_users => { - users = users.concat(_users); - queueUserIds = queueUserIds.slice(FETCH_USERS_LIMIT); - }).finally(() => { - fetching = false; }); } @@ -114,12 +107,12 @@ function addUser() { listId: list.id, userId: user.id, }).then(() => { - users.push(user); + paginationEl.value.reload(); }); }); } -async function removeUser(user, ev) { +async function removeUser(item, ev) { os.popupMenu([{ text: i18n.ts.remove, icon: 'ti ti-x', @@ -128,9 +121,28 @@ async function removeUser(user, ev) { if (!list) return; os.api('users/lists/pull', { listId: list.id, - userId: user.id, + userId: item.userId, + }).then(() => { + paginationEl.value.removeItem(item.id); + }); + }, + }], ev.currentTarget ?? ev.target); +} + +async function showMembershipMenu(item, ev) { + os.popupMenu([{ + text: item.withReplies ? i18n.ts.hideRepliesToOthersInTimeline : i18n.ts.showRepliesToOthersInTimeline, + icon: item.withReplies ? 'ti ti-messages-off' : 'ti ti-messages', + action: async () => { + os.api('users/lists/update-membership', { + listId: list.id, + userId: item.userId, + withReplies: !item.withReplies, }).then(() => { - users = users.filter(x => x.id !== user.id); + paginationEl.value.updateItem(item.id, (old) => ({ + ...old, + withReplies: !item.withReplies, + })); }); }, }], ev.currentTarget ?? ev.target); @@ -202,6 +214,12 @@ definePageMetadata(computed(() => list ? { align-self: center; } +.menu { + width: 32px; + height: 32px; + align-self: center; +} + .more { margin-left: auto; margin-right: auto; diff --git a/packages/frontend/src/pages/settings/word-mute.vue b/packages/frontend/src/pages/settings/word-mute.vue index 1fefbdc92b..4e698698fe 100644 --- a/packages/frontend/src/pages/settings/word-mute.vue +++ b/packages/frontend/src/pages/settings/word-mute.vue @@ -5,29 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only