summaryrefslogtreecommitdiff
path: root/packages/frontend/src
diff options
context:
space:
mode:
authormisskey-release-bot[bot] <157398866+misskey-release-bot[bot]@users.noreply.github.com>2025-06-02 00:58:34 +0000
committerGitHub <noreply@github.com>2025-06-02 00:58:34 +0000
commit1620477a1c51c82e2a4e8967f19e15ebc91a3cd1 (patch)
tree2554f67e0ff21df2f485a23f3e364aa1b24d425d /packages/frontend/src
parentMerge pull request #16005 from misskey-dev/develop (diff)
parentRelease: 2025.6.0 (diff)
downloadmisskey-1620477a1c51c82e2a4e8967f19e15ebc91a3cd1.tar.gz
misskey-1620477a1c51c82e2a4e8967f19e15ebc91a3cd1.tar.bz2
misskey-1620477a1c51c82e2a4e8967f19e15ebc91a3cd1.zip
Merge pull request #16134 from misskey-dev/develop
Release: 2025.6.0
Diffstat (limited to 'packages/frontend/src')
-rw-r--r--packages/frontend/src/components/MkDrive.folder.vue4
-rw-r--r--packages/frontend/src/components/MkDrive.vue2
-rw-r--r--packages/frontend/src/components/MkFolderPage.vue2
-rw-r--r--packages/frontend/src/components/MkPostFormAttaches.vue8
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.vue4
-rw-r--r--packages/frontend/src/components/global/MkCustomEmoji.vue2
-rw-r--r--packages/frontend/src/composables/use-note-capture.ts13
-rw-r--r--packages/frontend/src/os.ts55
-rw-r--r--packages/frontend/src/pages/about-misskey.vue1
-rw-r--r--packages/frontend/src/pages/admin-user.vue8
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue8
-rw-r--r--packages/frontend/src/pages/avatar-decorations.vue6
-rw-r--r--packages/frontend/src/pages/chat/XMessage.vue4
-rw-r--r--packages/frontend/src/pages/custom-emojis-manager.vue6
-rw-r--r--packages/frontend/src/pages/drive.file.info.vue4
-rw-r--r--packages/frontend/src/pages/emojis.emoji.vue2
-rw-r--r--packages/frontend/src/pages/flash/flash.vue4
-rw-r--r--packages/frontend/src/pages/gallery/post.vue4
-rw-r--r--packages/frontend/src/pages/page.vue4
-rw-r--r--packages/frontend/src/pages/reset-password.vue4
-rw-r--r--packages/frontend/src/pages/settings/2fa.vue2
-rw-r--r--packages/frontend/src/pages/settings/avatar-decoration.vue4
-rw-r--r--packages/frontend/src/pages/settings/connect.vue4
-rw-r--r--packages/frontend/src/pages/settings/preferences.vue2
-rw-r--r--packages/frontend/src/plugin.ts4
-rw-r--r--packages/frontend/src/preferences/def.ts10
-rw-r--r--packages/frontend/src/preferences/manager.ts8
-rw-r--r--packages/frontend/src/ui/_common_/common.ts6
-rw-r--r--packages/frontend/src/ui/_common_/navbar-h.vue4
-rw-r--r--packages/frontend/src/ui/_common_/navbar.vue4
-rw-r--r--packages/frontend/src/ui/deck/antenna-column.vue2
-rw-r--r--packages/frontend/src/ui/deck/notifications-column.vue4
-rw-r--r--packages/frontend/src/utility/drive.ts8
-rw-r--r--packages/frontend/src/utility/get-drive-file-menu.ts4
-rw-r--r--packages/frontend/src/utility/get-embed-code.ts4
-rw-r--r--packages/frontend/src/utility/get-note-menu.ts4
-rw-r--r--packages/frontend/src/utility/get-user-menu.ts4
-rw-r--r--packages/frontend/src/utility/please-login.ts7
-rw-r--r--packages/frontend/src/widgets/WidgetNotifications.vue4
39 files changed, 148 insertions, 86 deletions
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index 8ba7520f35..d7dd12408c 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -282,8 +282,8 @@ function onContextmenu(ev: MouseEvent) {
menu = [{
text: i18n.ts.openInWindow,
icon: 'ti ti-app-window',
- action: () => {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkDriveWindow.vue')), {
+ action: async () => {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkDriveWindow.vue').then(x => x.default), {
initialFolder: props.folder,
}, {
closed: () => dispose(),
diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue
index 7e955f1529..4daba779d4 100644
--- a/packages/frontend/src/components/MkDrive.vue
+++ b/packages/frontend/src/components/MkDrive.vue
@@ -113,7 +113,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="filesPaginator.items.value.length == 0 && foldersPaginator.items.value.length == 0 && !fetching" :class="$style.empty">
<div v-if="draghover">{{ i18n.ts['empty-draghover'] }}</div>
- <div v-if="!draghover && folder == null"><strong>{{ i18n.ts.emptyDrive }}</strong><br/>{{ i18n.ts['empty-drive-description'] }}</div>
+ <div v-if="!draghover && folder == null"><strong>{{ i18n.ts.emptyDrive }}</strong></div>
<div v-if="!draghover && folder != null">{{ i18n.ts.emptyFolder }}</div>
</div>
</div>
diff --git a/packages/frontend/src/components/MkFolderPage.vue b/packages/frontend/src/components/MkFolderPage.vue
index edc954cf91..21700c9dd8 100644
--- a/packages/frontend/src/components/MkFolderPage.vue
+++ b/packages/frontend/src/components/MkFolderPage.vue
@@ -41,7 +41,7 @@ const emit = defineEmits<{
(_: 'closed'): void
}>();
-const zIndex = claimZIndex('middle');
+const zIndex = claimZIndex('low');
const showing = ref(true);
function closePage() {
diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue
index dd594ef7f1..f429db94df 100644
--- a/packages/frontend/src/components/MkPostFormAttaches.vue
+++ b/packages/frontend/src/components/MkPostFormAttaches.vue
@@ -126,7 +126,7 @@ async function rename(file) {
async function describe(file: Misskey.entities.DriveFile) {
if (mock) return;
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkFileCaptionEditWindow.vue').then(x => x.default), {
default: file.comment !== null ? file.comment : '',
file: file,
}, {
@@ -168,9 +168,11 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | Keyboar
menuItems.push({
text: i18n.ts.preview,
icon: 'ti ti-photo-search',
- action: () => {
- os.popup(defineAsyncComponent(() => import('@/components/MkImgPreviewDialog.vue')), {
+ action: async () => {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkImgPreviewDialog.vue').then(x => x.default), {
file: file,
+ }, {
+ closed: () => dispose(),
});
},
});
diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue
index 82214ed5a5..b4ef35c221 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.vue
@@ -174,8 +174,8 @@ function setupComplete() {
function launchTutorial() {
setupComplete();
- nextTick(() => {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTutorialDialog.vue')), {
+ nextTick(async () => {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkTutorialDialog.vue').then(x => x.default), {
initialPage: 1,
}, {
closed: () => dispose(),
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue
index ed114d8d31..31c358eee7 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.vue
+++ b/packages/frontend/src/components/global/MkCustomEmoji.vue
@@ -185,7 +185,7 @@ async function edit(name: string) {
const emoji = await misskeyApi('emoji', {
name: name,
});
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/pages/emoji-edit-dialog.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/pages/emoji-edit-dialog.vue').then(x => x.default), {
emoji: emoji,
}, {
closed: () => dispose(),
diff --git a/packages/frontend/src/composables/use-note-capture.ts b/packages/frontend/src/composables/use-note-capture.ts
index 90a5922b3e..2aeb9074e5 100644
--- a/packages/frontend/src/composables/use-note-capture.ts
+++ b/packages/frontend/src/composables/use-note-capture.ts
@@ -194,9 +194,9 @@ export function useNoteCapture(props: {
parentNote: Misskey.entities.Note | null;
mock?: boolean;
}): {
- $note: Reactive<ReactiveNoteData>;
- subscribe: () => void;
-} {
+ $note: Reactive<ReactiveNoteData>;
+ subscribe: () => void;
+ } {
const { note, parentNote, mock } = props;
const $note = reactive<ReactiveNoteData>({
@@ -225,8 +225,8 @@ export function useNoteCapture(props: {
let latestPollVotedKey: string | null = null;
function onReacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }): void {
- const normalizedName = ctx.reaction.replace(/^:(\w+):$/, ':$1@.:');
-
+ let normalizedName = ctx.reaction.replace(/^:(\w+):$/, ':$1@.:');
+ normalizedName = normalizedName.match('\u200d') ? normalizedName : normalizedName.replace(/\ufe0f/g, '');
if (reactionUserMap.has(ctx.userId) && reactionUserMap.get(ctx.userId) === normalizedName) return;
reactionUserMap.set(ctx.userId, normalizedName);
@@ -245,7 +245,8 @@ export function useNoteCapture(props: {
}
function onUnreacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }): void {
- const normalizedName = ctx.reaction.replace(/^:(\w+):$/, ':$1@.:');
+ let normalizedName = ctx.reaction.replace(/^:(\w+):$/, ':$1@.:');
+ normalizedName = normalizedName.match('\u200d') ? normalizedName : normalizedName.replace(/\ufe0f/g, '');
// 確実に一度リアクションされて取り消されている場合のみ処理をとめる(APIで初回読み込み→Streamでアップデート等の場合、reactionUserMapに情報がないため)
if (reactionUserMap.has(ctx.userId) && reactionUserMap.get(ctx.userId) === noReaction) return;
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 08291a5595..be247f96c4 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -206,6 +206,57 @@ export function popup<T extends Component>(
};
}
+export async function popupAsyncWithDialog<T extends Component>(
+ componentFetching: Promise<T>,
+ props: ComponentProps<T>,
+ events: Partial<ComponentEmit<T>> = {},
+): Promise<{ dispose: () => void }> {
+ let component: T;
+ let closeWaiting = () => {};
+
+ const timer = window.setTimeout(() => {
+ closeWaiting = waiting();
+ }, 100); // コンポーネントがキャッシュされている場合にもwaitingが表示されて画面がちらつくのを防止するためにラグを追加
+
+ try {
+ component = await componentFetching;
+ } catch (err) {
+ window.clearTimeout(timer);
+ closeWaiting();
+ alert({
+ type: 'error',
+ title: i18n.ts.somethingHappened,
+ text: 'CODE: ASYNC_COMP_LOAD_FAIL',
+ });
+ throw err;
+ }
+
+ window.clearTimeout(timer);
+ closeWaiting();
+
+ markRaw(component);
+
+ const id = ++popupIdCount;
+ const dispose = () => {
+ // このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ?
+ window.setTimeout(() => {
+ popups.value = popups.value.filter(p => p.id !== id);
+ }, 0);
+ };
+ const state = {
+ component,
+ props,
+ events,
+ id,
+ };
+
+ popups.value.push(state);
+
+ return {
+ dispose,
+ };
+}
+
export function pageWindow(path: string) {
const { dispose } = popup(MkPageWindow, {
initialPath: path,
@@ -787,9 +838,9 @@ export function launchUploader(
multiple?: boolean;
},
): Promise<Misskey.entities.DriveFile[]> {
- return new Promise((res, rej) => {
+ return new Promise(async (res, rej) => {
if (files.length === 0) return rej();
- const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUploaderDialog.vue')), {
+ const { dispose } = await popupAsyncWithDialog(import('@/components/MkUploaderDialog.vue').then(x => x.default), {
files: markRaw(files),
folderId: options?.folderId,
multiple: options?.multiple,
diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index 7916cc7834..9812f24982 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -391,6 +391,7 @@ const patrons = [
'asata',
'ruru',
'みりめい',
+ '東雲 琥珀',
];
const thereIsTreasure = ref($i && !claimedAchievements.includes('foundTreasure'));
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 15cd219834..b0862ccaa4 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -477,16 +477,16 @@ function toggleRoleItem(role) {
}
}
-function createAnnouncement() {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
+async function createAnnouncement() {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkUserAnnouncementEditDialog.vue').then(x => x.default), {
user: user.value,
}, {
closed: () => dispose(),
});
}
-function editAnnouncement(announcement) {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
+async function editAnnouncement(announcement) {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkUserAnnouncementEditDialog.vue').then(x => x.default), {
user: user.value,
announcement,
}, {
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue
index 68c7048ae1..4c2c26ec45 100644
--- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue
+++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue
@@ -525,10 +525,10 @@ const headerPageMetadata = computed(() => ({
const headerActions = computed(() => [{
icon: 'ti ti-search',
text: i18n.ts.search,
- handler: () => {
+ handler: async () => {
if (searchWindowOpening) return;
searchWindowOpening = true;
- const { dispose } = os.popup(defineAsyncComponent(() => import('./custom-emojis-manager.local.list.search.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('./custom-emojis-manager.local.list.search.vue').then(x => x.default), {
query: searchQuery.value,
}, {
queryUpdated: (query: EmojiSearchQuery) => {
@@ -584,8 +584,8 @@ const headerActions = computed(() => [{
}, {
icon: 'ti ti-notes',
text: i18n.ts._customEmojisManager._gridCommon.registrationLogs,
- handler: () => {
- const { dispose } = os.popup(defineAsyncComponent(() => import('./custom-emojis-manager.local.list.logs.vue')), {
+ handler: async () => {
+ const { dispose } = await os.popupAsyncWithDialog(import('./custom-emojis-manager.local.list.logs.vue').then(x => x.default), {
logs: requestLogs.value,
}, {
closed: () => {
diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue
index 675e558de9..f96c02a567 100644
--- a/packages/frontend/src/pages/avatar-decorations.vue
+++ b/packages/frontend/src/pages/avatar-decorations.vue
@@ -46,7 +46,7 @@ function load() {
load();
async function add(ev: MouseEvent) {
- const { dispose } = os.popup(defineAsyncComponent(() => import('./avatar-decoration-edit-dialog.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('./avatar-decoration-edit-dialog.vue').then(x => x.default), {
}, {
done: result => {
if (result.created) {
@@ -57,8 +57,8 @@ async function add(ev: MouseEvent) {
});
}
-function edit(avatarDecoration) {
- const { dispose } = os.popup(defineAsyncComponent(() => import('./avatar-decoration-edit-dialog.vue')), {
+async function edit(avatarDecoration) {
+ const { dispose } = await os.popupAsyncWithDialog(import('./avatar-decoration-edit-dialog.vue').then(x => x.default), {
avatarDecoration: avatarDecoration,
}, {
done: result => {
diff --git a/packages/frontend/src/pages/chat/XMessage.vue b/packages/frontend/src/pages/chat/XMessage.vue
index 95f6d870dc..c5e8d0fdb6 100644
--- a/packages/frontend/src/pages/chat/XMessage.vue
+++ b/packages/frontend/src/pages/chat/XMessage.vue
@@ -181,9 +181,9 @@ function showMenu(ev: MouseEvent, contextmenu = false) {
menu.push({
text: i18n.ts.reportAbuse,
icon: 'ti ti-exclamation-circle',
- action: () => {
+ action: async () => {
const localUrl = `${url}/chat/messages/${props.message.id}`;
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAbuseReportWindow.vue').then(x => x.default), {
user: props.message.fromUser!,
initialComment: `${localUrl}\n-----\n`,
}, {
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index c2bc621f6a..5aba0f68a3 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -128,7 +128,7 @@ const toggleSelect = (emoji) => {
};
const add = async (ev: MouseEvent) => {
- const { dispose } = os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('./emoji-edit-dialog.vue').then(x => x.default), {
}, {
done: result => {
if (result.created) {
@@ -139,8 +139,8 @@ const add = async (ev: MouseEvent) => {
});
};
-const edit = (emoji) => {
- const { dispose } = os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
+const edit = async (emoji) => {
+ const { dispose } = await os.popupAsyncWithDialog(import('./emoji-edit-dialog.vue').then(x => x.default), {
emoji: emoji,
}, {
done: result => {
diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue
index e8ac13c223..1def215afc 100644
--- a/packages/frontend/src/pages/drive.file.info.vue
+++ b/packages/frontend/src/pages/drive.file.info.vue
@@ -174,10 +174,10 @@ function rename() {
});
}
-function describe() {
+async function describe() {
if (!file.value) return;
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkFileCaptionEditWindow.vue').then(x => x.default), {
default: file.value.comment ?? '',
file: file.value,
}, {
diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue
index d5570eb20a..aaf433e78e 100644
--- a/packages/frontend/src/pages/emojis.emoji.vue
+++ b/packages/frontend/src/pages/emojis.emoji.vue
@@ -67,7 +67,7 @@ function menu(ev) {
}
const edit = async (emoji) => {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/pages/emoji-edit-dialog.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/pages/emoji-edit-dialog.vue').then(x => x.default), {
emoji: emoji,
}, {
closed: () => dispose(),
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index 8eb6521aac..1c9cb92bc2 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -238,12 +238,12 @@ async function run() {
}
}
-function reportAbuse() {
+async function reportAbuse() {
if (!flash.value) return;
const pageUrl = `${url}/play/${flash.value.id}`;
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAbuseReportWindow.vue').then(x => x.default), {
user: flash.value.user,
initialComment: `Play: ${pageUrl}\n-----\n`,
}, {
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index 891f58ad08..3db003d9e2 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -153,12 +153,12 @@ function edit() {
router.push(`/gallery/${post.value.id}/edit`);
}
-function reportAbuse() {
+async function reportAbuse() {
if (!post.value) return;
const pageUrl = `${url}/gallery/${post.value.id}`;
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAbuseReportWindow.vue').then(x => x.default), {
user: post.value.user,
initialComment: `Post: ${pageUrl}\n-----\n`,
}, {
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 82c953a2df..2cd8718968 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -245,12 +245,12 @@ function pin(pin) {
});
}
-function reportAbuse() {
+async function reportAbuse() {
if (!page.value) return;
const pageUrl = `${url}/@${props.username}/pages/${props.pageName}`;
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAbuseReportWindow.vue').then(x => x.default), {
user: page.value.user,
initialComment: `Page: ${pageUrl}\n-----\n`,
}, {
diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue
index 6584888148..9e2069f0e5 100644
--- a/packages/frontend/src/pages/reset-password.vue
+++ b/packages/frontend/src/pages/reset-password.vue
@@ -41,9 +41,9 @@ async function save() {
mainRouter.push('/');
}
-onMounted(() => {
+onMounted(async () => {
if (props.token == null) {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkForgotPassword.vue')), {}, {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkForgotPassword.vue').then(x => x.default), {}, {
closed: () => dispose(),
});
mainRouter.push('/');
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index f47ffc984e..2f639cd090 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -117,7 +117,7 @@ async function registerTOTP(): Promise<void> {
token: auth.result.token,
});
- const { dispose } = os.popup(defineAsyncComponent(() => import('./2fa.qrdialog.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('./2fa.qrdialog.vue').then(x => x.default), {
twoFactorData,
}, {
closed: () => dispose(),
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue
index 14c3a03d2b..c58cd57c65 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.vue
@@ -68,8 +68,8 @@ misskeyApi('get-avatar-decorations').then(_avatarDecorations => {
loading.value = false;
});
-function openDecoration(avatarDecoration, index?: number) {
- const { dispose } = os.popup(defineAsyncComponent(() => import('./avatar-decoration.dialog.vue')), {
+async function openDecoration(avatarDecoration, index?: number) {
+ const { dispose } = await os.popupAsyncWithDialog(import('./avatar-decoration.dialog.vue').then(x => x.default), {
decoration: avatarDecoration,
usingIndex: index,
}, {
diff --git a/packages/frontend/src/pages/settings/connect.vue b/packages/frontend/src/pages/settings/connect.vue
index 280ee546dc..959442d25f 100644
--- a/packages/frontend/src/pages/settings/connect.vue
+++ b/packages/frontend/src/pages/settings/connect.vue
@@ -81,8 +81,8 @@ const pagination = {
noPaging: true,
};
-function generateToken() {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {}, {
+async function generateToken() {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkTokenGenerateWindow.vue').then(x => x.default), {}, {
done: async result => {
const { name, permissions } = result;
const { token } = await misskeyApi('miauth/gen-token', {
diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue
index 18dfc2250c..05e491ae37 100644
--- a/packages/frontend/src/pages/settings/preferences.vue
+++ b/packages/frontend/src/pages/settings/preferences.vue
@@ -604,7 +604,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInfo>
<div class="_gaps_s">
- <div>{{ i18n.ts._clientPerformanceIssueTip.title }}</div>
+ <div>{{ i18n.ts._clientPerformanceIssueTip.title }}:</div>
<div>
<div><b>{{ i18n.ts._clientPerformanceIssueTip.makeSureDisabledAdBlocker }}</b></div>
<div>{{ i18n.ts._clientPerformanceIssueTip.makeSureDisabledAdBlocker_description }}</div>
diff --git a/packages/frontend/src/plugin.ts b/packages/frontend/src/plugin.ts
index 1b51850e77..f557d1047a 100644
--- a/packages/frontend/src/plugin.ts
+++ b/packages/frontend/src/plugin.ts
@@ -95,8 +95,8 @@ export async function authorizePlugin(plugin: Plugin) {
if (plugin.permissions == null || plugin.permissions.length === 0) return;
if (Object.hasOwn(store.s.pluginTokens, plugin.installId)) return;
- const token = await new Promise<string>((res, rej) => {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {
+ const token = await new Promise<string>(async (res, rej) => {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkTokenGenerateWindow.vue').then(x => x.default), {
title: i18n.ts.tokenRequested,
information: i18n.ts.pluginTokenRequestedDescription,
initialName: plugin.name,
diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts
index 86d5c8af98..2cbeea2883 100644
--- a/packages/frontend/src/preferences/def.ts
+++ b/packages/frontend/src/preferences/def.ts
@@ -6,12 +6,12 @@
import * as Misskey from 'misskey-js';
import { hemisphere } from '@@/js/intl-const.js';
import { v4 as uuid } from 'uuid';
+import { definePreferences } from './manager.js';
import type { Theme } from '@/theme.js';
import type { SoundType } from '@/utility/sound.js';
import type { Plugin } from '@/plugin.js';
import type { DeviceKind } from '@/utility/device-kind.js';
import type { DeckProfile } from '@/deck.js';
-import type { PreferencesDefinition } from './manager.js';
import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js';
import { deepEqual } from '@/utility/deep-equal.js';
@@ -33,7 +33,7 @@ export type SoundStore = {
// NOTE: デフォルト値は他の設定の状態に依存してはならない(依存していた場合、ユーザーがその設定項目単体で「初期値にリセット」した場合不具合の原因になる)
-export const PREF_DEF = {
+export const PREF_DEF = definePreferences({
accounts: {
default: [] as [host: string, user: {
id: string;
@@ -88,7 +88,7 @@ export const PREF_DEF = {
emojis: string[];
}[],
mergeStrategy: (a, b) => {
- const mergedItems = [] as (typeof a)[];
+ const mergedItems = [] as typeof a;
for (const x of a.concat(b)) {
const sameIdItem = mergedItems.find(y => y.id === x.id);
if (sameIdItem != null) {
@@ -119,7 +119,7 @@ export const PREF_DEF = {
themes: {
default: [] as Theme[],
mergeStrategy: (a, b) => {
- const mergedItems = [] as (typeof a)[];
+ const mergedItems = [] as typeof a;
for (const x of a.concat(b)) {
const sameIdItem = mergedItems.find(y => y.id === x.id);
if (sameIdItem != null) {
@@ -464,4 +464,4 @@ export const PREF_DEF = {
'experimental.enableFolderPageView': {
default: false,
},
-} satisfies PreferencesDefinition;
+});
diff --git a/packages/frontend/src/preferences/manager.ts b/packages/frontend/src/preferences/manager.ts
index cede145e74..603aac851c 100644
--- a/packages/frontend/src/preferences/manager.ts
+++ b/packages/frontend/src/preferences/manager.ts
@@ -96,6 +96,14 @@ type PreferencesDefinitionRecord<Default, T = Default extends (...args: any) =>
export type PreferencesDefinition = Record<string, PreferencesDefinitionRecord<any>>;
+export function definePreferences<T extends Record<string, unknown>>(x: {
+ [K in keyof T]: PreferencesDefinitionRecord<T[K]>
+}): {
+ [K in keyof T]: PreferencesDefinitionRecord<T[K]>
+ } {
+ return x;
+}
+
export function getInitialPrefValue<K extends keyof PREF>(k: K): ValueOf<K> {
if (typeof PREF_DEF[k].default === 'function') { // factory
return PREF_DEF[k].default();
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index 819e1fa42f..9872937d21 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -4,10 +4,10 @@
*/
import { defineAsyncComponent } from 'vue';
+import { host } from '@@/js/config.js';
import type { MenuItem } from '@/types/menu.js';
import * as os from '@/os.js';
import { instance } from '@/instance.js';
-import { host } from '@@/js/config.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/i.js';
@@ -146,8 +146,8 @@ export function openInstanceMenu(ev: MouseEvent) {
menuItems.push({
text: i18n.ts._initialTutorial.launchTutorial,
icon: 'ti ti-presentation',
- action: () => {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTutorialDialog.vue')), {}, {
+ action: async () => {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkTutorialDialog.vue').then(x => x.default), {}, {
closed: () => dispose(),
});
},
diff --git a/packages/frontend/src/ui/_common_/navbar-h.vue b/packages/frontend/src/ui/_common_/navbar-h.vue
index 24e2b28f1c..688e195ce6 100644
--- a/packages/frontend/src/ui/_common_/navbar-h.vue
+++ b/packages/frontend/src/ui/_common_/navbar-h.vue
@@ -71,8 +71,8 @@ const otherNavItemIndicated = computed<boolean>(() => {
return false;
});
-function more(ev: MouseEvent) {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
+async function more(ev: MouseEvent) {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkLaunchPad.vue').then(x => x.default), {
anchorElement: ev.currentTarget ?? ev.target,
anchor: { x: 'center', y: 'bottom' },
}, {
diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue
index c8b7491895..28913a54ab 100644
--- a/packages/frontend/src/ui/_common_/navbar.vue
+++ b/packages/frontend/src/ui/_common_/navbar.vue
@@ -176,10 +176,10 @@ function openAccountMenu(ev: MouseEvent) {
}, ev);
}
-function more(ev: MouseEvent) {
+async function more(ev: MouseEvent) {
const target = getHTMLElementOrNull(ev.currentTarget ?? ev.target);
if (!target) return;
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkLaunchPad.vue').then(x => x.default), {
anchorElement: target,
}, {
closed: () => dispose(),
diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue
index 716f0ba995..8de894ee88 100644
--- a/packages/frontend/src/ui/deck/antenna-column.vue
+++ b/packages/frontend/src/ui/deck/antenna-column.vue
@@ -72,7 +72,7 @@ async function setAntenna() {
if (canceled || antenna == null) return;
if (antenna === '_CREATE_') {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAntennaEditorDialog.vue')), {}, {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAntennaEditorDialog.vue').then(x => x.default), {}, {
created: (newAntenna: MisskeyEntities.Antenna) => {
antennasCache.delete();
updateColumn(props.column.id, {
diff --git a/packages/frontend/src/ui/deck/notifications-column.vue b/packages/frontend/src/ui/deck/notifications-column.vue
index 0e84ed3572..469e9472ab 100644
--- a/packages/frontend/src/ui/deck/notifications-column.vue
+++ b/packages/frontend/src/ui/deck/notifications-column.vue
@@ -27,8 +27,8 @@ const props = defineProps<{
const notificationsComponent = useTemplateRef('notificationsComponent');
-function func() {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), {
+async function func() {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkNotificationSelectWindow.vue').then(x => x.default), {
excludeTypes: props.column.excludeTypes,
}, {
done: async (res) => {
diff --git a/packages/frontend/src/utility/drive.ts b/packages/frontend/src/utility/drive.ts
index f171a4d14d..fcc847653d 100644
--- a/packages/frontend/src/utility/drive.ts
+++ b/packages/frontend/src/utility/drive.ts
@@ -172,8 +172,8 @@ export function chooseFileFromPcAndUpload(
export function chooseDriveFile(options: {
multiple?: boolean;
} = {}): Promise<Misskey.entities.DriveFile[]> {
- return new Promise(resolve => {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkDriveFileSelectDialog.vue')), {
+ return new Promise(async resolve => {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkDriveFileSelectDialog.vue').then(x => x.default), {
multiple: options.multiple ?? false,
}, {
done: files => {
@@ -286,8 +286,8 @@ export async function createCroppedImageDriveFileFromImageDriveFile(imageDriveFi
}
export async function selectDriveFolder(initialFolder: Misskey.entities.DriveFolder['id'] | null): Promise<Misskey.entities.DriveFolder[]> {
- return new Promise(resolve => {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkDriveFolderSelectDialog.vue')), {
+ return new Promise(async resolve => {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkDriveFolderSelectDialog.vue').then(x => x.default), {
initialFolder,
}, {
done: folders => {
diff --git a/packages/frontend/src/utility/get-drive-file-menu.ts b/packages/frontend/src/utility/get-drive-file-menu.ts
index 4b4bab3125..040cf8f976 100644
--- a/packages/frontend/src/utility/get-drive-file-menu.ts
+++ b/packages/frontend/src/utility/get-drive-file-menu.ts
@@ -28,8 +28,8 @@ function rename(file: Misskey.entities.DriveFile) {
});
}
-function describe(file: Misskey.entities.DriveFile) {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
+async function describe(file: Misskey.entities.DriveFile) {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkFileCaptionEditWindow.vue').then(x => x.default), {
default: file.comment ?? '',
file: file,
}, {
diff --git a/packages/frontend/src/utility/get-embed-code.ts b/packages/frontend/src/utility/get-embed-code.ts
index d458e64f19..de36314ac2 100644
--- a/packages/frontend/src/utility/get-embed-code.ts
+++ b/packages/frontend/src/utility/get-embed-code.ts
@@ -64,7 +64,7 @@ export function getEmbedCode(path: string, params?: EmbedParams): string {
*
* カスタマイズ機能がいらない場合(事前にパラメータを指定する場合)は getEmbedCode を直接使ってください
*/
-export function genEmbedCode(entity: EmbeddableEntity, id: string, params?: EmbedParams) {
+export async function genEmbedCode(entity: EmbeddableEntity, id: string, params?: EmbedParams) {
const _params = { ...params };
if (embedRouteWithScrollbar.includes(entity) && _params.maxHeight == null) {
@@ -75,7 +75,7 @@ export function genEmbedCode(entity: EmbeddableEntity, id: string, params?: Embe
if (window.innerWidth < MOBILE_THRESHOLD) {
copyToClipboard(getEmbedCode(`/embed/${entity}/${id}`, _params));
} else {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkEmbedCodeGenDialog.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkEmbedCodeGenDialog.vue').then(x => x.default), {
entity,
id,
params: _params,
diff --git a/packages/frontend/src/utility/get-note-menu.ts b/packages/frontend/src/utility/get-note-menu.ts
index dc813cb78e..5b99be5fdb 100644
--- a/packages/frontend/src/utility/get-note-menu.ts
+++ b/packages/frontend/src/utility/get-note-menu.ts
@@ -135,12 +135,12 @@ export function getAbuseNoteMenu(note: Misskey.entities.Note, text: string): Men
return {
icon: 'ti ti-exclamation-circle',
text,
- action: (): void => {
+ action: async (): Promise<void> => {
const localUrl = `${url}/notes/${note.id}`;
let noteInfo = '';
if (note.url ?? note.uri != null) noteInfo = `Note: ${note.url ?? note.uri}\n`;
noteInfo += `Local Note: ${localUrl}\n`;
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAbuseReportWindow.vue').then(x => x.default), {
user: note.user,
initialComment: `${noteInfo}-----\n`,
}, {
diff --git a/packages/frontend/src/utility/get-user-menu.ts b/packages/frontend/src/utility/get-user-menu.ts
index 563e45c446..5c08b8c462 100644
--- a/packages/frontend/src/utility/get-user-menu.ts
+++ b/packages/frontend/src/utility/get-user-menu.ts
@@ -94,8 +94,8 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
});
}
- function reportAbuse() {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
+ async function reportAbuse() {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkAbuseReportWindow.vue').then(x => x.default), {
user: user,
}, {
closed: () => dispose(),
diff --git a/packages/frontend/src/utility/please-login.ts b/packages/frontend/src/utility/please-login.ts
index 9253105f48..737e7d7c6e 100644
--- a/packages/frontend/src/utility/please-login.ts
+++ b/packages/frontend/src/utility/please-login.ts
@@ -3,11 +3,10 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { defineAsyncComponent } from 'vue';
import { $i } from '@/i.js';
import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
-import { popup } from '@/os.js';
+import { popupAsyncWithDialog } from '@/os.js';
export type OpenOnRemoteOptions = {
/**
@@ -45,7 +44,7 @@ export type OpenOnRemoteOptions = {
params: Record<string, string>;
};
-export function pleaseLogin(opts: {
+export async function pleaseLogin(opts: {
path?: string;
message?: string;
openOnRemote?: OpenOnRemoteOptions;
@@ -59,7 +58,7 @@ export function pleaseLogin(opts: {
_openOnRemote = opts.openOnRemote;
}
- const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {
+ const { dispose } = await popupAsyncWithDialog(import('@/components/MkSigninDialog.vue').then(x => x.default), {
autoSet: true,
message: opts.message ?? (_openOnRemote ? i18n.ts.signinOrContinueOnRemote : i18n.ts.signinRequired),
openOnRemote: _openOnRemote,
diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue
index b21a82179e..a1af5d084c 100644
--- a/packages/frontend/src/widgets/WidgetNotifications.vue
+++ b/packages/frontend/src/widgets/WidgetNotifications.vue
@@ -54,8 +54,8 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
emit,
);
-const configureNotification = () => {
- const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), {
+const configureNotification = async () => {
+ const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkNotificationSelectWindow.vue').then(x => x.default), {
excludeTypes: widgetProps.excludeTypes,
}, {
done: async (res) => {