From c371af34e877845e01cd92683aca0ff3334e314a Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 31 Mar 2025 14:53:02 -0400 Subject: [PATCH] copy sharkey settings into new frontend preferences model --- packages/frontend-embed/src/pages/note.vue | 2 +- packages/frontend/src/boot/main-boot.ts | 8 +- .../frontend/src/components/DynamicNote.vue | 2 +- .../src/components/MkDateSeparatedList.vue | 4 +- packages/frontend/src/components/MkGoogle.vue | 4 +- .../frontend/src/components/MkMediaAudio.vue | 2 +- .../frontend/src/components/MkMediaImage.vue | 2 +- .../frontend/src/components/MkMediaVideo.vue | 2 +- packages/frontend/src/components/MkNote.vue | 49 ++--- .../src/components/MkNoteDetailed.vue | 33 ++-- .../frontend/src/components/MkNoteSimple.vue | 4 +- .../frontend/src/components/MkNoteSub.vue | 125 +++++-------- .../frontend/src/components/MkPostForm.vue | 4 +- .../src/components/MkPostFormAttaches.vue | 2 +- .../src/components/MkSubNoteContent.vue | 16 +- .../frontend/src/components/MkUrlPreview.vue | 3 +- .../src/components/MkUrlWarningDialog.vue | 10 +- .../src/components/MkVisibilityPicker.vue | 3 +- .../frontend/src/components/SkFetchNote.vue | 2 +- .../frontend/src/components/SkFlashPlayer.vue | 14 +- .../frontend/src/components/SkModPlayer.vue | 4 +- packages/frontend/src/components/SkNote.vue | 63 ++++--- .../src/components/SkNoteDetailed.vue | 34 ++-- .../frontend/src/components/SkNoteHeader.vue | 4 +- .../frontend/src/components/SkNoteSimple.vue | 2 +- .../frontend/src/components/SkNoteSub.vue | 42 ++--- .../src/components/SkOldNoteWindow.vue | 8 +- .../frontend/src/components/global/MkMfm.ts | 2 +- packages/frontend/src/pages/about-sharkey.vue | 25 ++- packages/frontend/src/pages/admin/users.vue | 2 +- packages/frontend/src/pages/channel.vue | 10 +- packages/frontend/src/pages/favorites.vue | 11 +- packages/frontend/src/pages/note.vue | 3 +- .../src/pages/settings/preferences.vue | 10 +- .../frontend/src/pages/settings/profile.vue | 12 +- .../frontend/src/pages/user-list-timeline.vue | 13 +- packages/frontend/src/pages/user/home.vue | 13 +- .../src/pages/user/index.timeline.vue | 12 +- packages/frontend/src/pref-migrate.ts | 20 +- packages/frontend/src/preferences/def.ts | 67 ++++++- packages/frontend/src/signout.ts | 2 +- packages/frontend/src/store.ts | 176 +++++++++--------- packages/frontend/src/ui/_common_/common.vue | 5 +- packages/frontend/src/use/use-note-capture.ts | 4 +- packages/frontend/src/utility/boost-quote.ts | 12 +- packages/frontend/src/utility/deep-equal.ts | 2 +- .../src/utility/following-feed-utils.ts | 10 +- .../frontend/src/utility/get-note-menu.ts | 4 +- .../src/utility/get-note-versions-menu.ts | 26 +-- packages/frontend/src/utility/merge.ts | 4 +- 50 files changed, 468 insertions(+), 425 deletions(-) diff --git a/packages/frontend-embed/src/pages/note.vue b/packages/frontend-embed/src/pages/note.vue index e879430286..4e0ea9c145 100644 --- a/packages/frontend-embed/src/pages/note.vue +++ b/packages/frontend-embed/src/pages/note.vue @@ -17,7 +17,7 @@ import EmNoteDetailed from '@/components/EmNoteDetailed.vue'; import XNotFound from '@/pages/not-found.vue'; import { DI } from '@/di.js'; import { misskeyApi } from '@/misskey-api.js'; -import { assertServerContext } from '@/server-context'; +import { assertServerContext } from '@/server-context.js'; const props = defineProps<{ noteId: string; diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 17ccb979d5..c5bd393fa5 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -25,7 +25,7 @@ import { deckStore } from '@/ui/deck/deck-store.js'; import { emojiPicker } from '@/utility/emoji-picker.js'; import { mainRouter } from '@/router.js'; import { setFavIconDot } from '@/utility/favicon-dot.js'; -import { type Keymap, makeHotkey } from '@/utility/hotkey.js'; +import { makeHotkey } from '@/utility/hotkey.js'; import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js'; import { prefer } from '@/preferences.js'; import { launchPlugins } from '@/plugin.js'; @@ -85,9 +85,7 @@ export async function mainBoot() { let reloadDialogShowing = false; stream.on('_disconnected_', async () => { - if (prefer.s.serverDisconnectedBehavior === 'reload') { - window.location.reload(); - } else if (prefer.s.serverDisconnectedBehavior === 'dialog') { + if (prefer.s.serverDisconnectedBehavior === 'dialog') { if (reloadDialogShowing) return; reloadDialogShowing = true; const { canceled } = await confirm({ @@ -351,7 +349,7 @@ export async function mainBoot() { } function attemptShowNotificationDot() { - if (defaultStore.state.enableFaviconNotificationDot) { + if (store.s.enableFaviconNotificationDot) { setFavIconDot(true); } } diff --git a/packages/frontend/src/components/DynamicNote.vue b/packages/frontend/src/components/DynamicNote.vue index 6703099591..b2133f8836 100644 --- a/packages/frontend/src/components/DynamicNote.vue +++ b/packages/frontend/src/components/DynamicNote.vue @@ -21,7 +21,7 @@ import { computed, defineAsyncComponent, shallowRef } from 'vue'; import type { ComponentExposed } from 'vue-component-type-helpers'; import type MkNote from '@/components/MkNote.vue'; import type SkNote from '@/components/SkNote.vue'; -import { defaultStore } from '@/store'; +import { defaultStore } from '@/store.js'; const XNote = computed(() => defineAsyncComponent(() => diff --git a/packages/frontend/src/components/MkDateSeparatedList.vue b/packages/frontend/src/components/MkDateSeparatedList.vue index f94f28de41..857b7e3f92 100644 --- a/packages/frontend/src/components/MkDateSeparatedList.vue +++ b/packages/frontend/src/components/MkDateSeparatedList.vue @@ -13,7 +13,7 @@ import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; import { instance } from '@/instance.js'; import { prefer } from '@/preferences.js'; -import { $i } from '@/account.js'; +import { $i } from '@/i.js'; export default defineComponent({ props: { @@ -116,7 +116,7 @@ export default defineComponent({ }); const renderChildren = () => { - const shouldHideAds = !defaultStore.state.forceShowAds && $i && $i.policies.canHideAds; + const shouldHideAds = (!prefer.s.forceShowAds && $i && $i.policies.canHideAds) ?? false; const children = renderChildrenImpl(shouldHideAds); if (isDebuggerEnabled(6864)) { diff --git a/packages/frontend/src/components/MkGoogle.vue b/packages/frontend/src/components/MkGoogle.vue index 6cdab2479e..e7ee7ffab5 100644 --- a/packages/frontend/src/components/MkGoogle.vue +++ b/packages/frontend/src/components/MkGoogle.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue index 659a82d8f2..c52bbbd44f 100644 --- a/packages/frontend/src/components/MkVisibilityPicker.vue +++ b/packages/frontend/src/components/MkVisibilityPicker.vue @@ -53,9 +53,10 @@ const props = withDefaults(defineProps<{ currentVisibility: typeof Misskey.noteVisibilities[number]; isSilenced: boolean; localOnly: boolean; - src?: HTMLElement; + src?: HTMLElement | null; isReplyVisibilitySpecified?: boolean; }>(), { + src: null, }); const emit = defineEmits<{ diff --git a/packages/frontend/src/components/SkFetchNote.vue b/packages/frontend/src/components/SkFetchNote.vue index d325ef1687..a40b99ae8d 100644 --- a/packages/frontend/src/components/SkFetchNote.vue +++ b/packages/frontend/src/components/SkFetchNote.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; -import { misskeyApi } from '@/utility/misskey-api'; +import { misskeyApi } from '@/utility/misskey-api.js'; import DynamicNote from '@/components/DynamicNote.vue'; const props = withDefaults(defineProps<{ diff --git a/packages/frontend/src/components/SkFlashPlayer.vue b/packages/frontend/src/components/SkFlashPlayer.vue index 2b61974ef7..0cdc7bf104 100644 --- a/packages/frontend/src/components/SkFlashPlayer.vue +++ b/packages/frontend/src/components/SkFlashPlayer.vue @@ -6,14 +6,14 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/use/use-note-capture.ts b/packages/frontend/src/use/use-note-capture.ts index 9cc4778edf..54d0e128e1 100644 --- a/packages/frontend/src/use/use-note-capture.ts +++ b/packages/frontend/src/use/use-note-capture.ts @@ -4,11 +4,11 @@ */ import { onUnmounted } from 'vue'; -import type { Ref, ShallowRef } from 'vue'; import * as Misskey from 'misskey-js'; +import type { Ref, ShallowRef } from 'vue'; import { useStream } from '@/stream.js'; import { $i } from '@/i.js'; -import { misskeyApi } from './misskey-api.js'; +import { misskeyApi } from '@/utility/misskey-api.js'; export function useNoteCapture(props: { rootEl: ShallowRef; diff --git a/packages/frontend/src/utility/boost-quote.ts b/packages/frontend/src/utility/boost-quote.ts index feb949772b..b5d2c7e6fe 100644 --- a/packages/frontend/src/utility/boost-quote.ts +++ b/packages/frontend/src/utility/boost-quote.ts @@ -3,11 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ref, Ref, computed, ComputedRef } from 'vue'; +import { ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; +import type { Ref, ComputedRef } from 'vue'; +import type { MenuItem } from '@/types/menu.js'; import { i18n } from '@/i18n.js'; -import { defaultStore } from '@/store.js'; -import { MenuItem } from '@/types/menu.js'; +import { prefer } from '@/preferences'; +import { store } from '@/store.js'; /* this script should eventually contain all Sharkey-specific bits of @@ -30,7 +32,7 @@ export function visibilityIsAtLeast(a: Visibility | string, b: Visibility | stri } export function boostMenuItems(appearNote: Ref, renote: (v: Visibility, l: boolean) => void): MenuItem[] { - const localOnly = ref(defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly); + const localOnly = ref(prefer.s.rememberNoteVisibility ? store.s.localOnly : prefer.s.defaultNoteLocalOnly); const effectiveVisibility = ( appearNote.value.channel?.isSensitive ? smallerVisibility(appearNote.value.visibility, 'home') @@ -83,7 +85,7 @@ export function boostMenuItems(appearNote: Ref, renote: ( export function computeRenoteTooltip(renoted: Ref): ComputedRef { return computed(() => { if (renoted.value) return i18n.ts.unrenote; - if (defaultStore.state.showVisibilitySelectorOnBoost) return i18n.ts.renote; + if (prefer.s.showVisibilitySelectorOnBoost) return i18n.ts.renote; return i18n.ts.renoteShift; }); } diff --git a/packages/frontend/src/utility/deep-equal.ts b/packages/frontend/src/utility/deep-equal.ts index 2859641dc7..09d2ff289e 100644 --- a/packages/frontend/src/utility/deep-equal.ts +++ b/packages/frontend/src/utility/deep-equal.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -type JsonLike = string | number | boolean | null | undefined | JsonLike[] | { [key: string]: JsonLike } | Map; +type JsonLike = string | number | boolean | null | undefined | JsonLike[] | { [key: string]: JsonLike | undefined } | Map; export function deepEqual(a: JsonLike, b: JsonLike): boolean { if (a === b) return true; diff --git a/packages/frontend/src/utility/following-feed-utils.ts b/packages/frontend/src/utility/following-feed-utils.ts index 4d1e81a183..47692aaacf 100644 --- a/packages/frontend/src/utility/following-feed-utils.ts +++ b/packages/frontend/src/utility/following-feed-utils.ts @@ -10,7 +10,7 @@ import type { MenuItem } from '@/types/menu.js'; import { deepMerge } from '@/utility/merge.js'; import { i18n } from '@/i18n.js'; import { popupMenu } from '@/os.js'; -import { prefer } from '@/preferences'; +import { prefer } from '@/preferences.js'; export const followingTab = 'following' as const; export const mutualsTab = 'mutuals' as const; @@ -37,7 +37,7 @@ export type FollowingFeedModel = { [Key in keyof FollowingFeedState]: WritableComputedRef; }; -export interface FollowingFeedState { +export type FollowingFeedState = { withNonPublic: boolean, withQuotes: boolean, withBots: boolean, @@ -45,7 +45,7 @@ export interface FollowingFeedState { onlyFiles: boolean, userList: FollowingFeedTab, remoteWarningDismissed: boolean, -} +}; export const defaultFollowingFeedState: FollowingFeedState = { withNonPublic: false, @@ -169,9 +169,9 @@ export function createModel(storage?: Ref): FollowingFeedModel }; } -function createDefaultStorage() { +function createDefaultStorage(): Ref { return computed(() => ({ - state: prefer.s.followingFeed, + state: prefer.r.followingFeed, save(updated: typeof prefer.s.followingFeed) { prefer.s.followingFeed = updated; }, diff --git a/packages/frontend/src/utility/get-note-menu.ts b/packages/frontend/src/utility/get-note-menu.ts index 6762ce7e43..3e09c74647 100644 --- a/packages/frontend/src/utility/get-note-menu.ts +++ b/packages/frontend/src/utility/get-note-menu.ts @@ -196,7 +196,7 @@ export function getNoteMenu(props: { noteId: appearNote.id, }); - if (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 60 && appearNote.userId === $i.id) { + if (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 60 && appearNote.userId === $i?.id) { claimAchievement('noteDeletedWithin1min'); } }); @@ -541,7 +541,7 @@ export function getNoteMenu(props: { }; return { - menu: menuItems, + popupMenu: menuItems, cleanup, }; } diff --git a/packages/frontend/src/utility/get-note-versions-menu.ts b/packages/frontend/src/utility/get-note-versions-menu.ts index 345cec9018..f5182b1193 100644 --- a/packages/frontend/src/utility/get-note-versions-menu.ts +++ b/packages/frontend/src/utility/get-note-versions-menu.ts @@ -3,13 +3,19 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Ref, defineAsyncComponent } from 'vue'; +import { defineAsyncComponent } from 'vue'; import * as Misskey from 'misskey-js'; -import { i18n } from '@/i18n.js'; -import * as os from '@/os.js'; import { misskeyApi } from './misskey-api.js'; -import { MenuItem } from '@/types/menu.js'; import { dateTimeFormat } from './intl-const.js'; +import type { Ref } from 'vue'; +import type { MenuItem } from '@/types/menu.js'; +import * as os from '@/os.js'; + +interface NoteEdit { + oldDate: string; + updatedAt: string; + text: string | null; +} export async function getNoteVersionsMenu(props: { note: Misskey.entities.Note; @@ -18,7 +24,7 @@ export async function getNoteVersionsMenu(props: { const isRenote = ( props.note.renote != null && props.note.text == null && - props.note.fileIds.length === 0 && + !props.note.fileIds?.length && props.note.poll == null ); @@ -26,11 +32,11 @@ export async function getNoteVersionsMenu(props: { const cleanups = [] as (() => void)[]; - function openVersion(info): void { + function openVersion(info: NoteEdit): void { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/SkOldNoteWindow.vue')), { note: appearNote, - oldText: info.text, - updatedAt: info.oldDate ? info.oldDate : info.updatedAt, + oldText: info.text ?? '', + updatedAt: info.updatedAt, }, { closed: () => dispose(), }); @@ -43,9 +49,7 @@ export async function getNoteVersionsMenu(props: { await statePromise.then((versions) => { for (const edit of versions) { - const _time = edit.oldDate == null ? NaN : - typeof edit.oldDate === 'number' ? edit.oldDate : - (edit.oldDate instanceof Date ? edit.oldDate : new Date(edit.oldDate)).getTime(); + const _time = new Date(edit.oldDate).getTime(); menu.push({ icon: 'ph-pencil-simple ph-bold ph-lg', diff --git a/packages/frontend/src/utility/merge.ts b/packages/frontend/src/utility/merge.ts index 6fd28bb314..6ad1577353 100644 --- a/packages/frontend/src/utility/merge.ts +++ b/packages/frontend/src/utility/merge.ts @@ -6,7 +6,7 @@ import { deepClone } from './clone.js'; import type { Cloneable } from './clone.js'; -export type DeepPartial = { +export type DeepPartial = T | { [P in keyof T]?: T[P] extends Record ? DeepPartial : T[P]; }; @@ -18,7 +18,7 @@ function isPureObject(value: unknown): value is Record { * valueにないキーをdefからもらう(再帰的)\ * nullはそのまま、undefinedはdefの値 **/ -export function deepMerge>(value: DeepPartial, def: X): X { +export function deepMerge>(value: DeepPartial, def: X): X { if (isPureObject(value) && isPureObject(def)) { const result = deepClone(value as Cloneable) as X; for (const [k, v] of Object.entries(def) as [keyof X, X[keyof X]][]) {