diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2023-01-07 10:13:02 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2023-01-07 10:13:02 +0900 |
| commit | 91503405b4e58cb9d39c0441d86ff540a27931e6 (patch) | |
| tree | 4c64371d1a88668e762733db1afaa3be7e284442 | |
| parent | 13.0.0-beta.26 (diff) | |
| download | sharkey-91503405b4e58cb9d39c0441d86ff540a27931e6.tar.gz sharkey-91503405b4e58cb9d39c0441d86ff540a27931e6.tar.bz2 sharkey-91503405b4e58cb9d39c0441d86ff540a27931e6.zip | |
refactor(client): typed localStorage
25 files changed, 157 insertions, 88 deletions
diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts index 0e991cdfb5..93916ccf2f 100644 --- a/packages/frontend/src/account.ts +++ b/packages/frontend/src/account.ts @@ -6,12 +6,13 @@ import { del, get, set } from '@/scripts/idb-proxy'; import { apiUrl } from '@/config'; import { waiting, api, popup, popupMenu, success, alert } from '@/os'; import { unisonReload, reloadChannel } from '@/scripts/unison-reload'; +import { miLocalStorage } from './local-storage'; // TODO: 他のタブと永続化されたstateを同期 type Account = misskey.entities.MeDetailed; -const accountData = localStorage.getItem('account'); +const accountData = miLocalStorage.getItem('account'); // TODO: 外部からはreadonlyに export const $i = accountData ? reactive(JSON.parse(accountData) as Account) : null; @@ -21,7 +22,7 @@ export const iAmAdmin = $i != null && $i.isAdmin; export async function signout() { waiting(); - localStorage.removeItem('account'); + miLocalStorage.removeItem('account'); await removeAccount($i.id); @@ -119,7 +120,7 @@ export function updateAccount(accountData) { for (const [key, value] of Object.entries(accountData)) { $i[key] = value; } - localStorage.setItem('account', JSON.stringify($i)); + miLocalStorage.setItem('account', JSON.stringify($i)); } export function refreshAccount() { @@ -130,7 +131,7 @@ export async function login(token: Account['token'], redirect?: string) { waiting(); if (_DEV_) console.log('logging as token ', token); const me = await fetchAccount(token); - localStorage.setItem('account', JSON.stringify(me)); + miLocalStorage.setItem('account', JSON.stringify(me)); document.cookie = `token=${token}; path=/; max-age=31536000`; // bull dashboardの認証とかで使う await addAccount(me.id, token); diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 08e2c29de2..8ed60bc5dc 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -46,6 +46,7 @@ import { defaultStore } from '@/store'; import { emojilist } from '@/scripts/emojilist'; import { instance } from '@/instance'; import { i18n } from '@/i18n'; +import { miLocalStorage } from '@/local-storage'; type EmojiDef = { emoji: string; @@ -208,7 +209,7 @@ function exec() { } } else if (props.type === 'hashtag') { if (!props.q || props.q === '') { - hashtags.value = JSON.parse(localStorage.getItem('hashtags') || '[]'); + hashtags.value = JSON.parse(miLocalStorage.getItem('hashtags') || '[]'); fetching.value = false; } else { const cacheKey = `autocomplete:hashtag:${props.q}`; diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue index 5a406c8635..dc10c7d3f3 100644 --- a/packages/frontend/src/components/MkFolder.vue +++ b/packages/frontend/src/components/MkFolder.vue @@ -25,8 +25,9 @@ <script lang="ts"> import { defineComponent } from 'vue'; import tinycolor from 'tinycolor2'; +import { miLocalStorage } from '@/local-storage'; -const localStoragePrefix = 'ui:folder:'; +const miLocalStoragePrefix = 'ui:folder:' as const; export default defineComponent({ props: { @@ -44,13 +45,13 @@ export default defineComponent({ data() { return { bg: null, - showBody: (this.persistKey && localStorage.getItem(localStoragePrefix + this.persistKey)) ? localStorage.getItem(localStoragePrefix + this.persistKey) === 't' : this.expanded, + showBody: (this.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${this.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${this.persistKey}`) === 't') : this.expanded, }; }, watch: { showBody() { if (this.persistKey) { - localStorage.setItem(localStoragePrefix + this.persistKey, this.showBody ? 't' : 'f'); + miLocalStorage.setItem(`${miLocalStoragePrefix}${this.persistKey}`, this.showBody ? 't' : 'f'); } }, }, diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 883ad9f14f..ff3b7ec1f5 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -98,6 +98,7 @@ import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account' import { uploadFile } from '@/scripts/upload'; import { deepClone } from '@/scripts/clone'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; +import { miLocalStorage } from '@/local-storage'; const modal = inject('modal'); @@ -156,7 +157,7 @@ let autocomplete = $ref(null); let draghover = $ref(false); let quoteId = $ref(null); let hasNotSpecifiedMentions = $ref(false); -let recentHashtags = $ref(JSON.parse(localStorage.getItem('hashtags') || '[]')); +let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') || '[]')); let imeText = $ref(''); const typing = throttle(3000, () => { @@ -543,7 +544,7 @@ function onDrop(ev): void { } function saveDraft() { - const draftData = JSON.parse(localStorage.getItem('drafts') || '{}'); + const draftData = JSON.parse(miLocalStorage.getItem('drafts') || '{}'); draftData[draftKey] = { updatedAt: new Date(), @@ -558,15 +559,15 @@ function saveDraft() { }, }; - localStorage.setItem('drafts', JSON.stringify(draftData)); + miLocalStorage.setItem('drafts', JSON.stringify(draftData)); } function deleteDraft() { - const draftData = JSON.parse(localStorage.getItem('drafts') ?? '{}'); + const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); delete draftData[draftKey]; - localStorage.setItem('drafts', JSON.stringify(draftData)); + miLocalStorage.setItem('drafts', JSON.stringify(draftData)); } async function post(ev?: MouseEvent) { @@ -622,8 +623,8 @@ async function post(ev?: MouseEvent) { emit('posted'); if (postData.text && postData.text !== '') { const hashtags_ = mfm.parse(postData.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag); - const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[]; - localStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); + const history = JSON.parse(miLocalStorage.getItem('hashtags') || '[]') as string[]; + miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); } posting = false; postAccount = null; @@ -698,7 +699,7 @@ onMounted(() => { nextTick(() => { // 書きかけの投稿を復元 if (!props.instant && !props.mention && !props.specified) { - const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[draftKey]; + const draft = JSON.parse(miLocalStorage.getItem('drafts') || '{}')[draftKey]; if (draft) { text = draft.data.text; useCw = draft.data.useCw; diff --git a/packages/frontend/src/config.ts b/packages/frontend/src/config.ts index f2022b0f02..4b084d365b 100644 --- a/packages/frontend/src/config.ts +++ b/packages/frontend/src/config.ts @@ -1,3 +1,5 @@ +import { miLocalStorage } from "./local-storage"; + const address = new URL(location.href); const siteName = (document.querySelector('meta[property="og:site_name"]') as HTMLMetaElement)?.content; @@ -6,10 +8,10 @@ export const hostname = address.hostname; export const url = address.origin; export const apiUrl = url + '/api'; export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming'; -export const lang = localStorage.getItem('lang'); +export const lang = miLocalStorage.getItem('lang'); export const langs = _LANGS_; -export const locale = JSON.parse(localStorage.getItem('locale')); +export const locale = JSON.parse(miLocalStorage.getItem('locale')); export const version = _VERSION_; export const instanceName = siteName === 'Misskey' ? host : siteName; -export const ui = localStorage.getItem('ui'); -export const debug = localStorage.getItem('debug') === 'true'; +export const ui = miLocalStorage.getItem('ui'); +export const debug = miLocalStorage.getItem('debug') === 'true'; diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index 45ade64127..bd515f47ea 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -9,9 +9,12 @@ import '@/style.scss'; //#region account indexedDB migration import { set } from '@/scripts/idb-proxy'; -if (localStorage.getItem('accounts') != null) { - set('accounts', JSON.parse(localStorage.getItem('accounts'))); - localStorage.removeItem('accounts'); +{ + const accounts = miLocalStorage.getItem('accounts'); + if (accounts) { + set('accounts', JSON.parse(accounts)); + miLocalStorage.removeItem('accounts'); + } } //#endregion @@ -40,6 +43,7 @@ import { reloadChannel } from '@/scripts/unison-reload'; import { reactionPicker } from '@/scripts/reaction-picker'; import { getUrlWithoutLoginId } from '@/scripts/login-id'; import { getAccountFromId } from '@/scripts/get-account-from-id'; +import { miLocalStorage } from './local-storage'; (async () => { console.info(`Misskey v${version}`); @@ -154,7 +158,7 @@ import { getAccountFromId } from '@/scripts/get-account-from-id'; const fetchInstanceMetaPromise = fetchInstance(); fetchInstanceMetaPromise.then(() => { - localStorage.setItem('v', instance.version); + miLocalStorage.setItem('v', instance.version); // Init service worker initializeSw(); @@ -223,12 +227,12 @@ import { getAccountFromId } from '@/scripts/get-account-from-id'; } // クライアントが更新されたか? - const lastVersion = localStorage.getItem('lastVersion'); + const lastVersion = miLocalStorage.getItem('lastVersion'); if (lastVersion !== version) { - localStorage.setItem('lastVersion', version); + miLocalStorage.setItem('lastVersion', version); // テーマリビルドするため - localStorage.removeItem('theme'); + miLocalStorage.removeItem('theme'); try { // 変なバージョン文字列来るとcompareVersionsでエラーになるため if (lastVersion != null && compareVersions(version, lastVersion) === 1) { @@ -244,7 +248,7 @@ import { getAccountFromId } from '@/scripts/get-account-from-id'; // NOTE: この処理は必ず↑のクライアント更新時処理より後に来ること(テーマ再構築のため) watch(defaultStore.reactiveState.darkMode, (darkMode) => { applyTheme(darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme')); - }, { immediate: localStorage.theme == null }); + }, { immediate: miLocalStorage.getItem('theme') == null }); const darkTheme = computed(ColdDeviceStorage.makeGetterSetter('darkTheme')); const lightTheme = computed(ColdDeviceStorage.makeGetterSetter('lightTheme')); @@ -341,7 +345,7 @@ import { getAccountFromId } from '@/scripts/get-account-from-id'; }); } - const lastUsed = localStorage.getItem('lastUsed'); + const lastUsed = miLocalStorage.getItem('lastUsed'); if (lastUsed) { const lastUsedDate = parseInt(lastUsed, 10); // 二時間以上前なら @@ -351,7 +355,7 @@ import { getAccountFromId } from '@/scripts/get-account-from-id'; })); } } - localStorage.setItem('lastUsed', Date.now().toString()); + miLocalStorage.setItem('lastUsed', Date.now().toString()); if ('Notification' in window) { // 許可を得ていなかったらリクエスト diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts index 51464f32fb..82d3e7aea2 100644 --- a/packages/frontend/src/instance.ts +++ b/packages/frontend/src/instance.ts @@ -1,10 +1,11 @@ import { computed, reactive } from 'vue'; import * as Misskey from 'misskey-js'; import { api } from './os'; +import { miLocalStorage } from './local-storage'; // TODO: 他のタブと永続化されたstateを同期 -const instanceData = localStorage.getItem('instance'); +const instanceData = miLocalStorage.getItem('instance'); // TODO: instanceをリアクティブにするかは再考の余地あり @@ -21,7 +22,7 @@ export async function fetchInstance() { instance[k] = v; } - localStorage.setItem('instance', JSON.stringify(instance)); + miLocalStorage.setItem('instance', JSON.stringify(instance)); } export const emojiCategories = computed(() => { diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts new file mode 100644 index 0000000000..5286a6f3a7 --- /dev/null +++ b/packages/frontend/src/local-storage.ts @@ -0,0 +1,31 @@ +type Keys = + 'v' | + 'lastVersion' | + 'instance' | + 'account' | + 'accounts' | + 'lastUsed' | + 'lang' | + 'drafts' | + 'hashtags' | + 'wallpaper' | + 'theme' | + 'colorSchema' | + 'useSystemFont' | + 'fontSize' | + 'ui' | + 'locale' | + 'theme' | + 'customCss' | + 'message_drafts' | + 'scratchpad' | + `miux:${string}` | + `ui:folder:${string}` | + `themes:${string}` | + `aiscript:${string}`; + +export const miLocalStorage = { + getItem: (key: Keys) => window.localStorage.getItem(key), + setItem: (key: Keys, value: string) => window.localStorage.setItem(key, value), + removeItem: (key: Keys) => window.localStorage.removeItem(key), +}; diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index efc0abfc6e..9ee78741dc 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -5,6 +5,7 @@ import * as os from '@/os'; import { i18n } from '@/i18n'; import { ui } from '@/config'; import { unisonReload } from '@/scripts/unison-reload'; +import { miLocalStorage } from './local-storage'; export const navbarItemDef = reactive({ notifications: { @@ -110,21 +111,21 @@ export const navbarItemDef = reactive({ text: i18n.ts.default, active: ui === 'default' || ui === null, action: () => { - localStorage.setItem('ui', 'default'); + miLocalStorage.setItem('ui', 'default'); unisonReload(); }, }, { text: i18n.ts.deck, active: ui === 'deck', action: () => { - localStorage.setItem('ui', 'deck'); + miLocalStorage.setItem('ui', 'deck'); unisonReload(); }, }, { text: i18n.ts.classic, active: ui === 'classic', action: () => { - localStorage.setItem('ui', 'classic'); + miLocalStorage.setItem('ui', 'classic'); unisonReload(); }, }], ev.currentTarget ?? ev.target); diff --git a/packages/frontend/src/pages/_error_.vue b/packages/frontend/src/pages/_error_.vue index da2889ba27..5001b5a8b4 100644 --- a/packages/frontend/src/pages/_error_.vue +++ b/packages/frontend/src/pages/_error_.vue @@ -26,6 +26,7 @@ import * as os from '@/os'; import { unisonReload } from '@/scripts/unison-reload'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { miLocalStorage } from '@/local-storage'; const props = withDefaults(defineProps<{ error?: Error; @@ -42,7 +43,7 @@ os.api('meta', { loaded = true; serverIsDead = false; meta = res; - localStorage.setItem('v', res.version); + miLocalStorage.setItem('v', res.version); }, () => { loaded = true; serverIsDead = true; diff --git a/packages/frontend/src/pages/messaging/messaging-room.form.vue b/packages/frontend/src/pages/messaging/messaging-room.form.vue index a0fa2677d0..e880129033 100644 --- a/packages/frontend/src/pages/messaging/messaging-room.form.vue +++ b/packages/frontend/src/pages/messaging/messaging-room.form.vue @@ -40,6 +40,7 @@ import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; //import { Autocomplete } from '@/scripts/autocomplete'; import { uploadFile } from '@/scripts/upload'; +import { miLocalStorage } from '@/local-storage'; const props = defineProps<{ user?: Misskey.entities.UserDetailed | null; @@ -188,7 +189,7 @@ function clear() { } function saveDraft() { - const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}'); + const drafts = JSON.parse(miLocalStorage.getItem('message_drafts') || '{}'); drafts[draftKey] = { updatedAt: new Date(), @@ -199,15 +200,15 @@ function saveDraft() { }, }; - localStorage.setItem('message_drafts', JSON.stringify(drafts)); + miLocalStorage.setItem('message_drafts', JSON.stringify(drafts)); } function deleteDraft() { - const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}'); + const drafts = JSON.parse(miLocalStorage.getItem('message_drafts') || '{}'); delete drafts[draftKey]; - localStorage.setItem('message_drafts', JSON.stringify(drafts)); + miLocalStorage.setItem('message_drafts', JSON.stringify(drafts)); } async function insertEmoji(ev: MouseEvent) { @@ -222,7 +223,7 @@ onMounted(() => { //new Autocomplete(textEl, this, { model: 'text' }); // 書きかけの投稿を復元 - const draft = JSON.parse(localStorage.getItem('message_drafts') || '{}')[draftKey]; + const draft = JSON.parse(miLocalStorage.getItem('message_drafts') || '{}')[draftKey]; if (draft) { text = draft.data.text; file = draft.data.file; diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue index 316133b968..ff5f06c8da 100644 --- a/packages/frontend/src/pages/scratchpad.vue +++ b/packages/frontend/src/pages/scratchpad.vue @@ -46,6 +46,7 @@ import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import { AsUiComponent, AsUiRoot, patch, registerAsUiLib, render } from '@/scripts/aiscript/ui'; import MkAsUi from '@/components/MkAsUi.vue'; +import { miLocalStorage } from '@/local-storage'; const parser = new Parser(); let aiscript: Interpreter; @@ -55,13 +56,13 @@ const root = ref<AsUiRoot>(); let components: Ref<AsUiComponent>[] = []; let uiKey = $ref(0); -const saved = localStorage.getItem('scratchpad'); +const saved = miLocalStorage.getItem('scratchpad'); if (saved) { code.value = saved; } watch(code, () => { - localStorage.setItem('scratchpad', code.value); + miLocalStorage.setItem('scratchpad', code.value); }); async function run() { diff --git a/packages/frontend/src/pages/settings/custom-css.vue b/packages/frontend/src/pages/settings/custom-css.vue index 9fa9bdd658..be2ec32ac2 100644 --- a/packages/frontend/src/pages/settings/custom-css.vue +++ b/packages/frontend/src/pages/settings/custom-css.vue @@ -16,11 +16,12 @@ import * as os from '@/os'; import { unisonReload } from '@/scripts/unison-reload'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { miLocalStorage } from '@/local-storage'; -const localCustomCss = ref(localStorage.getItem('customCss') ?? ''); +const localCustomCss = ref(miLocalStorage.getItem('customCss') ?? ''); async function apply() { - localStorage.setItem('customCss', localCustomCss.value); + miLocalStorage.setItem('customCss', localCustomCss.value); const { canceled } = await os.confirm({ type: 'info', diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index b90dc3da0e..580c38149a 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -120,10 +120,11 @@ import * as os from '@/os'; import { unisonReload } from '@/scripts/unison-reload'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { miLocalStorage } from '@/local-storage'; -const lang = ref(localStorage.getItem('lang')); -const fontSize = ref(localStorage.getItem('fontSize')); -const useSystemFont = ref(localStorage.getItem('useSystemFont') != null); +const lang = ref(miLocalStorage.getItem('lang')); +const fontSize = ref(miLocalStorage.getItem('fontSize')); +const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null); async function reloadAsk() { const { canceled } = await os.confirm({ @@ -157,23 +158,23 @@ const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars')); const aiChanMode = computed(defaultStore.makeGetterSetter('aiChanMode')); watch(lang, () => { - localStorage.setItem('lang', lang.value as string); - localStorage.removeItem('locale'); + miLocalStorage.setItem('lang', lang.value as string); + miLocalStorage.removeItem('locale'); }); watch(fontSize, () => { if (fontSize.value == null) { - localStorage.removeItem('fontSize'); + miLocalStorage.removeItem('fontSize'); } else { - localStorage.setItem('fontSize', fontSize.value); + miLocalStorage.setItem('fontSize', fontSize.value); } }); watch(useSystemFont, () => { if (useSystemFont.value) { - localStorage.setItem('useSystemFont', 't'); + miLocalStorage.setItem('useSystemFont', 't'); } else { - localStorage.removeItem('useSystemFont'); + miLocalStorage.removeItem('useSystemFont'); } }); diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 119a75b650..3468d44e00 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -33,6 +33,7 @@ import { instance } from '@/instance'; import { useRouter } from '@/router'; import { definePageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; import * as os from '@/os'; +import { miLocalStorage } from '@/local-storage'; const indexInfo = { title: i18n.ts.settings, @@ -180,8 +181,8 @@ const menuDef = computed(() => [{ icon: 'ti ti-trash', text: i18n.ts.clearCache, action: () => { - localStorage.removeItem('locale'); - localStorage.removeItem('theme'); + miLocalStorage.removeItem('locale'); + miLocalStorage.removeItem('theme'); unisonReload(); }, }, { diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index 0c32676c89..87a08612fc 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -45,6 +45,7 @@ import { $i } from '@/account'; import { i18n } from '@/i18n'; import { version, host } from '@/config'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { miLocalStorage } from '@/local-storage'; const { t, ts } = i18n; useCssModule(); @@ -170,9 +171,9 @@ function getSettings(): Profile['settings'] { return { hot, cold, - fontSize: localStorage.getItem('fontSize'), - useSystemFont: localStorage.getItem('useSystemFont') as 't' | null, - wallpaper: localStorage.getItem('wallpaper'), + fontSize: miLocalStorage.getItem('fontSize'), + useSystemFont: miLocalStorage.getItem('useSystemFont') as 't' | null, + wallpaper: miLocalStorage.getItem('wallpaper'), }; } @@ -279,23 +280,23 @@ async function applyProfile(id: string): Promise<void> { // fontSize if (settings.fontSize) { - localStorage.setItem('fontSize', settings.fontSize); + miLocalStorage.setItem('fontSize', settings.fontSize); } else { - localStorage.removeItem('fontSize'); + miLocalStorage.removeItem('fontSize'); } // useSystemFont if (settings.useSystemFont) { - localStorage.setItem('useSystemFont', settings.useSystemFont); + miLocalStorage.setItem('useSystemFont', settings.useSystemFont); } else { - localStorage.removeItem('useSystemFont'); + miLocalStorage.removeItem('useSystemFont'); } // wallpaper if (settings.wallpaper != null) { - localStorage.setItem('wallpaper', settings.wallpaper); + miLocalStorage.setItem('wallpaper', settings.wallpaper); } else { - localStorage.removeItem('wallpaper'); + miLocalStorage.removeItem('wallpaper'); } const { canceled: cancel2 } = await os.confirm({ diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index 60aa80647d..a2dc9bc95f 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -82,6 +82,7 @@ import { instance } from '@/instance'; import { uniqueBy } from '@/scripts/array'; import { fetchThemes, getThemes } from '@/theme-store'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { miLocalStorage } from '@/local-storage'; const installedThemes = ref(getThemes()); const builtinThemes = getBuiltinThemesRef(); @@ -120,7 +121,7 @@ const lightThemeId = computed({ }); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const syncDeviceDarkMode = computed(ColdDeviceStorage.makeGetterSetter('syncDeviceDarkMode')); -const wallpaper = ref(localStorage.getItem('wallpaper')); +const wallpaper = ref(miLocalStorage.getItem('wallpaper')); const themesCount = installedThemes.value.length; watch(syncDeviceDarkMode, () => { @@ -131,9 +132,9 @@ watch(syncDeviceDarkMode, () => { watch(wallpaper, () => { if (wallpaper.value == null) { - localStorage.removeItem('wallpaper'); + miLocalStorage.removeItem('wallpaper'); } else { - localStorage.setItem('wallpaper', wallpaper.value); + miLocalStorage.setItem('wallpaper', wallpaper.value); } location.reload(); }); diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts index 6debcb8a13..2a44223080 100644 --- a/packages/frontend/src/scripts/aiscript/api.ts +++ b/packages/frontend/src/scripts/aiscript/api.ts @@ -1,6 +1,7 @@ import { utils, values } from '@syuilo/aiscript'; import * as os from '@/os'; import { $i } from '@/account'; +import { miLocalStorage } from '@/local-storage'; export function createAiScriptEnv(opts) { let apiRequests = 0; @@ -32,12 +33,12 @@ export function createAiScriptEnv(opts) { }), 'Mk:save': values.FN_NATIVE(([key, value]) => { utils.assertString(key); - localStorage.setItem('aiscript:' + opts.storageKey + ':' + key.value, JSON.stringify(utils.valToJs(value))); + miLocalStorage.setItem(`aiscript:${opts.storageKey}:${key.value}`, JSON.stringify(utils.valToJs(value))); return values.NULL; }), 'Mk:load': values.FN_NATIVE(([key]) => { utils.assertString(key); - return utils.jsToVal(JSON.parse(localStorage.getItem('aiscript:' + opts.storageKey + ':' + key.value))); + return utils.jsToVal(JSON.parse(miLocalStorage.getItem(`aiscript:${opts.storageKey}:${key.value}`))); }), }; } diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index 7656770894..1b723220ee 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -9,6 +9,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard'; import { url } from '@/config'; import { noteActions } from '@/store'; import { notePage } from '@/filters/note'; +import { miLocalStorage } from '@/local-storage'; export function getNoteMenu(props: { note: misskey.entities.Note; @@ -181,7 +182,7 @@ export function getNoteMenu(props: { props.translating.value = true; const res = await os.api('notes/translate', { noteId: appearNote.id, - targetLang: localStorage.getItem('lang') || navigator.language, + targetLang: miLocalStorage.getItem('lang') || navigator.language, }); props.translating.value = false; props.translation.value = res; diff --git a/packages/frontend/src/scripts/idb-proxy.ts b/packages/frontend/src/scripts/idb-proxy.ts index 77bb84463c..218682bb56 100644 --- a/packages/frontend/src/scripts/idb-proxy.ts +++ b/packages/frontend/src/scripts/idb-proxy.ts @@ -22,15 +22,15 @@ if (idbAvailable) { export async function get(key: string) { if (idbAvailable) return iget(key); - return JSON.parse(localStorage.getItem(fallbackName(key))); + return JSON.parse(window.localStorage.getItem(fallbackName(key))); } export async function set(key: string, val: any) { if (idbAvailable) return iset(key, val); - return localStorage.setItem(fallbackName(key), JSON.stringify(val)); + return window.localStorage.setItem(fallbackName(key), JSON.stringify(val)); } export async function del(key: string) { if (idbAvailable) return idel(key); - return localStorage.removeItem(fallbackName(key)); + return window.localStorage.removeItem(fallbackName(key)); } diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts index 62a2b9459a..42cb00265d 100644 --- a/packages/frontend/src/scripts/theme.ts +++ b/packages/frontend/src/scripts/theme.ts @@ -14,6 +14,7 @@ export type Theme = { import lightTheme from '@/themes/_light.json5'; import darkTheme from '@/themes/_dark.json5'; import { deepClone } from './clone'; +import { miLocalStorage } from '@/local-storage'; export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X')); @@ -84,8 +85,8 @@ export function applyTheme(theme: Theme, persist = true) { document.documentElement.style.setProperty('color-schema', colorSchema); if (persist) { - localStorage.setItem('theme', JSON.stringify(props)); - localStorage.setItem('colorSchema', colorSchema); + miLocalStorage.setItem('theme', JSON.stringify(props)); + miLocalStorage.setItem('colorSchema', colorSchema); } // 色計算など再度行えるようにクライアント全体に通知 diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 4b1f47c2bc..97b6ebc188 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -86,6 +86,14 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: [] as string[], }, + latestDonateDialogShowAt: { + where: 'account', + default: null, + }, + neverShowDonateDialog: { + where: 'account', + default: false, + }, menu: { where: 'deviceAccount', @@ -274,7 +282,7 @@ export const defaultStore = markRaw(new Storage('base', { // TODO: 他のタブと永続化されたstateを同期 -const PREFIX = 'miux:'; +const PREFIX = 'miux:' as const; type Plugin = { id: string; @@ -296,6 +304,7 @@ interface Watcher { import lightTheme from '@/themes/l-light.json5'; import darkTheme from '@/themes/d-green-lime.json5'; import { Note, UserDetailed } from 'misskey-js/built/entities'; +import { miLocalStorage } from './local-storage'; export class ColdDeviceStorage { public static default = { @@ -320,7 +329,7 @@ export class ColdDeviceStorage { // TODO: indexedDBにする // ただしその際はnullチェックではなくキー存在チェックにしないとダメ // (indexedDBはnullを保存できるため、ユーザーが意図してnullを格納した可能性がある) - const value = localStorage.getItem(PREFIX + key); + const value = miLocalStorage.getItem(`${PREFIX}${key}`); if (value == null) { return ColdDeviceStorage.default[key]; } else { @@ -330,14 +339,14 @@ export class ColdDeviceStorage { public static set<T extends keyof typeof ColdDeviceStorage.default>(key: T, value: typeof ColdDeviceStorage.default[T]): void { // 呼び出し側のバグ等で undefined が来ることがある - // undefined を文字列として localStorage に入れると参照する際の JSON.parse でコケて不具合の元になるため無視 + // undefined を文字列として miLocalStorage に入れると参照する際の JSON.parse でコケて不具合の元になるため無視 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (value === undefined) { console.error(`attempt to store undefined value for key '${key}'`); return; } - localStorage.setItem(PREFIX + key, JSON.stringify(value)); + miLocalStorage.setItem(`${PREFIX}${key}`, JSON.stringify(value)); for (const watcher of this.watchers) { if (watcher.key === key) watcher.callback(value); diff --git a/packages/frontend/src/theme-store.ts b/packages/frontend/src/theme-store.ts index fdc92ed793..aa1244665b 100644 --- a/packages/frontend/src/theme-store.ts +++ b/packages/frontend/src/theme-store.ts @@ -1,11 +1,13 @@ import { api } from '@/os'; import { $i } from '@/account'; import { Theme } from './scripts/theme'; +import { miLocalStorage } from './local-storage'; -const lsCacheKey = $i ? `themes:${$i.id}` : ''; +const lsCacheKey = $i ? `themes:${$i.id}` as const : null; export function getThemes(): Theme[] { - return JSON.parse(localStorage.getItem(lsCacheKey) || '[]'); + if ($i == null) return []; + return JSON.parse(miLocalStorage.getItem(lsCacheKey!) || '[]'); } export async function fetchThemes(): Promise<void> { @@ -13,7 +15,7 @@ export async function fetchThemes(): Promise<void> { try { const themes = await api('i/registry/get', { scope: ['client'], key: 'themes' }); - localStorage.setItem(lsCacheKey, JSON.stringify(themes)); + miLocalStorage.setItem(lsCacheKey!, JSON.stringify(themes)); } catch (err) { if (err.code === 'NO_SUCH_KEY') return; throw err; @@ -21,14 +23,16 @@ export async function fetchThemes(): Promise<void> { } export async function addTheme(theme: Theme): Promise<void> { + if ($i == null) return; await fetchThemes(); const themes = getThemes().concat(theme); await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); - localStorage.setItem(lsCacheKey, JSON.stringify(themes)); + miLocalStorage.setItem(lsCacheKey!, JSON.stringify(themes)); } export async function removeTheme(theme: Theme): Promise<void> { + if ($i == null) return; const themes = getThemes().filter(t => t.id !== theme.id); await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); - localStorage.setItem(lsCacheKey, JSON.stringify(themes)); + miLocalStorage.setItem(lsCacheKey!, JSON.stringify(themes)); } diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue index 280e69e7dd..f220501ee2 100644 --- a/packages/frontend/src/ui/classic.vue +++ b/packages/frontend/src/ui/classic.vue @@ -51,6 +51,7 @@ import { mainRouter } from '@/router'; import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; +import { miLocalStorage } from '@/local-storage'; const XHeaderMenu = defineAsyncComponent(() => import('./classic.header.vue')); const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); @@ -62,7 +63,7 @@ let pageMetadata = $ref<null | ComputedRef<PageMetadata>>(); let widgetsShowing = $ref(false); let fullView = $ref(false); let globalHeaderHeight = $ref(0); -const wallpaper = localStorage.getItem('wallpaper') != null; +const wallpaper = miLocalStorage.getItem('wallpaper') != null; const showMenuOnTop = $computed(() => defaultStore.state.menuDisplay === 'top'); let live2d = $shallowRef<HTMLIFrameElement>(); let widgetsLeft = $ref(); @@ -123,7 +124,7 @@ function onAiClick(ev) { } if (window.innerWidth < 1024) { - localStorage.setItem('ui', 'default'); + miLocalStorage.setItem('ui', 'default'); location.reload(); } diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 9e1fee5b6b..06129ffc87 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -71,6 +71,7 @@ import { Router } from '@/nirax'; import { mainRouter } from '@/router'; import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; import { deviceKind } from '@/scripts/device-kind'; +import { miLocalStorage } from '@/local-storage'; const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue')); const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); @@ -170,7 +171,7 @@ function top() { window.scroll({ top: 0, behavior: 'smooth' }); } -const wallpaper = localStorage.getItem('wallpaper') != null; +const wallpaper = miLocalStorage.getItem('wallpaper') != null; </script> <style lang="scss" scoped> |