diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0fe5b0ac2853..4758e2537a78 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,10 +9,12 @@
- Enhance: サイズ制限を超過するファイルをアップロードしようとした際にエラーを出すように
- Enhance: アイコンデコレーション管理画面にプレビューを追加
- Enhance: コントロールパネル内のファイル一覧でセンシティブなファイルを区別しやすく
+- Enhance: ScratchpadにUIインスペクターを追加
- Fix: サーバーメトリクスが2つ以上あるとリロード直後の表示がおかしくなる問題を修正
- Fix: 月の違う同じ日はセパレータが表示されないのを修正
- Fix: 縦横比が極端なカスタム絵文字を表示する際にレイアウトが崩れる箇所があるのを修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/725)
+- Fix: 設定変更時のリロード確認ダイアログが複数個表示されることがある問題を修正
- Fix: ERROR CODE: APP_IMPORT on iOS <= 16.3 (SyntaxError: Invalid regular expression: invalid group specifier name)
### Server
diff --git a/locales/index.d.ts b/locales/index.d.ts
index fecc5703950d..b06e0f245b19 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -3121,7 +3121,7 @@ export interface Locale extends ILocale {
*/
"narrow": string;
/**
- * 設定はページリロード後に反映されます。今すぐリロードしますか?
+ * 設定はページリロード後に反映されます。
*/
"reloadToApplySetting": string;
/**
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index a1210bad2948..292569cc5afa 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -592,6 +592,8 @@ ascendingOrder: "昇順"
descendingOrder: "降順"
scratchpad: "スクラッチパッド"
scratchpadDescription: "スクラッチパッドは、AiScriptの実験環境を提供します。Misskeyと対話するコードの記述、実行、結果の確認ができます。"
+uiInspector: "UIインスペクター"
+uiInspectorDescription: "メモリ上に存在しているUIコンポーネントのインスタンスの一覧を見ることができます。UIコンポーネントはUi:C:系関数により生成されます。"
output: "出力"
script: "スクリプト"
disablePagesScript: "Pagesのスクリプトを無効にする"
@@ -776,7 +778,7 @@ left: "左"
center: "中央"
wide: "広い"
narrow: "狭い"
-reloadToApplySetting: "設定はページリロード後に反映されます。今すぐリロードしますか?"
+reloadToApplySetting: "設定はページリロード後に反映されます。"
needReloadToApply: "反映には再起動が必要です。"
showTitlebar: "タイトルバーを表示する"
clearCache: "キャッシュをクリア"
diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts
index 793d8974b34b..e827ffa68c0f 100644
--- a/packages/backend/src/core/AntennaService.ts
+++ b/packages/backend/src/core/AntennaService.ts
@@ -123,11 +123,14 @@ export class AntennaService implements OnApplicationShutdown {
if (antenna.src === 'home') {
// TODO
} else if (antenna.src === 'list') {
- const listUsers = (await this.userListMembershipsRepository.findBy({
- userListId: antenna.userListId!,
- })).map(x => x.userId);
-
- if (!listUsers.includes(note.userId)) return false;
+ if (antenna.userListId == null) return false;
+ const exists = await this.userListMembershipsRepository.exists({
+ where: {
+ userListId: antenna.userListId,
+ userId: note.userId,
+ },
+ });
+ if (!exists) return false;
} else if (antenna.src === 'users') {
const accts = antenna.users.map(x => {
const { username, host } = Acct.parse(x);
diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue
index 9aaa8ff9c633..897ff6acdf09 100644
--- a/packages/frontend/src/pages/scratchpad.vue
+++ b/packages/frontend/src/pages/scratchpad.vue
@@ -30,6 +30,24 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+ {{ i18n.ts.uiInspector }}
+
+
+
{{ c.value.type }}
+
{{ c.value.id }}
+
+
+
+
+
+
{{ i18n.ts.uiInspectorDescription }}
+
+
+
{{ i18n.ts.scratchpadDescription }}
@@ -43,6 +61,7 @@ import { onDeactivated, onUnmounted, Ref, ref, watch, computed } from 'vue';
import { Interpreter, Parser, utils } from '@syuilo/aiscript';
import MkContainer from '@/components/MkContainer.vue';
import MkButton from '@/components/MkButton.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
import MkCodeEditor from '@/components/MkCodeEditor.vue';
import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js';
import * as os from '@/os.js';
@@ -61,6 +80,7 @@ const logs = ref([]);
const root = ref();
const components = ref[[]>([]);
const uiKey = ref(0);
+const uiInspectorOpenedComponents = ref(new Map);
const saved = miLocalStorage.getItem('scratchpad');
if (saved) {
@@ -71,6 +91,14 @@ watch(code, () => {
miLocalStorage.setItem('scratchpad', code.value);
});
+function stringifyUiProps(uiProps) {
+ return JSON.stringify(
+ { ...uiProps, type: undefined, id: undefined },
+ (k, v) => typeof v === 'function' ? '' : v,
+ 2
+ );
+}
+
async function run() {
if (aiscript) aiscript.abort();
root.value = undefined;
@@ -192,4 +220,35 @@ definePageMetadata(() => ({
}
}
}
+
+.uiInspector {
+ display: grid;
+ gap: 8px;
+ padding: 16px;
+}
+
+.uiInspectorType {
+ display: inline-block;
+ border: hidden;
+ border-radius: 10px;
+ background-color: var(--panelHighlight);
+ padding: 2px 8px;
+ font-size: 12px;
+}
+
+.uiInspectorId {
+ display: inline-block;
+ padding-left: 8px;
+}
+
+.uiInspectorDescription {
+ display: block;
+ font-size: 12px;
+ padding-top: 16px;
+}
+
+.uiInspectorPropsToggle {
+ background: none;
+ border: none;
+}
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 15af5617cc5a..69238b043693 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -258,7 +258,7 @@ import { langs } from '@@/js/config.js';
import { defaultStore } from '@/store.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
-import { unisonReload } from '@/scripts/unison-reload.js';
+import { reloadAsk } from '@/scripts/reload-ask.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { miLocalStorage } from '@/local-storage.js';
@@ -270,16 +270,6 @@ const fontSize = ref(miLocalStorage.getItem('fontSize'));
const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null);
const dataSaver = ref(defaultStore.state.dataSaver);
-async function reloadAsk() {
- const { canceled } = await os.confirm({
- type: 'info',
- text: i18n.ts.reloadToApplySetting,
- });
- if (canceled) return;
-
- unisonReload();
-}
-
const hemisphere = computed(defaultStore.makeGetterSetter('hemisphere'));
const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind'));
const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior'));
@@ -369,7 +359,7 @@ watch([
confirmWhenRevealingSensitiveMedia,
contextMenu,
], async () => {
- await reloadAsk();
+ await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
});
const emojiIndexLangs = ['en-US', 'ja-JP', 'ja-JP_hira'] as const;
diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue
index 7f8460e31640..a0e6cad9c8d4 100644
--- a/packages/frontend/src/pages/settings/navbar.vue
+++ b/packages/frontend/src/pages/settings/navbar.vue
@@ -54,7 +54,7 @@ import MkContainer from '@/components/MkContainer.vue';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
import { defaultStore } from '@/store.js';
-import { unisonReload } from '@/scripts/unison-reload.js';
+import { reloadAsk } from '@/scripts/reload-ask.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -67,16 +67,6 @@ const items = ref(defaultStore.state.menu.map(x => ({
const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay'));
-async function reloadAsk() {
- const { canceled } = await os.confirm({
- type: 'info',
- text: i18n.ts.reloadToApplySetting,
- });
- if (canceled) return;
-
- unisonReload();
-}
-
async function addItem() {
const menu = Object.keys(navbarItemDef).filter(k => !defaultStore.state.menu.includes(k));
const { canceled, result: item } = await os.select({
@@ -100,7 +90,7 @@ function removeItem(index: number) {
async function save() {
defaultStore.set('menu', items.value.map(x => x.type));
- await reloadAsk();
+ await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
}
function reset() {
@@ -111,7 +101,7 @@ function reset() {
}
watch(menuDisplay, async () => {
- await reloadAsk();
+ await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
});
const headerActions = computed(() => []);
diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue
index a1cb2ea1c428..0f7609c83e95 100644
--- a/packages/frontend/src/pages/settings/other.vue
+++ b/packages/frontend/src/pages/settings/other.vue
@@ -98,7 +98,7 @@ import { defaultStore } from '@/store.js';
import { signout, signinRequired } from '@/account.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { unisonReload } from '@/scripts/unison-reload.js';
+import { reloadAsk } from '@/scripts/reload-ask.js';
import FormSection from '@/components/form/section.vue';
const $i = signinRequired();
@@ -132,16 +132,6 @@ async function deleteAccount() {
await signout();
}
-async function reloadAsk() {
- const { canceled } = await os.confirm({
- type: 'info',
- text: i18n.ts.reloadToApplySetting,
- });
- if (canceled) return;
-
- unisonReload();
-}
-
async function updateRepliesAll(withReplies: boolean) {
const { canceled } = await os.confirm({
type: 'warning',
@@ -155,7 +145,7 @@ async function updateRepliesAll(withReplies: boolean) {
watch([
enableCondensedLineForAcct,
], async () => {
- await reloadAsk();
+ await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
});
const headerActions = computed(() => []);
diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue
index 7d192bcbea6e..ce8ec686928a 100644
--- a/packages/frontend/src/pages/settings/theme.vue
+++ b/packages/frontend/src/pages/settings/theme.vue
@@ -88,19 +88,9 @@ import { uniqueBy } from '@/scripts/array.js';
import { fetchThemes, getThemes } from '@/theme-store.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { miLocalStorage } from '@/local-storage.js';
-import { unisonReload } from '@/scripts/unison-reload.js';
+import { reloadAsk } from '@/scripts/reload-ask.js';
import * as os from '@/os.js';
-async function reloadAsk() {
- const { canceled } = await os.confirm({
- type: 'info',
- text: i18n.ts.reloadToApplySetting,
- });
- if (canceled) return;
-
- unisonReload();
-}
-
const installedThemes = ref(getThemes());
const builtinThemes = getBuiltinThemesRef();
@@ -148,13 +138,13 @@ watch(syncDeviceDarkMode, () => {
}
});
-watch(wallpaper, () => {
+watch(wallpaper, async () => {
if (wallpaper.value == null) {
miLocalStorage.removeItem('wallpaper');
} else {
miLocalStorage.setItem('wallpaper', wallpaper.value);
}
- reloadAsk();
+ await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
});
onActivated(() => {
diff --git a/packages/frontend/src/scripts/reload-ask.ts b/packages/frontend/src/scripts/reload-ask.ts
new file mode 100644
index 000000000000..733d91b85ab4
--- /dev/null
+++ b/packages/frontend/src/scripts/reload-ask.ts
@@ -0,0 +1,40 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
+
+let isReloadConfirming = false;
+
+export async function reloadAsk(opts: {
+ unison?: boolean;
+ reason?: string;
+}) {
+ if (isReloadConfirming) {
+ return;
+ }
+
+ isReloadConfirming = true;
+
+ const { canceled } = await os.confirm(opts.reason == null ? {
+ type: 'info',
+ text: i18n.ts.reloadConfirm,
+ } : {
+ type: 'info',
+ title: i18n.ts.reloadConfirm,
+ text: opts.reason,
+ }).finally(() => {
+ isReloadConfirming = false;
+ });
+
+ if (canceled) return;
+
+ if (opts.unison) {
+ unisonReload();
+ } else {
+ location.reload();
+ }
+}
]