From bf8636e516694b6141a77fa7ba405806ffa6a11c Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Mon, 25 Aug 2025 16:56:05 +0900 Subject: [PATCH 01/61] refactor --- packages/frontend/src/pages/admin/relays.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue index fca9776c0a..9eba68022a 100644 --- a/packages/frontend/src/pages/admin/relays.vue +++ b/packages/frontend/src/pages/admin/relays.vue @@ -41,7 +41,7 @@ async function addRelay() { type: 'url', placeholder: i18n.ts.inboxUrl, }); - if (canceled) return; + if (canceled || inbox == null) return; misskeyApi('admin/relays/add', { inbox, }).then((relay: any) => { From 0121d1964521baf89457fd6f49fbd749774d0c2d Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Mon, 25 Aug 2025 16:56:10 +0900 Subject: [PATCH 02/61] refactor --- packages/frontend/src/components/MkSelect.vue | 35 ++++++++---- .../frontend/src/pages/about.federation.vue | 55 ++++++++++++++----- 2 files changed, 66 insertions(+), 24 deletions(-) diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue index 485d163ac4..58b1d12d34 100644 --- a/packages/frontend/src/components/MkSelect.vue +++ b/packages/frontend/src/components/MkSelect.vue @@ -39,13 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only - + + - - From 7e7dc03796ffb852056aa1720ebe897aae5ceeb9 Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 26 Aug 2025 07:43:59 +0900 Subject: [PATCH 10/61] =?UTF-8?q?fix(frontend):=20ap/show=E3=81=A7?= =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=82=AB=E3=83=AB=E3=83=A6=E3=83=BC=E3=82=B6?= =?UTF-8?q?=E3=83=BC=E3=82=92=E8=A7=A3=E6=B1=BA=E3=81=97=E3=81=9F=E9=9A=9B?= =?UTF-8?q?@username@null=E3=81=AB=E9=A3=9B=E3=81=B0=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1646?= =?UTF-8?q?0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/utility/lookup.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/utility/lookup.ts b/packages/frontend/src/utility/lookup.ts index 47d0db125d..9baf40b731 100644 --- a/packages/frontend/src/utility/lookup.ts +++ b/packages/frontend/src/utility/lookup.ts @@ -8,6 +8,7 @@ import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; import { mainRouter } from '@/router.js'; +import { acct } from '@/filters/user'; export async function lookup(router?: Router) { const _router = router ?? mainRouter; @@ -38,7 +39,7 @@ export async function lookup(router?: Router) { if (res.type === 'User') { _router.push('/@:acct/:page?', { params: { - acct: `${res.object.username}@${res.object.host}`, + acct: acct(res.object), }, }); } else if (res.type === 'Note') { From 0c8545ec1c5ea20c79cb816135373f850429ff16 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 26 Aug 2025 07:44:26 +0900 Subject: [PATCH 11/61] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cceb6e432..dd9b4ff183 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ - Fix: 複数のメンションを1行に記述した場合に、サジェストが正しく表示されない問題を修正 - Fix: メンションとしての条件を満たしていても、特定の条件(`-`が含まれる場合など)で正しくサジェストされない問題を一部修正 - Fix: ユーザーの前後ノートを閲覧する機能が動作しない問題を修正 +- Fix: 照会ダイアログでap/showでローカルユーザーを解決した際@username@nullに飛ばされる問題を修正 ### Server - Feat: サーバー管理コマンド From 506c8a259becee338f4aabb2307a5c68e6891589 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 26 Aug 2025 08:50:34 +0900 Subject: [PATCH 12/61] refactor --- .../src/pages/settings/webhook.new.vue | 2 +- packages/frontend/src/pages/theme-editor.vue | 22 +++++++++++-------- .../src/pages/user/activity.following.vue | 2 +- .../frontend/src/pages/user/follow-list.vue | 2 +- packages/frontend/src/pages/user/home.vue | 10 ++++----- packages/frontend/src/pages/user/raw.vue | 2 +- packages/frontend/src/utility/admin-lookup.ts | 4 ++-- .../frontend/src/utility/get-note-menu.ts | 4 ++-- 8 files changed, 26 insertions(+), 22 deletions(-) diff --git a/packages/frontend/src/pages/settings/webhook.new.vue b/packages/frontend/src/pages/settings/webhook.new.vue index e853f967cb..6c4dff5551 100644 --- a/packages/frontend/src/pages/settings/webhook.new.vue +++ b/packages/frontend/src/pages/settings/webhook.new.vue @@ -61,7 +61,7 @@ const event_reaction = ref(true); const event_mention = ref(true); async function create(): Promise { - const events = []; + const events: string[] = []; if (event_follow.value) events.push('follow'); if (event_followed.value) events.push('followed'); if (event_note.value) events.push('note'); diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue index d1be9e38b7..af3891ac8e 100644 --- a/packages/frontend/src/pages/theme-editor.vue +++ b/packages/frontend/src/pages/theme-editor.vue @@ -11,12 +11,12 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
@@ -75,17 +75,17 @@ SPDX-License-Identifier: AGPL-3.0-only import { watch, ref, computed } from 'vue'; import { toUnicode } from 'punycode.js'; import tinycolor from 'tinycolor2'; -import { genId } from '@/utility/id.js'; import JSON5 from 'json5'; import lightTheme from '@@/themes/_light.json5'; import darkTheme from '@@/themes/_dark.json5'; import { host } from '@@/js/config.js'; import type { Theme } from '@/theme.js'; +import { genId } from '@/utility/id.js'; import MkButton from '@/components/MkButton.vue'; import MkCodeEditor from '@/components/MkCodeEditor.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkFolder from '@/components/MkFolder.vue'; -import { $i } from '@/i.js'; +import { ensureSignin } from '@/i.js'; import { addTheme, applyTheme } from '@/theme.js'; import * as os from '@/os.js'; import { store } from '@/store.js'; @@ -94,6 +94,8 @@ import { useLeaveGuard } from '@/composables/use-leave-guard.js'; import { definePage } from '@/page.js'; import { prefer } from '@/preferences.js'; +const $i = ensureSignin(); + const bgColors = [ { color: '#f5f5f5', kind: 'light', forPreview: '#f5f5f5' }, { color: '#f0eee9', kind: 'light', forPreview: '#f3e2b9' }, @@ -123,12 +125,15 @@ const fgColors = [ { color: 'pink', forLight: '#84667d', forDark: '#e4d1e0', forPreview: '#b12390' }, ]; -const theme = ref>({ +const theme = ref({ + id: genId(), + name: 'untitled', + author: `@${$i.username}@${toUnicode(host)}`, base: 'light', props: lightTheme.props, }); const description = ref(null); -const themeCode = ref(null); +const themeCode = ref(''); const changed = ref(false); useLeaveGuard(changed); @@ -194,7 +199,6 @@ async function saveAs() { theme.value.id = genId(); theme.value.name = name; - theme.value.author = `@${$i.username}@${toUnicode(host)}`; if (description.value) theme.value.desc = description.value; await addTheme(theme.value); applyTheme(theme.value); diff --git a/packages/frontend/src/pages/user/activity.following.vue b/packages/frontend/src/pages/user/activity.following.vue index f2a5ad8e75..2cd825f3dc 100644 --- a/packages/frontend/src/pages/user/activity.following.vue +++ b/packages/frontend/src/pages/user/activity.following.vue @@ -36,7 +36,7 @@ const props = defineProps<{ const chartEl = useTemplateRef('chartEl'); const legendEl = useTemplateRef('legendEl'); const now = new Date(); -let chartInstance: Chart = null; +let chartInstance: Chart | null = null; const chartLimit = 30; const fetching = ref(true); diff --git a/packages/frontend/src/pages/user/follow-list.vue b/packages/frontend/src/pages/user/follow-list.vue index 6bb1360d42..c383b9b7bd 100644 --- a/packages/frontend/src/pages/user/follow-list.vue +++ b/packages/frontend/src/pages/user/follow-list.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index ed3ae6a2aa..e10c44960a 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -25,8 +25,8 @@ SPDX-License-Identifier: AGPL-3.0-only
- - + + @@ -43,8 +43,8 @@ SPDX-License-Identifier: AGPL-3.0-only
- - + +
@@ -228,7 +228,7 @@ const bannerEl = ref(null); const memoTextareaEl = ref(null); const memoDraft = ref(props.user.memo); const isEditingMemo = ref(false); -const moderationNote = ref(props.user.moderationNote); +const moderationNote = ref(props.user.moderationNote ?? ''); const editModerationNote = ref(false); watch(moderationNote, async () => { diff --git a/packages/frontend/src/pages/user/raw.vue b/packages/frontend/src/pages/user/raw.vue index f0e675b913..145ef5dd92 100644 --- a/packages/frontend/src/pages/user/raw.vue +++ b/packages/frontend/src/pages/user/raw.vue @@ -48,7 +48,7 @@ import FormSection from '@/components/form/section.vue'; import MkObjectView from '@/components/MkObjectView.vue'; const props = defineProps<{ - user: Misskey.entities.User; + user: Misskey.entities.UserDetailed & { isModerator?: boolean; }; }>(); const moderator = computed(() => props.user.isModerator ?? false); diff --git a/packages/frontend/src/utility/admin-lookup.ts b/packages/frontend/src/utility/admin-lookup.ts index 7405e229fe..eccc88d8a9 100644 --- a/packages/frontend/src/utility/admin-lookup.ts +++ b/packages/frontend/src/utility/admin-lookup.ts @@ -12,7 +12,7 @@ export async function lookupUser() { const { canceled, result } = await os.inputText({ title: i18n.ts.usernameOrUserId, }); - if (canceled) return; + if (canceled || result == null) return; const show = (user) => { os.pageWindow(`/admin/user/${user.id}`); @@ -46,7 +46,7 @@ export async function lookupUserByEmail() { title: i18n.ts.emailAddress, type: 'email', }); - if (canceled) return; + if (canceled || result == null) return; try { const user = await os.apiWithDialog('admin/accounts/find-by-email', { email: result }); diff --git a/packages/frontend/src/utility/get-note-menu.ts b/packages/frontend/src/utility/get-note-menu.ts index 11c87dc653..f7b56040cc 100644 --- a/packages/frontend/src/utility/get-note-menu.ts +++ b/packages/frontend/src/utility/get-note-menu.ts @@ -179,7 +179,7 @@ export function getNoteMenu(props: { translating: Ref; currentClip?: Misskey.entities.Clip; }) { - const appearNote = getAppearNote(props.note); + const appearNote = getAppearNote(props.note) ?? props.note; const link = appearNote.url ?? appearNote.uri; const cleanups = [] as (() => void)[]; @@ -554,7 +554,7 @@ export function getRenoteMenu(props: { renoteButton: ShallowRef; mock?: boolean; }) { - const appearNote = getAppearNote(props.note); + const appearNote = getAppearNote(props.note) ?? props.note; const channelRenoteItems: MenuItem[] = []; const normalRenoteItems: MenuItem[] = []; From 120af977a9cb0af4744e590b36e829bfb140ae4a Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 26 Aug 2025 08:57:36 +0900 Subject: [PATCH 13/61] refactoe --- packages/frontend/src/pages/lookup.vue | 17 +++++++---------- packages/frontend/src/pages/my-lists/index.vue | 6 +++--- packages/frontend/src/pages/my-lists/list.vue | 2 +- packages/frontend/src/pages/notifications.vue | 4 ++-- packages/frontend/src/pages/settings/2fa.vue | 6 +++--- .../frontend/src/pages/settings/accounts.vue | 4 ++-- packages/frontend/src/pages/settings/apps.vue | 4 ++-- packages/frontend/src/pages/settings/email.vue | 2 +- 8 files changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/frontend/src/pages/lookup.vue b/packages/frontend/src/pages/lookup.vue index d5ee0cdf97..8a1e952d85 100644 --- a/packages/frontend/src/pages/lookup.vue +++ b/packages/frontend/src/pages/lookup.vue @@ -29,7 +29,7 @@ import MkButton from '@/components/MkButton.vue'; const state = ref<'fetching' | 'done'>('fetching'); -function fetch() { +function _fetch_() { const params = new URL(window.location.href).searchParams; // acctのほうはdeprecated @@ -44,20 +44,18 @@ function fetch() { if (uri.startsWith('https://')) { promise = misskeyApi('ap/show', { uri, - }); - - promise.then(res => { + }).then(res => { if (res.type === 'User') { mainRouter.replace('/@:acct/:page?', { params: { acct: res.host != null ? `${res.object.username}@${res.object.host}` : res.object.username, - } + }, }); } else if (res.type === 'Note') { mainRouter.replace('/notes/:noteId/:initialTab?', { params: { noteId: res.object.id, - } + }, }); } else { os.alert({ @@ -70,12 +68,11 @@ function fetch() { if (uri.startsWith('acct:')) { uri = uri.slice(5); } - promise = misskeyApi('users/show', Misskey.acct.parse(uri)); - promise.then(user => { + promise = misskeyApi('users/show', Misskey.acct.parse(uri)).then(user => { mainRouter.replace('/@:acct/:page?', { params: { acct: user.host != null ? `${user.username}@${user.host}` : user.username, - } + }, }); }); } @@ -96,7 +93,7 @@ function goToMisskey(): void { window.location.href = '/'; } -fetch(); +_fetch_(); const headerActions = computed(() => []); diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue index fb31cd542c..0933618f54 100644 --- a/packages/frontend/src/pages/my-lists/index.vue +++ b/packages/frontend/src/pages/my-lists/index.vue @@ -17,8 +17,8 @@ SPDX-License-Identifier: AGPL-3.0-only
-
{{ list.name }} ({{ i18n.tsx.nUsers({ n: `${list.userIds.length}/${$i.policies['userEachUserListsLimit']}` }) }})
- +
{{ list.name }} ({{ i18n.tsx.nUsers({ n: `${list.userIds!.length}/${$i.policies['userEachUserListsLimit']}` }) }})
+
@@ -50,7 +50,7 @@ async function create() { const { canceled, result: name } = await os.inputText({ title: i18n.ts.enterListName, }); - if (canceled) return; + if (canceled || name == null) return; await os.apiWithDialog('users/lists/create', { name: name }); userListsCache.delete(); fetch(); diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue index 6b5a797023..eb8e26be3b 100644 --- a/packages/frontend/src/pages/my-lists/list.vue +++ b/packages/frontend/src/pages/my-lists/list.vue @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only - +
{{ i18n.ts.addUser }} diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index a8c1fb654c..71c957460c 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -31,7 +31,7 @@ import { Paginator } from '@/utility/paginator.js'; const tab = ref('all'); const includeTypes = ref(null); -const excludeTypes = computed(() => includeTypes.value ? notificationTypes.filter(t => !includeTypes.value.includes(t)) : null); +const excludeTypes = computed(() => includeTypes.value ? notificationTypes.filter(t => !includeTypes.value!.includes(t)) : null); const mentionsPaginator = markRaw(new Paginator('notes/mentions', { limit: 10, @@ -71,7 +71,7 @@ const headerActions = computed(() => [tab.value === 'all' ? { text: i18n.ts.markAllAsRead, icon: 'ti ti-check', handler: () => { - os.apiWithDialog('notifications/mark-all-as-read'); + os.apiWithDialog('notifications/mark-all-as-read', {}); }, } : undefined].filter(x => x !== undefined)); diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue index 1f98fab618..ca404b43c4 100644 --- a/packages/frontend/src/pages/settings/2fa.vue +++ b/packages/frontend/src/pages/settings/2fa.vue @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- - + @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only - + diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue index fb8f51041e..469a3c2f1c 100644 --- a/packages/frontend/src/pages/settings/email.vue +++ b/packages/frontend/src/pages/settings/email.vue @@ -74,7 +74,7 @@ import { instance } from '@/instance.js'; const $i = ensureSignin(); -const emailAddress = ref($i.email); +const emailAddress = ref($i.email ?? ''); const onChangeReceiveAnnouncementEmail = (v) => { misskeyApi('i/update', { From 9e5c8d94bff0352bca3b15fd75a7c6ccaa1df2ff Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 26 Aug 2025 09:08:00 +0900 Subject: [PATCH 14/61] refactor --- packages/frontend/src/components/MkAchievements.vue | 6 +++--- packages/frontend/src/components/MkAnimBg.vue | 2 ++ packages/frontend/src/pages/admin-file.vue | 4 ++-- packages/frontend/src/pages/announcement.vue | 6 +++--- packages/frontend/src/pages/drive.file.info.vue | 12 ++++++------ packages/frontend/src/pages/install-extensions.vue | 4 ++-- packages/frontend/src/pages/instance-info.vue | 4 ++-- packages/frontend/src/pages/my-antennas/index.vue | 6 +++--- packages/frontend/src/pages/my-lists/index.vue | 10 +++++----- packages/frontend/src/pages/settings/index.vue | 6 +++++- packages/frontend/src/pages/settings/profile.vue | 2 +- .../frontend/src/pages/settings/theme.install.vue | 6 +++--- .../frontend/src/pages/settings/theme.manage.vue | 2 +- packages/frontend/src/theme.ts | 1 + 14 files changed, 39 insertions(+), 32 deletions(-) diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue index 70766634ce..3b7b59b4d3 100644 --- a/packages/frontend/src/components/MkAchievements.vue +++ b/packages/frontend/src/components/MkAchievements.vue @@ -71,7 +71,7 @@ const props = withDefaults(defineProps<{ const achievements = ref(null); const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x))); -function fetch() { +function _fetch_() { misskeyApi('users/achievements', { userId: props.user.id }).then(res => { achievements.value = []; for (const t of ACHIEVEMENT_TYPES) { @@ -84,11 +84,11 @@ function fetch() { function clickHere() { claimAchievement('clickedClickHere'); - fetch(); + _fetch_(); } onMounted(() => { - fetch(); + _fetch_(); }); diff --git a/packages/frontend/src/components/MkAnimBg.vue b/packages/frontend/src/components/MkAnimBg.vue index 82606c9aa4..19a21f6e24 100644 --- a/packages/frontend/src/components/MkAnimBg.vue +++ b/packages/frontend/src/components/MkAnimBg.vue @@ -265,6 +265,8 @@ onUnmounted(() => { if (handle) { window.cancelAnimationFrame(handle); } + + // TODO: WebGLリソースの解放 }); diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue index 7a49ba542f..90b3ca81cf 100644 --- a/packages/frontend/src/pages/admin-file.vue +++ b/packages/frontend/src/pages/admin-file.vue @@ -111,13 +111,13 @@ const props = defineProps<{ fileId: string, }>(); -async function fetch() { +async function _fetch_() { file.value = await misskeyApi('drive/files/show', { fileId: props.fileId }); info.value = await misskeyApi('admin/drive/show-file', { fileId: props.fileId }); isSensitive.value = file.value.isSensitive; } -fetch(); +_fetch_(); async function del() { const { canceled } = await os.confirm({ diff --git a/packages/frontend/src/pages/announcement.vue b/packages/frontend/src/pages/announcement.vue index f9b870eda1..0bcfd28f67 100644 --- a/packages/frontend/src/pages/announcement.vue +++ b/packages/frontend/src/pages/announcement.vue @@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.gotIt }}
- +
@@ -66,7 +66,7 @@ const announcement = ref(null); const error = ref(null); const path = computed(() => props.announcementId); -function fetch() { +function _fetch_() { announcement.value = null; misskeyApi('announcements/show', { announcementId: props.announcementId, @@ -96,7 +96,7 @@ async function read(target: Misskey.entities.Announcement): Promise { } } -watch(() => path.value, fetch, { immediate: true }); +watch(() => path.value, _fetch_, { immediate: true }); const headerActions = computed(() => []); diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue index 1def215afc..79c249413a 100644 --- a/packages/frontend/src/pages/drive.file.info.vue +++ b/packages/frontend/src/pages/drive.file.info.vue @@ -105,7 +105,7 @@ const folderHierarchy = computed(() => { }); const isImage = computed(() => file.value?.type.startsWith('image/')); -async function fetch() { +async function _fetch_() { fetching.value = true; file.value = await misskeyApi('drive/files/show', { @@ -134,7 +134,7 @@ function move() { fileId: file.value.id, folderId: folder[0] ? folder[0].id : null, }).then(async () => { - await fetch(); + await _fetch_(); }); }); } @@ -146,7 +146,7 @@ function toggleSensitive() { fileId: file.value.id, isSensitive: !file.value.isSensitive, }).then(async () => { - await fetch(); + await _fetch_(); }).catch(err => { os.alert({ type: 'error', @@ -169,7 +169,7 @@ function rename() { fileId: file.value.id, name: name, }).then(async () => { - await fetch(); + await _fetch_(); }); }); } @@ -186,7 +186,7 @@ async function describe() { fileId: file.value.id, comment: caption.length === 0 ? null : caption, }).then(async () => { - await fetch(); + await _fetch_(); }); }, closed: () => dispose(), @@ -212,7 +212,7 @@ async function deleteFile() { } onMounted(async () => { - await fetch(); + await _fetch_(); }); diff --git a/packages/frontend/src/pages/install-extensions.vue b/packages/frontend/src/pages/install-extensions.vue index 1b3c6616cc..4b87e0da6b 100644 --- a/packages/frontend/src/pages/install-extensions.vue +++ b/packages/frontend/src/pages/install-extensions.vue @@ -80,7 +80,7 @@ function close_(): void { } } -async function fetch() { +async function _fetch_() { if (!url.value || !hash.value) { errorKV.value = { title: i18n.ts._externalResourceInstaller._errors._invalidParams.title, @@ -229,7 +229,7 @@ async function install() { const urlParams = new URLSearchParams(window.location.search); url.value = urlParams.get('url'); hash.value = urlParams.get('hash'); -fetch(); +_fetch_(); definePage(() => ({ title: i18n.ts._externalResourceInstaller.title, diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 4be5fa447d..473207fe6e 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -198,7 +198,7 @@ if (iAmModerator) { }); } -async function fetch(): Promise { +async function _fetch_(): Promise { if (iAmAdmin) { meta.value = await misskeyApi('admin/meta'); } @@ -276,7 +276,7 @@ function refreshMetadata(): void { }); } -fetch(); +_fetch_(); const headerActions = computed(() => [{ text: `https://${props.host}`, diff --git a/packages/frontend/src/pages/my-antennas/index.vue b/packages/frontend/src/pages/my-antennas/index.vue index 95a3108e3a..d7625a8a1c 100644 --- a/packages/frontend/src/pages/my-antennas/index.vue +++ b/packages/frontend/src/pages/my-antennas/index.vue @@ -30,11 +30,11 @@ import { antennasCache } from '@/cache.js'; const antennas = computed(() => antennasCache.value.value ?? []); -function fetch() { +function _fetch_() { antennasCache.fetch(); } -fetch(); +_fetch_(); const headerActions = computed(() => [{ asFullButton: true, @@ -42,7 +42,7 @@ const headerActions = computed(() => [{ text: i18n.ts.reload, handler: () => { antennasCache.delete(); - fetch(); + _fetch_(); }, }]); diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue index 0933618f54..43d5432f66 100644 --- a/packages/frontend/src/pages/my-lists/index.vue +++ b/packages/frontend/src/pages/my-lists/index.vue @@ -40,11 +40,11 @@ const $i = ensureSignin(); const items = computed(() => userListsCache.value.value ?? []); -function fetch() { +function _fetch_() { userListsCache.fetch(); } -fetch(); +_fetch_(); async function create() { const { canceled, result: name } = await os.inputText({ @@ -53,7 +53,7 @@ async function create() { if (canceled || name == null) return; await os.apiWithDialog('users/lists/create', { name: name }); userListsCache.delete(); - fetch(); + _fetch_(); } const headerActions = computed(() => [{ @@ -62,7 +62,7 @@ const headerActions = computed(() => [{ text: i18n.ts.reload, handler: () => { userListsCache.delete(); - fetch(); + _fetch_(); }, }]); @@ -74,7 +74,7 @@ definePage(() => ({ })); onActivated(() => { - fetch(); + _fetch_(); }); diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index eda9dfde7b..250c1735be 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -188,6 +188,8 @@ const menuDef = computed(() => [{ }]); onMounted(() => { + if (el.value == null) return; // TSを黙らすため + ro.observe(el.value); narrow.value = el.value.offsetWidth < NARROW_THRESHOLD; @@ -198,6 +200,8 @@ onMounted(() => { }); onActivated(() => { + if (el.value == null) return; // TSを黙らすため + narrow.value = el.value.offsetWidth < NARROW_THRESHOLD; if (!narrow.value && currentPage.value?.route.name == null) { @@ -215,7 +219,7 @@ watch(router.currentRef, (to) => { } }); -const emailNotConfigured = computed(() => instance.enableEmail && ($i.email == null || !$i.emailVerified)); +const emailNotConfigured = computed(() => $i && instance.enableEmail && ($i.email == null || !$i.emailVerified)); provideMetadataReceiver((metadataGetter) => { const info = metadataGetter(); diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index e2679623ef..4816a6e33b 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
{{ i18n.ts._profile.changeBanner }} diff --git a/packages/frontend/src/pages/settings/theme.install.vue b/packages/frontend/src/pages/settings/theme.install.vue index ac95279402..f79357c361 100644 --- a/packages/frontend/src/pages/settings/theme.install.vue +++ b/packages/frontend/src/pages/settings/theme.install.vue @@ -10,8 +10,8 @@ SPDX-License-Identifier: AGPL-3.0-only
- {{ i18n.ts.preview }} - {{ i18n.ts.install }} + {{ i18n.ts.preview }} + {{ i18n.ts.install }}
@@ -39,7 +39,7 @@ async function install(code: string): Promise { }); installThemeCode.value = null; router.push('/settings/theme'); - } catch (err) { + } catch (err: any) { switch (err.message.toLowerCase()) { case 'this theme is already installed': os.alert({ diff --git a/packages/frontend/src/pages/settings/theme.manage.vue b/packages/frontend/src/pages/settings/theme.manage.vue index fcd0b293e0..e972184278 100644 --- a/packages/frontend/src/pages/settings/theme.manage.vue +++ b/packages/frontend/src/pages/settings/theme.manage.vue @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only - {{ i18n.ts.uninstall }} + {{ i18n.ts.uninstall }}
diff --git a/packages/frontend/src/theme.ts b/packages/frontend/src/theme.ts index 036b86cff8..b715426917 100644 --- a/packages/frontend/src/theme.ts +++ b/packages/frontend/src/theme.ts @@ -23,6 +23,7 @@ export type Theme = { author: string; desc?: string; base?: 'dark' | 'light'; + kind?: 'dark' | 'light'; // legacy props: Record; codeHighlighter?: { base: BundledTheme; From dbb6c71c5c7098c33824b6070b6526416d3bdd69 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 26 Aug 2025 09:39:23 +0900 Subject: [PATCH 15/61] refactor --- .../frontend/src/pages/channel-editor.vue | 37 +++++++++++-------- packages/frontend/src/pages/gallery/edit.vue | 6 +-- packages/frontend/src/pages/registry.vue | 2 +- .../frontend/src/pages/settings/privacy.vue | 28 +++++++++----- packages/frontend/src/pages/tag.vue | 2 +- packages/frontend/src/utility/chart-vline.ts | 5 ++- packages/frontend/src/utility/popout.ts | 4 +- .../frontend/src/utility/sticky-sidebar.ts | 2 + 8 files changed, 51 insertions(+), 35 deletions(-) diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue index 80dfb8e84e..ce26a26109 100644 --- a/packages/frontend/src/pages/channel-editor.vue +++ b/packages/frontend/src/pages/channel-editor.vue @@ -92,7 +92,7 @@ const props = defineProps<{ }>(); const channel = ref(null); -const name = ref(null); +const name = ref(''); const description = ref(null); const bannerUrl = ref(null); const bannerId = ref(null); @@ -114,20 +114,22 @@ watch(() => bannerId.value, async () => { async function fetchChannel() { if (props.channelId == null) return; - channel.value = await misskeyApi('channels/show', { + const result = await misskeyApi('channels/show', { channelId: props.channelId, }); - name.value = channel.value.name; - description.value = channel.value.description; - bannerId.value = channel.value.bannerId; - bannerUrl.value = channel.value.bannerUrl; - isSensitive.value = channel.value.isSensitive; - pinnedNotes.value = channel.value.pinnedNoteIds.map(id => ({ + name.value = result.name; + description.value = result.description; + bannerId.value = result.bannerId; + bannerUrl.value = result.bannerUrl; + isSensitive.value = result.isSensitive; + pinnedNotes.value = result.pinnedNoteIds.map(id => ({ id, })); - color.value = channel.value.color; - allowRenoteToExternal.value = channel.value.allowRenoteToExternal; + color.value = result.color; + allowRenoteToExternal.value = result.allowRenoteToExternal; + + channel.value = result; } fetchChannel(); @@ -154,15 +156,17 @@ function save() { name: name.value, description: description.value, bannerId: bannerId.value, - pinnedNoteIds: pinnedNotes.value.map(x => x.id), color: color.value, isSensitive: isSensitive.value, allowRenoteToExternal: allowRenoteToExternal.value, - }; + } satisfies Misskey.entities.ChannelsCreateRequest; - if (props.channelId) { - params.channelId = props.channelId; - os.apiWithDialog('channels/update', params); + if (props.channelId != null) { + os.apiWithDialog('channels/update', { + ...params, + channelId: props.channelId, + pinnedNoteIds: pinnedNotes.value.map(x => x.id), + }); } else { os.apiWithDialog('channels/create', params).then(created => { router.push('/channels/:channelId', { @@ -175,12 +179,13 @@ function save() { } async function archive() { + if (props.channelId == null) return; + const { canceled } = await os.confirm({ type: 'warning', title: i18n.tsx.channelArchiveConfirmTitle({ name: name.value }), text: i18n.ts.channelArchiveConfirmDescription, }); - if (canceled) return; misskeyApi('channels/update', { diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue index cf0d700962..3fd462e0b9 100644 --- a/packages/frontend/src/pages/gallery/edit.vue +++ b/packages/frontend/src/pages/gallery/edit.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
{{ file.name }}
@@ -88,7 +88,7 @@ async function save() { router.push('/gallery/:postId', { params: { postId: props.postId, - } + }, }); } else { const created = await os.apiWithDialog('gallery/posts/create', { @@ -100,7 +100,7 @@ async function save() { router.push('/gallery/:postId', { params: { postId: created.id, - } + }, }); } } diff --git a/packages/frontend/src/pages/registry.vue b/packages/frontend/src/pages/registry.vue index 3762dadd12..389438242e 100644 --- a/packages/frontend/src/pages/registry.vue +++ b/packages/frontend/src/pages/registry.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts._registry.createKey }}
- +
{{ scope.length === 0 ? '(root)' : scope.join('/') }} diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index ab012841dc..54a6c0af82 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -160,10 +160,18 @@ SPDX-License-Identifier: AGPL-3.0-only
- - - - + @@ -262,7 +270,7 @@ const makeNotesFollowersOnlyBefore_presets = [ const makeNotesFollowersOnlyBefore_isCustomMode = ref( makeNotesFollowersOnlyBefore.value != null && makeNotesFollowersOnlyBefore.value < 0 && - !makeNotesFollowersOnlyBefore_presets.some((preset) => preset.value === makeNotesFollowersOnlyBefore.value) + !makeNotesFollowersOnlyBefore_presets.some((preset) => preset.value === makeNotesFollowersOnlyBefore.value), ); const makeNotesFollowersOnlyBefore_selection = computed({ @@ -270,14 +278,14 @@ const makeNotesFollowersOnlyBefore_selection = computed({ set(value) { makeNotesFollowersOnlyBefore_isCustomMode.value = value === 'custom'; if (value !== 'custom') makeNotesFollowersOnlyBefore.value = value; - } + }, }); const makeNotesFollowersOnlyBefore_customMonths = computed({ get: () => makeNotesFollowersOnlyBefore.value ? Math.abs(makeNotesFollowersOnlyBefore.value) / (30 * 24 * 60 * 60) : null, set(value) { if (value != null && value > 0) makeNotesFollowersOnlyBefore.value = -Math.abs(Math.floor(Number(value))) * 30 * 24 * 60 * 60; - } + }, }); const makeNotesHiddenBefore_type = computed(() => { @@ -303,7 +311,7 @@ const makeNotesHiddenBefore_presets = [ const makeNotesHiddenBefore_isCustomMode = ref( makeNotesHiddenBefore.value != null && makeNotesHiddenBefore.value < 0 && - !makeNotesHiddenBefore_presets.some((preset) => preset.value === makeNotesHiddenBefore.value) + !makeNotesHiddenBefore_presets.some((preset) => preset.value === makeNotesHiddenBefore.value), ); const makeNotesHiddenBefore_selection = computed({ @@ -311,14 +319,14 @@ const makeNotesHiddenBefore_selection = computed({ set(value) { makeNotesHiddenBefore_isCustomMode.value = value === 'custom'; if (value !== 'custom') makeNotesHiddenBefore.value = value; - } + }, }); const makeNotesHiddenBefore_customMonths = computed({ get: () => makeNotesHiddenBefore.value ? Math.abs(makeNotesHiddenBefore.value) / (30 * 24 * 60 * 60) : null, set(value) { if (value != null && value > 0) makeNotesHiddenBefore.value = -Math.abs(Math.floor(Number(value))) * 30 * 24 * 60 * 60; - } + }, }); watch([makeNotesFollowersOnlyBefore, makeNotesHiddenBefore], () => { diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue index b5a4503b68..047e68f583 100644 --- a/packages/frontend/src/pages/tag.vue +++ b/packages/frontend/src/pages/tag.vue @@ -52,7 +52,7 @@ async function post() { const headerActions = computed(() => [{ icon: 'ti ti-dots', - label: i18n.ts.more, + text: i18n.ts.more, handler: (ev: MouseEvent) => { os.popupMenu([{ text: i18n.ts.embed, diff --git a/packages/frontend/src/utility/chart-vline.ts b/packages/frontend/src/utility/chart-vline.ts index 465ca591c6..2fe4bdb83b 100644 --- a/packages/frontend/src/utility/chart-vline.ts +++ b/packages/frontend/src/utility/chart-vline.ts @@ -8,9 +8,10 @@ import type { Plugin } from 'chart.js'; export const chartVLine = (vLineColor: string) => ({ id: 'vLine', beforeDraw(chart, args, options) { - if (chart.tooltip?._active?.length) { + const tooltip = chart.tooltip as any; + if (tooltip?._active?.length) { const ctx = chart.ctx; - const xs = chart.tooltip._active.map(a => a.element.x); + const xs = tooltip._active.map(a => a.element.x); const x = xs.reduce((a, b) => a + b, 0) / xs.length; const topY = chart.scales.y.top; const bottomY = chart.scales.y.bottom; diff --git a/packages/frontend/src/utility/popout.ts b/packages/frontend/src/utility/popout.ts index 5b141222e8..7e0222c459 100644 --- a/packages/frontend/src/utility/popout.ts +++ b/packages/frontend/src/utility/popout.ts @@ -20,8 +20,8 @@ export function popout(path: string, w?: HTMLElement) { } else { const width = 400; const height = 500; - const x = window.top.outerHeight / 2 + window.top.screenY - (height / 2); - const y = window.top.outerWidth / 2 + window.top.screenX - (width / 2); + const x = window.top == null ? 0 : window.top.outerHeight / 2 + window.top.screenY - (height / 2); + const y = window.top == null ? 0 : window.top.outerWidth / 2 + window.top.screenX - (width / 2); window.open(url, url, `width=${width}, height=${height}, top=${x}, left=${y}`); } diff --git a/packages/frontend/src/utility/sticky-sidebar.ts b/packages/frontend/src/utility/sticky-sidebar.ts index 867c9b8324..435555896f 100644 --- a/packages/frontend/src/utility/sticky-sidebar.ts +++ b/packages/frontend/src/utility/sticky-sidebar.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +/* export class StickySidebar { private lastScrollTop = 0; private container: HTMLElement; @@ -53,3 +54,4 @@ export class StickySidebar { this.lastScrollTop = scrollTop <= 0 ? 0 : scrollTop; } } +*/ From eb9915baf880146007bf035a8fe770acee016358 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 26 Aug 2025 10:56:09 +0900 Subject: [PATCH 16/61] refactor and fix --- CHANGELOG.md | 1 + packages/frontend/src/os.ts | 21 +----------------- .../src/pages/settings/avatar-decoration.vue | 22 ++++++++++++++----- .../pages/settings/drive.WatermarkItem.vue | 2 +- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd9b4ff183..770b37a206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ - Fix: メンションとしての条件を満たしていても、特定の条件(`-`が含まれる場合など)で正しくサジェストされない問題を一部修正 - Fix: ユーザーの前後ノートを閲覧する機能が動作しない問題を修正 - Fix: 照会ダイアログでap/showでローカルユーザーを解決した際@username@nullに飛ばされる問題を修正 +- Fix: アイコンのデコレーションを付ける際にデコレーションが表示されなくなる問題を修正 ### Server - Feat: サーバー管理コマンド diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index bf0e5e1b37..6ba3af72b9 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -9,7 +9,7 @@ import { markRaw, ref, defineAsyncComponent, nextTick } from 'vue'; import { EventEmitter } from 'eventemitter3'; import * as Misskey from 'misskey-js'; import type { Component, Ref } from 'vue'; -import type { ComponentProps as CP } from 'vue-component-type-helpers'; +import type { ComponentEmit, ComponentProps as CP } from 'vue-component-type-helpers'; import type { Form, GetFormResultType } from '@/utility/form.js'; import type { MenuItem } from '@/types/menu.js'; import type { PostFormProps } from '@/types/post-form.js'; @@ -157,28 +157,9 @@ export function claimZIndex(priority: keyof typeof zIndexes = 'low'): number { return zIndexes[priority]; } -// InstanceType['$emit'] だとインターセクション型が返ってきて -// 使い物にならないので、代わりに ['$props'] から色々省くことで emit の型を生成する -// FIXME: 何故か *.ts ファイルからだと型がうまく取れない?ことがあるのをなんとかしたい -type ComponentEmit = T extends new () => { $props: infer Props } - ? [keyof Pick>] extends [never] - ? Record // *.ts ファイルから型がうまく取れないとき用(これがないと {} になって型エラーがうるさい) - : EmitsExtractor - : T extends (...args: any) => any - ? ReturnType extends { [x: string]: any; __ctx?: { [x: string]: any; props: infer Props } } - ? [keyof Pick>] extends [never] - ? Record - : EmitsExtractor - : never - : never; - // props に ref を許可するようにする type ComponentProps = { [K in keyof CP]: CP[K] | Ref[K]> }; -type EmitsExtractor = { - [K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize : K extends string ? never : K]: T[K]; -}; - export function popup( component: T, props: ComponentProps, diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue index c58cd57c65..4b8ac9a26c 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.vue @@ -17,13 +17,13 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -50,6 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref, defineAsyncComponent, computed } from 'vue'; import * as Misskey from 'misskey-js'; import XDecoration from './avatar-decoration.decoration.vue'; +import XDialog from './avatar-decoration.dialog.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; @@ -68,14 +69,24 @@ misskeyApi('get-avatar-decorations').then(_avatarDecorations => { loading.value = false; }); -async function openDecoration(avatarDecoration, index?: number) { - const { dispose } = await os.popupAsyncWithDialog(import('./avatar-decoration.dialog.vue').then(x => x.default), { +function openAttachedDecoration(index: number) { + openDecoration(avatarDecorations.value.find(d => d.id === $i.avatarDecorations[index].id) ?? { id: '', url: '', name: '?', roleIdsThatCanBeUsedThisDecoration: [] }, index); +} + +async function openDecoration(avatarDecoration: { + id: string; + url: string; + name: string; + roleIdsThatCanBeUsedThisDecoration: string[]; +}, index?: number) { + const { dispose } = os.popup(XDialog, { decoration: avatarDecoration, - usingIndex: index, + usingIndex: index ?? null, }, { 'attach': async (payload) => { const decoration = { id: avatarDecoration.id, + url: avatarDecoration.url, angle: payload.angle, flipH: payload.flipH, offsetX: payload.offsetX, @@ -90,6 +101,7 @@ async function openDecoration(avatarDecoration, index?: number) { 'update': async (payload) => { const decoration = { id: avatarDecoration.id, + url: avatarDecoration.url, angle: payload.angle, flipH: payload.flipH, offsetX: payload.offsetX, diff --git a/packages/frontend/src/pages/settings/drive.WatermarkItem.vue b/packages/frontend/src/pages/settings/drive.WatermarkItem.vue index b466f35fc5..bb91d5e212 100644 --- a/packages/frontend/src/pages/settings/drive.WatermarkItem.vue +++ b/packages/frontend/src/pages/settings/drive.WatermarkItem.vue @@ -43,7 +43,7 @@ async function edit() { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkWatermarkEditorDialog.vue')), { preset: deepClone(props.preset), }, { - ok: (preset: WatermarkPreset) => { + ok: (preset) => { emit('updatePreset', preset); }, closed: () => dispose(), From d6a1046361d3d38726f2a86588960c3614f72a9f Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 26 Aug 2025 13:34:41 +0900 Subject: [PATCH 17/61] refactor --- .../entities/NoteReactionEntityService.ts | 50 +++++++++++++---- packages/backend/src/misc/json-schema.ts | 3 +- .../src/models/json-schema/note-reaction.ts | 31 ++++++++++- .../api/endpoints/admin/announcements/list.ts | 28 ++++++++++ .../server/api/endpoints/users/lists/show.ts | 10 ++++ .../server/api/endpoints/users/reactions.ts | 4 +- packages/frontend/src/boot/main-boot.ts | 5 -- .../src/components/MkAchievements.vue | 2 +- packages/frontend/src/components/MkChart.vue | 55 +++++++++++++++---- .../src/components/MkCropperDialog.vue | 44 +++++++++------ .../frontend/src/components/MkEmojiPicker.vue | 2 +- .../src/components/MkEmojiPickerDialog.vue | 4 +- packages/frontend/src/components/MkModal.vue | 4 +- .../components/MkReactionsViewer.reaction.vue | 27 ++++++--- .../components/global/MkPageHeader.tabs.vue | 2 +- packages/frontend/src/pages/admin-user.vue | 4 +- .../frontend/src/pages/drive.file.info.vue | 24 +++++--- packages/frontend/src/pages/explore.vue | 6 -- packages/frontend/src/pages/list.vue | 1 + packages/frontend/src/pages/note.vue | 2 +- .../frontend/src/pages/registry.value.vue | 4 +- .../src/pages/settings/avatar-decoration.vue | 4 +- .../src/pages/settings/notifications.vue | 11 ++-- .../frontend/src/pages/settings/security.vue | 4 +- .../frontend/src/pages/settings/theme.vue | 3 +- .../src/pages/settings/webhook.edit.vue | 2 - packages/frontend/src/pages/timeline.vue | 2 +- .../src/pages/user/activity.following.vue | 2 + .../src/pages/user/activity.notes.vue | 4 +- .../frontend/src/pages/user/activity.pv.vue | 4 +- packages/frontend/src/preferences/def.ts | 4 +- packages/frontend/src/preferences/manager.ts | 7 ++- .../src/ui/_common_/announcements.vue | 2 +- packages/frontend/src/ui/_common_/common.ts | 34 ++++++++---- .../src/ui/_common_/statusbar-rss.vue | 6 +- packages/frontend/src/ui/deck.vue | 2 +- packages/frontend/src/utility/admin-lookup.ts | 2 +- packages/frontend/src/utility/chart-legend.ts | 2 +- packages/frontend/src/utility/clicker-game.ts | 16 +----- .../frontend/src/utility/get-note-menu.ts | 2 +- .../src/widgets/WidgetInstanceInfo.vue | 4 +- 41 files changed, 289 insertions(+), 140 deletions(-) diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts index 46ec13704c..54ce4d472a 100644 --- a/packages/backend/src/core/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts @@ -49,15 +49,12 @@ export class NoteReactionEntityService implements OnModuleInit { public async pack( src: MiNoteReaction['id'] | MiNoteReaction, me?: { id: MiUser['id'] } | null | undefined, - options?: { - withNote: boolean; - }, + options?: object, hints?: { packedUser?: Packed<'UserLite'> }, ): Promise> { const opts = Object.assign({ - withNote: false, }, options); const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src }); @@ -67,9 +64,6 @@ export class NoteReactionEntityService implements OnModuleInit { createdAt: this.idService.parse(reaction.id).date.toISOString(), user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me), type: this.reactionService.convertLegacyReaction(reaction.reaction), - ...(opts.withNote ? { - note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me), - } : {}), }; } @@ -77,16 +71,50 @@ export class NoteReactionEntityService implements OnModuleInit { public async packMany( reactions: MiNoteReaction[], me?: { id: MiUser['id'] } | null | undefined, - options?: { - withNote: boolean; - }, + options?: object, ): Promise[]> { const opts = Object.assign({ - withNote: false, }, options); const _users = reactions.map(({ user, userId }) => user ?? userId); const _userMap = await this.userEntityService.packMany(_users, me) .then(users => new Map(users.map(u => [u.id, u]))); return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) }))); } + + @bindThis + public async packWithNote( + src: MiNoteReaction['id'] | MiNoteReaction, + me?: { id: MiUser['id'] } | null | undefined, + options?: object, + hints?: { + packedUser?: Packed<'UserLite'> + }, + ): Promise> { + const opts = Object.assign({ + }, options); + + const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src }); + + return { + id: reaction.id, + createdAt: this.idService.parse(reaction.id).date.toISOString(), + user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me), + type: this.reactionService.convertLegacyReaction(reaction.reaction), + note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me), + }; + } + + @bindThis + public async packManyWithNote( + reactions: MiNoteReaction[], + me?: { id: MiUser['id'] } | null | undefined, + options?: object, + ): Promise[]> { + const opts = Object.assign({ + }, options); + const _users = reactions.map(({ user, userId }) => user ?? userId); + const _userMap = await this.userEntityService.packMany(_users, me) + .then(users => new Map(users.map(u => [u.id, u]))); + return Promise.all(reactions.map(reaction => this.packWithNote(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) }))); + } } diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index ed47edff9b..dca92e1037 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -22,7 +22,7 @@ import { packedFollowingSchema } from '@/models/json-schema/following.js'; import { packedMutingSchema } from '@/models/json-schema/muting.js'; import { packedRenoteMutingSchema } from '@/models/json-schema/renote-muting.js'; import { packedBlockingSchema } from '@/models/json-schema/blocking.js'; -import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js'; +import { packedNoteReactionSchema, packedNoteReactionWithNoteSchema } from '@/models/json-schema/note-reaction.js'; import { packedHashtagSchema } from '@/models/json-schema/hashtag.js'; import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js'; import { packedPageBlockSchema, packedPageSchema } from '@/models/json-schema/page.js'; @@ -92,6 +92,7 @@ export const refs = { Note: packedNoteSchema, NoteDraft: packedNoteDraftSchema, NoteReaction: packedNoteReactionSchema, + NoteReactionWithNote: packedNoteReactionWithNoteSchema, NoteFavorite: packedNoteFavoriteSchema, Notification: packedNotificationSchema, DriveFile: packedDriveFileSchema, diff --git a/packages/backend/src/models/json-schema/note-reaction.ts b/packages/backend/src/models/json-schema/note-reaction.ts index 95658ace1f..04c9f34232 100644 --- a/packages/backend/src/models/json-schema/note-reaction.ts +++ b/packages/backend/src/models/json-schema/note-reaction.ts @@ -10,7 +10,6 @@ export const packedNoteReactionSchema = { type: 'string', optional: false, nullable: false, format: 'id', - example: 'xxxxxxxxxx', }, createdAt: { type: 'string', @@ -28,3 +27,33 @@ export const packedNoteReactionSchema = { }, }, } as const; + +export const packedNoteReactionWithNoteSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + user: { + type: 'object', + optional: false, nullable: false, + ref: 'UserLite', + }, + type: { + type: 'string', + optional: false, nullable: false, + }, + note: { + type: 'object', + optional: false, nullable: false, + ref: 'Note', + }, + }, +} as const; diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index 81a788de2b..804bd5d9b9 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -49,6 +49,34 @@ export const meta = { type: 'string', optional: false, nullable: false, }, + icon: { + type: 'string', + optional: false, nullable: true, + }, + display: { + type: 'string', + optional: false, nullable: false, + }, + isActive: { + type: 'boolean', + optional: false, nullable: false, + }, + forExistingUsers: { + type: 'boolean', + optional: false, nullable: false, + }, + silence: { + type: 'boolean', + optional: false, nullable: false, + }, + needConfirmationToRead: { + type: 'boolean', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: true, + }, imageUrl: { type: 'string', optional: false, nullable: true, diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts index 8756801fe4..ed5952d4c5 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -23,6 +23,16 @@ export const meta = { type: 'object', optional: false, nullable: false, ref: 'UserList', + properties: { + likedCount: { + type: 'number', + optional: true, nullable: false, + }, + isLiked: { + type: 'boolean', + optional: true, nullable: false, + }, + }, }, errors: { diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index d6f1ecd8ed..d84a191f7a 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -28,7 +28,7 @@ export const meta = { items: { type: 'object', optional: false, nullable: false, - ref: 'NoteReaction', + ref: 'NoteReactionWithNote', }, }, @@ -120,7 +120,7 @@ export default class extends Endpoint { // eslint- return true; }); - return await this.noteReactionEntityService.packMany(reactions, me, { withNote: true }); + return await this.noteReactionEntityService.packManyWithNote(reactions, me); }); } } diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 6ae8379801..18817d3f79 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -368,11 +368,6 @@ export async function mainBoot() { }); }); - main.on('unreadAntenna', () => { - updateCurrentAccountPartial({ hasUnreadAntenna: true }); - sound.playMisskeySfx('antenna'); - }); - main.on('newChatMessage', () => { updateCurrentAccountPartial({ hasUnreadChatMessages: true }); sound.playMisskeySfx('chatMessage'); diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue index 3b7b59b4d3..bf39c1e983 100644 --- a/packages/frontend/src/components/MkAchievements.vue +++ b/packages/frontend/src/components/MkAchievements.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only [$style.iconFrame_platinum]: ACHIEVEMENT_BADGES[achievement.name].frame === 'platinum', }]" > -
+
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue index 4d67bba70d..c54081ad42 100644 --- a/packages/frontend/src/components/MkChart.vue +++ b/packages/frontend/src/components/MkChart.vue @@ -589,7 +589,10 @@ const fetchDriveFilesChart = async (): Promise => { }; const fetchInstanceRequestsChart = async (): Promise => { - const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); + const host = props.args?.host; + if (host == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span }); return { series: [{ name: 'In', @@ -611,7 +614,10 @@ const fetchInstanceRequestsChart = async (): Promise => { }; const fetchInstanceUsersChart = async (total: boolean): Promise => { - const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); + const host = props.args?.host; + if (host == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span }); return { series: [{ name: 'Users', @@ -626,7 +632,10 @@ const fetchInstanceUsersChart = async (total: boolean): Promise => { - const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); + const host = props.args?.host; + if (host == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span }); return { series: [{ name: 'Notes', @@ -641,7 +650,10 @@ const fetchInstanceNotesChart = async (total: boolean): Promise => { - const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); + const host = props.args?.host; + if (host == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span }); return { series: [{ name: 'Following', @@ -664,7 +676,10 @@ const fetchInstanceFfChart = async (total: boolean): Promise = }; const fetchInstanceDriveUsageChart = async (total: boolean): Promise => { - const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); + const host = props.args?.host; + if (host == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span }); return { bytes: true, series: [{ @@ -680,7 +695,10 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise => { - const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); + const host = props.args?.host; + if (host == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span }); return { series: [{ name: 'Drive files', @@ -695,7 +713,10 @@ const fetchInstanceDriveFilesChart = async (total: boolean): Promise => { - const raw = await misskeyApiGet('charts/user/notes', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); + const userId = props.args?.user?.id; + if (userId == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/user/notes', { userId: userId, limit: props.limit, span: props.span }); return { series: [...(props.args?.withoutAll ? [] : [{ name: 'All', @@ -727,7 +748,10 @@ const fetchPerUserNotesChart = async (): Promise => { }; const fetchPerUserPvChart = async (): Promise => { - const raw = await misskeyApiGet('charts/user/pv', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); + const userId = props.args?.user?.id; + if (userId == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/user/pv', { userId: userId, limit: props.limit, span: props.span }); return { series: [{ name: 'Unique PV (user)', @@ -754,7 +778,10 @@ const fetchPerUserPvChart = async (): Promise => { }; const fetchPerUserFollowingChart = async (): Promise => { - const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); + const userId = props.args?.user?.id; + if (userId == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/user/following', { userId: userId, limit: props.limit, span: props.span }); return { series: [{ name: 'Local', @@ -769,7 +796,10 @@ const fetchPerUserFollowingChart = async (): Promise => { }; const fetchPerUserFollowersChart = async (): Promise => { - const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); + const userId = props.args?.user?.id; + if (userId == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/user/following', { userId: userId, limit: props.limit, span: props.span }); return { series: [{ name: 'Local', @@ -784,7 +814,10 @@ const fetchPerUserFollowersChart = async (): Promise => { }; const fetchPerUserDriveChart = async (): Promise => { - const raw = await misskeyApiGet('charts/user/drive', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); + const userId = props.args?.user?.id; + if (userId == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/user/drive', { userId: userId, limit: props.limit, span: props.span }); return { bytes: true, series: [{ diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue index 7f592fba79..6c07eac47a 100644 --- a/packages/frontend/src/components/MkCropperDialog.vue +++ b/packages/frontend/src/components/MkCropperDialog.vue @@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only