refactor(frontend): prefer.model, store.modelではcustomRefを使用するように (#17058)
* refactor(frontend): prefer.model, store.modelではcustomRefを使用するように * fix: watchの解除に失敗してもエラーで落ちないように * Update packages/frontend/src/lib/pizzax.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
b5454cb2c4
commit
443e1ed29e
|
|
@ -329,8 +329,8 @@ const canSaveAsServerDraft = computed((): boolean => {
|
|||
return canPost.value && (textLength.value > 0 || files.value.length > 0 || poll.value != null);
|
||||
});
|
||||
|
||||
const withHashtags = computed(store.makeGetterSetter('postFormWithHashtags'));
|
||||
const hashtags = computed(store.makeGetterSetter('postFormHashtags'));
|
||||
const withHashtags = store.model('postFormWithHashtags');
|
||||
const hashtags = store.model('postFormHashtags');
|
||||
|
||||
watch(text, () => {
|
||||
checkMissingMention();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
// TODO: Misskeyのドメイン知識があるのでutilityなどに移動する
|
||||
|
||||
import { onUnmounted, ref, watch } from 'vue';
|
||||
import { customRef, ref, watch, onScopeDispose } from 'vue';
|
||||
import { BroadcastChannel } from 'broadcast-channel';
|
||||
import type { Ref } from 'vue';
|
||||
import { $i } from '@/i.js';
|
||||
|
|
@ -223,44 +223,43 @@ export class Pizzax<T extends StateDef> {
|
|||
}
|
||||
|
||||
/**
|
||||
* 特定のキーの、簡易的なgetter/setterを作ります
|
||||
* 特定のキーの、簡易的なcomputed refを作ります
|
||||
* 主にvue上で設定コントロールのmodelとして使う用
|
||||
*/
|
||||
// TODO: 廃止
|
||||
public makeGetterSetter<K extends keyof T, R = T[K]['default']>(
|
||||
public model<K extends keyof T, R = T[K]['default']>(
|
||||
key: K,
|
||||
): Ref<R>;
|
||||
public model<K extends keyof T, R extends Exclude<any, T[K]['default']>>(
|
||||
key: K,
|
||||
getter: (v: T[K]['default']) => R,
|
||||
setter: (v: R) => T[K]['default'],
|
||||
): Ref<R>;
|
||||
|
||||
public model<K extends keyof T, R>(
|
||||
key: K,
|
||||
getter?: (v: T[K]['default']) => R,
|
||||
setter?: (v: R) => T[K]['default'],
|
||||
): {
|
||||
get: () => R;
|
||||
set: (value: R) => void;
|
||||
} {
|
||||
const valueRef = ref(this.s[key]);
|
||||
): Ref<R> {
|
||||
return customRef<R>((track, trigger) => {
|
||||
const watchStop = watch(this.r[key], () => {
|
||||
trigger();
|
||||
});
|
||||
|
||||
const stop = watch(this.r[key], val => {
|
||||
valueRef.value = val;
|
||||
onScopeDispose(() => {
|
||||
watchStop();
|
||||
}, true);
|
||||
|
||||
return {
|
||||
get: () => {
|
||||
track();
|
||||
return (getter != null ? getter(this.s[key]) : this.s[key]) as R;
|
||||
},
|
||||
set: (value) => {
|
||||
const val = setter != null ? setter(value) : value;
|
||||
this.set(key, val as T[K]['default']);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// NOTE: vueコンポーネント内で呼ばれない限りは、onUnmounted は無意味なのでメモリリークする
|
||||
onUnmounted(() => {
|
||||
stop();
|
||||
});
|
||||
|
||||
// TODO: VueのcustomRef使うと良い感じになるかも
|
||||
return {
|
||||
get: () => {
|
||||
if (getter) {
|
||||
return getter(valueRef.value);
|
||||
} else {
|
||||
return valueRef.value;
|
||||
}
|
||||
},
|
||||
set: (value) => {
|
||||
const val = setter ? setter(value) : value;
|
||||
this.set(key, val);
|
||||
valueRef.value = val;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// localStorage => indexedDBのマイグレーション
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ const items = ref(prefer.s.menu.map(x => ({
|
|||
})));
|
||||
const itemTypeValues = computed(() => items.value.map(x => x.type));
|
||||
|
||||
const menuDisplay = computed(store.makeGetterSetter('menuDisplay'));
|
||||
const menuDisplay = store.model('menuDisplay');
|
||||
const showNavbarSubButtons = prefer.model('showNavbarSubButtons');
|
||||
|
||||
async function addItem() {
|
||||
|
|
|
|||
|
|
@ -855,7 +855,7 @@ const $i = ensureSignin();
|
|||
|
||||
const lang = ref(miLocalStorage.getItem('lang'));
|
||||
const dataSaver = ref(prefer.s.dataSaver);
|
||||
const realtimeMode = computed(store.makeGetterSetter('realtimeMode'));
|
||||
const realtimeMode = store.model('realtimeMode');
|
||||
|
||||
const overridedDeviceKind = prefer.model('overridedDeviceKind');
|
||||
const pollingInterval = prefer.model('pollingInterval');
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ const $i = ensureSignin();
|
|||
|
||||
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
||||
|
||||
const reactionAcceptance = computed(store.makeGetterSetter('reactionAcceptance'));
|
||||
const reactionAcceptance = store.model('reactionAcceptance');
|
||||
|
||||
function assertVaildLang(lang: string | null): lang is keyof typeof langmap {
|
||||
return lang != null && lang in langmap;
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { computed, onUnmounted, ref, watch } from 'vue';
|
||||
import { customRef, ref, watch, onScopeDispose } from 'vue';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { host, version } from '@@/js/config.js';
|
||||
import { PREF_DEF } from './def.js';
|
||||
import type { Ref, WritableComputedRef } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
import type { MenuItem } from '@/types/menu.js';
|
||||
import { genId } from '@/utility/id.js';
|
||||
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
|
||||
|
|
@ -299,36 +299,39 @@ export class PreferencesManager extends EventEmitter<PreferencesManagerEvents> {
|
|||
* 特定のキーの、簡易的なcomputed refを作ります
|
||||
* 主にvue上で設定コントロールのmodelとして使う用
|
||||
*/
|
||||
public model<K extends keyof PREF, V extends ValueOf<K> = ValueOf<K>>(
|
||||
public model<K extends keyof PREF, V = ValueOf<K>>(
|
||||
key: K,
|
||||
): Ref<V>;
|
||||
public model<K extends keyof PREF, V extends Exclude<any, ValueOf<K>>>(
|
||||
key: K,
|
||||
getter: (v: ValueOf<K>) => V,
|
||||
setter: (v: V) => ValueOf<K>,
|
||||
): Ref<V>;
|
||||
|
||||
public model<K extends keyof PREF, V>(
|
||||
key: K,
|
||||
getter?: (v: ValueOf<K>) => V,
|
||||
setter?: (v: V) => ValueOf<K>,
|
||||
): WritableComputedRef<V> {
|
||||
const valueRef = ref(this.s[key]);
|
||||
): Ref<V> {
|
||||
return customRef<V>((track, trigger) => {
|
||||
const watchStop = watch(this.r[key], () => {
|
||||
trigger();
|
||||
});
|
||||
|
||||
const stop = watch(this.r[key], val => {
|
||||
valueRef.value = val;
|
||||
});
|
||||
onScopeDispose(() => {
|
||||
watchStop();
|
||||
}, true);
|
||||
|
||||
// NOTE: vueコンポーネント内で呼ばれない限りは、onUnmounted は無意味なのでメモリリークする
|
||||
onUnmounted(() => {
|
||||
stop();
|
||||
});
|
||||
|
||||
// TODO: VueのcustomRef使うと良い感じになるかも
|
||||
return computed({
|
||||
get: () => {
|
||||
if (getter) {
|
||||
return getter(valueRef.value);
|
||||
} else {
|
||||
return valueRef.value;
|
||||
}
|
||||
},
|
||||
set: (value) => {
|
||||
const val = setter ? setter(value) : value;
|
||||
this.commit(key, val);
|
||||
valueRef.value = val;
|
||||
},
|
||||
return {
|
||||
get: () => {
|
||||
track();
|
||||
return (getter != null ? getter(this.s[key]) : this.s[key]) as V;
|
||||
},
|
||||
set: (value) => {
|
||||
const val = setter != null ? setter(value) : value;
|
||||
this.commit(key, val as ValueOf<K>);
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ const props = defineProps<{
|
|||
|
||||
const settingsWindowed = ref(window.innerWidth > WINDOW_THRESHOLD);
|
||||
const menu = ref(prefer.s.menu);
|
||||
// const menuDisplay = computed(store.makeGetterSetter('menuDisplay'));
|
||||
// const menuDisplay = store.model('menuDisplay');
|
||||
const otherNavItemIndicated = computed<boolean>(() => {
|
||||
for (const def in navbarItemDef) {
|
||||
if (menu.value.includes(def)) continue;
|
||||
|
|
|
|||
Loading…
Reference in New Issue