mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-07-25 19:05:03 +02:00
wip
This commit is contained in:
@@ -1312,6 +1312,7 @@ noteOfThisUser: "このユーザーのノート一覧"
|
||||
clipNoteLimitExceeded: "これ以上このクリップにノートを追加できません。"
|
||||
performance: "パフォーマンス"
|
||||
modified: "変更あり"
|
||||
modifiedAt: "変更日時"
|
||||
discard: "破棄"
|
||||
thereAreNChanges: "{n}件の変更があります"
|
||||
signinWithPasskey: "パスキーでログイン"
|
||||
|
||||
@@ -36,11 +36,12 @@ const io: StorageProvider = {
|
||||
const cloudData = await misskeyApi('i/registry/get', {
|
||||
scope: ['client', 'preferences', 'sync'],
|
||||
key: syncGroup + ':' + ctx.key,
|
||||
}) as [any, any][];
|
||||
}) as [any, any, any][];
|
||||
const target = cloudData.find(([scope]) => isSameScope(scope, ctx.scope));
|
||||
if (target == null) return null;
|
||||
return {
|
||||
value: target[1],
|
||||
meta: target[2],
|
||||
};
|
||||
} catch (err: any) {
|
||||
if (err.code === 'NO_SUCH_KEY') { // TODO: いちいちエラーキャッチするのは面倒なのでキーが無くてもエラーにならない maybe-get のようなエンドポイントをバックエンドに実装する
|
||||
@@ -52,12 +53,12 @@ const io: StorageProvider = {
|
||||
},
|
||||
|
||||
cloudSet: async (ctx) => {
|
||||
let cloudData: [any, any][] = [];
|
||||
let cloudData: [any, any, any][] = [];
|
||||
try {
|
||||
cloudData = await misskeyApi('i/registry/get', {
|
||||
scope: ['client', 'preferences', 'sync'],
|
||||
key: syncGroup + ':' + ctx.key,
|
||||
}) as [any, any][];
|
||||
}) as [any, any, any][];
|
||||
} catch (err: any) {
|
||||
if (err.code === 'NO_SUCH_KEY') { // TODO: いちいちエラーキャッチするのは面倒なのでキーが無くてもエラーにならない maybe-get のようなエンドポイントをバックエンドに実装する
|
||||
cloudData = [];
|
||||
@@ -69,9 +70,9 @@ const io: StorageProvider = {
|
||||
const i = cloudData.findIndex(([scope]) => isSameScope(scope, ctx.scope));
|
||||
|
||||
if (i === -1) {
|
||||
cloudData.push([ctx.scope, ctx.value]);
|
||||
cloudData.push([ctx.scope, ctx.value, ctx.meta]);
|
||||
} else {
|
||||
cloudData[i] = [ctx.scope, ctx.value];
|
||||
cloudData[i] = [ctx.scope, ctx.value, ctx.meta];
|
||||
}
|
||||
|
||||
await misskeyApi('i/registry/set', {
|
||||
@@ -86,10 +87,10 @@ const io: StorageProvider = {
|
||||
const fetchings = ctx.needs.map(need => io.cloudGet(need).then(res => [need.key, res] as const));
|
||||
const cloudDatas = await Promise.all(fetchings);
|
||||
|
||||
const res = {} as Partial<Record<string, any>>;
|
||||
const res = {} as Partial<Record<string, { value: any; meta: any; }>>;
|
||||
for (const cloudData of cloudDatas) {
|
||||
if (cloudData[1] != null) {
|
||||
res[cloudData[0]] = cloudData[1].value;
|
||||
res[cloudData[0]] = cloudData[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ type Scope = Partial<{
|
||||
|
||||
type ValueMeta = Partial<{
|
||||
sync: boolean;
|
||||
// TODO: modifiedAtをここでも(Record個別に)持っておいた方が値の新旧比較に使えて便利そう
|
||||
modifiedAt?: number;
|
||||
}>;
|
||||
|
||||
type PrefRecord<K extends keyof PREF> = [scope: Scope, value: ValueOf<K>, meta: ValueMeta];
|
||||
@@ -89,9 +89,9 @@ export type PossiblyNonNormalizedPreferencesProfile = Omit<PreferencesProfile, '
|
||||
export type StorageProvider = {
|
||||
load: () => PossiblyNonNormalizedPreferencesProfile | null;
|
||||
save: (ctx: { profile: PreferencesProfile; }) => void;
|
||||
cloudGetBulk: <K extends keyof PREF>(ctx: { needs: { key: K; scope: Scope; }[] }) => Promise<Partial<Record<K, ValueOf<K>>>>;
|
||||
cloudGet: <K extends keyof PREF>(ctx: { key: K; scope: Scope; }) => Promise<{ value: ValueOf<K>; } | null>;
|
||||
cloudSet: <K extends keyof PREF>(ctx: { key: K; scope: Scope; value: ValueOf<K>; }) => Promise<void>;
|
||||
cloudGetBulk: <K extends keyof PREF>(ctx: { needs: { key: K; scope: Scope; }[] }) => Promise<Partial<Record<K, { value: ValueOf<K>; meta: { modifiedAt: ValueMeta['modifiedAt'] }; }>>>;
|
||||
cloudGet: <K extends keyof PREF>(ctx: { key: K; scope: Scope; }) => Promise<{ value: ValueOf<K>; meta: { modifiedAt: ValueMeta['modifiedAt'] }; } | null>;
|
||||
cloudSet: <K extends keyof PREF>(ctx: { key: K; scope: Scope; value: ValueOf<K>; meta: { modifiedAt: ValueMeta['modifiedAt'] }; }) => Promise<void>;
|
||||
};
|
||||
|
||||
type PreferencesDefinitionRecord<Default, T = Default extends (...args: any) => infer R ? R : Default> = {
|
||||
@@ -244,13 +244,13 @@ export class PreferencesManager extends EventEmitter<PreferencesManagerEvents> {
|
||||
}
|
||||
|
||||
// TODO: desync対策 cloudの値のfetchが正常に完了していない状態でcommitすると多分値が上書きされる
|
||||
public commit<K extends keyof PREF>(key: K, value: ValueOf<K>) {
|
||||
public commit<K extends keyof PREF>(key: K, value: ValueOf<K>): PrefRecord<K> | null {
|
||||
const currentAccount = this.currentAccount; // TSを黙らせるため
|
||||
const v = JSON.parse(JSON.stringify(value)); // deep copy 兼 vueのプロキシ解除
|
||||
|
||||
if (deepEqual(this.s[key], v)) {
|
||||
if (_DEV_) console.log('(skip) prefer:commit', key, v);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_DEV_) console.log('prefer:commit', key, v);
|
||||
@@ -260,30 +260,39 @@ export class PreferencesManager extends EventEmitter<PreferencesManagerEvents> {
|
||||
const record = this.getMatchedRecordOf(key);
|
||||
|
||||
if (parseScope(record[0]).account == null && isAccountDependentKey(key) && currentAccount != null) {
|
||||
this.profile.preferences[key].push([makeScope({
|
||||
const newRecord = [makeScope({
|
||||
server: host,
|
||||
account: currentAccount.id,
|
||||
}), v, {}]);
|
||||
}), v, {
|
||||
modifiedAt: Date.now(),
|
||||
}] as PrefRecord<K>;
|
||||
this.profile.preferences[key].push(newRecord);
|
||||
this.save();
|
||||
return;
|
||||
return newRecord;
|
||||
}
|
||||
|
||||
if (parseScope(record[0]).server == null && isServerDependentKey(key)) {
|
||||
this.profile.preferences[key].push([makeScope({
|
||||
const newRecord = [makeScope({
|
||||
server: host,
|
||||
}), v, {}]);
|
||||
}), v, {
|
||||
modifiedAt: Date.now(),
|
||||
}] as PrefRecord<K>;
|
||||
this.profile.preferences[key].push(newRecord);
|
||||
this.save();
|
||||
return;
|
||||
return newRecord;
|
||||
}
|
||||
|
||||
record[1] = v;
|
||||
record[2].modifiedAt = Date.now();
|
||||
this.save();
|
||||
|
||||
if (record[2].sync) {
|
||||
// awaitの必要なし
|
||||
// TODO: リクエストを間引く
|
||||
this.io.cloudSet({ key, scope: record[0], value: record[1] });
|
||||
this.io.cloudSet({ key, scope: record[0], value: record[1], meta: { modifiedAt: record[2].modifiedAt } });
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,8 +369,9 @@ export class PreferencesManager extends EventEmitter<PreferencesManagerEvents> {
|
||||
if (record[2].sync && Object.hasOwn(cloudValues, key) && cloudValues[key] !== undefined) {
|
||||
const cloudValue = cloudValues[key];
|
||||
if (!deepEqual(cloudValue, record[1])) {
|
||||
this.rewriteRawState(key, cloudValue);
|
||||
record[1] = cloudValue;
|
||||
this.rewriteRawState(key, cloudValue.value);
|
||||
record[1] = cloudValue.value;
|
||||
record[2].modifiedAt = cloudValue.meta.modifiedAt;
|
||||
modified = true;
|
||||
if (_DEV_) console.log('cloud fetched', key, cloudValue);
|
||||
}
|
||||
@@ -503,12 +513,12 @@ export class PreferencesManager extends EventEmitter<PreferencesManagerEvents> {
|
||||
newValue = resolvedValue;
|
||||
}
|
||||
|
||||
this.commit(key, newValue);
|
||||
const commitedRecord = this.commit(key, newValue);
|
||||
|
||||
const done = os.waiting();
|
||||
|
||||
try {
|
||||
await this.io.cloudSet({ key, scope: record[0], value: newValue });
|
||||
await this.io.cloudSet({ key, scope: record[0], value: newValue, meta: { modifiedAt: record[2].modifiedAt } });
|
||||
} catch (err) {
|
||||
done();
|
||||
|
||||
@@ -607,6 +617,11 @@ export class PreferencesManager extends EventEmitter<PreferencesManagerEvents> {
|
||||
icon: 'ti ti-cloud-cog',
|
||||
text: i18n.ts.syncBetweenDevices,
|
||||
ref: sync,
|
||||
}, {
|
||||
type: 'divider',
|
||||
}, {
|
||||
type: 'label',
|
||||
text: i18n.ts.modifiedAt + ': ' + (this.getMatchedRecordOf(key)[2].modifiedAt ? new Date(this.getMatchedRecordOf(key)[2].modifiedAt!).toLocaleString() : '-'),
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5260,6 +5260,10 @@ export interface Locale extends ILocale {
|
||||
* 変更あり
|
||||
*/
|
||||
"modified": string;
|
||||
/**
|
||||
* 変更日時
|
||||
*/
|
||||
"modifiedAt": string;
|
||||
/**
|
||||
* 破棄
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user