Compare commits
18 Commits
8090429e9a
...
0e8e523d84
Author | SHA1 | Date |
---|---|---|
|
0e8e523d84 | |
|
f14787bd10 | |
|
c67e2e2a16 | |
|
98b4e0b446 | |
|
14cbc78031 | |
|
62e333191d | |
|
0c2d799acd | |
|
0ec029a3f1 | |
|
aa4c7a1313 | |
|
9782c8118a | |
|
9735c87a9c | |
|
1a62cc2dbb | |
|
1fa77f1751 | |
|
fde05bfbf5 | |
|
fed783773c | |
|
f5bd895e18 | |
|
ca3710bc1f | |
|
743995e469 |
|
@ -54,6 +54,7 @@
|
||||||
- フロントエンドの読み込みサイズを軽量化しました
|
- フロントエンドの読み込みサイズを軽量化しました
|
||||||
- ほとんどの言語のハイライトは問題なく行えますが、互換性の問題により一部の言語が正常にハイライトできなくなる可能性があります。詳しくは https://shiki.style/references/engine-js-compat をご覧ください。
|
- ほとんどの言語のハイライトは問題なく行えますが、互換性の問題により一部の言語が正常にハイライトできなくなる可能性があります。詳しくは https://shiki.style/references/engine-js-compat をご覧ください。
|
||||||
- Fix: チャットに動画ファイルを送付すると、動画の表示が崩れてしまい視聴出来ない問題を修正
|
- Fix: チャットに動画ファイルを送付すると、動画の表示が崩れてしまい視聴出来ない問題を修正
|
||||||
|
- Fix: アカウント依存かつ初期状態である設定値をサーバー同期しようとした際に正しくコンフリクト検出されない問題を修正
|
||||||
- Fix: "時計"ウィジェット(Clock)において、Transparent設定が有効でも、その背景が透過されない問題を修正
|
- Fix: "時計"ウィジェット(Clock)において、Transparent設定が有効でも、その背景が透過されない問題を修正
|
||||||
- Fix: 一定時間操作がなかったら動画プレイヤーのコントロールを隠すように
|
- Fix: 一定時間操作がなかったら動画プレイヤーのコントロールを隠すように
|
||||||
- Fix: Twitchのクリップがプレイヤーで再生できない問題を修正
|
- Fix: Twitchのクリップがプレイヤーで再生できない問題を修正
|
||||||
|
@ -71,7 +72,7 @@
|
||||||
- Fix: ミュート対象ユーザーが引用されているノートがRNされたときにミュートを貫通してしまう問題を修正 #16009
|
- Fix: ミュート対象ユーザーが引用されているノートがRNされたときにミュートを貫通してしまう問題を修正 #16009
|
||||||
- Fix: 連合モードが「なし」の場合に、生成されるHTML内のactivity jsonへのリンクタグを省略するように
|
- Fix: 連合モードが「なし」の場合に、生成されるHTML内のactivity jsonへのリンクタグを省略するように
|
||||||
- Fix: コントロールパネルから招待コードを作成すると作成者の情報が記録されない問題を修正
|
- Fix: コントロールパネルから招待コードを作成すると作成者の情報が記録されない問題を修正
|
||||||
|
- Fix: コントロールパネルのジョブキューページからPausedなジョブ一覧を閲覧できない問題を修正
|
||||||
|
|
||||||
## 2025.5.0
|
## 2025.5.0
|
||||||
|
|
||||||
|
|
|
@ -12041,6 +12041,10 @@ export interface Locale extends ILocale {
|
||||||
* 画像にクレジット情報などのウォーターマークを追加することができます。
|
* 画像にクレジット情報などのウォーターマークを追加することができます。
|
||||||
*/
|
*/
|
||||||
"tip": string;
|
"tip": string;
|
||||||
|
/**
|
||||||
|
* 保存せずに終了しますか?
|
||||||
|
*/
|
||||||
|
"quitWithoutSaveConfirm": string;
|
||||||
/**
|
/**
|
||||||
* ウォーターマークの編集
|
* ウォーターマークの編集
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3225,6 +3225,7 @@ watermark: "ウォーターマーク"
|
||||||
defaultPreset: "デフォルトのプリセット"
|
defaultPreset: "デフォルトのプリセット"
|
||||||
_watermarkEditor:
|
_watermarkEditor:
|
||||||
tip: "画像にクレジット情報などのウォーターマークを追加することができます。"
|
tip: "画像にクレジット情報などのウォーターマークを追加することができます。"
|
||||||
|
quitWithoutSaveConfirm: "保存せずに終了しますか?"
|
||||||
title: "ウォーターマークの編集"
|
title: "ウォーターマークの編集"
|
||||||
cover: "全体に被せる"
|
cover: "全体に被せる"
|
||||||
repeat: "敷き詰める"
|
repeat: "敷き詰める"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "2025.5.1-beta.5",
|
"version": "2025.5.1-beta.6",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -28,7 +28,7 @@ export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
queue: { type: 'string', enum: QUEUE_TYPES },
|
queue: { type: 'string', enum: QUEUE_TYPES },
|
||||||
state: { type: 'array', items: { type: 'string', enum: ['active', 'wait', 'delayed', 'completed', 'failed'] } },
|
state: { type: 'array', items: { type: 'string', enum: ['active', 'wait', 'delayed', 'completed', 'failed', 'paused'] } },
|
||||||
search: { type: 'string' },
|
search: { type: 'string' },
|
||||||
},
|
},
|
||||||
required: ['queue', 'state'],
|
required: ['queue', 'state'],
|
||||||
|
|
|
@ -126,8 +126,8 @@ onMounted(async () => {
|
||||||
|
|
||||||
imageBitmap = await window.createImageBitmap(props.image);
|
imageBitmap = await window.createImageBitmap(props.image);
|
||||||
|
|
||||||
const MAX_W = 500;
|
const MAX_W = 1000;
|
||||||
const MAX_H = 500;
|
const MAX_H = 1000;
|
||||||
let w = imageBitmap.width;
|
let w = imageBitmap.width;
|
||||||
let h = imageBitmap.height;
|
let h = imageBitmap.height;
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,12 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const dialog = useTemplateRef('dialog');
|
const dialog = useTemplateRef('dialog');
|
||||||
|
|
||||||
function cancel() {
|
async function cancel() {
|
||||||
|
const { canceled } = await os.confirm({
|
||||||
|
text: i18n.ts._watermarkEditor.quitWithoutSaveConfirm,
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
emit('cancel');
|
emit('cancel');
|
||||||
dialog.value?.close();
|
dialog.value?.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -547,14 +547,28 @@ export function success(): Promise<void> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function waiting(text?: string | null): () => void {
|
export function waiting(options: { text?: string } = {}) {
|
||||||
window.document.body.setAttribute('inert', 'true');
|
window.document.body.setAttribute('inert', 'true');
|
||||||
|
|
||||||
const showing = ref(true);
|
const showing = ref(true);
|
||||||
|
const isSuccess = ref(false);
|
||||||
|
|
||||||
|
function done(doneOptions: { success?: boolean } = {}) {
|
||||||
|
if (doneOptions.success) {
|
||||||
|
isSuccess.value = true;
|
||||||
|
window.setTimeout(() => {
|
||||||
|
showing.value = false;
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
showing.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: dynamic importすると挙動がおかしくなる(showingの変更が伝播しない)
|
||||||
const { dispose } = popup(MkWaitingDialog, {
|
const { dispose } = popup(MkWaitingDialog, {
|
||||||
success: false,
|
success: isSuccess,
|
||||||
showing: showing,
|
showing: showing,
|
||||||
text,
|
text: options.text,
|
||||||
}, {
|
}, {
|
||||||
closed: () => {
|
closed: () => {
|
||||||
window.document.body.removeAttribute('inert');
|
window.document.body.removeAttribute('inert');
|
||||||
|
@ -562,9 +576,7 @@ export function waiting(text?: string | null): () => void {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return done;
|
||||||
showing.value = false;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function form<F extends Form>(title: string, f: F): Promise<{ canceled: true, result?: undefined } | { canceled?: false, result: GetFormResultType<F> }> {
|
export function form<F extends Form>(title: string, f: F): Promise<{ canceled: true, result?: undefined } | { canceled?: false, result: GetFormResultType<F> }> {
|
||||||
|
|
|
@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
<div class="_buttonsCenter">
|
<div class="_buttonsCenter">
|
||||||
<MkButton primary rounded @click="onFileSelectClicked">{{ i18n.ts.uplaod }}</MkButton>
|
<MkButton primary rounded @click="onFileSelectClicked">{{ i18n.ts.upload }}</MkButton>
|
||||||
<MkButton primary rounded @click="onDriveSelectClicked">{{ i18n.ts.fromDrive }}</MkButton>
|
<MkButton primary rounded @click="onDriveSelectClicked">{{ i18n.ts.fromDrive }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.uploadableFileTypes, 'uploadableFileTypes'])">
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.uploadableFileTypes, 'uploadableFileTypes'])">
|
||||||
<template #label>{{ i18n.ts._role._options.uploadableFileTypes }}</template>
|
<template #label>{{ i18n.ts._role._options.uploadableFileTypes }}</template>
|
||||||
<template #suffix>...</template>
|
<template #suffix>...</template>
|
||||||
<MkTextarea :modelValue="policies.uploadableFileTypes.join('\n')">
|
<MkTextarea :modelValue="policies.uploadableFileTypes.join('\n')" @update:modelValue="v => policies.uploadableFileTypes = v.split('\n')">
|
||||||
<template #caption>
|
<template #caption>
|
||||||
<div>{{ i18n.ts._role._options.uploadableFileTypes_caption }}</div>
|
<div>{{ i18n.ts._role._options.uploadableFileTypes_caption }}</div>
|
||||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.tsx._role._options.uploadableFileTypes_caption2({ x: 'application/octet-stream' }) }}</div>
|
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.tsx._role._options.uploadableFileTypes_caption2({ x: 'application/octet-stream' }) }}</div>
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
// TODO: そのうち消す
|
// TODO: そのうち消す
|
||||||
export function migrateOldSettings() {
|
export function migrateOldSettings() {
|
||||||
os.waiting(i18n.ts.settingsMigrating);
|
os.waiting({ text: i18n.ts.settingsMigrating });
|
||||||
|
|
||||||
store.loaded.then(async () => {
|
store.loaded.then(async () => {
|
||||||
misskeyApi('i/registry/get', { scope: ['client'], key: 'themes' }).catch(() => []).then((themes: any) => {
|
misskeyApi('i/registry/get', { scope: ['client'], key: 'themes' }).catch(() => []).then((themes: any) => {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import type { DeckProfile } from '@/deck.js';
|
||||||
import type { PreferencesDefinition } from './manager.js';
|
import type { PreferencesDefinition } from './manager.js';
|
||||||
import type { WatermarkPreset } from '@/utility/watermark.js';
|
import type { WatermarkPreset } from '@/utility/watermark.js';
|
||||||
import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js';
|
import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js';
|
||||||
|
import { deepEqual } from '@/utility/deep-equal.js';
|
||||||
|
|
||||||
/** サウンド設定 */
|
/** サウンド設定 */
|
||||||
export type SoundStore = {
|
export type SoundStore = {
|
||||||
|
@ -88,9 +89,20 @@ export const PREF_DEF = {
|
||||||
emojis: string[];
|
emojis: string[];
|
||||||
}[],
|
}[],
|
||||||
mergeStrategy: (a, b) => {
|
mergeStrategy: (a, b) => {
|
||||||
const sameIdExists = a.some(x => b.some(y => x.id === y.id));
|
const mergedItems = [] as (typeof a)[];
|
||||||
if (sameIdExists) throw new Error();
|
for (const x of a.concat(b)) {
|
||||||
return a.concat(b);
|
const sameIdItem = mergedItems.find(y => y.id === x.id);
|
||||||
|
if (sameIdItem != null) {
|
||||||
|
if (deepEqual(x, sameIdItem)) { // 完全な重複は無視
|
||||||
|
continue;
|
||||||
|
} else { // IDは同じなのに内容が違う場合はマージ不可とする
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mergedItems.push(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mergedItems;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
emojiPaletteForReaction: {
|
emojiPaletteForReaction: {
|
||||||
|
@ -108,9 +120,20 @@ export const PREF_DEF = {
|
||||||
themes: {
|
themes: {
|
||||||
default: [] as Theme[],
|
default: [] as Theme[],
|
||||||
mergeStrategy: (a, b) => {
|
mergeStrategy: (a, b) => {
|
||||||
const sameIdExists = a.some(x => b.some(y => x.id === y.id));
|
const mergedItems = [] as (typeof a)[];
|
||||||
if (sameIdExists) throw new Error();
|
for (const x of a.concat(b)) {
|
||||||
return a.concat(b);
|
const sameIdItem = mergedItems.find(y => y.id === x.id);
|
||||||
|
if (sameIdItem != null) {
|
||||||
|
if (deepEqual(x, sameIdItem)) { // 完全な重複は無視
|
||||||
|
continue;
|
||||||
|
} else { // IDは同じなのに内容が違う場合はマージ不可とする
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mergedItems.push(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mergedItems;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lightTheme: {
|
lightTheme: {
|
||||||
|
@ -375,9 +398,20 @@ export const PREF_DEF = {
|
||||||
accountDependent: true,
|
accountDependent: true,
|
||||||
default: [] as WatermarkPreset[],
|
default: [] as WatermarkPreset[],
|
||||||
mergeStrategy: (a, b) => {
|
mergeStrategy: (a, b) => {
|
||||||
const sameIdExists = a.some(x => b.some(y => x.id === y.id));
|
const mergedItems = [] as (typeof a)[];
|
||||||
if (sameIdExists) throw new Error();
|
for (const x of a.concat(b)) {
|
||||||
return a.concat(b);
|
const sameIdItem = mergedItems.find(y => y.id === x.id);
|
||||||
|
if (sameIdItem != null) {
|
||||||
|
if (deepEqual(x, sameIdItem)) { // 完全な重複は無視
|
||||||
|
continue;
|
||||||
|
} else { // IDは同じなのに内容が違う場合はマージ不可とする
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mergedItems.push(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mergedItems;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultWatermarkPresetId: {
|
defaultWatermarkPresetId: {
|
||||||
|
|
|
@ -104,6 +104,8 @@ export function getInitialPrefValue<K extends keyof PREF>(k: K): ValueOf<K> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: PreferencesManagerForGuest のような非ログイン専用のクラスを分離すれば$iのnullチェックやaccountがnullであるスコープのレコード挿入などが不要になり綺麗になるかもしれない
|
||||||
|
// NOTE: accountDependentな設定は初期状態であってもアカウントごとのスコープでレコードを作成しておかないと、サーバー同期する際に正しく動作しなくなる
|
||||||
export class PreferencesManager {
|
export class PreferencesManager {
|
||||||
private storageProvider: StorageProvider;
|
private storageProvider: StorageProvider;
|
||||||
public profile: PreferencesProfile;
|
public profile: PreferencesProfile;
|
||||||
|
@ -139,11 +141,11 @@ export class PreferencesManager {
|
||||||
// TODO: 定期的にクラウドの値をフェッチ
|
// TODO: 定期的にクラウドの値をフェッチ
|
||||||
}
|
}
|
||||||
|
|
||||||
private isAccountDependentKey<K extends keyof PREF>(key: K): boolean {
|
private static isAccountDependentKey<K extends keyof PREF>(key: K): boolean {
|
||||||
return (PREF_DEF as PreferencesDefinition)[key].accountDependent === true;
|
return (PREF_DEF as PreferencesDefinition)[key].accountDependent === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private isServerDependentKey<K extends keyof PREF>(key: K): boolean {
|
private static isServerDependentKey<K extends keyof PREF>(key: K): boolean {
|
||||||
return (PREF_DEF as PreferencesDefinition)[key].serverDependent === true;
|
return (PREF_DEF as PreferencesDefinition)[key].serverDependent === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +168,7 @@ export class PreferencesManager {
|
||||||
|
|
||||||
const record = this.getMatchedRecordOf(key);
|
const record = this.getMatchedRecordOf(key);
|
||||||
|
|
||||||
if (parseScope(record[0]).account == null && this.isAccountDependentKey(key)) {
|
if (parseScope(record[0]).account == null && PreferencesManager.isAccountDependentKey(key)) {
|
||||||
this.profile.preferences[key].push([makeScope({
|
this.profile.preferences[key].push([makeScope({
|
||||||
server: host,
|
server: host,
|
||||||
account: $i!.id,
|
account: $i!.id,
|
||||||
|
@ -175,7 +177,7 @@ export class PreferencesManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parseScope(record[0]).server == null && this.isServerDependentKey(key)) {
|
if (parseScope(record[0]).server == null && PreferencesManager.isServerDependentKey(key)) {
|
||||||
this.profile.preferences[key].push([makeScope({
|
this.profile.preferences[key].push([makeScope({
|
||||||
server: host,
|
server: host,
|
||||||
}), v, {}]);
|
}), v, {}]);
|
||||||
|
@ -276,7 +278,19 @@ export class PreferencesManager {
|
||||||
public static newProfile(): PreferencesProfile {
|
public static newProfile(): PreferencesProfile {
|
||||||
const data = {} as PreferencesProfile['preferences'];
|
const data = {} as PreferencesProfile['preferences'];
|
||||||
for (const key in PREF_DEF) {
|
for (const key in PREF_DEF) {
|
||||||
data[key] = [[makeScope({}), getInitialPrefValue(key as keyof typeof PREF_DEF), {}]];
|
const v = getInitialPrefValue(key as keyof typeof PREF_DEF);
|
||||||
|
if (PreferencesManager.isAccountDependentKey(key as keyof typeof PREF_DEF)) {
|
||||||
|
data[key] = $i ? [[makeScope({}), v, {}], [makeScope({
|
||||||
|
server: host,
|
||||||
|
account: $i.id,
|
||||||
|
}), v, {}]] : [[makeScope({}), v, {}]];
|
||||||
|
} else if (PreferencesManager.isServerDependentKey(key as keyof typeof PREF_DEF)) {
|
||||||
|
data[key] = [[makeScope({
|
||||||
|
server: host,
|
||||||
|
}), v, {}]];
|
||||||
|
} else {
|
||||||
|
data[key] = [[makeScope({}), v, {}]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
|
@ -293,18 +307,36 @@ export class PreferencesManager {
|
||||||
for (const key in PREF_DEF) {
|
for (const key in PREF_DEF) {
|
||||||
const records = profileLike.preferences[key];
|
const records = profileLike.preferences[key];
|
||||||
if (records == null || records.length === 0) {
|
if (records == null || records.length === 0) {
|
||||||
data[key] = [[makeScope({}), getInitialPrefValue(key as keyof typeof PREF_DEF), {}]];
|
const v = getInitialPrefValue(key as keyof typeof PREF_DEF);
|
||||||
|
if (PreferencesManager.isAccountDependentKey(key as keyof typeof PREF_DEF)) {
|
||||||
|
data[key] = $i ? [[makeScope({}), v, {}], [makeScope({
|
||||||
|
server: host,
|
||||||
|
account: $i.id,
|
||||||
|
}), v, {}]] : [[makeScope({}), v, {}]];
|
||||||
|
} else if (PreferencesManager.isServerDependentKey(key as keyof typeof PREF_DEF)) {
|
||||||
|
data[key] = [[makeScope({
|
||||||
|
server: host,
|
||||||
|
}), v, {}]];
|
||||||
|
} else {
|
||||||
|
data[key] = [[makeScope({}), v, {}]];
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
data[key] = records;
|
if ($i && PreferencesManager.isAccountDependentKey(key as keyof typeof PREF_DEF) && !records.some(([scope]) => parseScope(scope).server === host && parseScope(scope).account === $i!.id)) {
|
||||||
|
data[key] = records.concat([[makeScope({
|
||||||
// alpha段階ではmetaが無かったのでマイグレート
|
server: host,
|
||||||
// TODO: そのうち消す
|
account: $i.id,
|
||||||
for (const record of data[key] as any[][]) {
|
}), getInitialPrefValue(key as keyof typeof PREF_DEF), {}]]);
|
||||||
if (record.length === 2) {
|
continue;
|
||||||
record.push({});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if ($i && PreferencesManager.isServerDependentKey(key as keyof typeof PREF_DEF) && !records.some(([scope]) => parseScope(scope).server === host)) {
|
||||||
|
data[key] = records.concat([[makeScope({
|
||||||
|
server: host,
|
||||||
|
}), getInitialPrefValue(key as keyof typeof PREF_DEF), {}]]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
data[key] = records;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,7 +374,7 @@ export class PreferencesManager {
|
||||||
|
|
||||||
public setAccountOverride<K extends keyof PREF>(key: K) {
|
public setAccountOverride<K extends keyof PREF>(key: K) {
|
||||||
if ($i == null) return;
|
if ($i == null) return;
|
||||||
if (this.isAccountDependentKey(key)) throw new Error('already account-dependent');
|
if (PreferencesManager.isAccountDependentKey(key)) throw new Error('already account-dependent');
|
||||||
if (this.isAccountOverrided(key)) return;
|
if (this.isAccountOverrided(key)) return;
|
||||||
|
|
||||||
const records = this.profile.preferences[key];
|
const records = this.profile.preferences[key];
|
||||||
|
@ -356,7 +388,7 @@ export class PreferencesManager {
|
||||||
|
|
||||||
public clearAccountOverride<K extends keyof PREF>(key: K) {
|
public clearAccountOverride<K extends keyof PREF>(key: K) {
|
||||||
if ($i == null) return;
|
if ($i == null) return;
|
||||||
if (this.isAccountDependentKey(key)) throw new Error('cannot clear override for this account-dependent property');
|
if (PreferencesManager.isAccountDependentKey(key)) throw new Error('cannot clear override for this account-dependent property');
|
||||||
|
|
||||||
const records = this.profile.preferences[key];
|
const records = this.profile.preferences[key];
|
||||||
|
|
||||||
|
@ -377,14 +409,12 @@ export class PreferencesManager {
|
||||||
public async enableSync<K extends keyof PREF>(key: K): Promise<{ enabled: boolean; } | null> {
|
public async enableSync<K extends keyof PREF>(key: K): Promise<{ enabled: boolean; } | null> {
|
||||||
if (this.isSyncEnabled(key)) return Promise.resolve(null);
|
if (this.isSyncEnabled(key)) return Promise.resolve(null);
|
||||||
|
|
||||||
const record = this.getMatchedRecordOf(key);
|
// undefined ... cancel
|
||||||
|
async function resolveConflict(local: ValueOf<K>, remote: ValueOf<K>): Promise<ValueOf<K> | undefined> {
|
||||||
const existing = await this.storageProvider.cloudGet({ key, scope: record[0] });
|
|
||||||
if (existing != null && !deepEqual(existing.value, record[1])) {
|
|
||||||
const merge = (PREF_DEF as PreferencesDefinition)[key].mergeStrategy;
|
const merge = (PREF_DEF as PreferencesDefinition)[key].mergeStrategy;
|
||||||
let mergedValue: ValueOf<K> | undefined = undefined; // null と区別したいため
|
let mergedValue: ValueOf<K> | undefined = undefined; // null と区別したいため
|
||||||
try {
|
try {
|
||||||
if (merge != null) mergedValue = merge(record[1], existing.value);
|
if (merge != null) mergedValue = merge(local, remote);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// nop
|
// nop
|
||||||
}
|
}
|
||||||
|
@ -406,23 +436,51 @@ export class PreferencesManager {
|
||||||
}],
|
}],
|
||||||
default: mergedValue !== undefined ? 'merge' : 'remote',
|
default: mergedValue !== undefined ? 'merge' : 'remote',
|
||||||
});
|
});
|
||||||
if (canceled || choice == null) return { enabled: false };
|
if (canceled || choice == null) return undefined;
|
||||||
|
|
||||||
if (choice === 'remote') {
|
if (choice === 'remote') {
|
||||||
this.commit(key, existing.value);
|
return remote;
|
||||||
} else if (choice === 'local') {
|
} else if (choice === 'local') {
|
||||||
// nop
|
return local;
|
||||||
} else if (choice === 'merge') {
|
} else if (choice === 'merge') {
|
||||||
this.commit(key, mergedValue!);
|
return mergedValue!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const record = this.getMatchedRecordOf(key);
|
||||||
|
|
||||||
|
let newValue = record[1];
|
||||||
|
|
||||||
|
const existing = await this.storageProvider.cloudGet({ key, scope: record[0] });
|
||||||
|
if (existing != null && !deepEqual(record[1], existing.value)) {
|
||||||
|
const resolvedValue = await resolveConflict(record[1], existing.value);
|
||||||
|
if (resolvedValue === undefined) return { enabled: false }; // canceled
|
||||||
|
newValue = resolvedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.commit(key, newValue);
|
||||||
|
|
||||||
|
const done = os.waiting();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.storageProvider.cloudSet({ key, scope: record[0], value: newValue });
|
||||||
|
} catch (err) {
|
||||||
|
done();
|
||||||
|
|
||||||
|
os.alert({
|
||||||
|
type: 'error',
|
||||||
|
title: i18n.ts.somethingHappened,
|
||||||
|
text: err,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { enabled: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
done({ success: true });
|
||||||
|
|
||||||
record[2].sync = true;
|
record[2].sync = true;
|
||||||
this.save();
|
this.save();
|
||||||
|
|
||||||
// awaitの必要性は無い
|
|
||||||
this.storageProvider.cloudSet({ key, scope: record[0], value: this.s[key] });
|
|
||||||
|
|
||||||
return { enabled: true };
|
return { enabled: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"name": "misskey-js",
|
"name": "misskey-js",
|
||||||
"version": "2025.5.1-beta.5",
|
"version": "2025.5.1-beta.6",
|
||||||
"description": "Misskey SDK for JavaScript",
|
"description": "Misskey SDK for JavaScript",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
|
|
|
@ -9074,7 +9074,7 @@ export type operations = {
|
||||||
'application/json': {
|
'application/json': {
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
queue: 'system' | 'endedPollNotification' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver';
|
queue: 'system' | 'endedPollNotification' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver';
|
||||||
state: ('active' | 'wait' | 'delayed' | 'completed' | 'failed')[];
|
state: ('active' | 'wait' | 'delayed' | 'completed' | 'failed' | 'paused')[];
|
||||||
search?: string;
|
search?: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue