summaryrefslogtreecommitdiff
path: root/packages/client/src
diff options
context:
space:
mode:
authortamaina <tamaina@hotmail.co.jp>2022-05-28 01:31:23 +0900
committertamaina <tamaina@hotmail.co.jp>2022-05-28 01:31:23 +0900
commitfa99d9c6fee3a7d6f72e254e0aa55972cd6538fb (patch)
tree43d941bbacc9cfaa911fc785fce0b34af4ef6fcc /packages/client/src
parentMerge branch 'develop' into pizzax-indexeddb (diff)
parentfix(docs): correct information for drive upload (#8736) (diff)
downloadsharkey-fa99d9c6fee3a7d6f72e254e0aa55972cd6538fb.tar.gz
sharkey-fa99d9c6fee3a7d6f72e254e0aa55972cd6538fb.tar.bz2
sharkey-fa99d9c6fee3a7d6f72e254e0aa55972cd6538fb.zip
Merge branch 'develop' into pizzax-indexeddb
Diffstat (limited to 'packages/client/src')
-rw-r--r--packages/client/src/account.ts16
-rw-r--r--packages/client/src/components/abuse-report-window.vue2
-rw-r--r--packages/client/src/components/abuse-report.vue10
-rw-r--r--packages/client/src/components/analog-clock.vue2
-rw-r--r--packages/client/src/components/channel-follow-button.vue4
-rw-r--r--packages/client/src/components/chart.vue1540
-rw-r--r--packages/client/src/components/cw-button.vue2
-rw-r--r--packages/client/src/components/dialog.vue16
-rw-r--r--packages/client/src/components/drive-file-thumbnail.vue2
-rw-r--r--packages/client/src/components/drive-select-dialog.vue4
-rw-r--r--packages/client/src/components/drive-window.vue2
-rw-r--r--packages/client/src/components/drive.file.vue26
-rw-r--r--packages/client/src/components/drive.folder.vue18
-rw-r--r--packages/client/src/components/drive.nav-folder.vue42
-rw-r--r--packages/client/src/components/drive.vue82
-rw-r--r--packages/client/src/components/emoji-picker-window.vue4
-rw-r--r--packages/client/src/components/emoji-picker.section.vue2
-rw-r--r--packages/client/src/components/emoji-picker.vue14
-rw-r--r--packages/client/src/components/follow-button.vue6
-rw-r--r--packages/client/src/components/forgot-password.vue4
-rw-r--r--packages/client/src/components/form-dialog.vue2
-rw-r--r--packages/client/src/components/form/range.vue4
-rw-r--r--packages/client/src/components/form/switch.vue2
-rw-r--r--packages/client/src/components/global/avatar.vue2
-rw-r--r--packages/client/src/components/global/emoji.vue2
-rw-r--r--packages/client/src/components/global/header.vue2
-rw-r--r--packages/client/src/components/global/loading.vue52
-rw-r--r--packages/client/src/components/global/misskey-flavored-markdown.vue26
-rw-r--r--packages/client/src/components/global/time.vue2
-rw-r--r--packages/client/src/components/global/url.vue4
-rw-r--r--packages/client/src/components/image-viewer.vue2
-rw-r--r--packages/client/src/components/instance-ticker.vue13
-rw-r--r--packages/client/src/components/link.vue4
-rw-r--r--packages/client/src/components/media-caption.vue16
-rw-r--r--packages/client/src/components/mention.vue42
-rw-r--r--packages/client/src/components/mfm.ts27
-rw-r--r--packages/client/src/components/note-detailed.vue4
-rw-r--r--packages/client/src/components/note.vue2
-rw-r--r--packages/client/src/components/page/page.image.vue28
-rw-r--r--packages/client/src/components/page/page.post.vue10
-rw-r--r--packages/client/src/components/page/page.vue10
-rw-r--r--packages/client/src/components/poll-editor.vue2
-rw-r--r--packages/client/src/components/post-form-attaches.vue4
-rw-r--r--packages/client/src/components/post-form.vue103
-rw-r--r--packages/client/src/components/reactions-viewer.reaction.vue12
-rw-r--r--packages/client/src/components/signin-dialog.vue16
-rw-r--r--packages/client/src/components/signin.vue359
-rw-r--r--packages/client/src/components/signup-dialog.vue4
-rw-r--r--packages/client/src/components/signup.vue28
-rw-r--r--packages/client/src/components/timeline.vue6
-rw-r--r--packages/client/src/components/toast.vue2
-rw-r--r--packages/client/src/components/ui/button.vue14
-rw-r--r--packages/client/src/components/ui/context-menu.vue6
-rw-r--r--packages/client/src/components/ui/folder.vue2
-rw-r--r--packages/client/src/components/ui/menu.vue2
-rw-r--r--packages/client/src/components/ui/modal-window.vue8
-rw-r--r--packages/client/src/components/ui/pagination.vue23
-rw-r--r--packages/client/src/components/ui/popup-menu.vue2
-rw-r--r--packages/client/src/components/ui/window.vue54
-rw-r--r--packages/client/src/components/user-preview.vue2
-rw-r--r--packages/client/src/components/user-select-dialog.vue6
-rw-r--r--packages/client/src/components/visibility-picker.vue6
-rw-r--r--packages/client/src/components/waiting-dialog.vue4
-rw-r--r--packages/client/src/components/widgets.vue4
-rw-r--r--packages/client/src/directives/tooltip.ts4
-rw-r--r--packages/client/src/directives/user-preview.ts4
-rw-r--r--packages/client/src/emojilist.json42
-rw-r--r--packages/client/src/filters/bytes.ts2
-rw-r--r--packages/client/src/init.ts49
-rw-r--r--packages/client/src/instance.ts4
-rw-r--r--packages/client/src/os.ts61
-rw-r--r--packages/client/src/pages/about-misskey.vue1
-rw-r--r--packages/client/src/pages/admin/ads.vue144
-rw-r--r--packages/client/src/pages/admin/announcements.vue148
-rw-r--r--packages/client/src/pages/admin/bot-protection.vue89
-rw-r--r--packages/client/src/pages/admin/database.vue31
-rw-r--r--packages/client/src/pages/admin/email-settings.vue161
-rw-r--r--packages/client/src/pages/admin/emoji-edit-dialog.vue116
-rw-r--r--packages/client/src/pages/admin/emojis.vue20
-rw-r--r--packages/client/src/pages/admin/file-dialog.vue86
-rw-r--r--packages/client/src/pages/admin/files.vue4
-rw-r--r--packages/client/src/pages/admin/index.vue539
-rw-r--r--packages/client/src/pages/admin/instance-block.vue58
-rw-r--r--packages/client/src/pages/admin/integrations.discord.vue65
-rw-r--r--packages/client/src/pages/admin/integrations.github.vue65
-rw-r--r--packages/client/src/pages/admin/integrations.twitter.vue63
-rw-r--r--packages/client/src/pages/admin/integrations.vue61
-rw-r--r--packages/client/src/pages/admin/object-storage.vue170
-rw-r--r--packages/client/src/pages/admin/other-settings.vue61
-rw-r--r--packages/client/src/pages/admin/overview.vue116
-rw-r--r--packages/client/src/pages/admin/proxy-account.vue83
-rw-r--r--packages/client/src/pages/admin/queue.chart.vue72
-rw-r--r--packages/client/src/pages/admin/queue.vue87
-rw-r--r--packages/client/src/pages/admin/relays.vue117
-rw-r--r--packages/client/src/pages/admin/security.vue71
-rw-r--r--packages/client/src/pages/admin/settings.vue266
-rw-r--r--packages/client/src/pages/api-console.vue4
-rw-r--r--packages/client/src/pages/channel-editor.vue4
-rw-r--r--packages/client/src/pages/emojis.category.vue4
-rw-r--r--packages/client/src/pages/emojis.vue4
-rw-r--r--packages/client/src/pages/follow.vue2
-rw-r--r--packages/client/src/pages/gallery/edit.vue4
-rw-r--r--packages/client/src/pages/gallery/post.vue4
-rw-r--r--packages/client/src/pages/messaging/index.vue8
-rw-r--r--packages/client/src/pages/messaging/messaging-room.form.vue61
-rw-r--r--packages/client/src/pages/messaging/messaging-room.vue32
-rw-r--r--packages/client/src/pages/mfm-cheat-sheet.vue16
-rw-r--r--packages/client/src/pages/my-antennas/edit.vue55
-rw-r--r--packages/client/src/pages/my-antennas/editor.vue187
-rw-r--r--packages/client/src/pages/my-antennas/index.vue41
-rw-r--r--packages/client/src/pages/note.vue8
-rw-r--r--packages/client/src/pages/page.vue4
-rw-r--r--packages/client/src/pages/reset-password.vue4
-rw-r--r--packages/client/src/pages/settings/2fa.vue318
-rw-r--r--packages/client/src/pages/settings/account-info.vue103
-rw-r--r--packages/client/src/pages/settings/accounts.vue139
-rw-r--r--packages/client/src/pages/settings/api.vue67
-rw-r--r--packages/client/src/pages/settings/apps.vue58
-rw-r--r--packages/client/src/pages/settings/custom-css.vue56
-rw-r--r--packages/client/src/pages/settings/deck.vue106
-rw-r--r--packages/client/src/pages/settings/delete-account.vue78
-rw-r--r--packages/client/src/pages/settings/drive.vue125
-rw-r--r--packages/client/src/pages/settings/email.vue121
-rw-r--r--packages/client/src/pages/settings/general.vue263
-rw-r--r--packages/client/src/pages/settings/menu.vue131
-rw-r--r--packages/client/src/pages/settings/notifications.vue88
-rw-r--r--packages/client/src/pages/settings/other.vue66
-rw-r--r--packages/client/src/pages/settings/plugin.install.vue186
-rw-r--r--packages/client/src/pages/settings/plugin.vue111
-rw-r--r--packages/client/src/pages/settings/reaction.vue4
-rw-r--r--packages/client/src/pages/settings/security.vue132
-rw-r--r--packages/client/src/pages/settings/sounds.vue162
-rw-r--r--packages/client/src/pages/settings/theme.install.vue4
-rw-r--r--packages/client/src/pages/settings/theme.manage.vue97
-rw-r--r--packages/client/src/pages/settings/theme.vue151
-rw-r--r--packages/client/src/pages/settings/webhook.edit.vue16
-rw-r--r--packages/client/src/pages/settings/word-mute.vue190
-rw-r--r--packages/client/src/pages/share.vue6
-rw-r--r--packages/client/src/pages/theme-editor.vue6
-rw-r--r--packages/client/src/pages/user-info.vue4
-rw-r--r--packages/client/src/pages/user/index.vue6
-rw-r--r--packages/client/src/router.ts51
-rw-r--r--packages/client/src/scripts/2fa.ts8
-rw-r--r--packages/client/src/scripts/autocomplete.ts28
-rw-r--r--packages/client/src/scripts/contains.ts2
-rw-r--r--packages/client/src/scripts/emojilist.ts2
-rw-r--r--packages/client/src/scripts/extract-avg-color-from-blurhash.ts2
-rw-r--r--packages/client/src/scripts/get-account-from-id.ts2
-rw-r--r--packages/client/src/scripts/get-md5.ts10
-rw-r--r--packages/client/src/scripts/get-note-menu.ts18
-rw-r--r--packages/client/src/scripts/get-note-summary.ts2
-rw-r--r--packages/client/src/scripts/get-user-menu.ts9
-rw-r--r--packages/client/src/scripts/hotkey.ts24
-rw-r--r--packages/client/src/scripts/hpml/evaluator.ts8
-rw-r--r--packages/client/src/scripts/hpml/lib.ts4
-rw-r--r--packages/client/src/scripts/idb-proxy.ts4
-rw-r--r--packages/client/src/scripts/lookup-user.ts6
-rw-r--r--packages/client/src/scripts/please-login.ts19
-rw-r--r--packages/client/src/scripts/reaction-picker.ts4
-rw-r--r--packages/client/src/scripts/select-file.ts10
-rw-r--r--packages/client/src/scripts/theme.ts38
-rw-r--r--packages/client/src/scripts/upload.ts34
-rw-r--r--packages/client/src/scripts/url.ts2
-rw-r--r--packages/client/src/scripts/use-note-capture.ts4
-rw-r--r--packages/client/src/store.ts7
-rw-r--r--packages/client/src/theme-store.ts8
-rw-r--r--packages/client/src/ui/_common_/common.vue2
-rw-r--r--packages/client/src/ui/_common_/sidebar-for-mobile.vue4
-rw-r--r--packages/client/src/ui/_common_/sidebar.vue4
-rw-r--r--packages/client/src/ui/_common_/sw-inject.ts23
-rw-r--r--packages/client/src/ui/classic.header.vue4
-rw-r--r--packages/client/src/ui/classic.sidebar.vue4
-rw-r--r--packages/client/src/ui/classic.widgets.vue4
-rw-r--r--packages/client/src/ui/deck/antenna-column.vue4
-rw-r--r--packages/client/src/ui/deck/column-core.vue2
-rw-r--r--packages/client/src/ui/deck/column.vue26
-rw-r--r--packages/client/src/ui/deck/deck-store.ts22
-rw-r--r--packages/client/src/ui/deck/direct-column.vue2
-rw-r--r--packages/client/src/ui/deck/list-column.vue4
-rw-r--r--packages/client/src/ui/deck/main-column.vue2
-rw-r--r--packages/client/src/ui/deck/mentions-column.vue2
-rw-r--r--packages/client/src/ui/deck/notifications-column.vue6
-rw-r--r--packages/client/src/ui/deck/tl-column.vue4
-rw-r--r--packages/client/src/ui/deck/widgets-column.vue4
-rw-r--r--packages/client/src/ui/universal.widgets.vue6
-rw-r--r--packages/client/src/widgets/activity.calendar.vue68
-rw-r--r--packages/client/src/widgets/activity.chart.vue89
-rw-r--r--packages/client/src/widgets/activity.vue6
-rw-r--r--packages/client/src/widgets/aichan.vue2
-rw-r--r--packages/client/src/widgets/aiscript.vue8
-rw-r--r--packages/client/src/widgets/button.vue8
-rw-r--r--packages/client/src/widgets/calendar.vue2
-rw-r--r--packages/client/src/widgets/clock.vue2
-rw-r--r--packages/client/src/widgets/digital-clock.vue2
-rw-r--r--packages/client/src/widgets/federation.vue2
-rw-r--r--packages/client/src/widgets/job-queue.vue2
-rw-r--r--packages/client/src/widgets/memo.vue2
-rw-r--r--packages/client/src/widgets/notifications.vue5
-rw-r--r--packages/client/src/widgets/online-users.vue2
-rw-r--r--packages/client/src/widgets/photos.vue2
-rw-r--r--packages/client/src/widgets/post-form.vue2
-rw-r--r--packages/client/src/widgets/rss.vue2
-rw-r--r--packages/client/src/widgets/server-metric/cpu-mem.vue125
-rw-r--r--packages/client/src/widgets/server-metric/cpu.vue49
-rw-r--r--packages/client/src/widgets/server-metric/index.vue4
-rw-r--r--packages/client/src/widgets/server-metric/mem.vue62
-rw-r--r--packages/client/src/widgets/server-metric/net.vue122
-rw-r--r--packages/client/src/widgets/slideshow.vue4
-rw-r--r--packages/client/src/widgets/timeline.vue2
-rw-r--r--packages/client/src/widgets/trends.vue2
-rw-r--r--packages/client/src/widgets/widget.ts2
211 files changed, 4558 insertions, 5274 deletions
diff --git a/packages/client/src/account.ts b/packages/client/src/account.ts
index f4dcab319c..ce4af61f18 100644
--- a/packages/client/src/account.ts
+++ b/packages/client/src/account.ts
@@ -1,5 +1,5 @@
import { del, get, set } from '@/scripts/idb-proxy';
-import { reactive } from 'vue';
+import { defineAsyncComponent, reactive } from 'vue';
import * as misskey from 'misskey-js';
import { apiUrl } from '@/config';
import { waiting, api, popup, popupMenu, success, alert } from '@/os';
@@ -11,10 +11,10 @@ import { i18n } from './i18n';
type Account = misskey.entities.MeDetailed;
-const data = localStorage.getItem('account');
+const accountData = localStorage.getItem('account');
// TODO: 外部からはreadonlyに
-export const $i = data ? reactive(JSON.parse(data) as Account) : null;
+export const $i = accountData ? reactive(JSON.parse(accountData) as Account) : null;
export const iAmModerator = $i != null && ($i.isAdmin || $i.isModerator);
@@ -52,7 +52,7 @@ export async function signout() {
return Promise.all(registrations.map(registration => registration.unregister()));
});
}
- } catch (e) {}
+ } catch (err) {}
//#endregion
document.cookie = `igi=; path=/`;
@@ -104,8 +104,8 @@ function fetchAccount(token: string): Promise<Account> {
});
}
-export function updateAccount(data) {
- for (const [key, value] of Object.entries(data)) {
+export function updateAccount(accountData) {
+ for (const [key, value] of Object.entries(accountData)) {
$i[key] = value;
}
localStorage.setItem('account', JSON.stringify($i));
@@ -141,7 +141,7 @@ export async function openAccountMenu(opts: {
onChoose?: (account: misskey.entities.UserDetailed) => void;
}, ev: MouseEvent) {
function showSigninDialog() {
- popup(import('@/components/signin-dialog.vue'), {}, {
+ popup(defineAsyncComponent(() => import('@/components/signin-dialog.vue')), {}, {
done: res => {
addAccount(res.id, res.i);
success();
@@ -150,7 +150,7 @@ export async function openAccountMenu(opts: {
}
function createAccount() {
- popup(import('@/components/signup-dialog.vue'), {}, {
+ popup(defineAsyncComponent(() => import('@/components/signup-dialog.vue')), {}, {
done: res => {
addAccount(res.id, res.i);
switchAccountWithToken(res.i);
diff --git a/packages/client/src/components/abuse-report-window.vue b/packages/client/src/components/abuse-report-window.vue
index f2cb369802..5114349620 100644
--- a/packages/client/src/components/abuse-report-window.vue
+++ b/packages/client/src/components/abuse-report-window.vue
@@ -37,7 +37,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'closed'): void;
+ (ev: 'closed'): void;
}>();
const window = ref<InstanceType<typeof XWindow>>();
diff --git a/packages/client/src/components/abuse-report.vue b/packages/client/src/components/abuse-report.vue
index b67cef209b..a947406f88 100644
--- a/packages/client/src/components/abuse-report.vue
+++ b/packages/client/src/components/abuse-report.vue
@@ -2,7 +2,7 @@
<div class="bcekxzvu _card _gap">
<div class="_content target">
<MkAvatar class="avatar" :user="report.targetUser" :show-indicator="true"/>
- <MkA class="info" :to="userPage(report.targetUser)" v-user-preview="report.targetUserId">
+ <MkA v-user-preview="report.targetUserId" class="info" :to="userPage(report.targetUser)">
<MkUserName class="name" :user="report.targetUser"/>
<MkAcct class="acct" :user="report.targetUser" style="display: block;"/>
</MkA>
@@ -43,20 +43,20 @@ export default defineComponent({
MkSwitch,
},
- emits: ['resolved'],
-
props: {
report: {
type: Object,
required: true,
}
- }
+ },
+
+ emits: ['resolved'],
data() {
return {
forward: this.report.forwarded,
};
- }
+ },
methods: {
acct,
diff --git a/packages/client/src/components/analog-clock.vue b/packages/client/src/components/analog-clock.vue
index 59b8e97304..18dd1e3f41 100644
--- a/packages/client/src/components/analog-clock.vue
+++ b/packages/client/src/components/analog-clock.vue
@@ -42,7 +42,7 @@
<script lang="ts" setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
withDefaults(defineProps<{
thickness: number;
diff --git a/packages/client/src/components/channel-follow-button.vue b/packages/client/src/components/channel-follow-button.vue
index 7bbf5ae663..dff02beec0 100644
--- a/packages/client/src/components/channel-follow-button.vue
+++ b/packages/client/src/components/channel-follow-button.vue
@@ -48,8 +48,8 @@ async function onClick() {
});
isFollowing.value = true;
}
- } catch (e) {
- console.error(e);
+ } catch (err) {
+ console.error(err);
} finally {
wait.value = false;
}
diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue
index cc1aa9c20a..4e9c4e587a 100644
--- a/packages/client/src/components/chart.vue
+++ b/packages/client/src/components/chart.vue
@@ -7,8 +7,13 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent, onMounted, ref, watch, PropType, onUnmounted, shallowRef } from 'vue';
+<script lang="ts" setup>
+/* eslint-disable id-denylist --
+ Chart.js has a `data` attribute in most chart definitions, which triggers the
+ id-denylist violation when setting it. This is causing about 60+ lint issues.
+ As this is part of Chart.js's API it makes sense to disable the check here.
+*/
+import { defineProps, onMounted, ref, watch, PropType, onUnmounted } from 'vue';
import {
Chart,
ArcElement,
@@ -29,11 +34,53 @@ import {
import 'chartjs-adapter-date-fns';
import { enUS } from 'date-fns/locale';
import zoomPlugin from 'chartjs-plugin-zoom';
-import gradient from 'chartjs-plugin-gradient';
+// https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114242002
+// We can't use gradient because Vite throws a error.
+//import gradient from 'chartjs-plugin-gradient';
import * as os from '@/os';
import { defaultStore } from '@/store';
import MkChartTooltip from '@/components/chart-tooltip.vue';
+const props = defineProps({
+ src: {
+ type: String,
+ required: true,
+ },
+ args: {
+ type: Object,
+ required: false,
+ },
+ limit: {
+ type: Number,
+ required: false,
+ default: 90
+ },
+ span: {
+ type: String as PropType<'hour' | 'day'>,
+ required: true,
+ },
+ detailed: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ stacked: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ bar: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ aspectRatio: {
+ type: Number,
+ required: false,
+ default: null
+ },
+});
+
Chart.register(
ArcElement,
LineElement,
@@ -50,7 +97,7 @@ Chart.register(
SubTitle,
Filler,
zoomPlugin,
- gradient,
+ //gradient,
);
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
@@ -78,826 +125,777 @@ const getColor = (i) => {
return colorSets[i % colorSets.length];
};
-export default defineComponent({
- props: {
- src: {
- type: String,
- required: true,
- },
- args: {
- type: Object,
- required: false,
- },
- limit: {
- type: Number,
- required: false,
- default: 90
- },
- span: {
- type: String as PropType<'hour' | 'day'>,
- required: true,
- },
- detailed: {
- type: Boolean,
- required: false,
- default: false
- },
- stacked: {
- type: Boolean,
- required: false,
- default: false
- },
- bar: {
- type: Boolean,
- required: false,
- default: false
- },
- aspectRatio: {
- type: Number,
- required: false,
- default: null
- },
- },
+const now = new Date();
+let chartInstance: Chart = null;
+let chartData: {
+ series: {
+ name: string;
+ type: 'line' | 'area';
+ color?: string;
+ dashed?: boolean;
+ hidden?: boolean;
+ data: {
+ x: number;
+ y: number;
+ }[];
+ }[];
+} = null;
- setup(props) {
- const now = new Date();
- let chartInstance: Chart = null;
- let data: {
- series: {
- name: string;
- type: 'line' | 'area';
- color?: string;
- dashed?: boolean;
- hidden?: boolean;
- data: {
- x: number;
- y: number;
- }[];
- }[];
- } = null;
+const chartEl = ref<HTMLCanvasElement>(null);
+const fetching = ref(true);
- const chartEl = ref<HTMLCanvasElement>(null);
- const fetching = ref(true);
+const getDate = (ago: number) => {
+ const y = now.getFullYear();
+ const m = now.getMonth();
+ const d = now.getDate();
+ const h = now.getHours();
- const getDate = (ago: number) => {
- const y = now.getFullYear();
- const m = now.getMonth();
- const d = now.getDate();
- const h = now.getHours();
-
- return props.span === 'day' ? new Date(y, m, d - ago) : new Date(y, m, d, h - ago);
- };
+ return props.span === 'day' ? new Date(y, m, d - ago) : new Date(y, m, d, h - ago);
+};
- const format = (arr) => {
- return arr.map((v, i) => ({
- x: getDate(i).getTime(),
- y: v
- }));
- };
+const format = (arr) => {
+ return arr.map((v, i) => ({
+ x: getDate(i).getTime(),
+ y: v
+ }));
+};
- const tooltipShowing = ref(false);
- const tooltipX = ref(0);
- const tooltipY = ref(0);
- const tooltipTitle = ref(null);
- const tooltipSeries = ref(null);
- let disposeTooltipComponent;
+const tooltipShowing = ref(false);
+const tooltipX = ref(0);
+const tooltipY = ref(0);
+const tooltipTitle = ref(null);
+const tooltipSeries = ref(null);
+let disposeTooltipComponent;
- os.popup(MkChartTooltip, {
- showing: tooltipShowing,
- x: tooltipX,
- y: tooltipY,
- title: tooltipTitle,
- series: tooltipSeries,
- }, {}).then(({ dispose }) => {
- disposeTooltipComponent = dispose;
- });
+os.popup(MkChartTooltip, {
+ showing: tooltipShowing,
+ x: tooltipX,
+ y: tooltipY,
+ title: tooltipTitle,
+ series: tooltipSeries,
+}, {}).then(({ dispose }) => {
+ disposeTooltipComponent = dispose;
+});
- function externalTooltipHandler(context) {
- if (context.tooltip.opacity === 0) {
- tooltipShowing.value = false;
- return;
- }
+function externalTooltipHandler(context) {
+ if (context.tooltip.opacity === 0) {
+ tooltipShowing.value = false;
+ return;
+ }
- tooltipTitle.value = context.tooltip.title[0];
- tooltipSeries.value = context.tooltip.body.map((b, i) => ({
- backgroundColor: context.tooltip.labelColors[i].backgroundColor,
- borderColor: context.tooltip.labelColors[i].borderColor,
- text: b.lines[0],
- }));
+ tooltipTitle.value = context.tooltip.title[0];
+ tooltipSeries.value = context.tooltip.body.map((b, i) => ({
+ backgroundColor: context.tooltip.labelColors[i].backgroundColor,
+ borderColor: context.tooltip.labelColors[i].borderColor,
+ text: b.lines[0],
+ }));
- const rect = context.chart.canvas.getBoundingClientRect();
+ const rect = context.chart.canvas.getBoundingClientRect();
- tooltipShowing.value = true;
- tooltipX.value = rect.left + window.pageXOffset + context.tooltip.caretX;
- tooltipY.value = rect.top + window.pageYOffset + context.tooltip.caretY;
- }
+ tooltipShowing.value = true;
+ tooltipX.value = rect.left + window.pageXOffset + context.tooltip.caretX;
+ tooltipY.value = rect.top + window.pageYOffset + context.tooltip.caretY;
+}
- const render = () => {
- if (chartInstance) {
- chartInstance.destroy();
- }
+const render = () => {
+ if (chartInstance) {
+ chartInstance.destroy();
+ }
- const gridColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
- const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
+ const gridColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
+ const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
- // フォントカラー
- Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg');
+ // フォントカラー
+ Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg');
- const maxes = data.series.map((x, i) => Math.max(...x.data.map(d => d.y)));
+ const maxes = chartData.series.map((x, i) => Math.max(...x.data.map(d => d.y)));
- chartInstance = new Chart(chartEl.value, {
- type: props.bar ? 'bar' : 'line',
- data: {
- labels: new Array(props.limit).fill(0).map((_, i) => getDate(i).toLocaleString()).slice().reverse(),
- datasets: data.series.map((x, i) => ({
- parsing: false,
- label: x.name,
- data: x.data.slice().reverse(),
- tension: 0.3,
- pointRadius: 0,
- borderWidth: props.bar ? 0 : 2,
- borderColor: x.color ? x.color : getColor(i),
- borderDash: x.dashed ? [5, 5] : [],
- borderJoinStyle: 'round',
- borderRadius: props.bar ? 3 : undefined,
- backgroundColor: props.bar ? (x.color ? x.color : getColor(i)) : alpha(x.color ? x.color : getColor(i), 0.1),
- gradient: props.bar ? undefined : {
- backgroundColor: {
- axis: 'y',
- colors: {
- 0: alpha(x.color ? x.color : getColor(i), 0),
- [maxes[i]]: alpha(x.color ? x.color : getColor(i), 0.2),
- },
- },
+ chartInstance = new Chart(chartEl.value, {
+ type: props.bar ? 'bar' : 'line',
+ data: {
+ labels: new Array(props.limit).fill(0).map((_, i) => getDate(i).toLocaleString()).slice().reverse(),
+ datasets: chartData.series.map((x, i) => ({
+ parsing: false,
+ label: x.name,
+ data: x.data.slice().reverse(),
+ tension: 0.3,
+ pointRadius: 0,
+ borderWidth: props.bar ? 0 : 2,
+ borderColor: x.color ? x.color : getColor(i),
+ borderDash: x.dashed ? [5, 5] : [],
+ borderJoinStyle: 'round',
+ borderRadius: props.bar ? 3 : undefined,
+ backgroundColor: props.bar ? (x.color ? x.color : getColor(i)) : alpha(x.color ? x.color : getColor(i), 0.1),
+ /*gradient: props.bar ? undefined : {
+ backgroundColor: {
+ axis: 'y',
+ colors: {
+ 0: alpha(x.color ? x.color : getColor(i), 0),
+ [maxes[i]]: alpha(x.color ? x.color : getColor(i), 0.2),
},
- barPercentage: 0.9,
- categoryPercentage: 0.9,
- fill: x.type === 'area',
- clip: 8,
- hidden: !!x.hidden,
- })),
+ },
+ },*/
+ barPercentage: 0.9,
+ categoryPercentage: 0.9,
+ fill: x.type === 'area',
+ clip: 8,
+ hidden: !!x.hidden,
+ })),
+ },
+ options: {
+ aspectRatio: props.aspectRatio || 2.5,
+ layout: {
+ padding: {
+ left: 0,
+ right: 8,
+ top: 0,
+ bottom: 0,
},
- options: {
- aspectRatio: props.aspectRatio || 2.5,
- layout: {
- padding: {
- left: 0,
- right: 8,
- top: 0,
- bottom: 0,
- },
+ },
+ scales: {
+ x: {
+ type: 'time',
+ stacked: props.stacked,
+ offset: false,
+ time: {
+ stepSize: 1,
+ unit: props.span === 'day' ? 'month' : 'day',
},
- scales: {
- x: {
- type: 'time',
- stacked: props.stacked,
- offset: false,
- time: {
- stepSize: 1,
- unit: props.span === 'day' ? 'month' : 'day',
- },
- grid: {
- color: gridColor,
- borderColor: 'rgb(0, 0, 0, 0)',
- },
- ticks: {
- display: props.detailed,
- maxRotation: 0,
- autoSkipPadding: 16,
- },
- adapters: {
- date: {
- locale: enUS,
- },
- },
- min: getDate(props.limit).getTime(),
- },
- y: {
- position: 'left',
- stacked: props.stacked,
- suggestedMax: 50,
- grid: {
- color: gridColor,
- borderColor: 'rgb(0, 0, 0, 0)',
- },
- ticks: {
- display: props.detailed,
- //mirror: true,
- },
- },
+ grid: {
+ color: gridColor,
+ borderColor: 'rgb(0, 0, 0, 0)',
},
- interaction: {
- intersect: false,
- mode: 'index',
+ ticks: {
+ display: props.detailed,
+ maxRotation: 0,
+ autoSkipPadding: 16,
},
- elements: {
- point: {
- hoverRadius: 5,
- hoverBorderWidth: 2,
+ adapters: {
+ date: {
+ locale: enUS,
},
},
- animation: false,
- plugins: {
- legend: {
- display: props.detailed,
- position: 'bottom',
- labels: {
- boxWidth: 16,
- },
+ min: getDate(props.limit).getTime(),
+ },
+ y: {
+ position: 'left',
+ stacked: props.stacked,
+ suggestedMax: 50,
+ grid: {
+ color: gridColor,
+ borderColor: 'rgb(0, 0, 0, 0)',
+ },
+ ticks: {
+ display: props.detailed,
+ //mirror: true,
+ },
+ },
+ },
+ interaction: {
+ intersect: false,
+ mode: 'index',
+ },
+ elements: {
+ point: {
+ hoverRadius: 5,
+ hoverBorderWidth: 2,
+ },
+ },
+ animation: false,
+ plugins: {
+ legend: {
+ display: props.detailed,
+ position: 'bottom',
+ labels: {
+ boxWidth: 16,
+ },
+ },
+ tooltip: {
+ enabled: false,
+ mode: 'index',
+ animation: {
+ duration: 0,
+ },
+ external: externalTooltipHandler,
+ },
+ zoom: props.detailed ? {
+ pan: {
+ enabled: true,
+ },
+ zoom: {
+ wheel: {
+ enabled: true,
},
- tooltip: {
+ pinch: {
+ enabled: true,
+ },
+ drag: {
enabled: false,
- mode: 'index',
- animation: {
- duration: 0,
- },
- external: externalTooltipHandler,
},
- zoom: props.detailed ? {
- pan: {
- enabled: true,
- },
- zoom: {
- wheel: {
- enabled: true,
- },
- pinch: {
- enabled: true,
- },
- drag: {
- enabled: false,
- },
- mode: 'x',
- },
- limits: {
- x: {
- min: 'original',
- max: 'original',
- },
- y: {
- min: 'original',
- max: 'original',
- },
- }
- } : undefined,
- gradient,
+ mode: 'x',
},
- },
- plugins: [{
- id: 'vLine',
- beforeDraw(chart, args, options) {
- if (chart.tooltip._active && chart.tooltip._active.length) {
- const activePoint = chart.tooltip._active[0];
- const ctx = chart.ctx;
- const x = activePoint.element.x;
- const topY = chart.scales.y.top;
- const bottomY = chart.scales.y.bottom;
-
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(x, bottomY);
- ctx.lineTo(x, topY);
- ctx.lineWidth = 1;
- ctx.strokeStyle = vLineColor;
- ctx.stroke();
- ctx.restore();
- }
+ limits: {
+ x: {
+ min: 'original',
+ max: 'original',
+ },
+ y: {
+ min: 'original',
+ max: 'original',
+ },
}
- }]
- });
- };
+ } : undefined,
+ //gradient,
+ },
+ },
+ plugins: [{
+ id: 'vLine',
+ beforeDraw(chart, args, options) {
+ if (chart.tooltip._active && chart.tooltip._active.length) {
+ const activePoint = chart.tooltip._active[0];
+ const ctx = chart.ctx;
+ const x = activePoint.element.x;
+ const topY = chart.scales.y.top;
+ const bottomY = chart.scales.y.bottom;
- const exportData = () => {
- // TODO
- };
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(x, bottomY);
+ ctx.lineTo(x, topY);
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = vLineColor;
+ ctx.stroke();
+ ctx.restore();
+ }
+ }
+ }]
+ });
+};
- const fetchFederationChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/federation', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Received',
- type: 'area',
- data: format(raw.inboxInstances),
- color: colors.blue,
- }, {
- name: 'Delivered',
- type: 'area',
- data: format(raw.deliveredInstances),
- color: colors.green,
- }, {
- name: 'Stalled',
- type: 'area',
- data: format(raw.stalled),
- color: colors.red,
- }, {
- name: 'Pub Active',
- type: 'line',
- data: format(raw.pubActive),
- color: colors.purple,
- }, {
- name: 'Sub Active',
- type: 'line',
- data: format(raw.subActive),
- color: colors.orange,
- }, {
- name: 'Pub & Sub',
- type: 'line',
- data: format(raw.pubsub),
- dashed: true,
- color: colors.cyan,
- }, {
- name: 'Pub',
- type: 'line',
- data: format(raw.pub),
- dashed: true,
- color: colors.purple,
- }, {
- name: 'Sub',
- type: 'line',
- data: format(raw.sub),
- dashed: true,
- color: colors.orange,
- }],
- };
- };
+const exportData = () => {
+ // TODO
+};
- const fetchApRequestChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/ap-request', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'In',
- type: 'area',
- color: '#008FFB',
- data: format(raw.inboxReceived)
- }, {
- name: 'Out (succ)',
- type: 'area',
- color: '#00E396',
- data: format(raw.deliverSucceeded)
- }, {
- name: 'Out (fail)',
- type: 'area',
- color: '#FEB019',
- data: format(raw.deliverFailed)
- }]
- };
- };
+const fetchFederationChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/federation', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Received',
+ type: 'area',
+ data: format(raw.inboxInstances),
+ color: colors.blue,
+ }, {
+ name: 'Delivered',
+ type: 'area',
+ data: format(raw.deliveredInstances),
+ color: colors.green,
+ }, {
+ name: 'Stalled',
+ type: 'area',
+ data: format(raw.stalled),
+ color: colors.red,
+ }, {
+ name: 'Pub Active',
+ type: 'line',
+ data: format(raw.pubActive),
+ color: colors.purple,
+ }, {
+ name: 'Sub Active',
+ type: 'line',
+ data: format(raw.subActive),
+ color: colors.orange,
+ }, {
+ name: 'Pub & Sub',
+ type: 'line',
+ data: format(raw.pubsub),
+ dashed: true,
+ color: colors.cyan,
+ }, {
+ name: 'Pub',
+ type: 'line',
+ data: format(raw.pub),
+ dashed: true,
+ color: colors.purple,
+ }, {
+ name: 'Sub',
+ type: 'line',
+ data: format(raw.sub),
+ dashed: true,
+ color: colors.orange,
+ }],
+ };
+};
- const fetchNotesChart = async (type: string): Promise<typeof data> => {
- const raw = await os.api('charts/notes', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'All',
- type: 'line',
- data: format(type == 'combined'
- ? sum(raw.local.inc, negate(raw.local.dec), raw.remote.inc, negate(raw.remote.dec))
- : sum(raw[type].inc, negate(raw[type].dec))
- ),
- color: '#888888',
- }, {
- name: 'Renotes',
- type: 'area',
- data: format(type == 'combined'
- ? sum(raw.local.diffs.renote, raw.remote.diffs.renote)
- : raw[type].diffs.renote
- ),
- color: colors.green,
- }, {
- name: 'Replies',
- type: 'area',
- data: format(type == 'combined'
- ? sum(raw.local.diffs.reply, raw.remote.diffs.reply)
- : raw[type].diffs.reply
- ),
- color: colors.yellow,
- }, {
- name: 'Normal',
- type: 'area',
- data: format(type == 'combined'
- ? sum(raw.local.diffs.normal, raw.remote.diffs.normal)
- : raw[type].diffs.normal
- ),
- color: colors.blue,
- }, {
- name: 'With file',
- type: 'area',
- data: format(type == 'combined'
- ? sum(raw.local.diffs.withFile, raw.remote.diffs.withFile)
- : raw[type].diffs.withFile
- ),
- color: colors.purple,
- }],
- };
- };
+const fetchApRequestChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/ap-request', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'In',
+ type: 'area',
+ color: '#008FFB',
+ data: format(raw.inboxReceived)
+ }, {
+ name: 'Out (succ)',
+ type: 'area',
+ color: '#00E396',
+ data: format(raw.deliverSucceeded)
+ }, {
+ name: 'Out (fail)',
+ type: 'area',
+ color: '#FEB019',
+ data: format(raw.deliverFailed)
+ }]
+ };
+};
- const fetchNotesTotalChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/notes', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Combined',
- type: 'line',
- data: format(sum(raw.local.total, raw.remote.total)),
- }, {
- name: 'Local',
- type: 'area',
- data: format(raw.local.total),
- }, {
- name: 'Remote',
- type: 'area',
- data: format(raw.remote.total),
- }],
- };
- };
+const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
+ const raw = await os.api('charts/notes', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'All',
+ type: 'line',
+ data: format(type === 'combined'
+ ? sum(raw.local.inc, negate(raw.local.dec), raw.remote.inc, negate(raw.remote.dec))
+ : sum(raw[type].inc, negate(raw[type].dec))
+ ),
+ color: '#888888',
+ }, {
+ name: 'Renotes',
+ type: 'area',
+ data: format(type === 'combined'
+ ? sum(raw.local.diffs.renote, raw.remote.diffs.renote)
+ : raw[type].diffs.renote
+ ),
+ color: colors.green,
+ }, {
+ name: 'Replies',
+ type: 'area',
+ data: format(type === 'combined'
+ ? sum(raw.local.diffs.reply, raw.remote.diffs.reply)
+ : raw[type].diffs.reply
+ ),
+ color: colors.yellow,
+ }, {
+ name: 'Normal',
+ type: 'area',
+ data: format(type === 'combined'
+ ? sum(raw.local.diffs.normal, raw.remote.diffs.normal)
+ : raw[type].diffs.normal
+ ),
+ color: colors.blue,
+ }, {
+ name: 'With file',
+ type: 'area',
+ data: format(type === 'combined'
+ ? sum(raw.local.diffs.withFile, raw.remote.diffs.withFile)
+ : raw[type].diffs.withFile
+ ),
+ color: colors.purple,
+ }],
+ };
+};
- const fetchUsersChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/users', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Combined',
- type: 'line',
- data: format(total
- ? sum(raw.local.total, raw.remote.total)
- : sum(raw.local.inc, negate(raw.local.dec), raw.remote.inc, negate(raw.remote.dec))
- ),
- }, {
- name: 'Local',
- type: 'area',
- data: format(total
- ? raw.local.total
- : sum(raw.local.inc, negate(raw.local.dec))
- ),
- }, {
- name: 'Remote',
- type: 'area',
- data: format(total
- ? raw.remote.total
- : sum(raw.remote.inc, negate(raw.remote.dec))
- ),
- }],
- };
- };
+const fetchNotesTotalChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/notes', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Combined',
+ type: 'line',
+ data: format(sum(raw.local.total, raw.remote.total)),
+ }, {
+ name: 'Local',
+ type: 'area',
+ data: format(raw.local.total),
+ }, {
+ name: 'Remote',
+ type: 'area',
+ data: format(raw.remote.total),
+ }],
+ };
+};
- const fetchActiveUsersChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/active-users', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Read & Write',
- type: 'area',
- data: format(raw.readWrite),
- color: colors.orange,
- }, {
- name: 'Write',
- type: 'area',
- data: format(raw.write),
- color: colors.lime,
- }, {
- name: 'Read',
- type: 'area',
- data: format(raw.read),
- color: colors.blue,
- }, {
- name: '< Week',
- type: 'area',
- data: format(raw.registeredWithinWeek),
- color: colors.green,
- }, {
- name: '< Month',
- type: 'area',
- data: format(raw.registeredWithinMonth),
- color: colors.yellow,
- }, {
- name: '< Year',
- type: 'area',
- data: format(raw.registeredWithinYear),
- color: colors.red,
- }, {
- name: '> Week',
- type: 'area',
- data: format(raw.registeredOutsideWeek),
- color: colors.yellow,
- }, {
- name: '> Month',
- type: 'area',
- data: format(raw.registeredOutsideMonth),
- color: colors.red,
- }, {
- name: '> Year',
- type: 'area',
- data: format(raw.registeredOutsideYear),
- color: colors.purple,
- }],
- };
- };
+const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/users', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Combined',
+ type: 'line',
+ data: format(total
+ ? sum(raw.local.total, raw.remote.total)
+ : sum(raw.local.inc, negate(raw.local.dec), raw.remote.inc, negate(raw.remote.dec))
+ ),
+ }, {
+ name: 'Local',
+ type: 'area',
+ data: format(total
+ ? raw.local.total
+ : sum(raw.local.inc, negate(raw.local.dec))
+ ),
+ }, {
+ name: 'Remote',
+ type: 'area',
+ data: format(total
+ ? raw.remote.total
+ : sum(raw.remote.inc, negate(raw.remote.dec))
+ ),
+ }],
+ };
+};
- const fetchDriveChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/drive', { limit: props.limit, span: props.span });
- return {
- bytes: true,
- series: [{
- name: 'All',
- type: 'line',
- dashed: true,
- data: format(
- sum(
- raw.local.incSize,
- negate(raw.local.decSize),
- raw.remote.incSize,
- negate(raw.remote.decSize)
- )
- ),
- }, {
- name: 'Local +',
- type: 'area',
- data: format(raw.local.incSize),
- }, {
- name: 'Local -',
- type: 'area',
- data: format(negate(raw.local.decSize)),
- }, {
- name: 'Remote +',
- type: 'area',
- data: format(raw.remote.incSize),
- }, {
- name: 'Remote -',
- type: 'area',
- data: format(negate(raw.remote.decSize)),
- }],
- };
- };
+const fetchActiveUsersChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/active-users', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Read & Write',
+ type: 'area',
+ data: format(raw.readWrite),
+ color: colors.orange,
+ }, {
+ name: 'Write',
+ type: 'area',
+ data: format(raw.write),
+ color: colors.lime,
+ }, {
+ name: 'Read',
+ type: 'area',
+ data: format(raw.read),
+ color: colors.blue,
+ }, {
+ name: '< Week',
+ type: 'area',
+ data: format(raw.registeredWithinWeek),
+ color: colors.green,
+ }, {
+ name: '< Month',
+ type: 'area',
+ data: format(raw.registeredWithinMonth),
+ color: colors.yellow,
+ }, {
+ name: '< Year',
+ type: 'area',
+ data: format(raw.registeredWithinYear),
+ color: colors.red,
+ }, {
+ name: '> Week',
+ type: 'area',
+ data: format(raw.registeredOutsideWeek),
+ color: colors.yellow,
+ }, {
+ name: '> Month',
+ type: 'area',
+ data: format(raw.registeredOutsideMonth),
+ color: colors.red,
+ }, {
+ name: '> Year',
+ type: 'area',
+ data: format(raw.registeredOutsideYear),
+ color: colors.purple,
+ }],
+ };
+};
- const fetchDriveFilesChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/drive', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'All',
- type: 'line',
- dashed: true,
- data: format(
- sum(
- raw.local.incCount,
- negate(raw.local.decCount),
- raw.remote.incCount,
- negate(raw.remote.decCount)
- )
- ),
- }, {
- name: 'Local +',
- type: 'area',
- data: format(raw.local.incCount),
- }, {
- name: 'Local -',
- type: 'area',
- data: format(negate(raw.local.decCount)),
- }, {
- name: 'Remote +',
- type: 'area',
- data: format(raw.remote.incCount),
- }, {
- name: 'Remote -',
- type: 'area',
- data: format(negate(raw.remote.decCount)),
- }],
- };
- };
+const fetchDriveChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/drive', { limit: props.limit, span: props.span });
+ return {
+ bytes: true,
+ series: [{
+ name: 'All',
+ type: 'line',
+ dashed: true,
+ data: format(
+ sum(
+ raw.local.incSize,
+ negate(raw.local.decSize),
+ raw.remote.incSize,
+ negate(raw.remote.decSize)
+ )
+ ),
+ }, {
+ name: 'Local +',
+ type: 'area',
+ data: format(raw.local.incSize),
+ }, {
+ name: 'Local -',
+ type: 'area',
+ data: format(negate(raw.local.decSize)),
+ }, {
+ name: 'Remote +',
+ type: 'area',
+ data: format(raw.remote.incSize),
+ }, {
+ name: 'Remote -',
+ type: 'area',
+ data: format(negate(raw.remote.decSize)),
+ }],
+ };
+};
- const fetchInstanceRequestsChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'In',
- type: 'area',
- color: '#008FFB',
- data: format(raw.requests.received)
- }, {
- name: 'Out (succ)',
- type: 'area',
- color: '#00E396',
- data: format(raw.requests.succeeded)
- }, {
- name: 'Out (fail)',
- type: 'area',
- color: '#FEB019',
- data: format(raw.requests.failed)
- }]
- };
- };
+const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/drive', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'All',
+ type: 'line',
+ dashed: true,
+ data: format(
+ sum(
+ raw.local.incCount,
+ negate(raw.local.decCount),
+ raw.remote.incCount,
+ negate(raw.remote.decCount)
+ )
+ ),
+ }, {
+ name: 'Local +',
+ type: 'area',
+ data: format(raw.local.incCount),
+ }, {
+ name: 'Local -',
+ type: 'area',
+ data: format(negate(raw.local.decCount)),
+ }, {
+ name: 'Remote +',
+ type: 'area',
+ data: format(raw.remote.incCount),
+ }, {
+ name: 'Remote -',
+ type: 'area',
+ data: format(negate(raw.remote.decCount)),
+ }],
+ };
+};
- const fetchInstanceUsersChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Users',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.users.total
- : sum(raw.users.inc, negate(raw.users.dec))
- )
- }]
- };
- };
+const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'In',
+ type: 'area',
+ color: '#008FFB',
+ data: format(raw.requests.received)
+ }, {
+ name: 'Out (succ)',
+ type: 'area',
+ color: '#00E396',
+ data: format(raw.requests.succeeded)
+ }, {
+ name: 'Out (fail)',
+ type: 'area',
+ color: '#FEB019',
+ data: format(raw.requests.failed)
+ }]
+ };
+};
- const fetchInstanceNotesChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Notes',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.notes.total
- : sum(raw.notes.inc, negate(raw.notes.dec))
- )
- }]
- };
- };
+const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Users',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.users.total
+ : sum(raw.users.inc, negate(raw.users.dec))
+ )
+ }]
+ };
+};
- const fetchInstanceFfChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Following',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.following.total
- : sum(raw.following.inc, negate(raw.following.dec))
- )
- }, {
- name: 'Followers',
- type: 'area',
- color: '#00E396',
- data: format(total
- ? raw.followers.total
- : sum(raw.followers.inc, negate(raw.followers.dec))
- )
- }]
- };
- };
+const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Notes',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.notes.total
+ : sum(raw.notes.inc, negate(raw.notes.dec))
+ )
+ }]
+ };
+};
- const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- bytes: true,
- series: [{
- name: 'Drive usage',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.drive.totalUsage
- : sum(raw.drive.incUsage, negate(raw.drive.decUsage))
- )
- }]
- };
- };
+const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Following',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.following.total
+ : sum(raw.following.inc, negate(raw.following.dec))
+ )
+ }, {
+ name: 'Followers',
+ type: 'area',
+ color: '#00E396',
+ data: format(total
+ ? raw.followers.total
+ : sum(raw.followers.inc, negate(raw.followers.dec))
+ )
+ }]
+ };
+};
- const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Drive files',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.drive.totalFiles
- : sum(raw.drive.incFiles, negate(raw.drive.decFiles))
- )
- }]
- };
- };
+const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ bytes: true,
+ series: [{
+ name: 'Drive usage',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.drive.totalUsage
+ : sum(raw.drive.incUsage, negate(raw.drive.decUsage))
+ )
+ }]
+ };
+};
- const fetchPerUserNotesChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/user/notes', { userId: props.args.user.id, limit: props.limit, span: props.span });
- return {
- series: [...(props.args.withoutAll ? [] : [{
- name: 'All',
- type: 'line',
- data: format(sum(raw.inc, negate(raw.dec))),
- color: '#888888',
- }]), {
- name: 'With file',
- type: 'area',
- data: format(raw.diffs.withFile),
- color: colors.purple,
- }, {
- name: 'Renotes',
- type: 'area',
- data: format(raw.diffs.renote),
- color: colors.green,
- }, {
- name: 'Replies',
- type: 'area',
- data: format(raw.diffs.reply),
- color: colors.yellow,
- }, {
- name: 'Normal',
- type: 'area',
- data: format(raw.diffs.normal),
- color: colors.blue,
- }],
- };
- };
+const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Drive files',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.drive.totalFiles
+ : sum(raw.drive.incFiles, negate(raw.drive.decFiles))
+ )
+ }]
+ };
+};
- const fetchPerUserFollowingChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Local',
- type: 'area',
- data: format(raw.local.followings.total),
- }, {
- name: 'Remote',
- type: 'area',
- data: format(raw.remote.followings.total),
- }],
- };
- };
+const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/user/notes', { userId: props.args.user.id, limit: props.limit, span: props.span });
+ return {
+ series: [...(props.args.withoutAll ? [] : [{
+ name: 'All',
+ type: 'line',
+ data: format(sum(raw.inc, negate(raw.dec))),
+ color: '#888888',
+ }]), {
+ name: 'With file',
+ type: 'area',
+ data: format(raw.diffs.withFile),
+ color: colors.purple,
+ }, {
+ name: 'Renotes',
+ type: 'area',
+ data: format(raw.diffs.renote),
+ color: colors.green,
+ }, {
+ name: 'Replies',
+ type: 'area',
+ data: format(raw.diffs.reply),
+ color: colors.yellow,
+ }, {
+ name: 'Normal',
+ type: 'area',
+ data: format(raw.diffs.normal),
+ color: colors.blue,
+ }],
+ };
+};
- const fetchPerUserFollowersChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Local',
- type: 'area',
- data: format(raw.local.followers.total),
- }, {
- name: 'Remote',
- type: 'area',
- data: format(raw.remote.followers.total),
- }],
- };
- };
+const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Local',
+ type: 'area',
+ data: format(raw.local.followings.total),
+ }, {
+ name: 'Remote',
+ type: 'area',
+ data: format(raw.remote.followings.total),
+ }],
+ };
+};
- const fetchPerUserDriveChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/user/drive', { userId: props.args.user.id, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Inc',
- type: 'area',
- data: format(raw.incSize),
- }, {
- name: 'Dec',
- type: 'area',
- data: format(raw.decSize),
- }],
- };
- };
+const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Local',
+ type: 'area',
+ data: format(raw.local.followers.total),
+ }, {
+ name: 'Remote',
+ type: 'area',
+ data: format(raw.remote.followers.total),
+ }],
+ };
+};
- const fetchAndRender = async () => {
- const fetchData = () => {
- switch (props.src) {
- case 'federation': return fetchFederationChart();
- case 'ap-request': return fetchApRequestChart();
- case 'users': return fetchUsersChart(false);
- case 'users-total': return fetchUsersChart(true);
- case 'active-users': return fetchActiveUsersChart();
- case 'notes': return fetchNotesChart('combined');
- case 'local-notes': return fetchNotesChart('local');
- case 'remote-notes': return fetchNotesChart('remote');
- case 'notes-total': return fetchNotesTotalChart();
- case 'drive': return fetchDriveChart();
- case 'drive-files': return fetchDriveFilesChart();
-
- case 'instance-requests': return fetchInstanceRequestsChart();
- case 'instance-users': return fetchInstanceUsersChart(false);
- case 'instance-users-total': return fetchInstanceUsersChart(true);
- case 'instance-notes': return fetchInstanceNotesChart(false);
- case 'instance-notes-total': return fetchInstanceNotesChart(true);
- case 'instance-ff': return fetchInstanceFfChart(false);
- case 'instance-ff-total': return fetchInstanceFfChart(true);
- case 'instance-drive-usage': return fetchInstanceDriveUsageChart(false);
- case 'instance-drive-usage-total': return fetchInstanceDriveUsageChart(true);
- case 'instance-drive-files': return fetchInstanceDriveFilesChart(false);
- case 'instance-drive-files-total': return fetchInstanceDriveFilesChart(true);
+const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/user/drive', { userId: props.args.user.id, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Inc',
+ type: 'area',
+ data: format(raw.incSize),
+ }, {
+ name: 'Dec',
+ type: 'area',
+ data: format(raw.decSize),
+ }],
+ };
+};
- case 'per-user-notes': return fetchPerUserNotesChart();
- case 'per-user-following': return fetchPerUserFollowingChart();
- case 'per-user-followers': return fetchPerUserFollowersChart();
- case 'per-user-drive': return fetchPerUserDriveChart();
- }
- };
- fetching.value = true;
- data = await fetchData();
- fetching.value = false;
- render();
- };
+const fetchAndRender = async () => {
+ const fetchData = () => {
+ switch (props.src) {
+ case 'federation': return fetchFederationChart();
+ case 'ap-request': return fetchApRequestChart();
+ case 'users': return fetchUsersChart(false);
+ case 'users-total': return fetchUsersChart(true);
+ case 'active-users': return fetchActiveUsersChart();
+ case 'notes': return fetchNotesChart('combined');
+ case 'local-notes': return fetchNotesChart('local');
+ case 'remote-notes': return fetchNotesChart('remote');
+ case 'notes-total': return fetchNotesTotalChart();
+ case 'drive': return fetchDriveChart();
+ case 'drive-files': return fetchDriveFilesChart();
+ case 'instance-requests': return fetchInstanceRequestsChart();
+ case 'instance-users': return fetchInstanceUsersChart(false);
+ case 'instance-users-total': return fetchInstanceUsersChart(true);
+ case 'instance-notes': return fetchInstanceNotesChart(false);
+ case 'instance-notes-total': return fetchInstanceNotesChart(true);
+ case 'instance-ff': return fetchInstanceFfChart(false);
+ case 'instance-ff-total': return fetchInstanceFfChart(true);
+ case 'instance-drive-usage': return fetchInstanceDriveUsageChart(false);
+ case 'instance-drive-usage-total': return fetchInstanceDriveUsageChart(true);
+ case 'instance-drive-files': return fetchInstanceDriveFilesChart(false);
+ case 'instance-drive-files-total': return fetchInstanceDriveFilesChart(true);
- watch(() => [props.src, props.span], fetchAndRender);
+ case 'per-user-notes': return fetchPerUserNotesChart();
+ case 'per-user-following': return fetchPerUserFollowingChart();
+ case 'per-user-followers': return fetchPerUserFollowersChart();
+ case 'per-user-drive': return fetchPerUserDriveChart();
+ }
+ };
+ fetching.value = true;
+ chartData = await fetchData();
+ fetching.value = false;
+ render();
+};
- onMounted(() => {
- fetchAndRender();
- });
+watch(() => [props.src, props.span], fetchAndRender);
- onUnmounted(() => {
- if (disposeTooltipComponent) disposeTooltipComponent();
- });
+onMounted(() => {
+ fetchAndRender();
+});
- return {
- chartEl,
- fetching,
- };
- },
+onUnmounted(() => {
+ if (disposeTooltipComponent) disposeTooltipComponent();
});
+/* eslint-enable id-denylist */
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/components/cw-button.vue b/packages/client/src/components/cw-button.vue
index e7c9aabe4e..dd906f9bf3 100644
--- a/packages/client/src/components/cw-button.vue
+++ b/packages/client/src/components/cw-button.vue
@@ -18,7 +18,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'update:modelValue', v: boolean): void;
+ (ev: 'update:modelValue', v: boolean): void;
}>();
const label = computed(() => {
diff --git a/packages/client/src/components/dialog.vue b/packages/client/src/components/dialog.vue
index 3e106a4f0c..b090f3cb4e 100644
--- a/packages/client/src/components/dialog.vue
+++ b/packages/client/src/components/dialog.vue
@@ -90,8 +90,8 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'done', v: { canceled: boolean; result: any }): void;
- (e: 'closed'): void;
+ (ev: 'done', v: { canceled: boolean; result: any }): void;
+ (ev: 'closed'): void;
}>();
const modal = ref<InstanceType<typeof MkModal>>();
@@ -122,14 +122,14 @@ function onBgClick() {
if (props.cancelableByBgClick) cancel();
}
*/
-function onKeydown(e: KeyboardEvent) {
- if (e.key === 'Escape') cancel();
+function onKeydown(evt: KeyboardEvent) {
+ if (evt.key === 'Escape') cancel();
}
-function onInputKeydown(e: KeyboardEvent) {
- if (e.key === 'Enter') {
- e.preventDefault();
- e.stopPropagation();
+function onInputKeydown(evt: KeyboardEvent) {
+ if (evt.key === 'Enter') {
+ evt.preventDefault();
+ evt.stopPropagation();
ok();
}
}
diff --git a/packages/client/src/components/drive-file-thumbnail.vue b/packages/client/src/components/drive-file-thumbnail.vue
index 81b80e7e8e..dd24440e82 100644
--- a/packages/client/src/components/drive-file-thumbnail.vue
+++ b/packages/client/src/components/drive-file-thumbnail.vue
@@ -42,7 +42,7 @@ const is = computed(() => {
"application/x-tar",
"application/gzip",
"application/x-7z-compressed"
- ].some(e => e === props.file.type)) return 'archive';
+ ].some(archiveType => archiveType === props.file.type)) return 'archive';
return 'unknown';
});
diff --git a/packages/client/src/components/drive-select-dialog.vue b/packages/client/src/components/drive-select-dialog.vue
index f6c59457d1..03974559d2 100644
--- a/packages/client/src/components/drive-select-dialog.vue
+++ b/packages/client/src/components/drive-select-dialog.vue
@@ -33,8 +33,8 @@ withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'done', r?: Misskey.entities.DriveFile[]): void;
- (e: 'closed'): void;
+ (ev: 'done', r?: Misskey.entities.DriveFile[]): void;
+ (ev: 'closed'): void;
}>();
const dialog = ref<InstanceType<typeof XModalWindow>>();
diff --git a/packages/client/src/components/drive-window.vue b/packages/client/src/components/drive-window.vue
index d08c5fb674..5bbfca83c9 100644
--- a/packages/client/src/components/drive-window.vue
+++ b/packages/client/src/components/drive-window.vue
@@ -24,6 +24,6 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'closed'): void;
+ (ev: 'closed'): void;
}>();
</script>
diff --git a/packages/client/src/components/drive.file.vue b/packages/client/src/components/drive.file.vue
index 262eae0de1..aaf7ca3ca3 100644
--- a/packages/client/src/components/drive.file.vue
+++ b/packages/client/src/components/drive.file.vue
@@ -31,7 +31,7 @@
</template>
<script lang="ts" setup>
-import { computed, ref } from 'vue';
+import { computed, defineAsyncComponent, ref } from 'vue';
import * as Misskey from 'misskey-js';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import MkDriveFileThumbnail from './drive-file-thumbnail.vue';
@@ -50,9 +50,9 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'chosen', r: Misskey.entities.DriveFile): void;
- (e: 'dragstart'): void;
- (e: 'dragend'): void;
+ (ev: 'chosen', r: Misskey.entities.DriveFile): void;
+ (ev: 'dragstart'): void;
+ (ev: 'dragend'): void;
}>();
const isDragging = ref(false);
@@ -99,14 +99,14 @@ function onClick(ev: MouseEvent) {
}
}
-function onContextmenu(e: MouseEvent) {
- os.contextMenu(getMenu(), e);
+function onContextmenu(ev: MouseEvent) {
+ os.contextMenu(getMenu(), ev);
}
-function onDragstart(e: DragEvent) {
- if (e.dataTransfer) {
- e.dataTransfer.effectAllowed = 'move';
- e.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FILE_, JSON.stringify(props.file));
+function onDragstart(ev: DragEvent) {
+ if (ev.dataTransfer) {
+ ev.dataTransfer.effectAllowed = 'move';
+ ev.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FILE_, JSON.stringify(props.file));
}
isDragging.value = true;
@@ -133,11 +133,11 @@ function rename() {
}
function describe() {
- os.popup(import('@/components/media-caption.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/media-caption.vue')), {
title: i18n.ts.describeFile,
input: {
placeholder: i18n.ts.inputNewDescription,
- default: props.file.comment !== null ? props.file.comment : '',
+ default: props.file.comment != null ? props.file.comment : '',
},
image: props.file
}, {
@@ -146,7 +146,7 @@ function describe() {
let comment = result.result;
os.api('drive/files/update', {
fileId: props.file.id,
- comment: comment.length == 0 ? null : comment
+ comment: comment.length === 0 ? null : comment
});
}
}, 'closed');
diff --git a/packages/client/src/components/drive.folder.vue b/packages/client/src/components/drive.folder.vue
index 57621bf097..d530f8beff 100644
--- a/packages/client/src/components/drive.folder.vue
+++ b/packages/client/src/components/drive.folder.vue
@@ -27,7 +27,7 @@
</template>
<script lang="ts" setup>
-import { computed, ref } from 'vue';
+import { computed, defineAsyncComponent, ref } from 'vue';
import * as Misskey from 'misskey-js';
import * as os from '@/os';
import { i18n } from '@/i18n';
@@ -84,12 +84,12 @@ function onDragover(ev: DragEvent) {
return;
}
- const isFile = ev.dataTransfer.items[0].kind == 'file';
- const isDriveFile = ev.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
- const isDriveFolder = ev.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FOLDER_;
+ const isFile = ev.dataTransfer.items[0].kind === 'file';
+ const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
+ const isDriveFolder = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FOLDER_;
if (isFile || isDriveFile || isDriveFolder) {
- ev.dataTransfer.dropEffect = ev.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+ ev.dataTransfer.dropEffect = ev.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
} else {
ev.dataTransfer.dropEffect = 'none';
}
@@ -118,7 +118,7 @@ function onDrop(ev: DragEvent) {
//#region ドライブのファイル
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile != '') {
+ if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile);
emit('removeFile', file.id);
os.api('drive/files/update', {
@@ -130,11 +130,11 @@ function onDrop(ev: DragEvent) {
//#region ドライブのフォルダ
const driveFolder = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
- if (driveFolder != null && driveFolder != '') {
+ if (driveFolder != null && driveFolder !== '') {
const folder = JSON.parse(driveFolder);
// 移動先が自分自身ならreject
- if (folder.id == props.folder.id) return;
+ if (folder.id === props.folder.id) return;
emit('removeFolder', folder.id);
os.api('drive/folders/update', {
@@ -230,7 +230,7 @@ function onContextmenu(ev: MouseEvent) {
text: i18n.ts.openInWindow,
icon: 'fas fa-window-restore',
action: () => {
- os.popup(import('./drive-window.vue'), {
+ os.popup(defineAsyncComponent(() => import('./drive-window.vue')), {
initialFolder: props.folder
}, {
}, 'closed');
diff --git a/packages/client/src/components/drive.nav-folder.vue b/packages/client/src/components/drive.nav-folder.vue
index 67223267c1..5482703317 100644
--- a/packages/client/src/components/drive.nav-folder.vue
+++ b/packages/client/src/components/drive.nav-folder.vue
@@ -24,10 +24,10 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'move', v?: Misskey.entities.DriveFolder): void;
- (e: 'upload', file: File, folder?: Misskey.entities.DriveFolder | null): void;
- (e: 'removeFile', v: Misskey.entities.DriveFile['id']): void;
- (e: 'removeFolder', v: Misskey.entities.DriveFolder['id']): void;
+ (ev: 'move', v?: Misskey.entities.DriveFolder): void;
+ (ev: 'upload', file: File, folder?: Misskey.entities.DriveFolder | null): void;
+ (ev: 'removeFile', v: Misskey.entities.DriveFile['id']): void;
+ (ev: 'removeFolder', v: Misskey.entities.DriveFolder['id']): void;
}>();
const hover = ref(false);
@@ -45,22 +45,22 @@ function onMouseout() {
hover.value = false;
}
-function onDragover(e: DragEvent) {
- if (!e.dataTransfer) return;
+function onDragover(ev: DragEvent) {
+ if (!ev.dataTransfer) return;
// このフォルダがルートかつカレントディレクトリならドロップ禁止
if (props.folder == null && props.parentFolder == null) {
- e.dataTransfer.dropEffect = 'none';
+ ev.dataTransfer.dropEffect = 'none';
}
- const isFile = e.dataTransfer.items[0].kind == 'file';
- const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
- const isDriveFolder = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FOLDER_;
+ const isFile = ev.dataTransfer.items[0].kind === 'file';
+ const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
+ const isDriveFolder = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FOLDER_;
if (isFile || isDriveFile || isDriveFolder) {
- e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+ ev.dataTransfer.dropEffect = ev.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
} else {
- e.dataTransfer.dropEffect = 'none';
+ ev.dataTransfer.dropEffect = 'none';
}
return false;
@@ -74,22 +74,22 @@ function onDragleave() {
if (props.folder || props.parentFolder) draghover.value = false;
}
-function onDrop(e: DragEvent) {
+function onDrop(ev: DragEvent) {
draghover.value = false;
- if (!e.dataTransfer) return;
+ if (!ev.dataTransfer) return;
// ファイルだったら
- if (e.dataTransfer.files.length > 0) {
- for (const file of Array.from(e.dataTransfer.files)) {
+ if (ev.dataTransfer.files.length > 0) {
+ for (const file of Array.from(ev.dataTransfer.files)) {
emit('upload', file, props.folder);
}
return;
}
//#region ドライブのファイル
- const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile != '') {
+ const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
+ if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile);
emit('removeFile', file.id);
os.api('drive/files/update', {
@@ -100,11 +100,11 @@ function onDrop(e: DragEvent) {
//#endregion
//#region ドライブのフォルダ
- const driveFolder = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
- if (driveFolder != null && driveFolder != '') {
+ const driveFolder = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
+ if (driveFolder != null && driveFolder !== '') {
const folder = JSON.parse(driveFolder);
// 移動先が自分自身ならreject
- if (props.folder && folder.id == props.folder.id) return;
+ if (props.folder && folder.id === props.folder.id) return;
emit('removeFolder', folder.id);
os.api('drive/folders/update', {
folderId: folder.id,
diff --git a/packages/client/src/components/drive.vue b/packages/client/src/components/drive.vue
index 2ec885b00c..42ec3a5995 100644
--- a/packages/client/src/components/drive.vue
+++ b/packages/client/src/components/drive.vue
@@ -110,11 +110,11 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'selected', v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder): void;
- (e: 'change-selection', v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void;
- (e: 'move-root'): void;
- (e: 'cd', v: Misskey.entities.DriveFolder | null): void;
- (e: 'open-folder', v: Misskey.entities.DriveFolder): void;
+ (ev: 'selected', v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder): void;
+ (ev: 'change-selection', v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void;
+ (ev: 'move-root'): void;
+ (ev: 'cd', v: Misskey.entities.DriveFolder | null): void;
+ (ev: 'open-folder', v: Misskey.entities.DriveFolder): void;
}>();
const loadMoreFiles = ref<InstanceType<typeof MkButton>>();
@@ -153,7 +153,7 @@ function onStreamDriveFileCreated(file: Misskey.entities.DriveFile) {
function onStreamDriveFileUpdated(file: Misskey.entities.DriveFile) {
const current = folder.value ? folder.value.id : null;
- if (current != file.folderId) {
+ if (current !== file.folderId) {
removeFile(file);
} else {
addFile(file, true);
@@ -170,7 +170,7 @@ function onStreamDriveFolderCreated(createdFolder: Misskey.entities.DriveFolder)
function onStreamDriveFolderUpdated(updatedFolder: Misskey.entities.DriveFolder) {
const current = folder.value ? folder.value.id : null;
- if (current != updatedFolder.parentId) {
+ if (current !== updatedFolder.parentId) {
removeFolder(updatedFolder);
} else {
addFolder(updatedFolder, true);
@@ -181,23 +181,23 @@ function onStreamDriveFolderDeleted(folderId: string) {
removeFolder(folderId);
}
-function onDragover(e: DragEvent): any {
- if (!e.dataTransfer) return;
+function onDragover(ev: DragEvent): any {
+ if (!ev.dataTransfer) return;
// ドラッグ元が自分自身の所有するアイテムだったら
if (isDragSource.value) {
// 自分自身にはドロップさせない
- e.dataTransfer.dropEffect = 'none';
+ ev.dataTransfer.dropEffect = 'none';
return;
}
- const isFile = e.dataTransfer.items[0].kind == 'file';
- const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
- const isDriveFolder = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FOLDER_;
+ const isFile = ev.dataTransfer.items[0].kind === 'file';
+ const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
+ const isDriveFolder = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FOLDER_;
if (isFile || isDriveFile || isDriveFolder) {
- e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+ ev.dataTransfer.dropEffect = ev.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
} else {
- e.dataTransfer.dropEffect = 'none';
+ ev.dataTransfer.dropEffect = 'none';
}
return false;
@@ -211,24 +211,24 @@ function onDragleave() {
draghover.value = false;
}
-function onDrop(e: DragEvent): any {
+function onDrop(ev: DragEvent): any {
draghover.value = false;
- if (!e.dataTransfer) return;
+ if (!ev.dataTransfer) return;
// ドロップされてきたものがファイルだったら
- if (e.dataTransfer.files.length > 0) {
- for (const file of Array.from(e.dataTransfer.files)) {
+ if (ev.dataTransfer.files.length > 0) {
+ for (const file of Array.from(ev.dataTransfer.files)) {
upload(file, folder.value);
}
return;
}
//#region ドライブのファイル
- const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile != '') {
+ const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
+ if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile);
- if (files.value.some(f => f.id == file.id)) return;
+ if (files.value.some(f => f.id === file.id)) return;
removeFile(file.id);
os.api('drive/files/update', {
fileId: file.id,
@@ -238,13 +238,13 @@ function onDrop(e: DragEvent): any {
//#endregion
//#region ドライブのフォルダ
- const driveFolder = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
- if (driveFolder != null && driveFolder != '') {
+ const driveFolder = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
+ if (driveFolder != null && driveFolder !== '') {
const droppedFolder = JSON.parse(driveFolder);
// 移動先が自分自身ならreject
- if (folder.value && droppedFolder.id == folder.value.id) return false;
- if (folders.value.some(f => f.id == droppedFolder.id)) return false;
+ if (folder.value && droppedFolder.id === folder.value.id) return false;
+ if (folders.value.some(f => f.id === droppedFolder.id)) return false;
removeFolder(droppedFolder.id);
os.api('drive/folders/update', {
folderId: droppedFolder.id,
@@ -357,16 +357,16 @@ function onChangeFileInput() {
}
function upload(file: File, folderToUpload?: Misskey.entities.DriveFolder | null) {
- uploadFile(file, (folderToUpload && typeof folderToUpload == 'object') ? folderToUpload.id : null, undefined, keepOriginal.value).then(res => {
+ uploadFile(file, (folderToUpload && typeof folderToUpload === 'object') ? folderToUpload.id : null, undefined, keepOriginal.value).then(res => {
addFile(res, true);
});
}
function chooseFile(file: Misskey.entities.DriveFile) {
- const isAlreadySelected = selectedFiles.value.some(f => f.id == file.id);
+ const isAlreadySelected = selectedFiles.value.some(f => f.id === file.id);
if (props.multiple) {
if (isAlreadySelected) {
- selectedFiles.value = selectedFiles.value.filter(f => f.id != file.id);
+ selectedFiles.value = selectedFiles.value.filter(f => f.id !== file.id);
} else {
selectedFiles.value.push(file);
}
@@ -382,10 +382,10 @@ function chooseFile(file: Misskey.entities.DriveFile) {
}
function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) {
- const isAlreadySelected = selectedFolders.value.some(f => f.id == folderToChoose.id);
+ const isAlreadySelected = selectedFolders.value.some(f => f.id === folderToChoose.id);
if (props.multiple) {
if (isAlreadySelected) {
- selectedFolders.value = selectedFolders.value.filter(f => f.id != folderToChoose.id);
+ selectedFolders.value = selectedFolders.value.filter(f => f.id !== folderToChoose.id);
} else {
selectedFolders.value.push(folderToChoose);
}
@@ -404,7 +404,7 @@ function move(target?: Misskey.entities.DriveFolder) {
if (!target) {
goRoot();
return;
- } else if (typeof target == 'object') {
+ } else if (typeof target === 'object') {
target = target.id;
}
@@ -430,9 +430,9 @@ function move(target?: Misskey.entities.DriveFolder) {
function addFolder(folderToAdd: Misskey.entities.DriveFolder, unshift = false) {
const current = folder.value ? folder.value.id : null;
- if (current != folderToAdd.parentId) return;
+ if (current !== folderToAdd.parentId) return;
- if (folders.value.some(f => f.id == folderToAdd.id)) {
+ if (folders.value.some(f => f.id === folderToAdd.id)) {
const exist = folders.value.map(f => f.id).indexOf(folderToAdd.id);
folders.value[exist] = folderToAdd;
return;
@@ -447,9 +447,9 @@ function addFolder(folderToAdd: Misskey.entities.DriveFolder, unshift = false) {
function addFile(fileToAdd: Misskey.entities.DriveFile, unshift = false) {
const current = folder.value ? folder.value.id : null;
- if (current != fileToAdd.folderId) return;
+ if (current !== fileToAdd.folderId) return;
- if (files.value.some(f => f.id == fileToAdd.id)) {
+ if (files.value.some(f => f.id === fileToAdd.id)) {
const exist = files.value.map(f => f.id).indexOf(fileToAdd.id);
files.value[exist] = fileToAdd;
return;
@@ -464,12 +464,12 @@ function addFile(fileToAdd: Misskey.entities.DriveFile, unshift = false) {
function removeFolder(folderToRemove: Misskey.entities.DriveFolder | string) {
const folderIdToRemove = typeof folderToRemove === 'object' ? folderToRemove.id : folderToRemove;
- folders.value = folders.value.filter(f => f.id != folderIdToRemove);
+ folders.value = folders.value.filter(f => f.id !== folderIdToRemove);
}
function removeFile(file: Misskey.entities.DriveFile | string) {
const fileId = typeof file === 'object' ? file.id : file;
- files.value = files.value.filter(f => f.id != fileId);
+ files.value = files.value.filter(f => f.id !== fileId);
}
function appendFile(file: Misskey.entities.DriveFile) {
@@ -512,7 +512,7 @@ async function fetch() {
folderId: folder.value ? folder.value.id : null,
limit: foldersMax + 1
}).then(fetchedFolders => {
- if (fetchedFolders.length == foldersMax + 1) {
+ if (fetchedFolders.length === foldersMax + 1) {
moreFolders.value = true;
fetchedFolders.pop();
}
@@ -524,7 +524,7 @@ async function fetch() {
type: props.type,
limit: filesMax + 1
}).then(fetchedFiles => {
- if (fetchedFiles.length == filesMax + 1) {
+ if (fetchedFiles.length === filesMax + 1) {
moreFiles.value = true;
fetchedFiles.pop();
}
@@ -551,7 +551,7 @@ function fetchMoreFiles() {
untilId: files.value[files.value.length - 1].id,
limit: max + 1
}).then(files => {
- if (files.length == max + 1) {
+ if (files.length === max + 1) {
moreFiles.value = true;
files.pop();
} else {
diff --git a/packages/client/src/components/emoji-picker-window.vue b/packages/client/src/components/emoji-picker-window.vue
index 4d27fb48ba..610690d701 100644
--- a/packages/client/src/components/emoji-picker-window.vue
+++ b/packages/client/src/components/emoji-picker-window.vue
@@ -25,8 +25,8 @@ withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'chosen', v: any): void;
- (e: 'closed'): void;
+ (ev: 'chosen', v: any): void;
+ (ev: 'closed'): void;
}>();
function chosen(emoji: any) {
diff --git a/packages/client/src/components/emoji-picker.section.vue b/packages/client/src/components/emoji-picker.section.vue
index 1026e894d1..52f7047487 100644
--- a/packages/client/src/components/emoji-picker.section.vue
+++ b/packages/client/src/components/emoji-picker.section.vue
@@ -24,7 +24,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'chosen', v: string, ev: MouseEvent): void;
+ (ev: 'chosen', v: string, event: MouseEvent): void;
}>();
const shown = ref(!!props.initialShown);
diff --git a/packages/client/src/components/emoji-picker.vue b/packages/client/src/components/emoji-picker.vue
index 8601ea121c..522f636474 100644
--- a/packages/client/src/components/emoji-picker.vue
+++ b/packages/client/src/components/emoji-picker.vue
@@ -61,7 +61,7 @@
</div>
<div>
<header class="_acrylic">{{ i18n.ts.emoji }}</header>
- <XSection v-for="category in categories" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)" @chosen="chosen">{{ category }}</XSection>
+ <XSection v-for="category in categories" :key="category" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)" @chosen="chosen">{{ category }}</XSection>
</div>
</div>
<div class="tabs">
@@ -97,7 +97,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'chosen', v: string): void;
+ (ev: 'chosen', v: string): void;
}>();
const search = ref<HTMLInputElement>();
@@ -138,7 +138,7 @@ watch(q, () => {
const emojis = customEmojis;
const matches = new Set<Misskey.entities.CustomEmoji>();
- const exactMatch = emojis.find(e => e.name === newQ);
+ const exactMatch = emojis.find(emoji => emoji.name === newQ);
if (exactMatch) matches.add(exactMatch);
if (newQ.includes(' ')) { // AND検索
@@ -201,7 +201,7 @@ watch(q, () => {
const emojis = emojilist;
const matches = new Set<UnicodeEmojiDef>();
- const exactMatch = emojis.find(e => e.name === newQ);
+ const exactMatch = emojis.find(emoji => emoji.name === newQ);
if (exactMatch) matches.add(exactMatch);
if (newQ.includes(' ')) { // AND検索
@@ -295,7 +295,7 @@ function chosen(emoji: any, ev?: MouseEvent) {
// 最近使った絵文字更新
if (!pinned.value.includes(key)) {
let recents = defaultStore.state.recentlyUsedEmojis;
- recents = recents.filter((e: any) => e !== key);
+ recents = recents.filter((emoji: any) => emoji !== key);
recents.unshift(key);
defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32));
}
@@ -313,12 +313,12 @@ function done(query?: any): boolean | void {
if (query == null || typeof query !== 'string') return;
const q2 = query.replace(/:/g, '');
- const exactMatchCustom = customEmojis.find(e => e.name === q2);
+ const exactMatchCustom = customEmojis.find(emoji => emoji.name === q2);
if (exactMatchCustom) {
chosen(exactMatchCustom);
return true;
}
- const exactMatchUnicode = emojilist.find(e => e.char === q2 || e.name === q2);
+ const exactMatchUnicode = emojilist.find(emoji => emoji.char === q2 || emoji.name === q2);
if (exactMatchUnicode) {
chosen(exactMatchUnicode);
return true;
diff --git a/packages/client/src/components/follow-button.vue b/packages/client/src/components/follow-button.vue
index 93c9e891c1..b3540bc316 100644
--- a/packages/client/src/components/follow-button.vue
+++ b/packages/client/src/components/follow-button.vue
@@ -58,7 +58,7 @@ if (props.user.isFollowing == null) {
}
function onFollowChange(user: Misskey.entities.UserDetailed) {
- if (user.id == props.user.id) {
+ if (user.id === props.user.id) {
isFollowing.value = user.isFollowing;
hasPendingFollowRequestFromYou.value = user.hasPendingFollowRequestFromYou;
}
@@ -96,8 +96,8 @@ async function onClick() {
hasPendingFollowRequestFromYou.value = true;
}
}
- } catch (e) {
- console.error(e);
+ } catch (err) {
+ console.error(err);
} finally {
wait.value = false;
}
diff --git a/packages/client/src/components/forgot-password.vue b/packages/client/src/components/forgot-password.vue
index 46cbf6bd70..19c1f23c85 100644
--- a/packages/client/src/components/forgot-password.vue
+++ b/packages/client/src/components/forgot-password.vue
@@ -41,8 +41,8 @@ import { instance } from '@/instance';
import { i18n } from '@/i18n';
const emit = defineEmits<{
- (e: 'done'): void;
- (e: 'closed'): void;
+ (ev: 'done'): void;
+ (ev: 'closed'): void;
}>();
let dialog: InstanceType<typeof XModalWindow> = $ref();
diff --git a/packages/client/src/components/form-dialog.vue b/packages/client/src/components/form-dialog.vue
index efd0da443d..11459f5937 100644
--- a/packages/client/src/components/form-dialog.vue
+++ b/packages/client/src/components/form-dialog.vue
@@ -44,7 +44,7 @@
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
</FormRange>
- <MkButton v-else-if="form[item].type === 'button'" @click="form[item].action($event, values)" class="_formBlock">
+ <MkButton v-else-if="form[item].type === 'button'" class="_formBlock" @click="form[item].action($event, values)">
<span v-text="form[item].content || item"></span>
</MkButton>
</template>
diff --git a/packages/client/src/components/form/range.vue b/packages/client/src/components/form/range.vue
index a82348d317..07f2c23124 100644
--- a/packages/client/src/components/form/range.vue
+++ b/packages/client/src/components/form/range.vue
@@ -16,7 +16,7 @@
</template>
<script lang="ts">
-import { computed, defineComponent, onMounted, onUnmounted, ref, watch } from 'vue';
+import { computed, defineAsyncComponent, defineComponent, onMounted, onUnmounted, ref, watch } from 'vue';
import * as os from '@/os';
export default defineComponent({
@@ -112,7 +112,7 @@ export default defineComponent({
ev.preventDefault();
const tooltipShowing = ref(true);
- os.popup(import('@/components/ui/tooltip.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/ui/tooltip.vue')), {
showing: tooltipShowing,
text: computed(() => {
return props.textConverter(finalValue.value);
diff --git a/packages/client/src/components/form/switch.vue b/packages/client/src/components/form/switch.vue
index b5a30d635c..fadb770aee 100644
--- a/packages/client/src/components/form/switch.vue
+++ b/packages/client/src/components/form/switch.vue
@@ -31,7 +31,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'update:modelValue', v: boolean): void;
+ (ev: 'update:modelValue', v: boolean): void;
}>();
let button = $ref<HTMLElement>();
diff --git a/packages/client/src/components/global/avatar.vue b/packages/client/src/components/global/avatar.vue
index 27cfb6e4d4..4868896c99 100644
--- a/packages/client/src/components/global/avatar.vue
+++ b/packages/client/src/components/global/avatar.vue
@@ -32,7 +32,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'click', ev: MouseEvent): void;
+ (ev: 'click', v: MouseEvent): void;
}>();
const url = $computed(() => defaultStore.state.disableShowingAnimatedImages
diff --git a/packages/client/src/components/global/emoji.vue b/packages/client/src/components/global/emoji.vue
index 92edb1caf9..0075e0867d 100644
--- a/packages/client/src/components/global/emoji.vue
+++ b/packages/client/src/components/global/emoji.vue
@@ -46,7 +46,7 @@ export default defineComponent({
const url = computed(() => {
if (char.value) {
let codes = Array.from(char.value).map(x => x.codePointAt(0).toString(16));
- if (!codes.includes('200d')) codes = codes.filter(x => x != 'fe0f');
+ if (!codes.includes('200d')) codes = codes.filter(x => x !== 'fe0f');
codes = codes.filter(x => x && x.length);
return `${twemojiSvgBase}/${codes.join('-')}.svg`;
} else {
diff --git a/packages/client/src/components/global/header.vue b/packages/client/src/components/global/header.vue
index e558614c12..63db19a520 100644
--- a/packages/client/src/components/global/header.vue
+++ b/packages/client/src/components/global/header.vue
@@ -38,7 +38,7 @@
<script lang="ts">
import { computed, defineComponent, onMounted, onUnmounted, PropType, ref, inject } from 'vue';
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
import { popupMenu } from '@/os';
import { url } from '@/config';
import { scrollToTop } from '@/scripts/scroll';
diff --git a/packages/client/src/components/global/loading.vue b/packages/client/src/components/global/loading.vue
index 43ea1395ed..fa2ce1800c 100644
--- a/packages/client/src/components/global/loading.vue
+++ b/packages/client/src/components/global/loading.vue
@@ -1,6 +1,17 @@
<template>
<div class="yxspomdl" :class="{ inline, colored, mini }">
- <div class="ring"></div>
+ <div class="container">
+ <svg class="spinner bg" viewBox="0 0 168 168" xmlns="http://www.w3.org/2000/svg">
+ <g transform="matrix(1.125,0,0,1.125,12,12)">
+ <circle cx="64" cy="64" r="64" style="fill:none;stroke:currentColor;stroke-width:21.33px;"/>
+ </g>
+ </svg>
+ <svg class="spinner fg" viewBox="0 0 168 168" xmlns="http://www.w3.org/2000/svg">
+ <g transform="matrix(1.125,0,0,1.125,12,12)">
+ <path d="M128,64C128,28.654 99.346,0 64,0C99.346,0 128,28.654 128,64Z" style="fill:none;stroke:currentColor;stroke-width:21.33px;"/>
+ </g>
+ </svg>
+ </div>
</div>
</template>
@@ -19,7 +30,7 @@ const props = withDefaults(defineProps<{
</script>
<style lang="scss" scoped>
-@keyframes ring {
+@keyframes spinner {
0% {
transform: rotate(0deg);
}
@@ -33,7 +44,7 @@ const props = withDefaults(defineProps<{
text-align: center;
cursor: wait;
- --size: 48px;
+ --size: 40px;
&.colored {
color: var(--accent);
@@ -50,32 +61,31 @@ const props = withDefaults(defineProps<{
--size: 32px;
}
- > .ring {
+ > .container {
position: relative;
- display: inline-block;
- vertical-align: middle;
+ width: var(--size);
+ height: var(--size);
+ margin: 0 auto;
- &:before,
- &:after {
- content: " ";
- display: block;
- box-sizing: border-box;
+ > .spinner {
+ position: absolute;
+ top: 0;
+ left: 0;
width: var(--size);
height: var(--size);
- border-radius: 50%;
- border: solid 4px;
+ fill-rule: evenodd;
+ clip-rule: evenodd;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-miterlimit: 1.5;
}
- &:before {
- border-color: currentColor;
- opacity: 0.3;
+ > .bg {
+ opacity: 0.275;
}
- &:after {
- position: absolute;
- top: 0;
- border-color: currentColor transparent transparent transparent;
- animation: ring 0.5s linear infinite;
+ > .fg {
+ animation: spinner 0.5s linear infinite;
}
}
}
diff --git a/packages/client/src/components/global/misskey-flavored-markdown.vue b/packages/client/src/components/global/misskey-flavored-markdown.vue
index 243d8614ba..70d0108e9f 100644
--- a/packages/client/src/components/global/misskey-flavored-markdown.vue
+++ b/packages/client/src/components/global/misskey-flavored-markdown.vue
@@ -31,6 +31,32 @@ const props = withDefaults(defineProps<{
}
}
+.mfm-x2 {
+ --mfm-zoom-size: 200%;
+}
+
+.mfm-x3 {
+ --mfm-zoom-size: 400%;
+}
+
+.mfm-x4 {
+ --mfm-zoom-size: 600%;
+}
+
+.mfm-x2, .mfm-x3, .mfm-x4 {
+ font-size: var(--mfm-zoom-size);
+
+ .mfm-x2, .mfm-x3, .mfm-x4 {
+ /* only half effective */
+ font-size: calc(var(--mfm-zoom-size) / 2 + 50%);
+
+ .mfm-x2, .mfm-x3, .mfm-x4 {
+ /* disabled */
+ font-size: 100%;
+ }
+ }
+}
+
@keyframes mfm-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
diff --git a/packages/client/src/components/global/time.vue b/packages/client/src/components/global/time.vue
index 5748d9de61..02351deb5f 100644
--- a/packages/client/src/components/global/time.vue
+++ b/packages/client/src/components/global/time.vue
@@ -17,7 +17,7 @@ const props = withDefaults(defineProps<{
mode: 'relative',
});
-const _time = typeof props.time == 'string' ? new Date(props.time) : props.time;
+const _time = typeof props.time === 'string' ? new Date(props.time) : props.time;
const absolute = _time.toLocaleString();
let now = $ref(new Date());
diff --git a/packages/client/src/components/global/url.vue b/packages/client/src/components/global/url.vue
index 55f6c5d5f9..34ba9024cc 100644
--- a/packages/client/src/components/global/url.vue
+++ b/packages/client/src/components/global/url.vue
@@ -18,7 +18,7 @@
</template>
<script lang="ts">
-import { defineComponent, ref } from 'vue';
+import { defineAsyncComponent, defineComponent, ref } from 'vue';
import { toUnicode as decodePunycode } from 'punycode/';
import { url as local } from '@/config';
import * as os from '@/os';
@@ -50,7 +50,7 @@ export default defineComponent({
const el = ref();
useTooltip(el, (showing) => {
- os.popup(import('@/components/url-preview-popup.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/url-preview-popup.vue')), {
showing,
url: props.url,
source: el.value,
diff --git a/packages/client/src/components/image-viewer.vue b/packages/client/src/components/image-viewer.vue
index c39076df16..7bc88399ef 100644
--- a/packages/client/src/components/image-viewer.vue
+++ b/packages/client/src/components/image-viewer.vue
@@ -25,7 +25,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'closed'): void;
+ (ev: 'closed'): void;
}>();
const modal = $ref<InstanceType<typeof MkModal>>();
diff --git a/packages/client/src/components/instance-ticker.vue b/packages/client/src/components/instance-ticker.vue
index 9b0a18ec90..c32409ecf4 100644
--- a/packages/client/src/components/instance-ticker.vue
+++ b/packages/client/src/components/instance-ticker.vue
@@ -39,6 +39,19 @@ const bg = {
border-radius: 4px 0 0 4px;
overflow: hidden;
color: #fff;
+ text-shadow: /* .866 ≈ sin(60deg) */
+ 1px 0 1px #000,
+ .866px .5px 1px #000,
+ .5px .866px 1px #000,
+ 0 1px 1px #000,
+ -.5px .866px 1px #000,
+ -.866px .5px 1px #000,
+ -1px 0 1px #000,
+ -.866px -.5px 1px #000,
+ -.5px -.866px 1px #000,
+ 0 -1px 1px #000,
+ .5px -.866px 1px #000,
+ .866px -.5px 1px #000;
> .icon {
height: 100%;
diff --git a/packages/client/src/components/link.vue b/packages/client/src/components/link.vue
index 317c931cec..846a9a3a76 100644
--- a/packages/client/src/components/link.vue
+++ b/packages/client/src/components/link.vue
@@ -8,7 +8,7 @@
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { defineAsyncComponent } from 'vue';
import { url as local } from '@/config';
import { useTooltip } from '@/scripts/use-tooltip';
import * as os from '@/os';
@@ -26,7 +26,7 @@ const target = self ? null : '_blank';
const el = $ref();
useTooltip($$(el), (showing) => {
- os.popup(import('@/components/url-preview-popup.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/url-preview-popup.vue')), {
showing,
url: props.url,
source: el,
diff --git a/packages/client/src/components/media-caption.vue b/packages/client/src/components/media-caption.vue
index ef546f3f70..feed3854f9 100644
--- a/packages/client/src/components/media-caption.vue
+++ b/packages/client/src/components/media-caption.vue
@@ -77,7 +77,7 @@ export default defineComponent({
computed: {
remainingLength(): number {
- if (typeof this.inputValue != "string") return 512;
+ if (typeof this.inputValue !== "string") return 512;
return 512 - length(this.inputValue);
}
},
@@ -116,17 +116,17 @@ export default defineComponent({
}
},
- onKeydown(e) {
- if (e.which === 27) { // ESC
+ onKeydown(evt) {
+ if (evt.which === 27) { // ESC
this.cancel();
}
},
- onInputKeydown(e) {
- if (e.which === 13) { // Enter
- if (e.ctrlKey) {
- e.preventDefault();
- e.stopPropagation();
+ onInputKeydown(evt) {
+ if (evt.which === 13) { // Enter
+ if (evt.ctrlKey) {
+ evt.preventDefault();
+ evt.stopPropagation();
this.ok();
}
}
diff --git a/packages/client/src/components/mention.vue b/packages/client/src/components/mention.vue
index 479acfbc8f..70c2f49afa 100644
--- a/packages/client/src/components/mention.vue
+++ b/packages/client/src/components/mention.vue
@@ -1,22 +1,22 @@
<template>
-<MkA v-if="url.startsWith('/')" v-user-preview="canonical" class="ldlomzub" :class="{ isMe }" :to="url" :style="{ background: bg }">
- <img class="icon" :src="`/avatar/@${username}@${host}`" alt="">
+<MkA v-if="url.startsWith('/')" v-user-preview="canonical" :class="[$style.root, { isMe }]" :to="url" :style="{ background: bg }">
+ <img :class="$style.icon" :src="`/avatar/@${username}@${host}`" alt="">
<span class="main">
<span class="username">@{{ username }}</span>
- <span v-if="(host != localHost) || $store.state.showFullAcct" class="host">@{{ toUnicode(host) }}</span>
+ <span v-if="(host != localHost) || $store.state.showFullAcct" :class="$style.mainHost">@{{ toUnicode(host) }}</span>
</span>
</MkA>
-<a v-else class="ldlomzub" :href="url" target="_blank" rel="noopener" :style="{ background: bg }">
+<a v-else :class="$style.root" :href="url" target="_blank" rel="noopener" :style="{ background: bg }">
<span class="main">
<span class="username">@{{ username }}</span>
- <span class="host">@{{ toUnicode(host) }}</span>
+ <span :class="$style.mainHost">@{{ toUnicode(host) }}</span>
</span>
</a>
</template>
<script lang="ts">
-import { defineComponent } from 'vue';
-import * as tinycolor from 'tinycolor2';
+import { defineComponent, useCssModule } from 'vue';
+import tinycolor from 'tinycolor2';
import { toUnicode } from 'punycode';
import { host as localHost } from '@/config';
import { $i } from '@/account';
@@ -45,6 +45,8 @@ export default defineComponent({
const bg = tinycolor(getComputedStyle(document.documentElement).getPropertyValue(isMe ? '--mentionMe' : '--mention'));
bg.setAlpha(0.1);
+ useCssModule();
+
return {
localHost,
isMe,
@@ -57,8 +59,8 @@ export default defineComponent({
});
</script>
-<style lang="scss" scoped>
-.ldlomzub {
+<style lang="scss" module>
+.root {
display: inline-block;
padding: 4px 8px 4px 4px;
border-radius: 999px;
@@ -67,18 +69,18 @@ export default defineComponent({
&.isMe {
color: var(--mentionMe);
}
+}
- > .icon {
- width: 1.5em;
- margin: 0 0.2em 0 0;
- vertical-align: bottom;
- border-radius: 100%;
- }
+.icon {
+ width: 1.5em;
+ height: 1.5em;
+ object-fit: cover;
+ margin: 0 0.2em 0 0;
+ vertical-align: bottom;
+ border-radius: 100%;
+}
- > .main {
- > .host {
- opacity: 0.5;
- }
- }
+.mainHost {
+ opacity: 0.5;
}
</style>
diff --git a/packages/client/src/components/mfm.ts b/packages/client/src/components/mfm.ts
index 37076652fd..4556a82d55 100644
--- a/packages/client/src/components/mfm.ts
+++ b/packages/client/src/components/mfm.ts
@@ -91,7 +91,8 @@ export default defineComponent({
let style;
switch (token.props.name) {
case 'tada': {
- style = `font-size: 150%;` + (this.$store.state.animatedMfm ? 'animation: tada 1s linear infinite both;' : '');
+ const speed = validTime(token.props.args.speed) || '1s';
+ style = 'font-size: 150%;' + (this.$store.state.animatedMfm ? `animation: tada ${speed} linear infinite both;` : '');
break;
}
case 'jelly': {
@@ -123,11 +124,13 @@ export default defineComponent({
break;
}
case 'jump': {
- style = this.$store.state.animatedMfm ? 'animation: mfm-jump 0.75s linear infinite;' : '';
+ const speed = validTime(token.props.args.speed) || '0.75s';
+ style = this.$store.state.animatedMfm ? `animation: mfm-jump ${speed} linear infinite;` : '';
break;
}
case 'bounce': {
- style = this.$store.state.animatedMfm ? 'animation: mfm-bounce 0.75s linear infinite; transform-origin: center bottom;' : '';
+ const speed = validTime(token.props.args.speed) || '0.75s';
+ style = this.$store.state.animatedMfm ? `animation: mfm-bounce ${speed} linear infinite; transform-origin: center bottom;` : '';
break;
}
case 'flip': {
@@ -139,16 +142,19 @@ export default defineComponent({
break;
}
case 'x2': {
- style = `font-size: 200%;`;
- break;
+ return h('span', {
+ class: 'mfm-x2',
+ }, genEl(token.children));
}
case 'x3': {
- style = `font-size: 400%;`;
- break;
+ return h('span', {
+ class: 'mfm-x3',
+ }, genEl(token.children));
}
case 'x4': {
- style = `font-size: 600%;`;
- break;
+ return h('span', {
+ class: 'mfm-x4',
+ }, genEl(token.children));
}
case 'font': {
const family =
@@ -168,7 +174,8 @@ export default defineComponent({
}, genEl(token.children));
}
case 'rainbow': {
- style = this.$store.state.animatedMfm ? 'animation: mfm-rainbow 1s linear infinite;' : '';
+ const speed = validTime(token.props.args.speed) || '1s';
+ style = this.$store.state.animatedMfm ? `animation: mfm-rainbow ${speed} linear infinite;` : '';
break;
}
case 'sparkle': {
diff --git a/packages/client/src/components/note-detailed.vue b/packages/client/src/components/note-detailed.vue
index d30284ca5f..14bbbd4f3c 100644
--- a/packages/client/src/components/note-detailed.vue
+++ b/packages/client/src/components/note-detailed.vue
@@ -2,9 +2,9 @@
<div
v-if="!muted"
v-show="!isDeleted"
+ ref="el"
v-hotkey="keymap"
v-size="{ max: [500, 450, 350, 300] }"
- ref="el"
class="lxwezrsl _block"
:tabindex="!isDeleted ? '-1' : null"
:class="{ renote: isRenote }"
@@ -197,7 +197,7 @@ const keymap = {
'q': () => renoteButton.value.renote(true),
'esc': blur,
'm|o': () => menu(true),
- 's': () => showContent.value != showContent.value,
+ 's': () => showContent.value !== showContent.value,
};
useNoteCapture({
diff --git a/packages/client/src/components/note.vue b/packages/client/src/components/note.vue
index 3cd7a819d4..bc8a0dd19d 100644
--- a/packages/client/src/components/note.vue
+++ b/packages/client/src/components/note.vue
@@ -185,7 +185,7 @@ const keymap = {
'down|j|tab': focusAfter,
'esc': blur,
'm|o': () => menu(true),
- 's': () => showContent.value != showContent.value,
+ 's': () => showContent.value !== showContent.value,
};
useNoteCapture({
diff --git a/packages/client/src/components/page/page.image.vue b/packages/client/src/components/page/page.image.vue
index 04ce74bd7c..6e38a9f424 100644
--- a/packages/client/src/components/page/page.image.vue
+++ b/packages/client/src/components/page/page.image.vue
@@ -1,34 +1,22 @@
<template>
<div class="lzyxtsnt">
- <img v-if="image" :src="image.url"/>
+ <ImgWithBlurhash v-if="image" :hash="image.blurhash" :src="image.url" :alt="image.comment" :title="image.comment" :cover="false"/>
</div>
</template>
-<script lang="ts">
+<script lang="ts" setup>
import { defineComponent, PropType } from 'vue';
+import ImgWithBlurhash from '@/components/img-with-blurhash.vue';
import * as os from '@/os';
import { ImageBlock } from '@/scripts/hpml/block';
import { Hpml } from '@/scripts/hpml/evaluator';
-export default defineComponent({
- props: {
- block: {
- type: Object as PropType<ImageBlock>,
- required: true
- },
- hpml: {
- type: Object as PropType<Hpml>,
- required: true
- }
- },
- setup(props, ctx) {
- const image = props.hpml.page.attachedFiles.find(x => x.id === props.block.fileId);
+const props = defineProps<{
+ block: PropType<ImageBlock>,
+ hpml: PropType<Hpml>,
+}>();
- return {
- image
- };
- }
-});
+const image = props.hpml.page.attachedFiles.find(x => x.id === props.block.fileId);
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/components/page/page.post.vue b/packages/client/src/components/page/page.post.vue
index 847da37c51..8ac8c46692 100644
--- a/packages/client/src/components/page/page.post.vue
+++ b/packages/client/src/components/page/page.post.vue
@@ -52,16 +52,16 @@ export default defineComponent({
const promise = new Promise((ok) => {
const canvas = this.hpml.canvases[this.block.canvasId];
canvas.toBlob(blob => {
- const data = new FormData();
- data.append('file', blob);
- data.append('i', this.$i.token);
+ const formData = new FormData();
+ formData.append('file', blob);
+ formData.append('i', this.$i.token);
if (this.$store.state.uploadFolder) {
- data.append('folderId', this.$store.state.uploadFolder);
+ formData.append('folderId', this.$store.state.uploadFolder);
}
fetch(apiUrl + '/drive/files/create', {
method: 'POST',
- body: data
+ body: formData,
})
.then(response => response.json())
.then(f => {
diff --git a/packages/client/src/components/page/page.vue b/packages/client/src/components/page/page.vue
index e54147bbd0..a067762372 100644
--- a/packages/client/src/components/page/page.vue
+++ b/packages/client/src/components/page/page.vue
@@ -38,8 +38,8 @@ export default defineComponent({
let ast;
try {
ast = parse(props.page.script);
- } catch (e) {
- console.error(e);
+ } catch (err) {
+ console.error(err);
/*os.alert({
type: 'error',
text: 'Syntax error :('
@@ -48,11 +48,11 @@ export default defineComponent({
}
hpml.aiscript.exec(ast).then(() => {
hpml.eval();
- }).catch(e => {
- console.error(e);
+ }).catch(err => {
+ console.error(err);
/*os.alert({
type: 'error',
- text: e
+ text: err
});*/
});
} else {
diff --git a/packages/client/src/components/poll-editor.vue b/packages/client/src/components/poll-editor.vue
index 6f3f23a2d3..9aa5510c7f 100644
--- a/packages/client/src/components/poll-editor.vue
+++ b/packages/client/src/components/poll-editor.vue
@@ -104,7 +104,7 @@ function add() {
}
function remove(i) {
- choices.value = choices.value.filter((_, _i) => _i != i);
+ choices.value = choices.value.filter((_, _i) => _i !== i);
}
function get() {
diff --git a/packages/client/src/components/post-form-attaches.vue b/packages/client/src/components/post-form-attaches.vue
index 9dd69a0ee5..3807769118 100644
--- a/packages/client/src/components/post-form-attaches.vue
+++ b/packages/client/src/components/post-form-attaches.vue
@@ -88,7 +88,7 @@ export default defineComponent({
},
async describe(file) {
- os.popup(import("@/components/media-caption.vue"), {
+ os.popup(defineAsyncComponent(() => import("@/components/media-caption.vue")), {
title: this.$ts.describeFile,
input: {
placeholder: this.$ts.inputNewDescription,
@@ -98,7 +98,7 @@ export default defineComponent({
}, {
done: result => {
if (!result || result.canceled) return;
- let comment = result.result.length == 0 ? null : result.result;
+ let comment = result.result.length === 0 ? null : result.result;
os.api('drive/files/update', {
fileId: file.id,
comment: comment,
diff --git a/packages/client/src/components/post-form.vue b/packages/client/src/components/post-form.vue
index 241c726c11..64ee873fd7 100644
--- a/packages/client/src/components/post-form.vue
+++ b/packages/client/src/components/post-form.vue
@@ -62,7 +62,7 @@
</template>
<script lang="ts" setup>
-import { inject, watch, nextTick, onMounted } from 'vue';
+import { inject, watch, nextTick, onMounted, defineAsyncComponent } from 'vue';
import * as mfm from 'mfm-js';
import * as misskey from 'misskey-js';
import insertTextAtCursor from 'insert-text-at-cursor';
@@ -107,7 +107,7 @@ const props = withDefaults(defineProps<{
fixed?: boolean;
autofocus?: boolean;
}>(), {
- initialVisibleUsers: [],
+ initialVisibleUsers: () => [],
autofocus: true,
});
@@ -228,7 +228,7 @@ if (props.mention) {
text += ' ';
}
-if (props.reply && (props.reply.user.username != $i.username || (props.reply.user.host != null && props.reply.user.host != host))) {
+if (props.reply && (props.reply.user.username !== $i.username || (props.reply.user.host != null && props.reply.user.host !== host))) {
text = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `;
}
@@ -239,16 +239,15 @@ if (props.reply && props.reply.text != null) {
for (const x of extractMentions(ast)) {
const mention = x.host ?
`@${x.username}@${toASCII(x.host)}` :
- (otherHost == null || otherHost == host) ?
+ (otherHost == null || otherHost === host) ?
`@${x.username}` :
`@${x.username}@${toASCII(otherHost)}`;
// 自分は除外
- if ($i.username == x.username && x.host == null) continue;
- if ($i.username == x.username && x.host == host) continue;
+ if ($i.username === x.username && (x.host == null || x.host === host)) continue;
// 重複は除外
- if (text.indexOf(`${mention} `) != -1) continue;
+ if (text.includes(`${mention} `)) continue;
text += `${mention} `;
}
@@ -303,7 +302,7 @@ function checkMissingMention() {
const ast = mfm.parse(text);
for (const x of extractMentions(ast)) {
- if (!visibleUsers.some(u => (u.username === x.username) && (u.host == x.host))) {
+ if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) {
hasNotSpecifiedMentions = true;
return;
}
@@ -316,7 +315,7 @@ function addMissingMention() {
const ast = mfm.parse(text);
for (const x of extractMentions(ast)) {
- if (!visibleUsers.some(u => (u.username === x.username) && (u.host == x.host))) {
+ if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) {
os.api('users/show', { username: x.username, host: x.host }).then(user => {
visibleUsers.push(user);
});
@@ -357,7 +356,7 @@ function chooseFileFrom(ev) {
}
function detachFile(id) {
- files = files.filter(x => x.id != id);
+ files = files.filter(x => x.id !== id);
}
function updateFiles(_files) {
@@ -384,7 +383,7 @@ function setVisibility() {
return;
}
- os.popup(import('./visibility-picker.vue'), {
+ os.popup(defineAsyncComponent(() => import('./visibility-picker.vue')), {
currentVisibility: visibility,
currentLocalOnly: localOnly,
src: visibilityButton,
@@ -427,24 +426,24 @@ function clear() {
quoteId = null;
}
-function onKeydown(e: KeyboardEvent) {
- if ((e.which === 10 || e.which === 13) && (e.ctrlKey || e.metaKey) && canPost) post();
- if (e.which === 27) emit('esc');
+function onKeydown(ev: KeyboardEvent) {
+ if ((ev.which === 10 || ev.which === 13) && (ev.ctrlKey || ev.metaKey) && canPost) post();
+ if (ev.which === 27) emit('esc');
typing();
}
-function onCompositionUpdate(e: CompositionEvent) {
- imeText = e.data;
+function onCompositionUpdate(ev: CompositionEvent) {
+ imeText = ev.data;
typing();
}
-function onCompositionEnd(e: CompositionEvent) {
+function onCompositionEnd(ev: CompositionEvent) {
imeText = '';
}
-async function onPaste(e: ClipboardEvent) {
- for (const { item, i } of Array.from(e.clipboardData.items).map((item, i) => ({item, i}))) {
- if (item.kind == 'file') {
+async function onPaste(ev: ClipboardEvent) {
+ for (const { item, i } of Array.from(ev.clipboardData.items).map((item, i) => ({item, i}))) {
+ if (item.kind === 'file') {
const file = item.getAsFile();
const lio = file.name.lastIndexOf('.');
const ext = lio >= 0 ? file.name.slice(lio) : '';
@@ -453,10 +452,10 @@ async function onPaste(e: ClipboardEvent) {
}
}
- const paste = e.clipboardData.getData('text');
+ const paste = ev.clipboardData.getData('text');
if (!props.renote && !quoteId && paste.startsWith(url + '/notes/')) {
- e.preventDefault();
+ ev.preventDefault();
os.confirm({
type: 'info',
@@ -472,49 +471,49 @@ async function onPaste(e: ClipboardEvent) {
}
}
-function onDragover(e) {
- if (!e.dataTransfer.items[0]) return;
- const isFile = e.dataTransfer.items[0].kind == 'file';
- const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
+function onDragover(ev) {
+ if (!ev.dataTransfer.items[0]) return;
+ const isFile = ev.dataTransfer.items[0].kind === 'file';
+ const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
if (isFile || isDriveFile) {
- e.preventDefault();
+ ev.preventDefault();
draghover = true;
- e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+ ev.dataTransfer.dropEffect = ev.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
}
}
-function onDragenter(e) {
+function onDragenter(ev) {
draghover = true;
}
-function onDragleave(e) {
+function onDragleave(ev) {
draghover = false;
}
-function onDrop(e): void {
+function onDrop(ev): void {
draghover = false;
// ファイルだったら
- if (e.dataTransfer.files.length > 0) {
- e.preventDefault();
- for (const x of Array.from(e.dataTransfer.files)) upload(x);
+ if (ev.dataTransfer.files.length > 0) {
+ ev.preventDefault();
+ for (const x of Array.from(ev.dataTransfer.files)) upload(x);
return;
}
//#region ドライブのファイル
- const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile != '') {
+ const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
+ if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile);
files.push(file);
- e.preventDefault();
+ ev.preventDefault();
}
//#endregion
}
function saveDraft() {
- const data = JSON.parse(localStorage.getItem('drafts') || '{}');
+ const draftData = JSON.parse(localStorage.getItem('drafts') || '{}');
- data[draftKey] = {
+ draftData[draftKey] = {
updatedAt: new Date(),
data: {
text: text,
@@ -527,20 +526,20 @@ function saveDraft() {
}
};
- localStorage.setItem('drafts', JSON.stringify(data));
+ localStorage.setItem('drafts', JSON.stringify(draftData));
}
function deleteDraft() {
- const data = JSON.parse(localStorage.getItem('drafts') || '{}');
+ const draftData = JSON.parse(localStorage.getItem('drafts') || '{}');
- delete data[draftKey];
+ delete draftData[draftKey];
- localStorage.setItem('drafts', JSON.stringify(data));
+ localStorage.setItem('drafts', JSON.stringify(draftData));
}
async function post() {
- let data = {
- text: text == '' ? undefined : text,
+ let postData = {
+ text: text === '' ? undefined : text,
fileIds: files.length > 0 ? files.map(f => f.id) : undefined,
replyId: props.reply ? props.reply.id : undefined,
renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined,
@@ -549,18 +548,18 @@ async function post() {
cw: useCw ? cw || '' : undefined,
localOnly: localOnly,
visibility: visibility,
- visibleUserIds: visibility == 'specified' ? visibleUsers.map(u => u.id) : undefined,
+ visibleUserIds: visibility === 'specified' ? visibleUsers.map(u => u.id) : undefined,
};
if (withHashtags && hashtags && hashtags.trim() !== '') {
const hashtags_ = hashtags.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
- data.text = data.text ? `${data.text} ${hashtags_}` : hashtags_;
+ postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
}
// plugin
if (notePostInterruptors.length > 0) {
for (const interruptor of notePostInterruptors) {
- data = await interruptor.handler(JSON.parse(JSON.stringify(data)));
+ postData = await interruptor.handler(JSON.parse(JSON.stringify(postData)));
}
}
@@ -572,13 +571,13 @@ async function post() {
}
posting = true;
- os.api('notes/create', data, token).then(() => {
+ os.api('notes/create', postData, token).then(() => {
clear();
nextTick(() => {
deleteDraft();
emit('posted');
- if (data.text && data.text != '') {
- const hashtags_ = mfm.parse(data.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag);
+ if (postData.text && postData.text !== '') {
+ const hashtags_ = mfm.parse(postData.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag);
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
localStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history))));
}
@@ -662,7 +661,7 @@ onMounted(() => {
cw = draft.data.cw;
visibility = draft.data.visibility;
localOnly = draft.data.localOnly;
- files = (draft.data.files || []).filter(e => e);
+ files = (draft.data.files || []).filter(draftFile => draftFile);
if (draft.data.poll) {
poll = draft.data.poll;
}
diff --git a/packages/client/src/components/reactions-viewer.reaction.vue b/packages/client/src/components/reactions-viewer.reaction.vue
index 7dc079fde6..91a90a6996 100644
--- a/packages/client/src/components/reactions-viewer.reaction.vue
+++ b/packages/client/src/components/reactions-viewer.reaction.vue
@@ -7,8 +7,8 @@
:class="{ reacted: note.myReaction == reaction, canToggle }"
@click="toggleReaction()"
>
- <XReactionIcon :reaction="reaction" :custom-emojis="note.emojis"/>
- <span>{{ count }}</span>
+ <XReactionIcon class="icon" :reaction="reaction" :custom-emojis="note.emojis"/>
+ <span class="count">{{ count }}</span>
</button>
</template>
@@ -141,12 +141,16 @@ export default defineComponent({
background: var(--accent);
}
- > span {
+ > .count {
color: var(--fgOnAccent);
}
+
+ > .icon {
+ filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.5));
+ }
}
- > span {
+ > .count {
font-size: 0.9em;
line-height: 32px;
margin: 0 0 0 4px;
diff --git a/packages/client/src/components/signin-dialog.vue b/packages/client/src/components/signin-dialog.vue
index 5c2048e7b0..848b11fada 100644
--- a/packages/client/src/components/signin-dialog.vue
+++ b/packages/client/src/components/signin-dialog.vue
@@ -2,12 +2,12 @@
<XModalWindow ref="dialog"
:width="370"
:height="400"
- @close="dialog.close()"
+ @close="onClose"
@closed="emit('closed')"
>
<template #header>{{ $ts.login }}</template>
- <MkSignin :auto-set="autoSet" @login="onLogin"/>
+ <MkSignin :auto-set="autoSet" :message="message" @login="onLogin"/>
</XModalWindow>
</template>
@@ -18,17 +18,25 @@ import MkSignin from './signin.vue';
const props = withDefaults(defineProps<{
autoSet?: boolean;
+ message?: string,
}>(), {
autoSet: false,
+ message: ''
});
const emit = defineEmits<{
- (e: 'done'): void;
- (e: 'closed'): void;
+ (ev: 'done'): void;
+ (ev: 'closed'): void;
+ (ev: 'cancelled'): void;
}>();
const dialog = $ref<InstanceType<typeof XModalWindow>>();
+function onClose() {
+ emit('cancelled');
+ dialog.close();
+}
+
function onLogin(res) {
emit('done', res);
dialog.close();
diff --git a/packages/client/src/components/signin.vue b/packages/client/src/components/signin.vue
index f640e948ad..d283a758a6 100644
--- a/packages/client/src/components/signin.vue
+++ b/packages/client/src/components/signin.vue
@@ -1,39 +1,44 @@
<template>
<form class="eppvobhk _monolithic_" :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
<div class="auth _section _formRoot">
- <div v-show="withAvatar" class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }"></div>
+ <div v-show="withAvatar" class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null, marginBottom: message ? '1.5em' : null }"></div>
+ <MkInfo v-if="message">
+ {{ message }}
+ </MkInfo>
<div v-if="!totpLogin" class="normal-signin">
- <MkInput v-model="username" class="_formBlock" :placeholder="$ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required data-cy-signin-username @update:modelValue="onUsernameChange">
+ <MkInput v-model="username" class="_formBlock" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required data-cy-signin-username @update:modelValue="onUsernameChange">
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
</MkInput>
- <MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" class="_formBlock" :placeholder="$ts.password" type="password" :with-password-toggle="true" required data-cy-signin-password>
+ <MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" class="_formBlock" :placeholder="i18n.ts.password" type="password" :with-password-toggle="true" required data-cy-signin-password>
<template #prefix><i class="fas fa-lock"></i></template>
- <template #caption><button class="_textButton" type="button" @click="resetPassword">{{ $ts.forgotPassword }}</button></template>
+ <template #caption><button class="_textButton" type="button" @click="resetPassword">{{ i18n.ts.forgotPassword }}</button></template>
</MkInput>
- <MkButton class="_formBlock" type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $ts.loggingIn : $ts.login }}</MkButton>
+ <MkCaptcha v-if="meta.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="meta.hcaptchaSiteKey"/>
+ <MkCaptcha v-if="meta.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="meta.recaptchaSiteKey"/>
+ <MkButton class="_formBlock" type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? i18n.ts.loggingIn : i18n.ts.login }}</MkButton>
</div>
<div v-if="totpLogin" class="2fa-signin" :class="{ securityKeys: user && user.securityKeys }">
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
- <p>{{ $ts.tapSecurityKey }}</p>
+ <p>{{ i18n.ts.tapSecurityKey }}</p>
<MkButton v-if="!queryingKey" @click="queryKey">
- {{ $ts.retry }}
+ {{ i18n.ts.retry }}
</MkButton>
</div>
<div v-if="user && user.securityKeys" class="or-hr">
- <p class="or-msg">{{ $ts.or }}</p>
+ <p class="or-msg">{{ i18n.ts.or }}</p>
</div>
<div class="twofa-group totp-group">
- <p style="margin-bottom:0;">{{ $ts.twoStepAuthentication }}</p>
+ <p style="margin-bottom:0;">{{ i18n.ts.twoStepAuthentication }}</p>
<MkInput v-if="user && user.usePasswordLessLogin" v-model="password" type="password" :with-password-toggle="true" required>
- <template #label>{{ $ts.password }}</template>
+ <template #label>{{ i18n.ts.password }}</template>
<template #prefix><i class="fas fa-lock"></i></template>
</MkInput>
<MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
- <template #label>{{ $ts.token }}</template>
+ <template #label>{{ i18n.ts.token }}</template>
<template #prefix><i class="fas fa-gavel"></i></template>
</MkInput>
- <MkButton type="submit" :disabled="signing" primary style="margin: 0 auto;">{{ signing ? $ts.loggingIn : $ts.login }}</MkButton>
+ <MkButton type="submit" :disabled="signing" primary style="margin: 0 auto;">{{ signing ? i18n.ts.loggingIn : i18n.ts.login }}</MkButton>
</div>
</div>
</div>
@@ -45,190 +50,192 @@
</form>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent } from 'vue';
import { toUnicode } from 'punycode/';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
-import { apiUrl, host } from '@/config';
+import MkInfo from '@/components/ui/info.vue';
+import { apiUrl, host as configHost } from '@/config';
import { byteify, hexify } from '@/scripts/2fa';
import * as os from '@/os';
import { login } from '@/account';
import { showSuspendedDialog } from '../scripts/show-suspended-dialog';
+import { instance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- MkInput,
- },
+const MkCaptcha = defineAsyncComponent(() => import('./captcha.vue'));
- props: {
- withAvatar: {
- type: Boolean,
- required: false,
- default: true
- },
- autoSet: {
- type: Boolean,
- required: false,
- default: false,
- }
- },
+let signing = $ref(false);
+let user = $ref(null);
+let username = $ref('');
+let password = $ref('');
+let token = $ref('');
+let host = $ref(toUnicode(configHost));
+let totpLogin = $ref(false);
+let credential = $ref(null);
+let challengeData = $ref(null);
+let queryingKey = $ref(false);
+let hCaptchaResponse = $ref(null);
+let reCaptchaResponse = $ref(null);
- emits: ['login'],
+const meta = $computed(() => instance);
- data() {
- return {
- signing: false,
- user: null,
- username: '',
- password: '',
- token: '',
- apiUrl,
- host: toUnicode(host),
- totpLogin: false,
- credential: null,
- challengeData: null,
- queryingKey: false,
- };
- },
+const emit = defineEmits<{
+ (ev: 'login', v: any): void;
+}>();
- computed: {
- meta() {
- return this.$instance;
- },
+const props = defineProps({
+ withAvatar: {
+ type: Boolean,
+ required: false,
+ default: true
},
+ autoSet: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ message: {
+ type: String,
+ required: false,
+ default: ''
+ }
+});
- methods: {
- onUsernameChange() {
- os.api('users/show', {
- username: this.username
- }).then(user => {
- this.user = user;
- }, () => {
- this.user = null;
- });
- },
-
- onLogin(res) {
- if (this.autoSet) {
- return login(res.i);
- } else {
- return;
- }
- },
-
- queryKey() {
- this.queryingKey = true;
- return navigator.credentials.get({
- publicKey: {
- challenge: byteify(this.challengeData.challenge, 'base64'),
- allowCredentials: this.challengeData.securityKeys.map(key => ({
- id: byteify(key.id, 'hex'),
- type: 'public-key',
- transports: ['usb', 'nfc', 'ble', 'internal']
- })),
- timeout: 60 * 1000
- }
- }).catch(() => {
- this.queryingKey = false;
- return Promise.reject(null);
- }).then(credential => {
- this.queryingKey = false;
- this.signing = true;
- return os.api('signin', {
- username: this.username,
- password: this.password,
- signature: hexify(credential.response.signature),
- authenticatorData: hexify(credential.response.authenticatorData),
- clientDataJSON: hexify(credential.response.clientDataJSON),
- credentialId: credential.id,
- challengeId: this.challengeData.challengeId
- });
- }).then(res => {
- this.$emit('login', res);
- return this.onLogin(res);
- }).catch(err => {
- if (err === null) return;
- os.alert({
- type: 'error',
- text: this.$ts.signinFailed
- });
- this.signing = false;
- });
- },
+function onUsernameChange() {
+ os.api('users/show', {
+ username: username
+ }).then(userResponse => {
+ user = userResponse;
+ }, () => {
+ user = null;
+ });
+}
- onSubmit() {
- this.signing = true;
- if (!this.totpLogin && this.user && this.user.twoFactorEnabled) {
- if (window.PublicKeyCredential && this.user.securityKeys) {
- os.api('signin', {
- username: this.username,
- password: this.password
- }).then(res => {
- this.totpLogin = true;
- this.signing = false;
- this.challengeData = res;
- return this.queryKey();
- }).catch(this.loginFailed);
- } else {
- this.totpLogin = true;
- this.signing = false;
- }
- } else {
- os.api('signin', {
- username: this.username,
- password: this.password,
- token: this.user && this.user.twoFactorEnabled ? this.token : undefined
- }).then(res => {
- this.$emit('login', res);
- this.onLogin(res);
- }).catch(this.loginFailed);
- }
- },
+function onLogin(res) {
+ if (props.autoSet) {
+ return login(res.i);
+ }
+}
- loginFailed(err) {
- switch (err.id) {
- case '6cc579cc-885d-43d8-95c2-b8c7fc963280': {
- os.alert({
- type: 'error',
- title: this.$ts.loginFailed,
- text: this.$ts.noSuchUser
- });
- break;
- }
- case '932c904e-9460-45b7-9ce6-7ed33be7eb2c': {
- os.alert({
- type: 'error',
- title: this.$ts.loginFailed,
- text: this.$ts.incorrectPassword,
- });
- break;
- }
- case 'e03a5f46-d309-4865-9b69-56282d94e1eb': {
- showSuspendedDialog();
- break;
- }
- default: {
- os.alert({
- type: 'error',
- title: this.$ts.loginFailed,
- text: JSON.stringify(err)
- });
- }
- }
+function queryKey() {
+ queryingKey = true;
+ return navigator.credentials.get({
+ publicKey: {
+ challenge: byteify(challengeData.challenge, 'base64'),
+ allowCredentials: challengeData.securityKeys.map(key => ({
+ id: byteify(key.id, 'hex'),
+ type: 'public-key',
+ transports: ['usb', 'nfc', 'ble', 'internal']
+ })),
+ timeout: 60 * 1000
+ }
+ }).catch(() => {
+ queryingKey = false;
+ return Promise.reject(null);
+ }).then(credential => {
+ queryingKey = false;
+ signing = true;
+ return os.api('signin', {
+ username,
+ password,
+ signature: hexify(credential.response.signature),
+ authenticatorData: hexify(credential.response.authenticatorData),
+ clientDataJSON: hexify(credential.response.clientDataJSON),
+ credentialId: credential.id,
+ challengeId: challengeData.challengeId,
+ 'hcaptcha-response': hCaptchaResponse,
+ 'g-recaptcha-response': reCaptchaResponse,
+ });
+ }).then(res => {
+ emit('login', res);
+ return onLogin(res);
+ }).catch(err => {
+ if (err === null) return;
+ os.alert({
+ type: 'error',
+ text: i18n.ts.signinFailed
+ });
+ signing = false;
+ });
+}
- this.challengeData = null;
- this.totpLogin = false;
- this.signing = false;
- },
+function onSubmit() {
+ signing = true;
+ console.log('submit')
+ if (!totpLogin && user && user.twoFactorEnabled) {
+ if (window.PublicKeyCredential && user.securityKeys) {
+ os.api('signin', {
+ username,
+ password,
+ 'hcaptcha-response': hCaptchaResponse,
+ 'g-recaptcha-response': reCaptchaResponse,
+ }).then(res => {
+ totpLogin = true;
+ signing = false;
+ challengeData = res;
+ return queryKey();
+ }).catch(loginFailed);
+ } else {
+ totpLogin = true;
+ signing = false;
+ }
+ } else {
+ os.api('signin', {
+ username,
+ password,
+ 'hcaptcha-response': hCaptchaResponse,
+ 'g-recaptcha-response': reCaptchaResponse,
+ token: user && user.twoFactorEnabled ? token : undefined
+ }).then(res => {
+ emit('login', res);
+ onLogin(res);
+ }).catch(loginFailed);
+ }
+}
- resetPassword() {
- os.popup(import('@/components/forgot-password.vue'), {}, {
- }, 'closed');
+function loginFailed(err) {
+ switch (err.id) {
+ case '6cc579cc-885d-43d8-95c2-b8c7fc963280': {
+ os.alert({
+ type: 'error',
+ title: i18n.ts.loginFailed,
+ text: i18n.ts.noSuchUser
+ });
+ break;
+ }
+ case '932c904e-9460-45b7-9ce6-7ed33be7eb2c': {
+ os.alert({
+ type: 'error',
+ title: i18n.ts.loginFailed,
+ text: i18n.ts.incorrectPassword,
+ });
+ break;
+ }
+ case 'e03a5f46-d309-4865-9b69-56282d94e1eb': {
+ showSuspendedDialog();
+ break;
+ }
+ default: {
+ console.log(err)
+ os.alert({
+ type: 'error',
+ title: i18n.ts.loginFailed,
+ text: JSON.stringify(err)
+ });
}
}
-});
+
+ challengeData = null;
+ totpLogin = false;
+ signing = false;
+}
+
+function resetPassword() {
+ os.popup(defineAsyncComponent(() => import('@/components/forgot-password.vue')), {}, {
+ }, 'closed');
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/components/signup-dialog.vue b/packages/client/src/components/signup-dialog.vue
index bda2495ba7..6dad9257a4 100644
--- a/packages/client/src/components/signup-dialog.vue
+++ b/packages/client/src/components/signup-dialog.vue
@@ -27,8 +27,8 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'done'): void;
- (e: 'closed'): void;
+ (ev: 'done'): void;
+ (ev: 'closed'): void;
}>();
const dialog = $ref<InstanceType<typeof XModalWindow>>();
diff --git a/packages/client/src/components/signup.vue b/packages/client/src/components/signup.vue
index 38a9fd55f1..58c15d81b1 100644
--- a/packages/client/src/components/signup.vue
+++ b/packages/client/src/components/signup.vue
@@ -58,8 +58,8 @@
</template>
</I18n>
</MkSwitch>
- <captcha v-if="meta.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="meta.hcaptchaSiteKey"/>
- <captcha v-if="meta.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="meta.recaptchaSiteKey"/>
+ <MkCaptcha v-if="meta.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="meta.hcaptchaSiteKey"/>
+ <MkCaptcha v-if="meta.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="meta.recaptchaSiteKey"/>
<MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ $ts.start }}</MkButton>
</template>
</form>
@@ -67,7 +67,7 @@
<script lang="ts">
import { defineComponent, defineAsyncComponent } from 'vue';
-const getPasswordStrength = require('syuilo-password-strength');
+const getPasswordStrength = await import('syuilo-password-strength');
import { toUnicode } from 'punycode/';
import { host, url } from '@/config';
import MkButton from './ui/button.vue';
@@ -81,7 +81,7 @@ export default defineComponent({
MkButton,
MkInput,
MkSwitch,
- captcha: defineAsyncComponent(() => import('./captcha.vue')),
+ MkCaptcha: defineAsyncComponent(() => import('./captcha.vue')),
},
props: {
@@ -124,20 +124,20 @@ export default defineComponent({
this.meta.tosUrl && !this.ToSAgreement ||
this.meta.enableHcaptcha && !this.hCaptchaResponse ||
this.meta.enableRecaptcha && !this.reCaptchaResponse ||
- this.passwordRetypeState == 'not-match';
+ this.passwordRetypeState === 'not-match';
},
shouldShowProfileUrl(): boolean {
- return (this.username != '' &&
- this.usernameState != 'invalid-format' &&
- this.usernameState != 'min-range' &&
- this.usernameState != 'max-range');
+ return (this.username !== '' &&
+ this.usernameState !== 'invalid-format' &&
+ this.usernameState !== 'min-range' &&
+ this.usernameState !== 'max-range');
}
},
methods: {
onChangeUsername() {
- if (this.username == '') {
+ if (this.username === '') {
this.usernameState = null;
return;
}
@@ -165,7 +165,7 @@ export default defineComponent({
},
onChangeEmail() {
- if (this.email == '') {
+ if (this.email === '') {
this.emailState = null;
return;
}
@@ -188,7 +188,7 @@ export default defineComponent({
},
onChangePassword() {
- if (this.password == '') {
+ if (this.password === '') {
this.passwordStrength = '';
return;
}
@@ -198,12 +198,12 @@ export default defineComponent({
},
onChangePasswordRetype() {
- if (this.retypedPassword == '') {
+ if (this.retypedPassword === '') {
this.passwordRetypeState = null;
return;
}
- this.passwordRetypeState = this.password == this.retypedPassword ? 'match' : 'not-match';
+ this.passwordRetypeState = this.password === this.retypedPassword ? 'match' : 'not-match';
},
onSubmit() {
diff --git a/packages/client/src/components/timeline.vue b/packages/client/src/components/timeline.vue
index 59956b9526..a3fa27ab78 100644
--- a/packages/client/src/components/timeline.vue
+++ b/packages/client/src/components/timeline.vue
@@ -19,8 +19,8 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'note'): void;
- (e: 'queue', count: number): void;
+ (ev: 'note'): void;
+ (ev: 'queue', count: number): void;
}>();
provide('inChannel', computed(() => props.src === 'channel'));
@@ -95,7 +95,7 @@ if (props.src === 'antenna') {
visibility: 'specified'
};
const onNote = note => {
- if (note.visibility == 'specified') {
+ if (note.visibility === 'specified') {
prepend(note);
}
};
diff --git a/packages/client/src/components/toast.vue b/packages/client/src/components/toast.vue
index 99933f3846..c9fad64eb6 100644
--- a/packages/client/src/components/toast.vue
+++ b/packages/client/src/components/toast.vue
@@ -19,7 +19,7 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'closed'): void;
+ (ev: 'closed'): void;
}>();
const zIndex = os.claimZIndex('high');
diff --git a/packages/client/src/components/ui/button.vue b/packages/client/src/components/ui/button.vue
index c7b6c8ba96..fe8f1c7cca 100644
--- a/packages/client/src/components/ui/button.vue
+++ b/packages/client/src/components/ui/button.vue
@@ -90,7 +90,7 @@ export default defineComponent({
}
},
methods: {
- onMousedown(e: MouseEvent) {
+ onMousedown(evt: MouseEvent) {
function distance(p, q) {
return Math.hypot(p.x - q.x, p.y - q.y);
}
@@ -104,18 +104,18 @@ export default defineComponent({
return Math.max(dist1, dist2, dist3, dist4) * 2;
}
- const rect = e.target.getBoundingClientRect();
+ const rect = evt.target.getBoundingClientRect();
const ripple = document.createElement('div');
- ripple.style.top = (e.clientY - rect.top - 1).toString() + 'px';
- ripple.style.left = (e.clientX - rect.left - 1).toString() + 'px';
+ ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px';
+ ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px';
this.$refs.ripples.appendChild(ripple);
- const circleCenterX = e.clientX - rect.left;
- const circleCenterY = e.clientY - rect.top;
+ const circleCenterX = evt.clientX - rect.left;
+ const circleCenterY = evt.clientY - rect.top;
- const scale = calcCircleScale(e.target.clientWidth, e.target.clientHeight, circleCenterX, circleCenterY);
+ const scale = calcCircleScale(evt.target.clientWidth, evt.target.clientHeight, circleCenterX, circleCenterY);
window.setTimeout(() => {
ripple.style.transform = 'scale(' + (scale / 2) + ')';
diff --git a/packages/client/src/components/ui/context-menu.vue b/packages/client/src/components/ui/context-menu.vue
index f491b43b46..e637d361cf 100644
--- a/packages/client/src/components/ui/context-menu.vue
+++ b/packages/client/src/components/ui/context-menu.vue
@@ -19,7 +19,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'closed'): void;
+ (ev: 'closed'): void;
}>();
let rootEl = $ref<HTMLDivElement>();
@@ -63,8 +63,8 @@ onBeforeUnmount(() => {
}
});
-function onMousedown(e: Event) {
- if (!contains(rootEl, e.target) && (rootEl != e.target)) emit('closed');
+function onMousedown(evt: Event) {
+ if (!contains(rootEl, evt.target) && (rootEl !== evt.target)) emit('closed');
}
</script>
diff --git a/packages/client/src/components/ui/folder.vue b/packages/client/src/components/ui/folder.vue
index fe1602b2bb..7daa82cbd3 100644
--- a/packages/client/src/components/ui/folder.vue
+++ b/packages/client/src/components/ui/folder.vue
@@ -23,7 +23,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
const localStoragePrefix = 'ui:folder:';
diff --git a/packages/client/src/components/ui/menu.vue b/packages/client/src/components/ui/menu.vue
index a93cc8cda8..ca56048262 100644
--- a/packages/client/src/components/ui/menu.vue
+++ b/packages/client/src/components/ui/menu.vue
@@ -60,7 +60,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'close'): void;
+ (ev: 'close'): void;
}>();
let itemsEl = $ref<HTMLDivElement>();
diff --git a/packages/client/src/components/ui/modal-window.vue b/packages/client/src/components/ui/modal-window.vue
index b4b8c2b965..6de29c83fa 100644
--- a/packages/client/src/components/ui/modal-window.vue
+++ b/packages/client/src/components/ui/modal-window.vue
@@ -79,10 +79,10 @@ export default defineComponent({
this.$refs.modal.close();
},
- onKeydown(e) {
- if (e.which === 27) { // Esc
- e.preventDefault();
- e.stopPropagation();
+ onKeydown(evt) {
+ if (evt.which === 27) { // Esc
+ evt.preventDefault();
+ evt.stopPropagation();
this.close();
}
},
diff --git a/packages/client/src/components/ui/pagination.vue b/packages/client/src/components/ui/pagination.vue
index ac6f59c332..c081e06acd 100644
--- a/packages/client/src/components/ui/pagination.vue
+++ b/packages/client/src/components/ui/pagination.vue
@@ -14,8 +14,14 @@
</div>
<div v-else ref="rootEl">
+ <div v-show="pagination.reversed && more" key="_more_" class="cxiknjgy _gap">
+ <MkButton v-if="!moreFetching" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMoreAhead">
+ {{ $ts.loadMore }}
+ </MkButton>
+ <MkLoading v-else class="loading"/>
+ </div>
<slot :items="items"></slot>
- <div v-show="more" key="_more_" class="cxiknjgy _gap">
+ <div v-show="!pagination.reversed && more" key="_more_" class="cxiknjgy _gap">
<MkButton v-if="!moreFetching" v-appear="($store.state.enableInfiniteScroll && !disableAutoLoad) ? fetchMore : null" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMore">
{{ $ts.loadMore }}
</MkButton>
@@ -62,7 +68,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'queue', count: number): void;
+ (ev: 'queue', count: number): void;
}>();
type Item = { id: string; [another: string]: unknown; };
@@ -106,7 +112,7 @@ const init = async (): Promise<void> => {
offset.value = res.length;
error.value = false;
fetching.value = false;
- }, e => {
+ }, err => {
error.value = true;
fetching.value = false;
});
@@ -149,7 +155,7 @@ const fetchMore = async (): Promise<void> => {
}
offset.value += res.length;
moreFetching.value = false;
- }, e => {
+ }, err => {
moreFetching.value = false;
});
};
@@ -177,7 +183,7 @@ const fetchMoreAhead = async (): Promise<void> => {
}
offset.value += res.length;
moreFetching.value = false;
- }, e => {
+ }, err => {
moreFetching.value = false;
});
};
@@ -244,6 +250,11 @@ const append = (item: Item): void => {
items.value.push(item);
};
+const removeItem = (finder: (item: Item) => boolean) => {
+ const i = items.value.findIndex(finder);
+ items.value.splice(i, 1);
+};
+
const updateItem = (id: Item['id'], replacer: (old: Item) => Item): void => {
const i = items.value.findIndex(item => item.id === id);
items.value[i] = replacer(items.value[i]);
@@ -273,9 +284,9 @@ defineExpose({
queue,
backed,
reload,
- fetchMoreAhead,
prepend,
append,
+ removeItem,
updateItem,
});
</script>
diff --git a/packages/client/src/components/ui/popup-menu.vue b/packages/client/src/components/ui/popup-menu.vue
index 8d6c1b5695..2bc7030d77 100644
--- a/packages/client/src/components/ui/popup-menu.vue
+++ b/packages/client/src/components/ui/popup-menu.vue
@@ -19,7 +19,7 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'closed'): void;
+ (ev: 'closed'): void;
}>();
let modal = $ref<InstanceType<typeof MkModal>>();
diff --git a/packages/client/src/components/ui/window.vue b/packages/client/src/components/ui/window.vue
index fa32ecfdef..2066cf579d 100644
--- a/packages/client/src/components/ui/window.vue
+++ b/packages/client/src/components/ui/window.vue
@@ -139,10 +139,10 @@ export default defineComponent({
this.showing = false;
},
- onKeydown(e) {
- if (e.which === 27) { // Esc
- e.preventDefault();
- e.stopPropagation();
+ onKeydown(evt) {
+ if (evt.which === 27) { // Esc
+ evt.preventDefault();
+ evt.stopPropagation();
this.close();
}
},
@@ -162,15 +162,15 @@ export default defineComponent({
this.top();
},
- onHeaderMousedown(e) {
+ onHeaderMousedown(evt) {
const main = this.$el as any;
if (!contains(main, document.activeElement)) main.focus();
const position = main.getBoundingClientRect();
- const clickX = e.touches && e.touches.length > 0 ? e.touches[0].clientX : e.clientX;
- const clickY = e.touches && e.touches.length > 0 ? e.touches[0].clientY : e.clientY;
+ const clickX = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientX : evt.clientX;
+ const clickY = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientY : evt.clientY;
const moveBaseX = clickX - position.left;
const moveBaseY = clickY - position.top;
const browserWidth = window.innerWidth;
@@ -204,10 +204,10 @@ export default defineComponent({
},
// 上ハンドル掴み時
- onTopHandleMousedown(e) {
+ onTopHandleMousedown(evt) {
const main = this.$el as any;
- const base = e.clientY;
+ const base = evt.clientY;
const height = parseInt(getComputedStyle(main, '').height, 10);
const top = parseInt(getComputedStyle(main, '').top, 10);
@@ -230,10 +230,10 @@ export default defineComponent({
},
// 右ハンドル掴み時
- onRightHandleMousedown(e) {
+ onRightHandleMousedown(evt) {
const main = this.$el as any;
- const base = e.clientX;
+ const base = evt.clientX;
const width = parseInt(getComputedStyle(main, '').width, 10);
const left = parseInt(getComputedStyle(main, '').left, 10);
const browserWidth = window.innerWidth;
@@ -254,10 +254,10 @@ export default defineComponent({
},
// 下ハンドル掴み時
- onBottomHandleMousedown(e) {
+ onBottomHandleMousedown(evt) {
const main = this.$el as any;
- const base = e.clientY;
+ const base = evt.clientY;
const height = parseInt(getComputedStyle(main, '').height, 10);
const top = parseInt(getComputedStyle(main, '').top, 10);
const browserHeight = window.innerHeight;
@@ -278,10 +278,10 @@ export default defineComponent({
},
// 左ハンドル掴み時
- onLeftHandleMousedown(e) {
+ onLeftHandleMousedown(evt) {
const main = this.$el as any;
- const base = e.clientX;
+ const base = evt.clientX;
const width = parseInt(getComputedStyle(main, '').width, 10);
const left = parseInt(getComputedStyle(main, '').left, 10);
@@ -304,27 +304,27 @@ export default defineComponent({
},
// 左上ハンドル掴み時
- onTopLeftHandleMousedown(e) {
- this.onTopHandleMousedown(e);
- this.onLeftHandleMousedown(e);
+ onTopLeftHandleMousedown(evt) {
+ this.onTopHandleMousedown(evt);
+ this.onLeftHandleMousedown(evt);
},
// 右上ハンドル掴み時
- onTopRightHandleMousedown(e) {
- this.onTopHandleMousedown(e);
- this.onRightHandleMousedown(e);
+ onTopRightHandleMousedown(evt) {
+ this.onTopHandleMousedown(evt);
+ this.onRightHandleMousedown(evt);
},
// 右下ハンドル掴み時
- onBottomRightHandleMousedown(e) {
- this.onBottomHandleMousedown(e);
- this.onRightHandleMousedown(e);
+ onBottomRightHandleMousedown(evt) {
+ this.onBottomHandleMousedown(evt);
+ this.onRightHandleMousedown(evt);
},
// 左下ハンドル掴み時
- onBottomLeftHandleMousedown(e) {
- this.onBottomHandleMousedown(e);
- this.onLeftHandleMousedown(e);
+ onBottomLeftHandleMousedown(evt) {
+ this.onBottomHandleMousedown(evt);
+ this.onLeftHandleMousedown(evt);
},
// 高さを適用
diff --git a/packages/client/src/components/user-preview.vue b/packages/client/src/components/user-preview.vue
index 51c5330564..f80947f75a 100644
--- a/packages/client/src/components/user-preview.vue
+++ b/packages/client/src/components/user-preview.vue
@@ -70,7 +70,7 @@ export default defineComponent({
},
mounted() {
- if (typeof this.q == 'object') {
+ if (typeof this.q === 'object') {
this.user = this.q;
this.fetched = true;
} else {
diff --git a/packages/client/src/components/user-select-dialog.vue b/packages/client/src/components/user-select-dialog.vue
index dbef34d547..b34d21af07 100644
--- a/packages/client/src/components/user-select-dialog.vue
+++ b/packages/client/src/components/user-select-dialog.vue
@@ -60,9 +60,9 @@ import * as os from '@/os';
import { defaultStore } from '@/store';
const emit = defineEmits<{
- (e: 'ok', selected: misskey.entities.UserDetailed): void;
- (e: 'cancel'): void;
- (e: 'closed'): void;
+ (ev: 'ok', selected: misskey.entities.UserDetailed): void;
+ (ev: 'cancel'): void;
+ (ev: 'closed'): void;
}>();
let username = $ref('');
diff --git a/packages/client/src/components/visibility-picker.vue b/packages/client/src/components/visibility-picker.vue
index 4b20063a51..c717c3a461 100644
--- a/packages/client/src/components/visibility-picker.vue
+++ b/packages/client/src/components/visibility-picker.vue
@@ -57,9 +57,9 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'changeVisibility', v: typeof misskey.noteVisibilities[number]): void;
- (e: 'changeLocalOnly', v: boolean): void;
- (e: 'closed'): void;
+ (ev: 'changeVisibility', v: typeof misskey.noteVisibilities[number]): void;
+ (ev: 'changeLocalOnly', v: boolean): void;
+ (ev: 'closed'): void;
}>();
let v = $ref(props.currentVisibility);
diff --git a/packages/client/src/components/waiting-dialog.vue b/packages/client/src/components/waiting-dialog.vue
index 7dfcc55695..9e631b55b1 100644
--- a/packages/client/src/components/waiting-dialog.vue
+++ b/packages/client/src/components/waiting-dialog.vue
@@ -21,8 +21,8 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'done');
- (e: 'closed');
+ (ev: 'done');
+ (ev: 'closed');
}>();
function done() {
diff --git a/packages/client/src/components/widgets.vue b/packages/client/src/components/widgets.vue
index da9d935281..b6835795cb 100644
--- a/packages/client/src/components/widgets.vue
+++ b/packages/client/src/components/widgets.vue
@@ -19,7 +19,7 @@
<div class="customize-container">
<button class="config _button" @click.prevent.stop="configWidget(element.id)"><i class="fas fa-cog"></i></button>
<button class="remove _button" @click.prevent.stop="removeWidget(element)"><i class="fas fa-times"></i></button>
- <component class="handle" :ref="el => widgetRefs[element.id] = el" :is="`mkw-${element.name}`" :widget="element" @updateProps="updateWidget(element.id, $event)"/>
+ <component :is="`mkw-${element.name}`" :ref="el => widgetRefs[element.id] = el" class="handle" :widget="element" @updateProps="updateWidget(element.id, $event)"/>
</div>
</template>
</XDraggable>
@@ -37,7 +37,7 @@ import { widgets as widgetDefs } from '@/widgets';
export default defineComponent({
components: {
- XDraggable: defineAsyncComponent(() => import('vuedraggable').then(x => x.default)),
+ XDraggable: defineAsyncComponent(() => import('vuedraggable')),
MkSelect,
MkButton,
},
diff --git a/packages/client/src/directives/tooltip.ts b/packages/client/src/directives/tooltip.ts
index dd715227a4..0e69da954e 100644
--- a/packages/client/src/directives/tooltip.ts
+++ b/packages/client/src/directives/tooltip.ts
@@ -1,7 +1,7 @@
// TODO: useTooltip関数使うようにしたい
// ただディレクティブ内でonUnmountedなどのcomposition api使えるのか不明
-import { Directive, ref } from 'vue';
+import { defineAsyncComponent, Directive, ref } from 'vue';
import { isTouchUsing } from '@/scripts/touch';
import { popup, alert } from '@/os';
@@ -45,7 +45,7 @@ export default {
if (self.text == null) return;
const showing = ref(true);
- popup(import('@/components/ui/tooltip.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/ui/tooltip.vue')), {
showing,
text: self.text,
targetElement: el,
diff --git a/packages/client/src/directives/user-preview.ts b/packages/client/src/directives/user-preview.ts
index cdd2afa194..9d18a69877 100644
--- a/packages/client/src/directives/user-preview.ts
+++ b/packages/client/src/directives/user-preview.ts
@@ -1,4 +1,4 @@
-import { Directive, ref } from 'vue';
+import { defineAsyncComponent, Directive, ref } from 'vue';
import autobind from 'autobind-decorator';
import { popup } from '@/os';
@@ -24,7 +24,7 @@ export class UserPreview {
const showing = ref(true);
- popup(import('@/components/user-preview.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/user-preview.vue')), {
showing,
q: this.user,
source: this.el
diff --git a/packages/client/src/emojilist.json b/packages/client/src/emojilist.json
index 75c424ab4b..402e82e33b 100644
--- a/packages/client/src/emojilist.json
+++ b/packages/client/src/emojilist.json
@@ -96,6 +96,13 @@
{ "category": "face", "char": "\uD83D\uDE36\u200D\uD83C\uDF2B\uFE0F", "name": "face_in_clouds", "keywords": [] },
{ "category": "face", "char": "\uD83D\uDE2E\u200D\uD83D\uDCA8", "name": "face_exhaling", "keywords": [] },
{ "category": "face", "char": "\uD83D\uDE35\u200D\uD83D\uDCAB", "name": "face_with_spiral_eyes", "keywords": [] },
+ { "category": "face", "char": "\uD83E\uDEE0", "name": "melting_face", "keywords": ["disappear", "dissolve", "liquid", "melt", "toketa"] },
+ { "category": "face", "char": "\uD83E\uDEE2", "name": "face_with_open_eyes_and_hand_over_mouth", "keywords": ["amazement", "awe", "disbelief", "embarrass", "scared", "surprise", "ohoho"] },
+ { "category": "face", "char": "\uD83E\uDEE3", "name": "face_with_peeking_eye", "keywords": ["captivated", "peep", "stare", "chunibyo"] },
+ { "category": "face", "char": "\uD83E\uDEE1", "name": "saluting_face", "keywords": ["ok", "salute", "sunny", "troops", "yes", "raja"] },
+ { "category": "face", "char": "\uD83E\uDEE5", "name": "dotted_line_face", "keywords": ["depressed", "disappear", "hide", "introvert", "invisible", "tensen"] },
+ { "category": "face", "char": "\uD83E\uDEE4", "name": "face_with_diagonal_mouth", "keywords": ["disappointed", "meh", "skeptical", "unsure"] },
+ { "category": "face", "char": "\uD83E\uDD79", "name": "face_holding_back_tears", "keywords": ["angry", "cry", "proud", "resist", "sad"] },
{ "category": "face", "char": "💩", "name": "poop", "keywords": ["hankey", "shitface", "fail", "turd", "shit"] },
{ "category": "face", "char": "😈", "name": "smiling_imp", "keywords": ["devil", "horns"] },
{ "category": "face", "char": "👿", "name": "imp", "keywords": ["devil", "angry", "horns"] },
@@ -149,11 +156,19 @@
{ "category": "people", "char": "🤞", "name": "crossed_fingers", "keywords": ["good", "lucky"] },
{ "category": "people", "char": "🖖", "name": "vulcan_salute", "keywords": ["hand", "fingers", "spock", "star trek"] },
{ "category": "people", "char": "✍", "name": "writing_hand", "keywords": ["lower_left_ballpoint_pen", "stationery", "write", "compose"] },
+ { "category": "people", "char": "\uD83E\uDEF0", "name": "hand_with_index_finger_and_thumb_crossed", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEF1", "name": "rightwards_hand", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEF2", "name": "leftwards_hand", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEF3", "name": "palm_down_hand", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEF4", "name": "palm_up_hand", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEF5", "name": "index_pointing_at_the_viewer", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEF6", "name": "heart_hands", "keywords": ["moemoekyun"] },
{ "category": "people", "char": "🤏", "name": "pinching_hand", "keywords": ["hand", "fingers"] },
{ "category": "people", "char": "🤌", "name": "pinched_fingers", "keywords": ["hand", "fingers"] },
{ "category": "people", "char": "🤳", "name": "selfie", "keywords": ["camera", "phone"] },
{ "category": "people", "char": "💅", "name": "nail_care", "keywords": ["beauty", "manicure", "finger", "fashion", "nail"] },
{ "category": "people", "char": "👄", "name": "lips", "keywords": ["mouth", "kiss"] },
+ { "category": "people", "char": "\uD83E\uDEE6", "name": "biting_lip", "keywords": [] },
{ "category": "people", "char": "🦷", "name": "tooth", "keywords": ["teeth", "dentist"] },
{ "category": "people", "char": "👅", "name": "tongue", "keywords": ["mouth", "playful"] },
{ "category": "people", "char": "👂", "name": "ear", "keywords": ["face", "hear", "sound", "listen"] },
@@ -275,7 +290,11 @@
{ "category": "people", "char": "🧚‍♀️", "name": "woman_fairy", "keywords": ["woman", "female"] },
{ "category": "people", "char": "🧚‍♂️", "name": "man_fairy", "keywords": ["man", "male"] },
{ "category": "people", "char": "👼", "name": "angel", "keywords": ["heaven", "wings", "halo"] },
+ { "category": "people", "char": "\uD83E\uDDCC", "name": "troll", "keywords": [] },
{ "category": "people", "char": "🤰", "name": "pregnant_woman", "keywords": ["baby"] },
+ { "category": "people", "char": "\uD83E\uDEC3", "name": "pregnant_man", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEC4", "name": "pregnant_person", "keywords": [] },
+ { "category": "people", "char": "\uD83E\uDEC5", "name": "person_with_crown", "keywords": [] },
{ "category": "people", "char": "🤱", "name": "breastfeeding", "keywords": ["nursing", "baby"] },
{ "category": "people", "char": "\uD83D\uDC69\u200D\uD83C\uDF7C", "name": "woman_feeding_baby", "keywords": [] },
{ "category": "people", "char": "\uD83D\uDC68\u200D\uD83C\uDF7C", "name": "man_feeding_baby", "keywords": [] },
@@ -459,7 +478,7 @@
{ "category": "animals_and_nature", "char": "🐛", "name": "bug", "keywords": ["animal", "insect", "nature", "worm"] },
{ "category": "animals_and_nature", "char": "🦋", "name": "butterfly", "keywords": ["animal", "insect", "nature", "caterpillar"] },
{ "category": "animals_and_nature", "char": "🐌", "name": "snail", "keywords": ["slow", "animal", "shell"] },
- { "category": "animals_and_nature", "char": "🐞", "name": "beetle", "keywords": ["animal", "insect", "nature", "ladybug"] },
+ { "category": "animals_and_nature", "char": "🐞", "name": "lady_beetle", "keywords": ["animal", "insect", "nature", "ladybug"] },
{ "category": "animals_and_nature", "char": "🐜", "name": "ant", "keywords": ["animal", "insect", "nature", "bug"] },
{ "category": "animals_and_nature", "char": "🦗", "name": "grasshopper", "keywords": ["animal", "cricket", "chirp"] },
{ "category": "animals_and_nature", "char": "🕷", "name": "spider", "keywords": ["animal", "arachnid"] },
@@ -615,6 +634,10 @@
{ "category": "animals_and_nature", "char": "💧", "name": "droplet", "keywords": ["water", "drip", "faucet", "spring"] },
{ "category": "animals_and_nature", "char": "💦", "name": "sweat_drops", "keywords": ["water", "drip", "oops"] },
{ "category": "animals_and_nature", "char": "🌊", "name": "ocean", "keywords": ["sea", "water", "wave", "nature", "tsunami", "disaster"] },
+ { "category": "animals_and_nature", "char": "\uD83E\uDEB7", "name": "lotus", "keywords": [] },
+ { "category": "animals_and_nature", "char": "\uD83E\uDEB8", "name": "coral", "keywords": [] },
+ { "category": "animals_and_nature", "char": "\uD83E\uDEB9", "name": "empty_nest", "keywords": [] },
+ { "category": "animals_and_nature", "char": "\uD83E\uDEBA", "name": "nest_with_eggs", "keywords": [] },
{ "category": "food_and_drink", "char": "🍏", "name": "green_apple", "keywords": ["fruit", "nature"] },
{ "category": "food_and_drink", "char": "🍎", "name": "apple", "keywords": ["fruit", "mac", "school"] },
{ "category": "food_and_drink", "char": "🍐", "name": "pear", "keywords": ["fruit", "nature", "food"] },
@@ -737,6 +760,9 @@
{ "category": "food_and_drink", "char": "🥣", "name": "bowl_with_spoon", "keywords": ["food", "breakfast", "cereal", "oatmeal", "porridge"] },
{ "category": "food_and_drink", "char": "🥡", "name": "takeout_box", "keywords": ["food", "leftovers"] },
{ "category": "food_and_drink", "char": "🥢", "name": "chopsticks", "keywords": ["food"] },
+ { "category": "food_and_drink", "char": "\uD83E\uDED7", "name": "pouring_liquid", "keywords": [] },
+ { "category": "food_and_drink", "char": "\uD83E\uDED8", "name": "beans", "keywords": [] },
+ { "category": "food_and_drink", "char": "\uD83E\uDED9", "name": "jar", "keywords": [] },
{ "category": "activity", "char": "⚽", "name": "soccer", "keywords": ["sports", "football"] },
{ "category": "activity", "char": "🏀", "name": "basketball", "keywords": ["sports", "balls", "NBA"] },
{ "category": "activity", "char": "🏈", "name": "football", "keywords": ["sports", "balls", "NFL"] },
@@ -844,6 +870,8 @@
{ "category": "activity", "char": "🪄", "name": "magic_wand", "keywords": [] },
{ "category": "activity", "char": "🪅", "name": "pinata", "keywords": [] },
{ "category": "activity", "char": "🪆", "name": "nesting_dolls", "keywords": [] },
+ { "category": "activity", "char": "\uD83E\uDEAC", "name": "hamsa", "keywords": [] },
+ { "category": "activity", "char": "\uD83E\uDEA9", "name": "mirror_ball", "keywords": [] },
{ "category": "travel_and_places", "char": "🚗", "name": "red_car", "keywords": ["red", "transportation", "vehicle"] },
{ "category": "travel_and_places", "char": "🚕", "name": "taxi", "keywords": ["uber", "vehicle", "cars", "transportation"] },
{ "category": "travel_and_places", "char": "🚙", "name": "blue_car", "keywords": ["transportation", "vehicle"] },
@@ -971,11 +999,12 @@
{ "category": "travel_and_places", "char": "🕋", "name": "kaaba", "keywords": ["mecca", "mosque", "islam"] },
{ "category": "travel_and_places", "char": "⛩", "name": "shinto_shrine", "keywords": ["temple", "japan", "kyoto"] },
{ "category": "travel_and_places", "char": "🛕", "name": "hindu_temple", "keywords": ["temple"] },
-
{ "category": "travel_and_places", "char": "🪨", "name": "rock", "keywords": [] },
{ "category": "travel_and_places", "char": "🪵", "name": "wood", "keywords": [] },
{ "category": "travel_and_places", "char": "🛖", "name": "hut", "keywords": [] },
-
+ { "category": "travel_and_places", "char": "\uD83D\uDEDD", "name": "playground_slide", "keywords": [] },
+ { "category": "travel_and_places", "char": "\uD83D\uDEDE", "name": "wheel", "keywords": [] },
+ { "category": "travel_and_places", "char": "\uD83D\uDEDF", "name": "ring_buoy", "keywords": [] },
{ "category": "objects", "char": "⌚", "name": "watch", "keywords": ["time", "accessories"] },
{ "category": "objects", "char": "📱", "name": "iphone", "keywords": ["technology", "apple", "gadgets", "dial"] },
{ "category": "objects", "char": "📲", "name": "calling", "keywords": ["iphone", "incoming"] },
@@ -1016,6 +1045,7 @@
{ "category": "objects", "char": "⌛", "name": "hourglass", "keywords": ["time", "clock", "oldschool", "limit", "exam", "quiz", "test"] },
{ "category": "objects", "char": "📡", "name": "satellite", "keywords": ["communication", "future", "radio", "space"] },
{ "category": "objects", "char": "🔋", "name": "battery", "keywords": ["power", "energy", "sustain"] },
+ { "category": "objects", "char": "\uD83E\uDEAB", "name": "battery", "keywords": [] },
{ "category": "objects", "char": "🔌", "name": "electric_plug", "keywords": ["charger", "power"] },
{ "category": "objects", "char": "💡", "name": "bulb", "keywords": ["light", "electricity", "idea"] },
{ "category": "objects", "char": "🔦", "name": "flashlight", "keywords": ["dark", "camping", "sight", "night"] },
@@ -1031,6 +1061,7 @@
{ "category": "objects", "char": "💰", "name": "moneybag", "keywords": ["dollar", "payment", "coins", "sale"] },
{ "category": "objects", "char": "🪙", "name": "coin", "keywords": ["dollar", "payment", "coins", "sale"] },
{ "category": "objects", "char": "💳", "name": "credit_card", "keywords": ["money", "sales", "dollar", "bill", "payment", "shopping"] },
+ { "category": "objects", "char": "\uD83E\uDEAB", "name": "identification_card", "keywords": [] },
{ "category": "objects", "char": "💎", "name": "gem", "keywords": ["blue", "ruby", "diamond", "jewelry"] },
{ "category": "objects", "char": "⚖", "name": "balance_scale", "keywords": ["law", "fairness", "weight"] },
{ "category": "objects", "char": "🧰", "name": "toolbox", "keywords": ["tools", "diy", "fix", "maintainer", "mechanic"] },
@@ -1077,6 +1108,8 @@
{ "category": "objects", "char": "🩹", "name": "adhesive_bandage", "keywords": ["health", "hospital", "medicine", "needle", "doctor", "nurse"] },
{ "category": "objects", "char": "🩺", "name": "stethoscope", "keywords": ["health", "hospital", "medicine", "needle", "doctor", "nurse"] },
{ "category": "objects", "char": "🪒", "name": "razor", "keywords": ["health"] },
+ { "category": "objects", "char": "\uD83E\uDE7B", "name": "xray", "keywords": [] },
+ { "category": "objects", "char": "\uD83E\uDE7C", "name": "crutch", "keywords": [] },
{ "category": "objects", "char": "🧬", "name": "dna", "keywords": ["biologist", "genetics", "life"] },
{ "category": "objects", "char": "🧫", "name": "petri_dish", "keywords": ["bacteria", "biology", "culture", "lab"] },
{ "category": "objects", "char": "🧪", "name": "test_tube", "keywords": ["chemistry", "experiment", "lab", "science"] },
@@ -1111,6 +1144,7 @@
{ "category": "objects", "char": "🪤", "name": "mouse_trap", "keywords": ["household"] },
{ "category": "objects", "char": "🪣", "name": "bucket", "keywords": ["household"] },
{ "category": "objects", "char": "🪥", "name": "toothbrush", "keywords": ["household"] },
+ { "category": "objects", "char": "\uD83E\uDEE7", "name": "bubbles", "keywords": [] },
{ "category": "objects", "char": "⛱", "name": "parasol_on_ground", "keywords": ["weather", "summer"] },
{ "category": "objects", "char": "🗿", "name": "moyai", "keywords": ["rock", "easter island", "moai"] },
{ "category": "objects", "char": "🛍", "name": "shopping", "keywords": ["mall", "buy", "purchase"] },
@@ -1404,6 +1438,7 @@
{ "category": "symbols", "char": "➖", "name": "heavy_minus_sign", "keywords": ["math", "calculation", "subtract", "less"] },
{ "category": "symbols", "char": "➗", "name": "heavy_division_sign", "keywords": ["divide", "math", "calculation"] },
{ "category": "symbols", "char": "✖️", "name": "heavy_multiplication_x", "keywords": ["math", "calculation"] },
+ { "category": "symbols", "char": "\uD83D\uDFF0", "name": "heavy_equals_sign", "keywords": [] },
{ "category": "symbols", "char": "♾", "name": "infinity", "keywords": ["forever"] },
{ "category": "symbols", "char": "💲", "name": "heavy_dollar_sign", "keywords": ["money", "sales", "payment", "currency", "buck"] },
{ "category": "symbols", "char": "💱", "name": "currency_exchange", "keywords": ["money", "sales", "dollar", "travel"] },
@@ -1747,3 +1782,4 @@
{ "category": "flags", "char": "🇺🇳", "name": "united_nations", "keywords": ["un", "flag", "banner"] },
{ "category": "flags", "char": "🏴‍☠️", "name": "pirate_flag", "keywords": ["skull", "crossbones", "flag", "banner"] }
]
+
diff --git a/packages/client/src/filters/bytes.ts b/packages/client/src/filters/bytes.ts
index 50e63534b6..c80f2f0ed2 100644
--- a/packages/client/src/filters/bytes.ts
+++ b/packages/client/src/filters/bytes.ts
@@ -1,7 +1,7 @@
export default (v, digits = 0) => {
if (v == null) return '?';
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
- if (v == 0) return '0';
+ if (v === 0) return '0';
const isMinus = v < 0;
if (isMinus) v = -v;
const i = Math.floor(Math.log(v) / Math.log(1024));
diff --git a/packages/client/src/init.ts b/packages/client/src/init.ts
index 58fdd25835..1fc597bb8a 100644
--- a/packages/client/src/init.ts
+++ b/packages/client/src/init.ts
@@ -13,9 +13,9 @@ if (localStorage.getItem('accounts') != null) {
}
//#endregion
-import { computed, createApp, watch, markRaw, version as vueVersion } from 'vue';
+import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue';
import compareVersions from 'compare-versions';
-import * as JSON5 from 'json5';
+import JSON5 from 'json5';
import widgets from '@/widgets';
import directives from '@/directives';
@@ -149,7 +149,7 @@ if ($i && $i.token) {
try {
document.body.innerHTML = '<div>Please wait...</div>';
await login(i);
- } catch (e) {
+ } catch (err) {
// Render the error screen
// TODO: ちゃんとしたコンポーネントをレンダリングする(v10とかのトラブルシューティングゲーム付きのやつみたいな)
document.body.innerHTML = '<div id="err">Oops!</div>';
@@ -171,14 +171,14 @@ fetchInstanceMetaPromise.then(() => {
initializeSw();
});
-const app = createApp(await (
- window.location.search === '?zen' ? import('@/ui/zen.vue') :
- !$i ? import('@/ui/visitor.vue') :
- ui === 'deck' ? import('@/ui/deck.vue') :
- ui === 'desktop' ? import('@/ui/desktop.vue') :
- ui === 'classic' ? import('@/ui/classic.vue') :
- import('@/ui/universal.vue')
-).then(x => x.default));
+const app = createApp(
+ window.location.search === '?zen' ? defineAsyncComponent(() => import('@/ui/zen.vue')) :
+ !$i ? defineAsyncComponent(() => import('@/ui/visitor.vue')) :
+ ui === 'deck' ? defineAsyncComponent(() => import('@/ui/deck.vue')) :
+ ui === 'desktop' ? defineAsyncComponent(() => import('@/ui/desktop.vue')) :
+ ui === 'classic' ? defineAsyncComponent(() => import('@/ui/classic.vue')) :
+ defineAsyncComponent(() => import('@/ui/universal.vue'))
+);
if (_DEV_) {
app.config.performance = true;
@@ -206,11 +206,26 @@ if (splash) splash.addEventListener('transitionend', () => {
splash.remove();
});
-const rootEl = document.createElement('div');
-document.body.appendChild(rootEl);
-
if (ui === 'deck') await deckStore.ready;
+// https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114239210
+// なぜかinit.tsの内容が2回実行されることがあるため、mountするdivを1つに制限する
+const rootEl = (() => {
+ const MISSKEY_MOUNT_DIV_ID = 'misskey_app';
+
+ const currentEl = document.getElementById(MISSKEY_MOUNT_DIV_ID);
+
+ if (currentEl) {
+ console.warn('multiple import detected');
+ return currentEl;
+ }
+
+ const rootEl = document.createElement('div');
+ rootEl.id = MISSKEY_MOUNT_DIV_ID;
+ document.body.appendChild(rootEl);
+ return rootEl;
+})();
+
app.mount(rootEl);
// boot.jsのやつを解除
@@ -236,10 +251,10 @@ if (lastVersion !== version) {
if (lastVersion != null && compareVersions(version, lastVersion) === 1) {
// ログインしてる場合だけ
if ($i) {
- popup(import('@/components/updated.vue'), {}, {}, 'closed');
+ popup(defineAsyncComponent(() => import('@/components/updated.vue')), {}, {}, 'closed');
}
}
- } catch (e) {
+ } catch (err) {
}
}
@@ -324,7 +339,7 @@ stream.on('_disconnected_', async () => {
}
});
-stream.on('emojiAdded', data => {
+stream.on('emojiAdded', emojiData => {
// TODO
//store.commit('instance/set', );
});
diff --git a/packages/client/src/instance.ts b/packages/client/src/instance.ts
index 6e912aa2e5..d24eb2419a 100644
--- a/packages/client/src/instance.ts
+++ b/packages/client/src/instance.ts
@@ -4,11 +4,11 @@ import { api } from './os';
// TODO: 他のタブと永続化されたstateを同期
-const data = localStorage.getItem('instance');
+const instanceData = localStorage.getItem('instance');
// TODO: instanceをリアクティブにするかは再考の余地あり
-export const instance: Misskey.entities.InstanceMetadata = reactive(data ? JSON.parse(data) : {
+export const instance: Misskey.entities.InstanceMetadata = reactive(instanceData ? JSON.parse(instanceData) : {
// TODO: set default values
});
diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts
index b8a3f94cc8..6baf538917 100644
--- a/packages/client/src/os.ts
+++ b/packages/client/src/os.ts
@@ -1,6 +1,6 @@
// TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する
-import { Component, markRaw, Ref, ref } from 'vue';
+import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue';
import { EventEmitter } from 'eventemitter3';
import insertTextAtCursor from 'insert-text-at-cursor';
import * as Misskey from 'misskey-js';
@@ -59,10 +59,10 @@ export const apiWithDialog = ((
token?: string | null | undefined,
) => {
const promise = api(endpoint, data, token);
- promiseDialog(promise, null, (e) => {
+ promiseDialog(promise, null, (err) => {
alert({
type: 'error',
- text: e.message + '\n' + (e as any).id,
+ text: err.message + '\n' + (err as any).id,
});
});
@@ -72,7 +72,7 @@ export const apiWithDialog = ((
export function promiseDialog<T extends Promise<any>>(
promise: T,
onSuccess?: ((res: any) => void) | null,
- onFailure?: ((e: Error) => void) | null,
+ onFailure?: ((err: Error) => void) | null,
text?: string,
): T {
const showing = ref(true);
@@ -88,14 +88,14 @@ export function promiseDialog<T extends Promise<any>>(
showing.value = false;
}, 1000);
}
- }).catch(e => {
+ }).catch(err => {
showing.value = false;
if (onFailure) {
- onFailure(e);
+ onFailure(err);
} else {
alert({
type: 'error',
- text: e
+ text: err,
});
}
});
@@ -110,10 +110,6 @@ export function promiseDialog<T extends Promise<any>>(
return promise;
}
-function isModule(x: any): x is typeof import('*.vue') {
- return x.default != null;
-}
-
let popupIdCount = 0;
export const popups = ref([]) as Ref<{
id: any;
@@ -131,10 +127,7 @@ export function claimZIndex(priority: 'low' | 'middle' | 'high' = 'low'): number
return zIndexes[priority];
}
-export async function popup(component: Component | typeof import('*.vue') | Promise<Component | typeof import('*.vue')>, props: Record<string, any>, events = {}, disposeEvent?: string) {
- if (component.then) component = await component;
-
- if (isModule(component)) component = component.default;
+export async function popup(component: Component, props: Record<string, any>, events = {}, disposeEvent?: string) {
markRaw(component);
const id = ++popupIdCount;
@@ -163,7 +156,7 @@ export async function popup(component: Component | typeof import('*.vue') | Prom
export function pageWindow(path: string) {
const { component, props } = resolve(path);
- popup(import('@/components/page-window.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/page-window.vue')), {
initialPath: path,
initialComponent: markRaw(component),
initialProps: props,
@@ -172,7 +165,7 @@ export function pageWindow(path: string) {
export function modalPageWindow(path: string) {
const { component, props } = resolve(path);
- popup(import('@/components/modal-page-window.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/modal-page-window.vue')), {
initialPath: path,
initialComponent: markRaw(component),
initialProps: props,
@@ -180,7 +173,7 @@ export function modalPageWindow(path: string) {
}
export function toast(message: string) {
- popup(import('@/components/toast.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/toast.vue')), {
message
}, {}, 'closed');
}
@@ -191,7 +184,7 @@ export function alert(props: {
text?: string | null;
}): Promise<void> {
return new Promise((resolve, reject) => {
- popup(import('@/components/dialog.vue'), props, {
+ popup(defineAsyncComponent(() => import('@/components/dialog.vue')), props, {
done: result => {
resolve();
},
@@ -205,7 +198,7 @@ export function confirm(props: {
text?: string | null;
}): Promise<{ canceled: boolean }> {
return new Promise((resolve, reject) => {
- popup(import('@/components/dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/dialog.vue')), {
...props,
showCancelButton: true,
}, {
@@ -226,7 +219,7 @@ export function inputText(props: {
canceled: false; result: string;
}> {
return new Promise((resolve, reject) => {
- popup(import('@/components/dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/dialog.vue')), {
title: props.title,
text: props.text,
input: {
@@ -251,7 +244,7 @@ export function inputNumber(props: {
canceled: false; result: number;
}> {
return new Promise((resolve, reject) => {
- popup(import('@/components/dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/dialog.vue')), {
title: props.title,
text: props.text,
input: {
@@ -276,7 +269,7 @@ export function inputDate(props: {
canceled: false; result: Date;
}> {
return new Promise((resolve, reject) => {
- popup(import('@/components/dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/dialog.vue')), {
title: props.title,
text: props.text,
input: {
@@ -313,7 +306,7 @@ export function select<C extends any = any>(props: {
canceled: false; result: C;
}> {
return new Promise((resolve, reject) => {
- popup(import('@/components/dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/dialog.vue')), {
title: props.title,
text: props.text,
select: {
@@ -335,7 +328,7 @@ export function success() {
window.setTimeout(() => {
showing.value = false;
}, 1000);
- popup(import('@/components/waiting-dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/waiting-dialog.vue')), {
success: true,
showing: showing
}, {
@@ -347,7 +340,7 @@ export function success() {
export function waiting() {
return new Promise((resolve, reject) => {
const showing = ref(true);
- popup(import('@/components/waiting-dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/waiting-dialog.vue')), {
success: false,
showing: showing
}, {
@@ -358,7 +351,7 @@ export function waiting() {
export function form(title, form) {
return new Promise((resolve, reject) => {
- popup(import('@/components/form-dialog.vue'), { title, form }, {
+ popup(defineAsyncComponent(() => import('@/components/form-dialog.vue')), { title, form }, {
done: result => {
resolve(result);
},
@@ -368,7 +361,7 @@ export function form(title, form) {
export async function selectUser() {
return new Promise((resolve, reject) => {
- popup(import('@/components/user-select-dialog.vue'), {}, {
+ popup(defineAsyncComponent(() => import('@/components/user-select-dialog.vue')), {}, {
ok: user => {
resolve(user);
},
@@ -378,7 +371,7 @@ export async function selectUser() {
export async function selectDriveFile(multiple: boolean) {
return new Promise((resolve, reject) => {
- popup(import('@/components/drive-select-dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/drive-select-dialog.vue')), {
type: 'file',
multiple
}, {
@@ -393,7 +386,7 @@ export async function selectDriveFile(multiple: boolean) {
export async function selectDriveFolder(multiple: boolean) {
return new Promise((resolve, reject) => {
- popup(import('@/components/drive-select-dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/drive-select-dialog.vue')), {
type: 'folder',
multiple
}, {
@@ -408,7 +401,7 @@ export async function selectDriveFolder(multiple: boolean) {
export async function pickEmoji(src: HTMLElement | null, opts) {
return new Promise((resolve, reject) => {
- popup(import('@/components/emoji-picker-dialog.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/emoji-picker-dialog.vue')), {
src,
...opts
}, {
@@ -458,7 +451,7 @@ export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea:
characterData: false,
});
- openingEmojiPicker = await popup(import('@/components/emoji-picker-window.vue'), {
+ openingEmojiPicker = await popup(defineAsyncComponent(() => import('@/components/emoji-picker-window.vue')), {
src,
...opts
}, {
@@ -480,7 +473,7 @@ export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement
}) {
return new Promise((resolve, reject) => {
let dispose;
- popup(import('@/components/ui/popup-menu.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/ui/popup-menu.vue')), {
items,
src,
width: options?.width,
@@ -501,7 +494,7 @@ export function contextMenu(items: MenuItem[] | Ref<MenuItem[]>, ev: MouseEvent)
ev.preventDefault();
return new Promise((resolve, reject) => {
let dispose;
- popup(import('@/components/ui/context-menu.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/ui/context-menu.vue')), {
items,
ev,
}, {
diff --git a/packages/client/src/pages/about-misskey.vue b/packages/client/src/pages/about-misskey.vue
index ff04ed84f2..691bc4f07b 100644
--- a/packages/client/src/pages/about-misskey.vue
+++ b/packages/client/src/pages/about-misskey.vue
@@ -150,6 +150,7 @@ const patrons = [
'Weeble',
'蝉暮せせせ',
'ThatOneCalculator',
+ 'pixeldesu',
];
let easterEggReady = false;
diff --git a/packages/client/src/pages/admin/ads.vue b/packages/client/src/pages/admin/ads.vue
index 8f164caa99..b18e08db96 100644
--- a/packages/client/src/pages/admin/ads.vue
+++ b/packages/client/src/pages/admin/ads.vue
@@ -7,7 +7,7 @@
<template #label>URL</template>
</MkInput>
<MkInput v-model="ad.imageUrl" class="_formBlock">
- <template #label>{{ $ts.imageUrl }}</template>
+ <template #label>{{ i18n.ts.imageUrl }}</template>
</MkInput>
<FormRadios v-model="ad.place" class="_formBlock">
<template #label>Form</template>
@@ -17,34 +17,34 @@
</FormRadios>
<!--
<div style="margin: 32px 0;">
- {{ $ts.priority }}
- <MkRadio v-model="ad.priority" value="high">{{ $ts.high }}</MkRadio>
- <MkRadio v-model="ad.priority" value="middle">{{ $ts.middle }}</MkRadio>
- <MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
+ {{ i18n.ts.priority }}
+ <MkRadio v-model="ad.priority" value="high">{{ i18n.ts.high }}</MkRadio>
+ <MkRadio v-model="ad.priority" value="middle">{{ i18n.ts.middle }}</MkRadio>
+ <MkRadio v-model="ad.priority" value="low">{{ i18n.ts.low }}</MkRadio>
</div>
-->
<FormSplit>
<MkInput v-model="ad.ratio" type="number">
- <template #label>{{ $ts.ratio }}</template>
+ <template #label>{{ i18n.ts.ratio }}</template>
</MkInput>
<MkInput v-model="ad.expiresAt" type="date">
- <template #label>{{ $ts.expiration }}</template>
+ <template #label>{{ i18n.ts.expiration }}</template>
</MkInput>
</FormSplit>
<MkTextarea v-model="ad.memo" class="_formBlock">
- <template #label>{{ $ts.memo }}</template>
+ <template #label>{{ i18n.ts.memo }}</template>
</MkTextarea>
<div class="buttons _formBlock">
- <MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
- <MkButton class="button" inline danger @click="remove(ad)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
+ <MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
+ <MkButton class="button" inline danger @click="remove(ad)"><i class="fas fa-trash-alt"></i> {{ i18n.ts.remove }}</MkButton>
</div>
</div>
</div>
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkTextarea from '@/components/form/textarea.vue';
@@ -52,81 +52,65 @@ import FormRadios from '@/components/form/radios.vue';
import FormSplit from '@/components/form/split.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- MkInput,
- MkTextarea,
- FormRadios,
- FormSplit,
- },
+let ads: any[] = $ref([]);
- emits: ['info'],
+os.api('admin/ad/list').then(adsResponse => {
+ ads = adsResponse;
+});
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.ads,
- icon: 'fas fa-audio-description',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-plus',
- text: this.$ts.add,
- handler: this.add,
- }],
- },
- ads: [],
- }
- },
+function add() {
+ ads.unshift({
+ id: null,
+ memo: '',
+ place: 'square',
+ priority: 'middle',
+ ratio: 1,
+ url: '',
+ imageUrl: null,
+ expiresAt: null,
+ });
+}
- created() {
- os.api('admin/ad/list').then(ads => {
- this.ads = ads;
+function remove(ad) {
+ os.confirm({
+ type: 'warning',
+ text: i18n.t('removeAreYouSure', { x: ad.url }),
+ }).then(({ canceled }) => {
+ if (canceled) return;
+ ads = ads.filter(x => x !== ad);
+ os.apiWithDialog('admin/ad/delete', {
+ id: ad.id
});
- },
-
- methods: {
- add() {
- this.ads.unshift({
- id: null,
- memo: '',
- place: 'square',
- priority: 'middle',
- ratio: 1,
- url: '',
- imageUrl: null,
- expiresAt: null,
- });
- },
+ });
+}
- remove(ad) {
- os.confirm({
- type: 'warning',
- text: this.$t('removeAreYouSure', { x: ad.url }),
- }).then(({ canceled }) => {
- if (canceled) return;
- this.ads = this.ads.filter(x => x != ad);
- os.apiWithDialog('admin/ad/delete', {
- id: ad.id
- });
- });
- },
+function save(ad) {
+ if (ad.id == null) {
+ os.apiWithDialog('admin/ad/create', {
+ ...ad,
+ expiresAt: new Date(ad.expiresAt).getTime()
+ });
+ } else {
+ os.apiWithDialog('admin/ad/update', {
+ ...ad,
+ expiresAt: new Date(ad.expiresAt).getTime()
+ });
+ }
+}
- save(ad) {
- if (ad.id == null) {
- os.apiWithDialog('admin/ad/create', {
- ...ad,
- expiresAt: new Date(ad.expiresAt).getTime()
- });
- } else {
- os.apiWithDialog('admin/ad/update', {
- ...ad,
- expiresAt: new Date(ad.expiresAt).getTime()
- });
- }
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.ads,
+ icon: 'fas fa-audio-description',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-plus',
+ text: i18n.ts.add,
+ handler: add,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/announcements.vue b/packages/client/src/pages/admin/announcements.vue
index a0d720bb29..97774975de 100644
--- a/packages/client/src/pages/admin/announcements.vue
+++ b/packages/client/src/pages/admin/announcements.vue
@@ -3,112 +3,98 @@
<section v-for="announcement in announcements" class="_card _gap announcements">
<div class="_content announcement">
<MkInput v-model="announcement.title">
- <template #label>{{ $ts.title }}</template>
+ <template #label>{{ i18n.ts.title }}</template>
</MkInput>
<MkTextarea v-model="announcement.text">
- <template #label>{{ $ts.text }}</template>
+ <template #label>{{ i18n.ts.text }}</template>
</MkTextarea>
<MkInput v-model="announcement.imageUrl">
- <template #label>{{ $ts.imageUrl }}</template>
+ <template #label>{{ i18n.ts.imageUrl }}</template>
</MkInput>
- <p v-if="announcement.reads">{{ $t('nUsersRead', { n: announcement.reads }) }}</p>
+ <p v-if="announcement.reads">{{ i18n.t('nUsersRead', { n: announcement.reads }) }}</p>
<div class="buttons">
- <MkButton class="button" inline primary @click="save(announcement)"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
- <MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
+ <MkButton class="button" inline primary @click="save(announcement)"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
+ <MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ i18n.ts.remove }}</MkButton>
</div>
</div>
</section>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkTextarea from '@/components/form/textarea.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- MkInput,
- MkTextarea,
- },
+let announcements: any[] = $ref([]);
- emits: ['info'],
+os.api('admin/announcements/list').then(announcementResponse => {
+ announcements = announcementResponse;
+});
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.announcements,
- icon: 'fas fa-broadcast-tower',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-plus',
- text: this.$ts.add,
- handler: this.add,
- }],
- },
- announcements: [],
- }
- },
+function add() {
+ announcements.unshift({
+ id: null,
+ title: '',
+ text: '',
+ imageUrl: null
+ });
+}
- created() {
- os.api('admin/announcements/list').then(announcements => {
- this.announcements = announcements;
- });
- },
+function remove(announcement) {
+ os.confirm({
+ type: 'warning',
+ text: i18n.t('removeAreYouSure', { x: announcement.title }),
+ }).then(({ canceled }) => {
+ if (canceled) return;
+ announcements = announcements.filter(x => x !== announcement);
+ os.api('admin/announcements/delete', announcement);
+ });
+}
- methods: {
- add() {
- this.announcements.unshift({
- id: null,
- title: '',
- text: '',
- imageUrl: null
+function save(announcement) {
+ if (announcement.id == null) {
+ os.api('admin/announcements/create', announcement).then(() => {
+ os.alert({
+ type: 'success',
+ text: i18n.ts.saved
});
- },
-
- remove(announcement) {
- os.confirm({
- type: 'warning',
- text: this.$t('removeAreYouSure', { x: announcement.title }),
- }).then(({ canceled }) => {
- if (canceled) return;
- this.announcements = this.announcements.filter(x => x != announcement);
- os.api('admin/announcements/delete', announcement);
+ }).catch(err => {
+ os.alert({
+ type: 'error',
+ text: err
});
- },
+ });
+ } else {
+ os.api('admin/announcements/update', announcement).then(() => {
+ os.alert({
+ type: 'success',
+ text: i18n.ts.saved
+ });
+ }).catch(err => {
+ os.alert({
+ type: 'error',
+ text: err
+ });
+ });
+ }
+}
- save(announcement) {
- if (announcement.id == null) {
- os.api('admin/announcements/create', announcement).then(() => {
- os.alert({
- type: 'success',
- text: this.$ts.saved
- });
- }).catch(e => {
- os.alert({
- type: 'error',
- text: e
- });
- });
- } else {
- os.api('admin/announcements/update', announcement).then(() => {
- os.alert({
- type: 'success',
- text: this.$ts.saved
- });
- }).catch(e => {
- os.alert({
- type: 'error',
- text: e
- });
- });
- }
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.announcements,
+ icon: 'fas fa-broadcast-tower',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-plus',
+ text: i18n.ts.add,
+ handler: add,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/bot-protection.vue b/packages/client/src/pages/admin/bot-protection.vue
index 5e0cdd96a5..30fee5015a 100644
--- a/packages/client/src/pages/admin/bot-protection.vue
+++ b/packages/client/src/pages/admin/bot-protection.vue
@@ -43,8 +43,8 @@
</div>
</template>
-<script lang="ts">
-import { defineAsyncComponent, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent } from 'vue';
import FormRadios from '@/components/form/radios.vue';
import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/ui/button.vue';
@@ -54,64 +54,39 @@ import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
-export default defineComponent({
- components: {
- FormRadios,
- FormInput,
- FormButton,
- FormSuspense,
- FormSlot,
- MkCaptcha: defineAsyncComponent(() => import('@/components/captcha.vue')),
- },
+const MkCaptcha = defineAsyncComponent(() => import('@/components/captcha.vue'));
- emits: ['info'],
+let provider = $ref(null);
+let hcaptchaSiteKey: string | null = $ref(null);
+let hcaptchaSecretKey: string | null = $ref(null);
+let recaptchaSiteKey: string | null = $ref(null);
+let recaptchaSecretKey: string | null = $ref(null);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.botProtection,
- icon: 'fas fa-shield-alt'
- },
- provider: null,
- enableHcaptcha: false,
- hcaptchaSiteKey: null,
- hcaptchaSecretKey: null,
- enableRecaptcha: false,
- recaptchaSiteKey: null,
- recaptchaSecretKey: null,
- }
- },
+const enableHcaptcha = $computed(() => provider === 'hcaptcha');
+const enableRecaptcha = $computed(() => provider === 'recaptcha');
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.enableHcaptcha = meta.enableHcaptcha;
- this.hcaptchaSiteKey = meta.hcaptchaSiteKey;
- this.hcaptchaSecretKey = meta.hcaptchaSecretKey;
- this.enableRecaptcha = meta.enableRecaptcha;
- this.recaptchaSiteKey = meta.recaptchaSiteKey;
- this.recaptchaSecretKey = meta.recaptchaSecretKey;
+async function init() {
+ const meta = await os.api('admin/meta');
+ enableHcaptcha = meta.enableHcaptcha;
+ hcaptchaSiteKey = meta.hcaptchaSiteKey;
+ hcaptchaSecretKey = meta.hcaptchaSecretKey;
+ enableRecaptcha = meta.enableRecaptcha;
+ recaptchaSiteKey = meta.recaptchaSiteKey;
+ recaptchaSecretKey = meta.recaptchaSecretKey;
- this.provider = this.enableHcaptcha ? 'hcaptcha' : this.enableRecaptcha ? 'recaptcha' : null;
+ provider = enableHcaptcha ? 'hcaptcha' : enableRecaptcha ? 'recaptcha' : null;
+}
- this.$watch(() => this.provider, () => {
- this.enableHcaptcha = this.provider === 'hcaptcha';
- this.enableRecaptcha = this.provider === 'recaptcha';
- });
- },
-
- save() {
- os.apiWithDialog('admin/update-meta', {
- enableHcaptcha: this.enableHcaptcha,
- hcaptchaSiteKey: this.hcaptchaSiteKey,
- hcaptchaSecretKey: this.hcaptchaSecretKey,
- enableRecaptcha: this.enableRecaptcha,
- recaptchaSiteKey: this.recaptchaSiteKey,
- recaptchaSecretKey: this.recaptchaSecretKey,
- }).then(() => {
- fetchInstance();
- });
- }
- }
-});
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ enableHcaptcha,
+ hcaptchaSiteKey,
+ hcaptchaSecretKey,
+ enableRecaptcha,
+ recaptchaSiteKey,
+ recaptchaSecretKey,
+ }).then(() => {
+ fetchInstance();
+ });
+}
</script>
diff --git a/packages/client/src/pages/admin/database.vue b/packages/client/src/pages/admin/database.vue
index 3a835eeafa..d3519922b1 100644
--- a/packages/client/src/pages/admin/database.vue
+++ b/packages/client/src/pages/admin/database.vue
@@ -9,36 +9,23 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSuspense from '@/components/form/suspense.vue';
import MkKeyValue from '@/components/key-value.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import bytes from '@/filters/bytes';
import number from '@/filters/number';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSuspense,
- MkKeyValue,
- },
+const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size));
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.database,
- icon: 'fas fa-database',
- bg: 'var(--bg)',
- },
- databasePromiseFactory: () => os.api('admin/get-table-stats', {}).then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size)),
- }
- },
-
- methods: {
- bytes, number,
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.database,
+ icon: 'fas fa-database',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/email-settings.vue b/packages/client/src/pages/admin/email-settings.vue
index 7df0b6db1c..aa13043193 100644
--- a/packages/client/src/pages/admin/email-settings.vue
+++ b/packages/client/src/pages/admin/email-settings.vue
@@ -3,37 +3,37 @@
<FormSuspense :p="init">
<div class="_formRoot">
<FormSwitch v-model="enableEmail" class="_formBlock">
- <template #label>{{ $ts.enableEmail }}</template>
- <template #caption>{{ $ts.emailConfigInfo }}</template>
+ <template #label>{{ i18n.ts.enableEmail }}</template>
+ <template #caption>{{ i18n.ts.emailConfigInfo }}</template>
</FormSwitch>
<template v-if="enableEmail">
<FormInput v-model="email" type="email" class="_formBlock">
- <template #label>{{ $ts.emailAddress }}</template>
+ <template #label>{{ i18n.ts.emailAddress }}</template>
</FormInput>
<FormSection>
- <template #label>{{ $ts.smtpConfig }}</template>
+ <template #label>{{ i18n.ts.smtpConfig }}</template>
<FormSplit :min-width="280">
<FormInput v-model="smtpHost" class="_formBlock">
- <template #label>{{ $ts.smtpHost }}</template>
+ <template #label>{{ i18n.ts.smtpHost }}</template>
</FormInput>
<FormInput v-model="smtpPort" type="number" class="_formBlock">
- <template #label>{{ $ts.smtpPort }}</template>
+ <template #label>{{ i18n.ts.smtpPort }}</template>
</FormInput>
</FormSplit>
<FormSplit :min-width="280">
<FormInput v-model="smtpUser" class="_formBlock">
- <template #label>{{ $ts.smtpUser }}</template>
+ <template #label>{{ i18n.ts.smtpUser }}</template>
</FormInput>
<FormInput v-model="smtpPass" type="password" class="_formBlock">
- <template #label>{{ $ts.smtpPass }}</template>
+ <template #label>{{ i18n.ts.smtpPass }}</template>
</FormInput>
</FormSplit>
- <FormInfo class="_formBlock">{{ $ts.emptyToDisableSmtpAuth }}</FormInfo>
+ <FormInfo class="_formBlock">{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo>
<FormSwitch v-model="smtpSecure" class="_formBlock">
- <template #label>{{ $ts.smtpSecure }}</template>
- <template #caption>{{ $ts.smtpSecureInfo }}</template>
+ <template #label>{{ i18n.ts.smtpSecure }}</template>
+ <template #caption>{{ i18n.ts.smtpSecureInfo }}</template>
</FormSwitch>
</FormSection>
</template>
@@ -42,8 +42,8 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue';
import FormInfo from '@/components/ui/info.vue';
@@ -52,86 +52,71 @@ import FormSplit from '@/components/form/split.vue';
import FormSection from '@/components/form/section.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
-import { fetchInstance } from '@/instance';
+import { fetchInstance, instance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormSplit,
- FormSection,
- FormInfo,
- FormSuspense,
- },
+let enableEmail: boolean = $ref(false);
+let email: any = $ref(null);
+let smtpSecure: boolean = $ref(false);
+let smtpHost: string = $ref('');
+let smtpPort: number = $ref(0);
+let smtpUser: string = $ref('');
+let smtpPass: string = $ref('');
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ enableEmail = meta.enableEmail;
+ email = meta.email;
+ smtpSecure = meta.smtpSecure;
+ smtpHost = meta.smtpHost;
+ smtpPort = meta.smtpPort;
+ smtpUser = meta.smtpUser;
+ smtpPass = meta.smtpPass;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.emailServer,
- icon: 'fas fa-envelope',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- text: this.$ts.testEmail,
- handler: this.testEmail,
- }, {
- asFullButton: true,
- icon: 'fas fa-check',
- text: this.$ts.save,
- handler: this.save,
- }],
- },
- enableEmail: false,
- email: null,
- smtpSecure: false,
- smtpHost: '',
- smtpPort: 0,
- smtpUser: '',
- smtpPass: '',
- }
- },
+async function testEmail() {
+ const { canceled, result: destination } = await os.inputText({
+ title: i18n.ts.destination,
+ type: 'email',
+ placeholder: instance.maintainerEmail
+ });
+ if (canceled) return;
+ os.apiWithDialog('admin/send-email', {
+ to: destination,
+ subject: 'Test email',
+ text: 'Yo'
+ });
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.enableEmail = meta.enableEmail;
- this.email = meta.email;
- this.smtpSecure = meta.smtpSecure;
- this.smtpHost = meta.smtpHost;
- this.smtpPort = meta.smtpPort;
- this.smtpUser = meta.smtpUser;
- this.smtpPass = meta.smtpPass;
- },
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ enableEmail,
+ email,
+ smtpSecure,
+ smtpHost,
+ smtpPort,
+ smtpUser,
+ smtpPass,
+ }).then(() => {
+ fetchInstance();
+ });
+}
- async testEmail() {
- const { canceled, result: destination } = await os.inputText({
- title: this.$ts.destination,
- type: 'email',
- placeholder: this.$instance.maintainerEmail
- });
- if (canceled) return;
- os.apiWithDialog('admin/send-email', {
- to: destination,
- subject: 'Test email',
- text: 'Yo'
- });
- },
-
- save() {
- os.apiWithDialog('admin/update-meta', {
- enableEmail: this.enableEmail,
- email: this.email,
- smtpSecure: this.smtpSecure,
- smtpHost: this.smtpHost,
- smtpPort: this.smtpPort,
- smtpUser: this.smtpUser,
- smtpPass: this.smtpPass,
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.emailServer,
+ icon: 'fas fa-envelope',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ text: i18n.ts.testEmail,
+ handler: testEmail,
+ }, {
+ asFullButton: true,
+ icon: 'fas fa-check',
+ text: i18n.ts.save,
+ handler: save,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/emoji-edit-dialog.vue b/packages/client/src/pages/admin/emoji-edit-dialog.vue
index 2e3903426e..d482fa49e6 100644
--- a/packages/client/src/pages/admin/emoji-edit-dialog.vue
+++ b/packages/client/src/pages/admin/emoji-edit-dialog.vue
@@ -27,85 +27,71 @@
</XModalWindow>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import XModalWindow from '@/components/ui/modal-window.vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import * as os from '@/os';
import { unique } from '@/scripts/array';
+import { i18n } from '@/i18n';
+import { emojiCategories } from '@/instance';
-export default defineComponent({
- components: {
- XModalWindow,
- MkButton,
- MkInput,
- },
+const props = defineProps<{
+ emoji: any,
+}>();
- props: {
- emoji: {
- required: true,
- }
- },
-
- emits: ['done', 'closed'],
+let dialog = $ref(null);
+let name: string = $ref(props.emoji.name);
+let category: string = $ref(props.emoji.category);
+let aliases: string = $ref(props.emoji.aliases.join(' '));
+let categories: string[] = $ref(emojiCategories);
- data() {
- return {
- name: this.emoji.name,
- category: this.emoji.category,
- aliases: this.emoji.aliases?.join(' '),
- categories: [],
- }
- },
+const emit = defineEmits<{
+ (ev: 'done', v: { deleted?: boolean, updated?: any }): void,
+ (ev: 'closed'): void
+}>();
- created() {
- os.api('meta', { detail: false }).then(({ emojis }) => {
- this.categories = unique(emojis.map((x: any) => x.category || '').filter((x: string) => x !== ''));
- });
- },
+function ok() {
+ update();
+}
- methods: {
- ok() {
- this.update();
- },
+async function update() {
+ await os.apiWithDialog('admin/emoji/update', {
+ id: props.emoji.id,
+ name,
+ category,
+ aliases: aliases.split(' '),
+ });
- async update() {
- await os.apiWithDialog('admin/emoji/update', {
- id: this.emoji.id,
- name: this.name,
- category: this.category,
- aliases: this.aliases.split(' '),
- });
+ emit('done', {
+ updated: {
+ id: props.emoji.id,
+ name,
+ category,
+ aliases: aliases.split(' '),
+ }
+ });
- this.$emit('done', {
- updated: {
- name: this.name,
- category: this.category,
- aliases: this.aliases.split(' '),
- }
- });
- this.$refs.dialog.close();
- },
+ dialog.close();
+}
- async del() {
- const { canceled } = await os.confirm({
- type: 'warning',
- text: this.$t('removeAreYouSure', { x: this.emoji.name }),
- });
- if (canceled) return;
+async function del() {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.t('removeAreYouSure', { x: name }),
+ });
+ if (canceled) return;
- os.api('admin/emoji/delete', {
- id: this.emoji.id
- }).then(() => {
- this.$emit('done', {
- deleted: true
- });
- this.$refs.dialog.close();
- });
- },
- }
-});
+ os.api('admin/emoji/delete', {
+ id: props.emoji.id
+ }).then(() => {
+ emit('done', {
+ deleted: true
+ });
+ dialog.close();
+ });
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/pages/admin/emojis.vue b/packages/client/src/pages/admin/emojis.vue
index a080ee9c23..38bcc41ea0 100644
--- a/packages/client/src/pages/admin/emojis.vue
+++ b/packages/client/src/pages/admin/emojis.vue
@@ -63,7 +63,7 @@
</template>
<script lang="ts" setup>
-import { computed, defineComponent, ref, toRef } from 'vue';
+import { computed, defineAsyncComponent, defineComponent, ref, toRef } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkPagination from '@/components/ui/pagination.vue';
@@ -130,17 +130,17 @@ const add = async (ev: MouseEvent) => {
};
const edit = (emoji) => {
- os.popup(import('./emoji-edit-dialog.vue'), {
+ os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
emoji: emoji
}, {
done: result => {
if (result.updated) {
- emojisPaginationComponent.value.replaceItem(item => item.id === emoji.id, {
- ...emoji,
+ emojisPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({
+ ...oldEmoji,
...result.updated
- });
+ }));
} else if (result.deleted) {
- emojisPaginationComponent.value.removeItem(item => item.id === emoji.id);
+ emojisPaginationComponent.value.removeItem((item) => item.id === emoji.id);
}
},
}, 'closed');
@@ -175,10 +175,10 @@ const menu = (ev: MouseEvent) => {
type: 'info',
text: i18n.ts.exportRequested,
});
- }).catch((e) => {
+ }).catch((err) => {
os.alert({
type: 'error',
- text: e.message,
+ text: err.message,
});
});
}
@@ -195,10 +195,10 @@ const menu = (ev: MouseEvent) => {
type: 'info',
text: i18n.ts.importRequested,
});
- }).catch((e) => {
+ }).catch((err) => {
os.alert({
type: 'error',
- text: e.message,
+ text: err.message,
});
});
}
diff --git a/packages/client/src/pages/admin/file-dialog.vue b/packages/client/src/pages/admin/file-dialog.vue
index 4c33f62399..0765548aab 100644
--- a/packages/client/src/pages/admin/file-dialog.vue
+++ b/packages/client/src/pages/admin/file-dialog.vue
@@ -34,74 +34,52 @@
</XModalWindow>
</template>
-<script lang="ts">
-import { computed, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkSwitch from '@/components/form/switch.vue';
import XModalWindow from '@/components/ui/modal-window.vue';
import MkDriveFileThumbnail from '@/components/drive-file-thumbnail.vue';
import bytes from '@/filters/bytes';
import * as os from '@/os';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- MkSwitch,
- XModalWindow,
- MkDriveFileThumbnail,
- },
+let file: any = $ref(null);
+let info: any = $ref(null);
+let isSensitive: boolean = $ref(false);
- props: {
- fileId: {
- required: true,
- }
- },
-
- emits: ['closed'],
-
- data() {
- return {
- file: null,
- info: null,
- isSensitive: false,
- };
- },
+const props = defineProps<{
+ fileId: string,
+}>();
- created() {
- this.fetch();
- },
-
- methods: {
- async fetch() {
- this.file = await os.api('drive/files/show', { fileId: this.fileId });
- this.info = await os.api('admin/drive/show-file', { fileId: this.fileId });
- this.isSensitive = this.file.isSensitive;
- },
+async function fetch() {
+ file = await os.api('drive/files/show', { fileId: props.fileId });
+ info = await os.api('admin/drive/show-file', { fileId: props.fileId });
+ isSensitive = file.isSensitive;
+}
- showUser() {
- os.pageWindow(`/user-info/${this.file.userId}`);
- },
+fetch();
- async del() {
- const { canceled } = await os.confirm({
- type: 'warning',
- text: this.$t('removeAreYouSure', { x: this.file.name }),
- });
- if (canceled) return;
+function showUser() {
+ os.pageWindow(`/user-info/${file.userId}`);
+}
- os.apiWithDialog('drive/files/delete', {
- fileId: this.file.id
- });
- },
+async function del() {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.t('removeAreYouSure', { x: file.name }),
+ });
+ if (canceled) return;
- async toggleIsSensitive(v) {
- await os.api('drive/files/update', { fileId: this.fileId, isSensitive: v });
- this.isSensitive = v;
- },
+ os.apiWithDialog('drive/files/delete', {
+ fileId: file.id
+ });
+}
- bytes
- }
-});
+async function toggleIsSensitive(v) {
+ await os.api('drive/files/update', { fileId: props.fileId, isSensitive: v });
+ isSensitive = v;
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/pages/admin/files.vue b/packages/client/src/pages/admin/files.vue
index c62f053092..3cda688698 100644
--- a/packages/client/src/pages/admin/files.vue
+++ b/packages/client/src/pages/admin/files.vue
@@ -55,7 +55,7 @@
</template>
<script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, defineAsyncComponent } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkSelect from '@/components/form/select.vue';
@@ -93,7 +93,7 @@ function clear() {
}
function show(file) {
- os.popup(import('./file-dialog.vue'), {
+ os.popup(defineAsyncComponent(() => import('./file-dialog.vue')), {
fileId: file.id
}, {}, 'closed');
}
diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue
index 6b11650f48..9b7fa5678e 100644
--- a/packages/client/src/pages/admin/index.vue
+++ b/packages/client/src/pages/admin/index.vue
@@ -1,6 +1,6 @@
<template>
<div ref="el" class="hiyeyicy" :class="{ wide: !narrow }">
- <div v-if="!narrow || page == null" class="nav">
+ <div v-if="!narrow || initialPage == null" class="nav">
<MkHeader :info="header"></MkHeader>
<MkSpacer :content-max="700" :margin-min="16">
@@ -12,21 +12,21 @@
<MkInfo v-if="noMaintainerInformation" warn class="info">{{ $ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ $ts.configure }}</MkA></MkInfo>
<MkInfo v-if="noBotProtection" warn class="info">{{ $ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ $ts.configure }}</MkA></MkInfo>
- <MkSuperMenu :def="menuDef" :grid="page == null"></MkSuperMenu>
+ <MkSuperMenu :def="menuDef" :grid="initialPage == null"></MkSuperMenu>
</div>
</MkSpacer>
</div>
- <div class="main">
+ <div v-if="!(narrow && initialPage == null)" class="main">
<MkStickyContainer>
<template #header><MkHeader v-if="childInfo && !childInfo.hideHeader" :info="childInfo"/></template>
- <component :is="component" :ref="el => pageChanged(el)" :key="page" v-bind="pageProps"/>
+ <component :is="component" :ref="el => pageChanged(el)" :key="initialPage" v-bind="pageProps"/>
</MkStickyContainer>
</div>
</div>
</template>
-<script lang="ts">
-import { computed, defineAsyncComponent, defineComponent, isRef, nextTick, onMounted, reactive, ref, watch } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent, nextTick, onMounted, onUnmounted, provide, watch } from 'vue';
import { i18n } from '@/i18n';
import MkSuperMenu from '@/components/ui/super-menu.vue';
import MkInfo from '@/components/ui/info.vue';
@@ -35,292 +35,277 @@ import { instance } from '@/instance';
import * as symbols from '@/symbols';
import * as os from '@/os';
import { lookupUser } from '@/scripts/lookup-user';
+import { MisskeyNavigator } from '@/scripts/navigate';
-export default defineComponent({
- components: {
- MkSuperMenu,
- MkInfo,
- },
+const isEmpty = (x: string | null) => x == null || x === '';
- provide: {
- shouldOmitHeaderTitle: false,
- },
+const nav = new MisskeyNavigator();
- props: {
- initialPage: {
- type: String,
- required: false
- }
- },
+const indexInfo = {
+ title: i18n.ts.controlPanel,
+ icon: 'fas fa-cog',
+ bg: 'var(--bg)',
+ hideHeader: true,
+};
- setup(props, context) {
- const indexInfo = {
- title: i18n.ts.controlPanel,
- icon: 'fas fa-cog',
- bg: 'var(--bg)',
- hideHeader: true,
- };
- const INFO = ref(indexInfo);
- const childInfo = ref(null);
- const page = ref(props.initialPage);
- const narrow = ref(false);
- const view = ref(null);
- const el = ref(null);
- const pageChanged = (page) => {
- if (page == null) return;
- const viewInfo = page[symbols.PAGE_INFO];
- if (isRef(viewInfo)) {
- watch(viewInfo, () => {
- childInfo.value = viewInfo.value;
- }, { immediate: true });
- } else {
- childInfo.value = viewInfo;
- }
- };
- const pageProps = ref({});
+const props = defineProps<{
+ initialPage?: string,
+}>();
- const isEmpty = (x: any) => x == null || x == '';
+provide('shouldOmitHeaderTitle', false);
- const noMaintainerInformation = ref(false);
- const noBotProtection = ref(false);
+let INFO = $ref(indexInfo);
+let childInfo = $ref(null);
+let page = $ref(props.initialPage);
+let narrow = $ref(false);
+let view = $ref(null);
+let el = $ref(null);
+let pageProps = $ref({});
+let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
+let noBotProtection = !instance.enableHcaptcha && !instance.enableRecaptcha;
- os.api('meta', { detail: true }).then(meta => {
- // TODO: 設定が完了しても残ったままになるので、ストリーミングでmeta更新イベントを受け取ってよしなに更新する
- noMaintainerInformation.value = isEmpty(meta.maintainerName) || isEmpty(meta.maintainerEmail);
- noBotProtection.value = !meta.enableHcaptcha && !meta.enableRecaptcha;
- });
+const NARROW_THRESHOLD = 600;
+const ro = new ResizeObserver((entries, observer) => {
+ if (entries.length === 0) return;
+ narrow = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
+});
- const menuDef = computed(() => [{
- title: i18n.ts.quickAction,
- items: [{
- type: 'button',
- icon: 'fas fa-search',
- text: i18n.ts.lookup,
- action: lookup,
- }, ...(instance.disableRegistration ? [{
- type: 'button',
- icon: 'fas fa-user',
- text: i18n.ts.invite,
- action: invite,
- }] : [])],
- }, {
- title: i18n.ts.administration,
- items: [{
- icon: 'fas fa-tachometer-alt',
- text: i18n.ts.dashboard,
- to: '/admin/overview',
- active: page.value === 'overview',
- }, {
- icon: 'fas fa-users',
- text: i18n.ts.users,
- to: '/admin/users',
- active: page.value === 'users',
- }, {
- icon: 'fas fa-laugh',
- text: i18n.ts.customEmojis,
- to: '/admin/emojis',
- active: page.value === 'emojis',
- }, {
- icon: 'fas fa-globe',
- text: i18n.ts.federation,
- to: '/admin/federation',
- active: page.value === 'federation',
- }, {
- icon: 'fas fa-clipboard-list',
- text: i18n.ts.jobQueue,
- to: '/admin/queue',
- active: page.value === 'queue',
- }, {
- icon: 'fas fa-cloud',
- text: i18n.ts.files,
- to: '/admin/files',
- active: page.value === 'files',
- }, {
- icon: 'fas fa-broadcast-tower',
- text: i18n.ts.announcements,
- to: '/admin/announcements',
- active: page.value === 'announcements',
- }, {
- icon: 'fas fa-audio-description',
- text: i18n.ts.ads,
- to: '/admin/ads',
- active: page.value === 'ads',
- }, {
- icon: 'fas fa-exclamation-circle',
- text: i18n.ts.abuseReports,
- to: '/admin/abuses',
- active: page.value === 'abuses',
- }],
- }, {
- title: i18n.ts.settings,
- items: [{
- icon: 'fas fa-cog',
- text: i18n.ts.general,
- to: '/admin/settings',
- active: page.value === 'settings',
- }, {
- icon: 'fas fa-envelope',
- text: i18n.ts.emailServer,
- to: '/admin/email-settings',
- active: page.value === 'email-settings',
- }, {
- icon: 'fas fa-cloud',
- text: i18n.ts.objectStorage,
- to: '/admin/object-storage',
- active: page.value === 'object-storage',
- }, {
- icon: 'fas fa-lock',
- text: i18n.ts.security,
- to: '/admin/security',
- active: page.value === 'security',
- }, {
- icon: 'fas fa-globe',
- text: i18n.ts.relays,
- to: '/admin/relays',
- active: page.value === 'relays',
- }, {
- icon: 'fas fa-share-alt',
- text: i18n.ts.integration,
- to: '/admin/integrations',
- active: page.value === 'integrations',
- }, {
- icon: 'fas fa-ban',
- text: i18n.ts.instanceBlocking,
- to: '/admin/instance-block',
- active: page.value === 'instance-block',
- }, {
- icon: 'fas fa-ghost',
- text: i18n.ts.proxyAccount,
- to: '/admin/proxy-account',
- active: page.value === 'proxy-account',
- }, {
- icon: 'fas fa-cogs',
- text: i18n.ts.other,
- to: '/admin/other-settings',
- active: page.value === 'other-settings',
- }],
- }, {
- title: i18n.ts.info,
- items: [{
- icon: 'fas fa-database',
- text: i18n.ts.database,
- to: '/admin/database',
- active: page.value === 'database',
- }],
- }]);
- const component = computed(() => {
- if (page.value == null) return null;
- switch (page.value) {
- case 'overview': return defineAsyncComponent(() => import('./overview.vue'));
- case 'users': return defineAsyncComponent(() => import('./users.vue'));
- case 'emojis': return defineAsyncComponent(() => import('./emojis.vue'));
- case 'federation': return defineAsyncComponent(() => import('../federation.vue'));
- case 'queue': return defineAsyncComponent(() => import('./queue.vue'));
- case 'files': return defineAsyncComponent(() => import('./files.vue'));
- case 'announcements': return defineAsyncComponent(() => import('./announcements.vue'));
- case 'ads': return defineAsyncComponent(() => import('./ads.vue'));
- case 'database': return defineAsyncComponent(() => import('./database.vue'));
- case 'abuses': return defineAsyncComponent(() => import('./abuses.vue'));
- case 'settings': return defineAsyncComponent(() => import('./settings.vue'));
- case 'email-settings': return defineAsyncComponent(() => import('./email-settings.vue'));
- case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue'));
- case 'security': return defineAsyncComponent(() => import('./security.vue'));
- case 'relays': return defineAsyncComponent(() => import('./relays.vue'));
- case 'integrations': return defineAsyncComponent(() => import('./integrations.vue'));
- case 'instance-block': return defineAsyncComponent(() => import('./instance-block.vue'));
- case 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue'));
- case 'other-settings': return defineAsyncComponent(() => import('./other-settings.vue'));
- }
- });
+const menuDef = $computed(() => [{
+ title: i18n.ts.quickAction,
+ items: [{
+ type: 'button',
+ icon: 'fas fa-search',
+ text: i18n.ts.lookup,
+ action: lookup,
+ }, ...(instance.disableRegistration ? [{
+ type: 'button',
+ icon: 'fas fa-user',
+ text: i18n.ts.invite,
+ action: invite,
+ }] : [])],
+}, {
+ title: i18n.ts.administration,
+ items: [{
+ icon: 'fas fa-tachometer-alt',
+ text: i18n.ts.dashboard,
+ to: '/admin/overview',
+ active: props.initialPage === 'overview',
+ }, {
+ icon: 'fas fa-users',
+ text: i18n.ts.users,
+ to: '/admin/users',
+ active: props.initialPage === 'users',
+ }, {
+ icon: 'fas fa-laugh',
+ text: i18n.ts.customEmojis,
+ to: '/admin/emojis',
+ active: props.initialPage === 'emojis',
+ }, {
+ icon: 'fas fa-globe',
+ text: i18n.ts.federation,
+ to: '/admin/federation',
+ active: props.initialPage === 'federation',
+ }, {
+ icon: 'fas fa-clipboard-list',
+ text: i18n.ts.jobQueue,
+ to: '/admin/queue',
+ active: props.initialPage === 'queue',
+ }, {
+ icon: 'fas fa-cloud',
+ text: i18n.ts.files,
+ to: '/admin/files',
+ active: props.initialPage === 'files',
+ }, {
+ icon: 'fas fa-broadcast-tower',
+ text: i18n.ts.announcements,
+ to: '/admin/announcements',
+ active: props.initialPage === 'announcements',
+ }, {
+ icon: 'fas fa-audio-description',
+ text: i18n.ts.ads,
+ to: '/admin/ads',
+ active: props.initialPage === 'ads',
+ }, {
+ icon: 'fas fa-exclamation-circle',
+ text: i18n.ts.abuseReports,
+ to: '/admin/abuses',
+ active: props.initialPage === 'abuses',
+ }],
+}, {
+ title: i18n.ts.settings,
+ items: [{
+ icon: 'fas fa-cog',
+ text: i18n.ts.general,
+ to: '/admin/settings',
+ active: props.initialPage === 'settings',
+ }, {
+ icon: 'fas fa-envelope',
+ text: i18n.ts.emailServer,
+ to: '/admin/email-settings',
+ active: props.initialPage === 'email-settings',
+ }, {
+ icon: 'fas fa-cloud',
+ text: i18n.ts.objectStorage,
+ to: '/admin/object-storage',
+ active: props.initialPage === 'object-storage',
+ }, {
+ icon: 'fas fa-lock',
+ text: i18n.ts.security,
+ to: '/admin/security',
+ active: props.initialPage === 'security',
+ }, {
+ icon: 'fas fa-globe',
+ text: i18n.ts.relays,
+ to: '/admin/relays',
+ active: props.initialPage === 'relays',
+ }, {
+ icon: 'fas fa-share-alt',
+ text: i18n.ts.integration,
+ to: '/admin/integrations',
+ active: props.initialPage === 'integrations',
+ }, {
+ icon: 'fas fa-ban',
+ text: i18n.ts.instanceBlocking,
+ to: '/admin/instance-block',
+ active: props.initialPage === 'instance-block',
+ }, {
+ icon: 'fas fa-ghost',
+ text: i18n.ts.proxyAccount,
+ to: '/admin/proxy-account',
+ active: props.initialPage === 'proxy-account',
+ }, {
+ icon: 'fas fa-cogs',
+ text: i18n.ts.other,
+ to: '/admin/other-settings',
+ active: props.initialPage === 'other-settings',
+ }],
+}, {
+ title: i18n.ts.info,
+ items: [{
+ icon: 'fas fa-database',
+ text: i18n.ts.database,
+ to: '/admin/database',
+ active: props.initialPage === 'database',
+ }],
+}]);
- watch(component, () => {
- pageProps.value = {};
+const component = $computed(() => {
+ if (props.initialPage == null) return null;
+ switch (props.initialPage) {
+ case 'overview': return defineAsyncComponent(() => import('./overview.vue'));
+ case 'users': return defineAsyncComponent(() => import('./users.vue'));
+ case 'emojis': return defineAsyncComponent(() => import('./emojis.vue'));
+ case 'federation': return defineAsyncComponent(() => import('../federation.vue'));
+ case 'queue': return defineAsyncComponent(() => import('./queue.vue'));
+ case 'files': return defineAsyncComponent(() => import('./files.vue'));
+ case 'announcements': return defineAsyncComponent(() => import('./announcements.vue'));
+ case 'ads': return defineAsyncComponent(() => import('./ads.vue'));
+ case 'database': return defineAsyncComponent(() => import('./database.vue'));
+ case 'abuses': return defineAsyncComponent(() => import('./abuses.vue'));
+ case 'settings': return defineAsyncComponent(() => import('./settings.vue'));
+ case 'email-settings': return defineAsyncComponent(() => import('./email-settings.vue'));
+ case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue'));
+ case 'security': return defineAsyncComponent(() => import('./security.vue'));
+ case 'relays': return defineAsyncComponent(() => import('./relays.vue'));
+ case 'integrations': return defineAsyncComponent(() => import('./integrations.vue'));
+ case 'instance-block': return defineAsyncComponent(() => import('./instance-block.vue'));
+ case 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue'));
+ case 'other-settings': return defineAsyncComponent(() => import('./other-settings.vue'));
+ }
+});
- nextTick(() => {
- scroll(el.value, { top: 0 });
- });
- }, { immediate: true });
+watch(component, () => {
+ pageProps = {};
- watch(() => props.initialPage, () => {
- if (props.initialPage == null && !narrow.value) {
- page.value = 'overview';
- } else {
- page.value = props.initialPage;
- if (props.initialPage == null) {
- INFO.value = indexInfo;
- }
- }
- });
+ nextTick(() => {
+ scroll(el, { top: 0 });
+ });
+}, { immediate: true });
- onMounted(() => {
- narrow.value = el.value.offsetWidth < 800;
- if (!narrow.value) {
- page.value = 'overview';
- }
- });
+watch(() => props.initialPage, () => {
+ if (props.initialPage == null && !narrow) {
+ nav.push('/admin/overview');
+ } else {
+ if (props.initialPage == null) {
+ INFO = indexInfo;
+ }
+ }
+});
- const invite = () => {
- os.api('admin/invite').then(x => {
- os.alert({
- type: 'info',
- text: x.code
- });
- }).catch(e => {
- os.alert({
- type: 'error',
- text: e
- });
- });
- };
+watch(narrow, () => {
+ if (props.initialPage == null && !narrow) {
+ nav.push('/admin/overview');
+ }
+});
- const lookup = (ev) => {
- os.popupMenu([{
- text: i18n.ts.user,
- icon: 'fas fa-user',
- action: () => {
- lookupUser();
- }
- }, {
- text: i18n.ts.note,
- icon: 'fas fa-pencil-alt',
- action: () => {
- alert('TODO');
- }
- }, {
- text: i18n.ts.file,
- icon: 'fas fa-cloud',
- action: () => {
- alert('TODO');
- }
- }, {
- text: i18n.ts.instance,
- icon: 'fas fa-globe',
- action: () => {
- alert('TODO');
- }
- }], ev.currentTarget ?? ev.target);
- };
+onMounted(() => {
+ ro.observe(el);
+
+ narrow = el.offsetWidth < NARROW_THRESHOLD;
+ if (props.initialPage == null && !narrow) {
+ nav.push('/admin/overview');
+ }
+});
+
+onUnmounted(() => {
+ ro.disconnect();
+});
+
+const pageChanged = (page) => {
+ if (page == null) {
+ childInfo = null;
+ } else {
+ childInfo = page[symbols.PAGE_INFO];
+ }
+};
- return {
- [symbols.PAGE_INFO]: INFO,
- menuDef,
- header: {
- title: i18n.ts.controlPanel,
- },
- noMaintainerInformation,
- noBotProtection,
- page,
- narrow,
- view,
- el,
- pageChanged,
- childInfo,
- pageProps,
- component,
- invite,
- lookup,
- };
- },
+const invite = () => {
+ os.api('admin/invite').then(x => {
+ os.alert({
+ type: 'info',
+ text: x.code
+ });
+ }).catch(err => {
+ os.alert({
+ type: 'error',
+ text: err,
+ });
+ });
+};
+
+const lookup = (ev) => {
+ os.popupMenu([{
+ text: i18n.ts.user,
+ icon: 'fas fa-user',
+ action: () => {
+ lookupUser();
+ }
+ }, {
+ text: i18n.ts.note,
+ icon: 'fas fa-pencil-alt',
+ action: () => {
+ alert('TODO');
+ }
+ }, {
+ text: i18n.ts.file,
+ icon: 'fas fa-cloud',
+ action: () => {
+ alert('TODO');
+ }
+ }, {
+ text: i18n.ts.instance,
+ icon: 'fas fa-globe',
+ action: () => {
+ alert('TODO');
+ }
+ }], ev.currentTarget ?? ev.target);
+};
+
+defineExpose({
+ [symbols.PAGE_INFO]: INFO,
+ header: {
+ title: i18n.ts.controlPanel,
+ }
});
</script>
diff --git a/packages/client/src/pages/admin/instance-block.vue b/packages/client/src/pages/admin/instance-block.vue
index 4cb8dc604e..3347846a80 100644
--- a/packages/client/src/pages/admin/instance-block.vue
+++ b/packages/client/src/pages/admin/instance-block.vue
@@ -2,57 +2,45 @@
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
<FormTextarea v-model="blockedHosts" class="_formBlock">
- <span>{{ $ts.blockedInstances }}</span>
- <template #caption>{{ $ts.blockedInstancesDescription }}</template>
+ <span>{{ i18n.ts.blockedInstances }}</span>
+ <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template>
</FormTextarea>
- <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton>
</FormSuspense>
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormButton from '@/components/ui/button.vue';
import FormTextarea from '@/components/form/textarea.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormButton,
- FormTextarea,
- FormSuspense,
- },
+let blockedHosts: string = $ref('');
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ blockedHosts = meta.blockedHosts.join('\n');
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.instanceBlocking,
- icon: 'fas fa-ban',
- bg: 'var(--bg)',
- },
- blockedHosts: '',
- }
- },
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ blockedHosts: blockedHosts.split('\n') || [],
+ }).then(() => {
+ fetchInstance();
+ });
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.blockedHosts = meta.blockedHosts.join('\n');
- },
-
- save() {
- os.apiWithDialog('admin/update-meta', {
- blockedHosts: this.blockedHosts.split('\n') || [],
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.instanceBlocking,
+ icon: 'fas fa-ban',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/integrations.discord.vue b/packages/client/src/pages/admin/integrations.discord.vue
index 6b50f1b0a9..9fdc51a6ca 100644
--- a/packages/client/src/pages/admin/integrations.discord.vue
+++ b/packages/client/src/pages/admin/integrations.discord.vue
@@ -24,57 +24,36 @@
</FormSuspense>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/ui/button.vue';
import FormInfo from '@/components/ui/info.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
-import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormInfo,
- FormButton,
- FormSuspense,
- },
+let uri: string = $ref('');
+let enableDiscordIntegration: boolean = $ref(false);
+let discordClientId: string | null = $ref(null);
+let discordClientSecret: string | null = $ref(null);
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ uri = meta.uri;
+ enableDiscordIntegration = meta.enableDiscordIntegration;
+ discordClientId = meta.discordClientId;
+ discordClientSecret = meta.discordClientSecret;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: 'Discord',
- icon: 'fab fa-discord'
- },
- enableDiscordIntegration: false,
- discordClientId: null,
- discordClientSecret: null,
- }
- },
-
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.uri = meta.uri;
- this.enableDiscordIntegration = meta.enableDiscordIntegration;
- this.discordClientId = meta.discordClientId;
- this.discordClientSecret = meta.discordClientSecret;
- },
- save() {
- os.apiWithDialog('admin/update-meta', {
- enableDiscordIntegration: this.enableDiscordIntegration,
- discordClientId: this.discordClientId,
- discordClientSecret: this.discordClientSecret,
- }).then(() => {
- fetchInstance();
- });
- }
- }
-});
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ enableDiscordIntegration,
+ discordClientId,
+ discordClientSecret,
+ }).then(() => {
+ fetchInstance();
+ });
+}
</script>
diff --git a/packages/client/src/pages/admin/integrations.github.vue b/packages/client/src/pages/admin/integrations.github.vue
index 67f299e1bc..b10ccb8394 100644
--- a/packages/client/src/pages/admin/integrations.github.vue
+++ b/packages/client/src/pages/admin/integrations.github.vue
@@ -24,57 +24,36 @@
</FormSuspense>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/ui/button.vue';
import FormInfo from '@/components/ui/info.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
-import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormInfo,
- FormButton,
- FormSuspense,
- },
+let uri: string = $ref('');
+let enableGithubIntegration: boolean = $ref(false);
+let githubClientId: string | null = $ref(null);
+let githubClientSecret: string | null = $ref(null);
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ uri = meta.uri;
+ enableGithubIntegration = meta.enableGithubIntegration;
+ githubClientId = meta.githubClientId;
+ githubClientSecret = meta.githubClientSecret;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: 'GitHub',
- icon: 'fab fa-github'
- },
- enableGithubIntegration: false,
- githubClientId: null,
- githubClientSecret: null,
- }
- },
-
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.uri = meta.uri;
- this.enableGithubIntegration = meta.enableGithubIntegration;
- this.githubClientId = meta.githubClientId;
- this.githubClientSecret = meta.githubClientSecret;
- },
- save() {
- os.apiWithDialog('admin/update-meta', {
- enableGithubIntegration: this.enableGithubIntegration,
- githubClientId: this.githubClientId,
- githubClientSecret: this.githubClientSecret,
- }).then(() => {
- fetchInstance();
- });
- }
- }
-});
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ enableGithubIntegration,
+ githubClientId,
+ githubClientSecret,
+ }).then(() => {
+ fetchInstance();
+ });
+}
</script>
diff --git a/packages/client/src/pages/admin/integrations.twitter.vue b/packages/client/src/pages/admin/integrations.twitter.vue
index a389c71506..11b5fd86b2 100644
--- a/packages/client/src/pages/admin/integrations.twitter.vue
+++ b/packages/client/src/pages/admin/integrations.twitter.vue
@@ -24,7 +24,7 @@
</FormSuspense>
</template>
-<script lang="ts">
+<script lang="ts" setup>
import { defineComponent } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue';
@@ -32,49 +32,28 @@ import FormButton from '@/components/ui/button.vue';
import FormInfo from '@/components/ui/info.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
-import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormInfo,
- FormButton,
- FormSuspense,
- },
+let uri: string = $ref('');
+let enableTwitterIntegration: boolean = $ref(false);
+let twitterConsumerKey: string | null = $ref(null);
+let twitterConsumerSecret: string | null = $ref(null);
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ uri = meta.uri;
+ enableTwitterIntegration = meta.enableTwitterIntegration;
+ twitterConsumerKey = meta.twitterConsumerKey;
+ twitterConsumerSecret = meta.twitterConsumerSecret;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: 'Twitter',
- icon: 'fab fa-twitter'
- },
- enableTwitterIntegration: false,
- twitterConsumerKey: null,
- twitterConsumerSecret: null,
- }
- },
-
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.uri = meta.uri;
- this.enableTwitterIntegration = meta.enableTwitterIntegration;
- this.twitterConsumerKey = meta.twitterConsumerKey;
- this.twitterConsumerSecret = meta.twitterConsumerSecret;
- },
- save() {
- os.apiWithDialog('admin/update-meta', {
- enableTwitterIntegration: this.enableTwitterIntegration,
- twitterConsumerKey: this.twitterConsumerKey,
- twitterConsumerSecret: this.twitterConsumerSecret,
- }).then(() => {
- fetchInstance();
- });
- }
- }
-});
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ enableTwitterIntegration,
+ twitterConsumerKey,
+ twitterConsumerSecret,
+ }).then(() => {
+ fetchInstance();
+ });
+}
</script>
diff --git a/packages/client/src/pages/admin/integrations.vue b/packages/client/src/pages/admin/integrations.vue
index 4db8a9e0a9..d6061d0e51 100644
--- a/packages/client/src/pages/admin/integrations.vue
+++ b/packages/client/src/pages/admin/integrations.vue
@@ -4,69 +4,52 @@
<FormFolder class="_formBlock">
<template #icon><i class="fab fa-twitter"></i></template>
<template #label>Twitter</template>
- <template #suffix>{{ enableTwitterIntegration ? $ts.enabled : $ts.disabled }}</template>
+ <template #suffix>{{ enableTwitterIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template>
<XTwitter/>
</FormFolder>
- <FormFolder to="/admin/integrations/github" class="_formBlock">
+ <FormFolder class="_formBlock">
<template #icon><i class="fab fa-github"></i></template>
<template #label>GitHub</template>
- <template #suffix>{{ enableGithubIntegration ? $ts.enabled : $ts.disabled }}</template>
+ <template #suffix>{{ enableGithubIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template>
<XGithub/>
</FormFolder>
- <FormFolder to="/admin/integrations/discord" class="_formBlock">
+ <FormFolder class="_formBlock">
<template #icon><i class="fab fa-discord"></i></template>
<template #label>Discord</template>
- <template #suffix>{{ enableDiscordIntegration ? $ts.enabled : $ts.disabled }}</template>
+ <template #suffix>{{ enableDiscordIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template>
<XDiscord/>
</FormFolder>
</FormSuspense>
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormFolder from '@/components/form/folder.vue';
-import FormSecion from '@/components/form/section.vue';
import FormSuspense from '@/components/form/suspense.vue';
import XTwitter from './integrations.twitter.vue';
import XGithub from './integrations.github.vue';
import XDiscord from './integrations.discord.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
-import { fetchInstance } from '@/instance';
-
-export default defineComponent({
- components: {
- FormFolder,
- FormSecion,
- FormSuspense,
- XTwitter,
- XGithub,
- XDiscord,
- },
+import { i18n } from '@/i18n';
- emits: ['info'],
+let enableTwitterIntegration: boolean = $ref(false);
+let enableGithubIntegration: boolean = $ref(false);
+let enableDiscordIntegration: boolean = $ref(false);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.integration,
- icon: 'fas fa-share-alt',
- bg: 'var(--bg)',
- },
- enableTwitterIntegration: false,
- enableGithubIntegration: false,
- enableDiscordIntegration: false,
- }
- },
+async function init() {
+ const meta = await os.api('admin/meta');
+ enableTwitterIntegration = meta.enableTwitterIntegration;
+ enableGithubIntegration = meta.enableGithubIntegration;
+ enableDiscordIntegration = meta.enableDiscordIntegration;
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.enableTwitterIntegration = meta.enableTwitterIntegration;
- this.enableGithubIntegration = meta.enableGithubIntegration;
- this.enableDiscordIntegration = meta.enableDiscordIntegration;
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.integration,
+ icon: 'fas fa-share-alt',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/object-storage.vue b/packages/client/src/pages/admin/object-storage.vue
index a1ee0761c8..d109db9c38 100644
--- a/packages/client/src/pages/admin/object-storage.vue
+++ b/packages/client/src/pages/admin/object-storage.vue
@@ -2,32 +2,32 @@
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
<div class="_formRoot">
- <FormSwitch v-model="useObjectStorage" class="_formBlock">{{ $ts.useObjectStorage }}</FormSwitch>
+ <FormSwitch v-model="useObjectStorage" class="_formBlock">{{ i18n.ts.useObjectStorage }}</FormSwitch>
<template v-if="useObjectStorage">
<FormInput v-model="objectStorageBaseUrl" class="_formBlock">
- <template #label>{{ $ts.objectStorageBaseUrl }}</template>
- <template #caption>{{ $ts.objectStorageBaseUrlDesc }}</template>
+ <template #label>{{ i18n.ts.objectStorageBaseUrl }}</template>
+ <template #caption>{{ i18n.ts.objectStorageBaseUrlDesc }}</template>
</FormInput>
<FormInput v-model="objectStorageBucket" class="_formBlock">
- <template #label>{{ $ts.objectStorageBucket }}</template>
- <template #caption>{{ $ts.objectStorageBucketDesc }}</template>
+ <template #label>{{ i18n.ts.objectStorageBucket }}</template>
+ <template #caption>{{ i18n.ts.objectStorageBucketDesc }}</template>
</FormInput>
<FormInput v-model="objectStoragePrefix" class="_formBlock">
- <template #label>{{ $ts.objectStoragePrefix }}</template>
- <template #caption>{{ $ts.objectStoragePrefixDesc }}</template>
+ <template #label>{{ i18n.ts.objectStoragePrefix }}</template>
+ <template #caption>{{ i18n.ts.objectStoragePrefixDesc }}</template>
</FormInput>
<FormInput v-model="objectStorageEndpoint" class="_formBlock">
- <template #label>{{ $ts.objectStorageEndpoint }}</template>
- <template #caption>{{ $ts.objectStorageEndpointDesc }}</template>
+ <template #label>{{ i18n.ts.objectStorageEndpoint }}</template>
+ <template #caption>{{ i18n.ts.objectStorageEndpointDesc }}</template>
</FormInput>
<FormInput v-model="objectStorageRegion" class="_formBlock">
- <template #label>{{ $ts.objectStorageRegion }}</template>
- <template #caption>{{ $ts.objectStorageRegionDesc }}</template>
+ <template #label>{{ i18n.ts.objectStorageRegion }}</template>
+ <template #caption>{{ i18n.ts.objectStorageRegionDesc }}</template>
</FormInput>
<FormSplit :min-width="280">
@@ -43,17 +43,17 @@
</FormSplit>
<FormSwitch v-model="objectStorageUseSSL" class="_formBlock">
- <template #label>{{ $ts.objectStorageUseSSL }}</template>
- <template #caption>{{ $ts.objectStorageUseSSLDesc }}</template>
+ <template #label>{{ i18n.ts.objectStorageUseSSL }}</template>
+ <template #caption>{{ i18n.ts.objectStorageUseSSLDesc }}</template>
</FormSwitch>
<FormSwitch v-model="objectStorageUseProxy" class="_formBlock">
- <template #label>{{ $ts.objectStorageUseProxy }}</template>
- <template #caption>{{ $ts.objectStorageUseProxyDesc }}</template>
+ <template #label>{{ i18n.ts.objectStorageUseProxy }}</template>
+ <template #caption>{{ i18n.ts.objectStorageUseProxyDesc }}</template>
</FormSwitch>
<FormSwitch v-model="objectStorageSetPublicRead" class="_formBlock">
- <template #label>{{ $ts.objectStorageSetPublicRead }}</template>
+ <template #label>{{ i18n.ts.objectStorageSetPublicRead }}</template>
</FormSwitch>
<FormSwitch v-model="objectStorageS3ForcePathStyle" class="_formBlock">
@@ -65,8 +65,8 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue';
import FormGroup from '@/components/form/group.vue';
@@ -76,84 +76,70 @@ import FormSection from '@/components/form/section.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormGroup,
- FormSuspense,
- FormSplit,
- FormSection,
- },
+let useObjectStorage: boolean = $ref(false);
+let objectStorageBaseUrl: string | null = $ref(null);
+let objectStorageBucket: string | null = $ref(null);
+let objectStoragePrefix: string | null = $ref(null);
+let objectStorageEndpoint: string | null = $ref(null);
+let objectStorageRegion: string | null = $ref(null);
+let objectStoragePort: number | null = $ref(null);
+let objectStorageAccessKey: string | null = $ref(null);
+let objectStorageSecretKey: string | null = $ref(null);
+let objectStorageUseSSL: boolean = $ref(false);
+let objectStorageUseProxy: boolean = $ref(false);
+let objectStorageSetPublicRead: boolean = $ref(false);
+let objectStorageS3ForcePathStyle: boolean = $ref(true);
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ useObjectStorage = meta.useObjectStorage;
+ objectStorageBaseUrl = meta.objectStorageBaseUrl;
+ objectStorageBucket = meta.objectStorageBucket;
+ objectStoragePrefix = meta.objectStoragePrefix;
+ objectStorageEndpoint = meta.objectStorageEndpoint;
+ objectStorageRegion = meta.objectStorageRegion;
+ objectStoragePort = meta.objectStoragePort;
+ objectStorageAccessKey = meta.objectStorageAccessKey;
+ objectStorageSecretKey = meta.objectStorageSecretKey;
+ objectStorageUseSSL = meta.objectStorageUseSSL;
+ objectStorageUseProxy = meta.objectStorageUseProxy;
+ objectStorageSetPublicRead = meta.objectStorageSetPublicRead;
+ objectStorageS3ForcePathStyle = meta.objectStorageS3ForcePathStyle;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.objectStorage,
- icon: 'fas fa-cloud',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-check',
- text: this.$ts.save,
- handler: this.save,
- }],
- },
- useObjectStorage: false,
- objectStorageBaseUrl: null,
- objectStorageBucket: null,
- objectStoragePrefix: null,
- objectStorageEndpoint: null,
- objectStorageRegion: null,
- objectStoragePort: null,
- objectStorageAccessKey: null,
- objectStorageSecretKey: null,
- objectStorageUseSSL: false,
- objectStorageUseProxy: false,
- objectStorageSetPublicRead: false,
- objectStorageS3ForcePathStyle: true,
- }
- },
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ useObjectStorage,
+ objectStorageBaseUrl,
+ objectStorageBucket,
+ objectStoragePrefix,
+ objectStorageEndpoint,
+ objectStorageRegion,
+ objectStoragePort,
+ objectStorageAccessKey,
+ objectStorageSecretKey,
+ objectStorageUseSSL,
+ objectStorageUseProxy,
+ objectStorageSetPublicRead,
+ objectStorageS3ForcePathStyle,
+ }).then(() => {
+ fetchInstance();
+ });
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.useObjectStorage = meta.useObjectStorage;
- this.objectStorageBaseUrl = meta.objectStorageBaseUrl;
- this.objectStorageBucket = meta.objectStorageBucket;
- this.objectStoragePrefix = meta.objectStoragePrefix;
- this.objectStorageEndpoint = meta.objectStorageEndpoint;
- this.objectStorageRegion = meta.objectStorageRegion;
- this.objectStoragePort = meta.objectStoragePort;
- this.objectStorageAccessKey = meta.objectStorageAccessKey;
- this.objectStorageSecretKey = meta.objectStorageSecretKey;
- this.objectStorageUseSSL = meta.objectStorageUseSSL;
- this.objectStorageUseProxy = meta.objectStorageUseProxy;
- this.objectStorageSetPublicRead = meta.objectStorageSetPublicRead;
- this.objectStorageS3ForcePathStyle = meta.objectStorageS3ForcePathStyle;
- },
- save() {
- os.apiWithDialog('admin/update-meta', {
- useObjectStorage: this.useObjectStorage,
- objectStorageBaseUrl: this.objectStorageBaseUrl ? this.objectStorageBaseUrl : null,
- objectStorageBucket: this.objectStorageBucket ? this.objectStorageBucket : null,
- objectStoragePrefix: this.objectStoragePrefix ? this.objectStoragePrefix : null,
- objectStorageEndpoint: this.objectStorageEndpoint ? this.objectStorageEndpoint : null,
- objectStorageRegion: this.objectStorageRegion ? this.objectStorageRegion : null,
- objectStoragePort: this.objectStoragePort ? this.objectStoragePort : null,
- objectStorageAccessKey: this.objectStorageAccessKey ? this.objectStorageAccessKey : null,
- objectStorageSecretKey: this.objectStorageSecretKey ? this.objectStorageSecretKey : null,
- objectStorageUseSSL: this.objectStorageUseSSL,
- objectStorageUseProxy: this.objectStorageUseProxy,
- objectStorageSetPublicRead: this.objectStorageSetPublicRead,
- objectStorageS3ForcePathStyle: this.objectStorageS3ForcePathStyle,
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.objectStorage,
+ icon: 'fas fa-cloud',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-check',
+ text: i18n.ts.save,
+ handler: save,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/other-settings.vue b/packages/client/src/pages/admin/other-settings.vue
index 99ea6a5f32..552b05f347 100644
--- a/packages/client/src/pages/admin/other-settings.vue
+++ b/packages/client/src/pages/admin/other-settings.vue
@@ -6,52 +6,35 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-import FormSwitch from '@/components/form/switch.vue';
-import FormInput from '@/components/form/input.vue';
-import FormSection from '@/components/form/section.vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormSection,
- FormSuspense,
- },
+async function init() {
+ await os.api('admin/meta');
+}
- emits: ['info'],
+function save() {
+ os.apiWithDialog('admin/update-meta').then(() => {
+ fetchInstance();
+ });
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.other,
- icon: 'fas fa-cogs',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-check',
- text: this.$ts.save,
- handler: this.save,
- }],
- },
- }
- },
-
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- },
- save() {
- os.apiWithDialog('admin/update-meta', {
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.other,
+ icon: 'fas fa-cogs',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-check',
+ text: i18n.ts.save,
+ handler: save,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/overview.vue b/packages/client/src/pages/admin/overview.vue
index b8ae8ad9e1..cc69424c3b 100644
--- a/packages/client/src/pages/admin/overview.vue
+++ b/packages/client/src/pages/admin/overview.vue
@@ -5,20 +5,20 @@
<div class="label">Users</div>
<div class="value _monospace">
{{ number(stats.originalUsersCount) }}
- <MkNumberDiff v-if="usersComparedToThePrevDay != null" v-tooltip="$ts.dayOverDayChanges" class="diff" :value="usersComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff>
+ <MkNumberDiff v-if="usersComparedToThePrevDay != null" v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="usersComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff>
</div>
</div>
<div class="number _panel">
<div class="label">Notes</div>
<div class="value _monospace">
{{ number(stats.originalNotesCount) }}
- <MkNumberDiff v-if="notesComparedToThePrevDay != null" v-tooltip="$ts.dayOverDayChanges" class="diff" :value="notesComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff>
+ <MkNumberDiff v-if="notesComparedToThePrevDay != null" v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="notesComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff>
</div>
</div>
</div>
<MkContainer :foldable="true" class="charts">
- <template #header><i class="fas fa-chart-bar"></i>{{ $ts.charts }}</template>
+ <template #header><i class="fas fa-chart-bar"></i>{{ i18n.ts.charts }}</template>
<div style="padding: 12px;">
<MkInstanceStats :chart-limit="500" :detailed="true"/>
</div>
@@ -38,7 +38,7 @@
<!--<XMetrics/>-->
<MkFolder style="margin: var(--margin)">
- <template #header><i class="fas fa-info-circle"></i> {{ $ts.info }}</template>
+ <template #header><i class="fas fa-info-circle"></i> {{ i18n.ts.info }}</template>
<div class="cfcdecdf">
<div class="number _panel">
<div class="label">Misskey</div>
@@ -65,103 +65,61 @@
</div>
</template>
-<script lang="ts">
-import { computed, defineComponent, markRaw, version as vueVersion } from 'vue';
+<script lang="ts" setup>
+import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue';
import MkInstanceStats from '@/components/instance-stats.vue';
-import MkButton from '@/components/ui/button.vue';
-import MkSelect from '@/components/form/select.vue';
import MkNumberDiff from '@/components/number-diff.vue';
import MkContainer from '@/components/ui/container.vue';
import MkFolder from '@/components/ui/folder.vue';
import MkQueueChart from '@/components/queue-chart.vue';
import { version, url } from '@/config';
-import bytes from '@/filters/bytes';
import number from '@/filters/number';
import XMetrics from './metrics.vue';
import * as os from '@/os';
import { stream } from '@/stream';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkNumberDiff,
- MkInstanceStats,
- MkContainer,
- MkFolder,
- MkQueueChart,
- XMetrics,
- },
+let stats: any = $ref(null);
+let serverInfo: any = $ref(null);
+let usersComparedToThePrevDay: any = $ref(null);
+let notesComparedToThePrevDay: any = $ref(null);
+const queueStatsConnection = markRaw(stream.useChannel('queueStats'));
- emits: ['info'],
+onMounted(async () => {
+ os.api('stats', {}).then(statsResponse => {
+ stats = statsResponse;
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.dashboard,
- icon: 'fas fa-tachometer-alt',
- bg: 'var(--bg)',
- },
- version,
- vueVersion,
- url,
- stats: null,
- meta: null,
- serverInfo: null,
- usersComparedToThePrevDay: null,
- notesComparedToThePrevDay: null,
- fetchJobs: () => os.api('admin/queue/deliver-delayed', {}),
- fetchModLogs: () => os.api('admin/show-moderation-logs', {}),
- queueStatsConnection: markRaw(stream.useChannel('queueStats')),
- }
- },
-
- async mounted() {
- os.api('meta', { detail: true }).then(meta => {
- this.meta = meta;
+ os.api('charts/users', { limit: 2, span: 'day' }).then(chart => {
+ usersComparedToThePrevDay = stats.originalUsersCount - chart.local.total[1];
});
-
- os.api('stats', {}).then(stats => {
- this.stats = stats;
-
- os.api('charts/users', { limit: 2, span: 'day' }).then(chart => {
- this.usersComparedToThePrevDay = this.stats.originalUsersCount - chart.local.total[1];
- });
- os.api('charts/notes', { limit: 2, span: 'day' }).then(chart => {
- this.notesComparedToThePrevDay = this.stats.originalNotesCount - chart.local.total[1];
- });
+ os.api('charts/notes', { limit: 2, span: 'day' }).then(chart => {
+ notesComparedToThePrevDay = stats.originalNotesCount - chart.local.total[1];
});
+ });
- os.api('admin/server-info', {}).then(serverInfo => {
- this.serverInfo = serverInfo;
- });
+ os.api('admin/server-info').then(serverInfoResponse => {
+ serverInfo = serverInfoResponse;
+ });
- this.$nextTick(() => {
- this.queueStatsConnection.send('requestLog', {
- id: Math.random().toString().substr(2, 8),
- length: 200
- });
+ nextTick(() => {
+ queueStatsConnection.send('requestLog', {
+ id: Math.random().toString().substr(2, 8),
+ length: 200
});
- },
-
- beforeUnmount() {
- this.queueStatsConnection.dispose();
- },
-
- methods: {
- async showInstanceInfo(q) {
- let instance = q;
- if (typeof q === 'string') {
- instance = await os.api('federation/show-instance', {
- host: q
- });
- }
- // TODO
- },
+ });
+});
- bytes,
+onBeforeUnmount(() => {
+ queueStatsConnection.dispose();
+});
- number,
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.dashboard,
+ icon: 'fas fa-tachometer-alt',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/proxy-account.vue b/packages/client/src/pages/admin/proxy-account.vue
index 00f14a176f..727e20e7e5 100644
--- a/packages/client/src/pages/admin/proxy-account.vue
+++ b/packages/client/src/pages/admin/proxy-account.vue
@@ -1,19 +1,19 @@
<template>
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
<FormSuspense :p="init">
- <MkInfo class="_formBlock">{{ $ts.proxyAccountDescription }}</MkInfo>
+ <MkInfo class="_formBlock">{{ i18n.ts.proxyAccountDescription }}</MkInfo>
<MkKeyValue class="_formBlock">
- <template #key>{{ $ts.proxyAccount }}</template>
- <template #value>{{ proxyAccount ? `@${proxyAccount.username}` : $ts.none }}</template>
+ <template #key>{{ i18n.ts.proxyAccount }}</template>
+ <template #value>{{ proxyAccount ? `@${proxyAccount.username}` : i18n.ts.none }}</template>
</MkKeyValue>
- <FormButton primary class="_formBlock" @click="chooseProxyAccount">{{ $ts.selectAccount }}</FormButton>
+ <FormButton primary class="_formBlock" @click="chooseProxyAccount">{{ i18n.ts.selectAccount }}</FormButton>
</FormSuspense>
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import MkKeyValue from '@/components/key-value.vue';
import FormButton from '@/components/ui/button.vue';
import MkInfo from '@/components/ui/info.vue';
@@ -21,53 +21,40 @@ import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkKeyValue,
- FormButton,
- MkInfo,
- FormSuspense,
- },
+let proxyAccount: any = $ref(null);
+let proxyAccountId: any = $ref(null);
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.proxyAccount,
- icon: 'fas fa-ghost',
- bg: 'var(--bg)',
- },
- proxyAccount: null,
- proxyAccountId: null,
- }
- },
+async function init() {
+ const meta = await os.api('admin/meta');
+ proxyAccountId = meta.proxyAccountId;
+ if (proxyAccountId) {
+ proxyAccount = await os.api('users/show', { userId: proxyAccountId });
+ }
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.proxyAccountId = meta.proxyAccountId;
- if (this.proxyAccountId) {
- this.proxyAccount = await os.api('users/show', { userId: this.proxyAccountId });
- }
- },
+function chooseProxyAccount() {
+ os.selectUser().then(user => {
+ proxyAccount = user;
+ proxyAccountId = user.id;
+ save();
+ });
+}
- chooseProxyAccount() {
- os.selectUser().then(user => {
- this.proxyAccount = user;
- this.proxyAccountId = user.id;
- this.save();
- });
- },
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ proxyAccountId: proxyAccountId,
+ }).then(() => {
+ fetchInstance();
+ });
+}
- save() {
- os.apiWithDialog('admin/update-meta', {
- proxyAccountId: this.proxyAccountId,
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.proxyAccount,
+ icon: 'fas fa-ghost',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/queue.chart.vue b/packages/client/src/pages/admin/queue.chart.vue
index 136fb63bb6..be63830bdd 100644
--- a/packages/client/src/pages/admin/queue.chart.vue
+++ b/packages/client/src/pages/admin/queue.chart.vue
@@ -26,62 +26,40 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent, markRaw, onMounted, onUnmounted, ref } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onUnmounted, ref } from 'vue';
import number from '@/filters/number';
import MkQueueChart from '@/components/queue-chart.vue';
import * as os from '@/os';
-export default defineComponent({
- components: {
- MkQueueChart
- },
+const activeSincePrevTick = ref(0);
+const active = ref(0);
+const waiting = ref(0);
+const delayed = ref(0);
+const jobs = ref([]);
- props: {
- domain: {
- type: String,
- required: true,
- },
- connection: {
- required: true,
- },
- },
+const props = defineProps<{
+ domain: string,
+ connection: any,
+}>();
- setup(props) {
- const activeSincePrevTick = ref(0);
- const active = ref(0);
- const waiting = ref(0);
- const delayed = ref(0);
- const jobs = ref([]);
+onMounted(() => {
+ os.api(props.domain === 'inbox' ? 'admin/queue/inbox-delayed' : props.domain === 'deliver' ? 'admin/queue/deliver-delayed' : null, {}).then(result => {
+ jobs.value = result;
+ });
- onMounted(() => {
- os.api(props.domain === 'inbox' ? 'admin/queue/inbox-delayed' : props.domain === 'deliver' ? 'admin/queue/deliver-delayed' : null, {}).then(result => {
- jobs.value = result;
- });
+ const onStats = (stats) => {
+ activeSincePrevTick.value = stats[props.domain].activeSincePrevTick;
+ active.value = stats[props.domain].active;
+ waiting.value = stats[props.domain].waiting;
+ delayed.value = stats[props.domain].delayed;
+ };
- const onStats = (stats) => {
- activeSincePrevTick.value = stats[props.domain].activeSincePrevTick;
- active.value = stats[props.domain].active;
- waiting.value = stats[props.domain].waiting;
- delayed.value = stats[props.domain].delayed;
- };
+ props.connection.on('stats', onStats);
- props.connection.on('stats', onStats);
-
- onUnmounted(() => {
- props.connection.off('stats', onStats);
- });
- });
-
- return {
- jobs,
- activeSincePrevTick,
- active,
- waiting,
- delayed,
- number,
- };
- },
+ onUnmounted(() => {
+ props.connection.off('stats', onStats);
+ });
});
</script>
diff --git a/packages/client/src/pages/admin/queue.vue b/packages/client/src/pages/admin/queue.vue
index 35fd618c82..e05098082a 100644
--- a/packages/client/src/pages/admin/queue.vue
+++ b/packages/client/src/pages/admin/queue.vue
@@ -6,71 +6,60 @@
<XQueue :connection="connection" domain="deliver">
<template #title>Out</template>
</XQueue>
- <MkButton danger @click="clear()"><i class="fas fa-trash-alt"></i> {{ $ts.clearQueue }}</MkButton>
+ <MkButton danger @click="clear()"><i class="fas fa-trash-alt"></i> {{ i18n.ts.clearQueue }}</MkButton>
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent, markRaw } from 'vue';
+<script lang="ts" setup>
+import { markRaw, onMounted, onBeforeUnmount, nextTick } from 'vue';
import MkButton from '@/components/ui/button.vue';
import XQueue from './queue.chart.vue';
import * as os from '@/os';
import { stream } from '@/stream';
import * as symbols from '@/symbols';
import * as config from '@/config';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- XQueue,
- },
+const connection = markRaw(stream.useChannel('queueStats'))
- emits: ['info'],
+function clear() {
+ os.confirm({
+ type: 'warning',
+ title: i18n.ts.clearQueueConfirmTitle,
+ text: i18n.ts.clearQueueConfirmText,
+ }).then(({ canceled }) => {
+ if (canceled) return;
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.jobQueue,
- icon: 'fas fa-clipboard-list',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-up-right-from-square',
- text: this.$ts.dashboard,
- handler: () => {
- window.open(config.url + '/queue', '_blank');
- },
- }],
- },
- connection: markRaw(stream.useChannel('queueStats')),
- }
- },
+ os.apiWithDialog('admin/queue/clear');
+ });
+}
- mounted() {
- this.$nextTick(() => {
- this.connection.send('requestLog', {
- id: Math.random().toString().substr(2, 8),
- length: 200
- });
+onMounted(() => {
+ nextTick(() => {
+ connection.send('requestLog', {
+ id: Math.random().toString().substr(2, 8),
+ length: 200
});
- },
-
- beforeUnmount() {
- this.connection.dispose();
- },
+ });
+})
- methods: {
- clear() {
- os.confirm({
- type: 'warning',
- title: this.$ts.clearQueueConfirmTitle,
- text: this.$ts.clearQueueConfirmText,
- }).then(({ canceled }) => {
- if (canceled) return;
+onBeforeUnmount(() => {
+ connection.dispose();
+});
- os.apiWithDialog('admin/queue/clear', {});
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.jobQueue,
+ icon: 'fas fa-clipboard-list',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-up-right-from-square',
+ text: i18n.ts.dashboard,
+ handler: () => {
+ window.open(config.url + '/queue', '_blank');
+ },
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/relays.vue b/packages/client/src/pages/admin/relays.vue
index bb840db0a2..1a36bb4753 100644
--- a/packages/client/src/pages/admin/relays.vue
+++ b/packages/client/src/pages/admin/relays.vue
@@ -8,84 +8,71 @@
<i v-else class="fas fa-clock icon requesting"></i>
<span>{{ $t(`_relayStatus.${relay.status}`) }}</span>
</div>
- <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
+ <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="fas fa-trash-alt"></i> {{ i18n.ts.remove }}</MkButton>
</div>
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import MkButton from '@/components/ui/button.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- },
+let relays: any[] = $ref([]);
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.relays,
- icon: 'fas fa-globe',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-plus',
- text: this.$ts.addRelay,
- handler: this.addRelay,
- }],
- },
- relays: [],
- inbox: '',
- }
- },
+async function addRelay() {
+ const { canceled, result: inbox } = await os.inputText({
+ title: i18n.ts.addRelay,
+ type: 'url',
+ placeholder: i18n.ts.inboxUrl
+ });
+ if (canceled) return;
+ os.api('admin/relays/add', {
+ inbox
+ }).then((relay: any) => {
+ refresh();
+ }).catch((err: any) => {
+ os.alert({
+ type: 'error',
+ text: err.message || err
+ });
+ });
+}
- created() {
- this.refresh();
- },
+function remove(inbox: string) {
+ os.api('admin/relays/remove', {
+ inbox
+ }).then(() => {
+ refresh();
+ }).catch((err: any) => {
+ os.alert({
+ type: 'error',
+ text: err.message || err
+ });
+ });
+}
- methods: {
- async addRelay() {
- const { canceled, result: inbox } = await os.inputText({
- title: this.$ts.addRelay,
- type: 'url',
- placeholder: this.$ts.inboxUrl
- });
- if (canceled) return;
- os.api('admin/relays/add', {
- inbox
- }).then((relay: any) => {
- this.refresh();
- }).catch((e: any) => {
- os.alert({
- type: 'error',
- text: e.message || e
- });
- });
- },
+function refresh() {
+ os.api('admin/relays/list').then((relayList: any) => {
+ relays = relayList;
+ });
+}
- remove(inbox: string) {
- os.api('admin/relays/remove', {
- inbox
- }).then(() => {
- this.refresh();
- }).catch((e: any) => {
- os.alert({
- type: 'error',
- text: e.message || e
- });
- });
- },
+refresh();
- refresh() {
- os.api('admin/relays/list').then((relays: any) => {
- this.relays = relays;
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.relays,
+ icon: 'fas fa-globe',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-plus',
+ text: i18n.ts.addRelay,
+ handler: addRelay,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/admin/security.vue b/packages/client/src/pages/admin/security.vue
index d1c979b3e0..6b8f70cca5 100644
--- a/packages/client/src/pages/admin/security.vue
+++ b/packages/client/src/pages/admin/security.vue
@@ -4,10 +4,10 @@
<div class="_formRoot">
<FormFolder class="_formBlock">
<template #icon><i class="fas fa-shield-alt"></i></template>
- <template #label>{{ $ts.botProtection }}</template>
+ <template #label>{{ i18n.ts.botProtection }}</template>
<template v-if="enableHcaptcha" #suffix>hCaptcha</template>
<template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template>
- <template v-else #suffix>{{ $ts.none }} ({{ $ts.notRecommended }})</template>
+ <template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template>
<XBotProtection/>
</FormFolder>
@@ -21,7 +21,7 @@
<template #label>Summaly Proxy URL</template>
</FormInput>
- <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+ <FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton>
</div>
</FormFolder>
</div>
@@ -29,8 +29,8 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineAsyncComponent, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormFolder from '@/components/form/folder.vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInfo from '@/components/ui/info.vue';
@@ -42,49 +42,32 @@ import XBotProtection from './bot-protection.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormFolder,
- FormSwitch,
- FormInfo,
- FormSection,
- FormSuspense,
- FormButton,
- FormInput,
- XBotProtection,
- },
+let summalyProxy: string = $ref('');
+let enableHcaptcha: boolean = $ref(false);
+let enableRecaptcha: boolean = $ref(false);
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ summalyProxy = meta.summalyProxy;
+ enableHcaptcha = meta.enableHcaptcha;
+ enableRecaptcha = meta.enableRecaptcha;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.security,
- icon: 'fas fa-lock',
- bg: 'var(--bg)',
- },
- summalyProxy: '',
- enableHcaptcha: false,
- enableRecaptcha: false,
- }
- },
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ summalyProxy,
+ }).then(() => {
+ fetchInstance();
+ });
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.summalyProxy = meta.summalyProxy;
- this.enableHcaptcha = meta.enableHcaptcha;
- this.enableRecaptcha = meta.enableRecaptcha;
- },
-
- save() {
- os.apiWithDialog('admin/update-meta', {
- summalyProxy: this.summalyProxy,
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.security,
+ icon: 'fas fa-lock',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/admin/settings.vue b/packages/client/src/pages/admin/settings.vue
index f2970d0459..6dc30fe50b 100644
--- a/packages/client/src/pages/admin/settings.vue
+++ b/packages/client/src/pages/admin/settings.vue
@@ -3,104 +3,104 @@
<FormSuspense :p="init">
<div class="_formRoot">
<FormInput v-model="name" class="_formBlock">
- <template #label>{{ $ts.instanceName }}</template>
+ <template #label>{{ i18n.ts.instanceName }}</template>
</FormInput>
<FormTextarea v-model="description" class="_formBlock">
- <template #label>{{ $ts.instanceDescription }}</template>
+ <template #label>{{ i18n.ts.instanceDescription }}</template>
</FormTextarea>
<FormInput v-model="tosUrl" class="_formBlock">
<template #prefix><i class="fas fa-link"></i></template>
- <template #label>{{ $ts.tosUrl }}</template>
+ <template #label>{{ i18n.ts.tosUrl }}</template>
</FormInput>
<FormSplit :min-width="300">
<FormInput v-model="maintainerName" class="_formBlock">
- <template #label>{{ $ts.maintainerName }}</template>
+ <template #label>{{ i18n.ts.maintainerName }}</template>
</FormInput>
<FormInput v-model="maintainerEmail" type="email" class="_formBlock">
<template #prefix><i class="fas fa-envelope"></i></template>
- <template #label>{{ $ts.maintainerEmail }}</template>
+ <template #label>{{ i18n.ts.maintainerEmail }}</template>
</FormInput>
</FormSplit>
<FormTextarea v-model="pinnedUsers" class="_formBlock">
- <template #label>{{ $ts.pinnedUsers }}</template>
- <template #caption>{{ $ts.pinnedUsersDescription }}</template>
+ <template #label>{{ i18n.ts.pinnedUsers }}</template>
+ <template #caption>{{ i18n.ts.pinnedUsersDescription }}</template>
</FormTextarea>
<FormSection>
<FormSwitch v-model="enableRegistration" class="_formBlock">
- <template #label>{{ $ts.enableRegistration }}</template>
+ <template #label>{{ i18n.ts.enableRegistration }}</template>
</FormSwitch>
<FormSwitch v-model="emailRequiredForSignup" class="_formBlock">
- <template #label>{{ $ts.emailRequiredForSignup }}</template>
+ <template #label>{{ i18n.ts.emailRequiredForSignup }}</template>
</FormSwitch>
</FormSection>
<FormSection>
- <FormSwitch v-model="enableLocalTimeline" class="_formBlock">{{ $ts.enableLocalTimeline }}</FormSwitch>
- <FormSwitch v-model="enableGlobalTimeline" class="_formBlock">{{ $ts.enableGlobalTimeline }}</FormSwitch>
- <FormInfo class="_formBlock">{{ $ts.disablingTimelinesInfo }}</FormInfo>
+ <FormSwitch v-model="enableLocalTimeline" class="_formBlock">{{ i18n.ts.enableLocalTimeline }}</FormSwitch>
+ <FormSwitch v-model="enableGlobalTimeline" class="_formBlock">{{ i18n.ts.enableGlobalTimeline }}</FormSwitch>
+ <FormInfo class="_formBlock">{{ i18n.ts.disablingTimelinesInfo }}</FormInfo>
</FormSection>
<FormSection>
- <template #label>{{ $ts.theme }}</template>
+ <template #label>{{ i18n.ts.theme }}</template>
<FormInput v-model="iconUrl" class="_formBlock">
<template #prefix><i class="fas fa-link"></i></template>
- <template #label>{{ $ts.iconUrl }}</template>
+ <template #label>{{ i18n.ts.iconUrl }}</template>
</FormInput>
<FormInput v-model="bannerUrl" class="_formBlock">
<template #prefix><i class="fas fa-link"></i></template>
- <template #label>{{ $ts.bannerUrl }}</template>
+ <template #label>{{ i18n.ts.bannerUrl }}</template>
</FormInput>
<FormInput v-model="backgroundImageUrl" class="_formBlock">
<template #prefix><i class="fas fa-link"></i></template>
- <template #label>{{ $ts.backgroundImageUrl }}</template>
+ <template #label>{{ i18n.ts.backgroundImageUrl }}</template>
</FormInput>
<FormInput v-model="themeColor" class="_formBlock">
<template #prefix><i class="fas fa-palette"></i></template>
- <template #label>{{ $ts.themeColor }}</template>
+ <template #label>{{ i18n.ts.themeColor }}</template>
<template #caption>#RRGGBB</template>
</FormInput>
<FormTextarea v-model="defaultLightTheme" class="_formBlock">
- <template #label>{{ $ts.instanceDefaultLightTheme }}</template>
- <template #caption>{{ $ts.instanceDefaultThemeDescription }}</template>
+ <template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template>
+ <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
</FormTextarea>
<FormTextarea v-model="defaultDarkTheme" class="_formBlock">
- <template #label>{{ $ts.instanceDefaultDarkTheme }}</template>
- <template #caption>{{ $ts.instanceDefaultThemeDescription }}</template>
+ <template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template>
+ <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
</FormTextarea>
</FormSection>
<FormSection>
- <template #label>{{ $ts.files }}</template>
+ <template #label>{{ i18n.ts.files }}</template>
<FormSwitch v-model="cacheRemoteFiles" class="_formBlock">
- <template #label>{{ $ts.cacheRemoteFiles }}</template>
- <template #caption>{{ $ts.cacheRemoteFilesDescription }}</template>
+ <template #label>{{ i18n.ts.cacheRemoteFiles }}</template>
+ <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}</template>
</FormSwitch>
<FormSplit :min-width="280">
<FormInput v-model="localDriveCapacityMb" type="number" class="_formBlock">
- <template #label>{{ $ts.driveCapacityPerLocalAccount }}</template>
+ <template #label>{{ i18n.ts.driveCapacityPerLocalAccount }}</template>
<template #suffix>MB</template>
- <template #caption>{{ $ts.inMb }}</template>
+ <template #caption>{{ i18n.ts.inMb }}</template>
</FormInput>
<FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles" class="_formBlock">
- <template #label>{{ $ts.driveCapacityPerRemoteAccount }}</template>
+ <template #label>{{ i18n.ts.driveCapacityPerRemoteAccount }}</template>
<template #suffix>MB</template>
- <template #caption>{{ $ts.inMb }}</template>
+ <template #caption>{{ i18n.ts.inMb }}</template>
</FormInput>
</FormSplit>
</FormSection>
@@ -109,8 +109,8 @@
<template #label>ServiceWorker</template>
<FormSwitch v-model="enableServiceWorker" class="_formBlock">
- <template #label>{{ $ts.enableServiceworker }}</template>
- <template #caption>{{ $ts.serviceworkerInfo }}</template>
+ <template #label>{{ i18n.ts.enableServiceworker }}</template>
+ <template #caption>{{ i18n.ts.serviceworkerInfo }}</template>
</FormSwitch>
<template v-if="enableServiceWorker">
@@ -142,8 +142,8 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormInput from '@/components/form/input.vue';
import FormTextarea from '@/components/form/textarea.vue';
@@ -154,119 +154,103 @@ import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSwitch,
- FormInput,
- FormSuspense,
- FormTextarea,
- FormInfo,
- FormSection,
- FormSplit,
- },
+let name: string | null = $ref(null);
+let description: string | null = $ref(null);
+let tosUrl: string | null = $ref(null);
+let maintainerName: string | null = $ref(null);
+let maintainerEmail: string | null = $ref(null);
+let iconUrl: string | null = $ref(null);
+let bannerUrl: string | null = $ref(null);
+let backgroundImageUrl: string | null = $ref(null);
+let themeColor: any = $ref(null);
+let defaultLightTheme: any = $ref(null);
+let defaultDarkTheme: any = $ref(null);
+let enableLocalTimeline: boolean = $ref(false);
+let enableGlobalTimeline: boolean = $ref(false);
+let pinnedUsers: string = $ref('');
+let cacheRemoteFiles: boolean = $ref(false);
+let localDriveCapacityMb: any = $ref(0);
+let remoteDriveCapacityMb: any = $ref(0);
+let enableRegistration: boolean = $ref(false);
+let emailRequiredForSignup: boolean = $ref(false);
+let enableServiceWorker: boolean = $ref(false);
+let swPublicKey: any = $ref(null);
+let swPrivateKey: any = $ref(null);
+let deeplAuthKey: string = $ref('');
+let deeplIsPro: boolean = $ref(false);
- emits: ['info'],
+async function init() {
+ const meta = await os.api('admin/meta');
+ name = meta.name;
+ description = meta.description;
+ tosUrl = meta.tosUrl;
+ iconUrl = meta.iconUrl;
+ bannerUrl = meta.bannerUrl;
+ backgroundImageUrl = meta.backgroundImageUrl;
+ themeColor = meta.themeColor;
+ defaultLightTheme = meta.defaultLightTheme;
+ defaultDarkTheme = meta.defaultDarkTheme;
+ maintainerName = meta.maintainerName;
+ maintainerEmail = meta.maintainerEmail;
+ enableLocalTimeline = !meta.disableLocalTimeline;
+ enableGlobalTimeline = !meta.disableGlobalTimeline;
+ pinnedUsers = meta.pinnedUsers.join('\n');
+ cacheRemoteFiles = meta.cacheRemoteFiles;
+ localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
+ remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
+ enableRegistration = !meta.disableRegistration;
+ emailRequiredForSignup = meta.emailRequiredForSignup;
+ enableServiceWorker = meta.enableServiceWorker;
+ swPublicKey = meta.swPublickey;
+ swPrivateKey = meta.swPrivateKey;
+ deeplAuthKey = meta.deeplAuthKey;
+ deeplIsPro = meta.deeplIsPro;
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.general,
- icon: 'fas fa-cog',
- bg: 'var(--bg)',
- actions: [{
- asFullButton: true,
- icon: 'fas fa-check',
- text: this.$ts.save,
- handler: this.save,
- }],
- },
- name: null,
- description: null,
- tosUrl: null as string | null,
- maintainerName: null,
- maintainerEmail: null,
- iconUrl: null,
- bannerUrl: null,
- backgroundImageUrl: null,
- themeColor: null,
- defaultLightTheme: null,
- defaultDarkTheme: null,
- enableLocalTimeline: false,
- enableGlobalTimeline: false,
- pinnedUsers: '',
- cacheRemoteFiles: false,
- localDriveCapacityMb: 0,
- remoteDriveCapacityMb: 0,
- enableRegistration: false,
- emailRequiredForSignup: false,
- enableServiceWorker: false,
- swPublicKey: null,
- swPrivateKey: null,
- deeplAuthKey: '',
- deeplIsPro: false,
- }
- },
+function save() {
+ os.apiWithDialog('admin/update-meta', {
+ name,
+ description,
+ tosUrl,
+ iconUrl,
+ bannerUrl,
+ backgroundImageUrl,
+ themeColor: themeColor === '' ? null : themeColor,
+ defaultLightTheme: defaultLightTheme === '' ? null : defaultLightTheme,
+ defaultDarkTheme: defaultDarkTheme === '' ? null : defaultDarkTheme,
+ maintainerName,
+ maintainerEmail,
+ disableLocalTimeline: !enableLocalTimeline,
+ disableGlobalTimeline: !enableGlobalTimeline,
+ pinnedUsers: pinnedUsers.split('\n'),
+ cacheRemoteFiles,
+ localDriveCapacityMb: parseInt(localDriveCapacityMb, 10),
+ remoteDriveCapacityMb: parseInt(remoteDriveCapacityMb, 10),
+ disableRegistration: !enableRegistration,
+ emailRequiredForSignup,
+ enableServiceWorker,
+ swPublicKey,
+ swPrivateKey,
+ deeplAuthKey,
+ deeplIsPro,
+ }).then(() => {
+ fetchInstance();
+ });
+}
- methods: {
- async init() {
- const meta = await os.api('admin/meta');
- this.name = meta.name;
- this.description = meta.description;
- this.tosUrl = meta.tosUrl;
- this.iconUrl = meta.iconUrl;
- this.bannerUrl = meta.bannerUrl;
- this.backgroundImageUrl = meta.backgroundImageUrl;
- this.themeColor = meta.themeColor;
- this.defaultLightTheme = meta.defaultLightTheme;
- this.defaultDarkTheme = meta.defaultDarkTheme;
- this.maintainerName = meta.maintainerName;
- this.maintainerEmail = meta.maintainerEmail;
- this.enableLocalTimeline = !meta.disableLocalTimeline;
- this.enableGlobalTimeline = !meta.disableGlobalTimeline;
- this.pinnedUsers = meta.pinnedUsers.join('\n');
- this.cacheRemoteFiles = meta.cacheRemoteFiles;
- this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
- this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
- this.enableRegistration = !meta.disableRegistration;
- this.emailRequiredForSignup = meta.emailRequiredForSignup;
- this.enableServiceWorker = meta.enableServiceWorker;
- this.swPublicKey = meta.swPublickey;
- this.swPrivateKey = meta.swPrivateKey;
- this.deeplAuthKey = meta.deeplAuthKey;
- this.deeplIsPro = meta.deeplIsPro;
- },
-
- save() {
- os.apiWithDialog('admin/update-meta', {
- name: this.name,
- description: this.description,
- tosUrl: this.tosUrl,
- iconUrl: this.iconUrl,
- bannerUrl: this.bannerUrl,
- backgroundImageUrl: this.backgroundImageUrl,
- themeColor: this.themeColor === '' ? null : this.themeColor,
- defaultLightTheme: this.defaultLightTheme === '' ? null : this.defaultLightTheme,
- defaultDarkTheme: this.defaultDarkTheme === '' ? null : this.defaultDarkTheme,
- maintainerName: this.maintainerName,
- maintainerEmail: this.maintainerEmail,
- disableLocalTimeline: !this.enableLocalTimeline,
- disableGlobalTimeline: !this.enableGlobalTimeline,
- pinnedUsers: this.pinnedUsers.split('\n'),
- cacheRemoteFiles: this.cacheRemoteFiles,
- localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10),
- remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
- disableRegistration: !this.enableRegistration,
- emailRequiredForSignup: this.emailRequiredForSignup,
- enableServiceWorker: this.enableServiceWorker,
- swPublicKey: this.swPublicKey,
- swPrivateKey: this.swPrivateKey,
- deeplAuthKey: this.deeplAuthKey,
- deeplIsPro: this.deeplIsPro,
- }).then(() => {
- fetchInstance();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.general,
+ icon: 'fas fa-cog',
+ bg: 'var(--bg)',
+ actions: [{
+ asFullButton: true,
+ icon: 'fas fa-check',
+ text: i18n.ts.save,
+ handler: save,
+ }],
}
});
</script>
diff --git a/packages/client/src/pages/api-console.vue b/packages/client/src/pages/api-console.vue
index 7f174a6318..88acbcd3a3 100644
--- a/packages/client/src/pages/api-console.vue
+++ b/packages/client/src/pages/api-console.vue
@@ -26,8 +26,8 @@
</template>
<script lang="ts" setup>
-import { defineExpose, ref } from 'vue';
-import * as JSON5 from 'json5';
+import { ref } from 'vue';
+import JSON5 from 'json5';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkTextarea from '@/components/form/textarea.vue';
diff --git a/packages/client/src/pages/channel-editor.vue b/packages/client/src/pages/channel-editor.vue
index 3818c7481a..ea3a5dab76 100644
--- a/packages/client/src/pages/channel-editor.vue
+++ b/packages/client/src/pages/channel-editor.vue
@@ -111,8 +111,8 @@ export default defineComponent({
}
},
- setBannerImage(e) {
- selectFile(e.currentTarget ?? e.target, null).then(file => {
+ setBannerImage(evt) {
+ selectFile(evt.currentTarget ?? evt.target, null).then(file => {
this.bannerId = file.id;
});
},
diff --git a/packages/client/src/pages/emojis.category.vue b/packages/client/src/pages/emojis.category.vue
index 9a317418be..1be004cf51 100644
--- a/packages/client/src/pages/emojis.category.vue
+++ b/packages/client/src/pages/emojis.category.vue
@@ -79,9 +79,9 @@ export default defineComponent({
}
if (this.selectedTags.size === 0) {
- this.searchEmojis = this.customEmojis.filter(e => e.name.includes(this.q) || e.aliases.includes(this.q));
+ this.searchEmojis = this.customEmojis.filter(emoji => emoji.name.includes(this.q) || emoji.aliases.includes(this.q));
} else {
- this.searchEmojis = this.customEmojis.filter(e => (e.name.includes(this.q) || e.aliases.includes(this.q)) && [...this.selectedTags].every(t => e.aliases.includes(t)));
+ this.searchEmojis = this.customEmojis.filter(emoji => (emoji.name.includes(this.q) || emoji.aliases.includes(this.q)) && [...this.selectedTags].every(t => emoji.aliases.includes(t)));
}
},
diff --git a/packages/client/src/pages/emojis.vue b/packages/client/src/pages/emojis.vue
index 886b5f7119..f44b29df04 100644
--- a/packages/client/src/pages/emojis.vue
+++ b/packages/client/src/pages/emojis.vue
@@ -25,10 +25,10 @@ function menu(ev) {
type: 'info',
text: i18n.ts.exportRequested,
});
- }).catch((e) => {
+ }).catch((err) => {
os.alert({
type: 'error',
- text: e.message,
+ text: err.message,
});
});
}
diff --git a/packages/client/src/pages/follow.vue b/packages/client/src/pages/follow.vue
index d8a6824dca..e69e0481e0 100644
--- a/packages/client/src/pages/follow.vue
+++ b/packages/client/src/pages/follow.vue
@@ -20,7 +20,7 @@ export default defineComponent({
uri: acct
});
promise.then(res => {
- if (res.type == 'User') {
+ if (res.type === 'User') {
this.follow(res.object);
} else if (res.type === 'Note') {
this.$router.push(`/notes/${res.object.id}`);
diff --git a/packages/client/src/pages/gallery/edit.vue b/packages/client/src/pages/gallery/edit.vue
index 25ee513186..a0c2d1a596 100644
--- a/packages/client/src/pages/gallery/edit.vue
+++ b/packages/client/src/pages/gallery/edit.vue
@@ -91,8 +91,8 @@ export default defineComponent({
},
methods: {
- selectFile(e) {
- selectFiles(e.currentTarget ?? e.target, null).then(files => {
+ selectFile(evt) {
+ selectFiles(evt.currentTarget ?? evt.target, null).then(files => {
this.files = this.files.concat(files);
});
},
diff --git a/packages/client/src/pages/gallery/post.vue b/packages/client/src/pages/gallery/post.vue
index 1755c23286..1ca3443e56 100644
--- a/packages/client/src/pages/gallery/post.vue
+++ b/packages/client/src/pages/gallery/post.vue
@@ -119,8 +119,8 @@ export default defineComponent({
postId: this.postId
}).then(post => {
this.post = post;
- }).catch(e => {
- this.error = e;
+ }).catch(err => {
+ this.error = err;
});
},
diff --git a/packages/client/src/pages/messaging/index.vue b/packages/client/src/pages/messaging/index.vue
index 88a1e07afc..61c8bb0ce3 100644
--- a/packages/client/src/pages/messaging/index.vue
+++ b/packages/client/src/pages/messaging/index.vue
@@ -90,14 +90,14 @@ export default defineComponent({
getAcct: Acct.toString,
isMe(message) {
- return message.userId == this.$i.id;
+ return message.userId === this.$i.id;
},
onMessage(message) {
if (message.recipientId) {
this.messages = this.messages.filter(m => !(
- (m.recipientId == message.recipientId && m.userId == message.userId) ||
- (m.recipientId == message.userId && m.userId == message.recipientId)));
+ (m.recipientId === message.recipientId && m.userId === message.userId) ||
+ (m.recipientId === message.userId && m.userId === message.recipientId)));
this.messages.unshift(message);
} else if (message.groupId) {
@@ -108,7 +108,7 @@ export default defineComponent({
onRead(ids) {
for (const id of ids) {
- const found = this.messages.find(m => m.id == id);
+ const found = this.messages.find(m => m.id === id);
if (found) {
if (found.recipientId) {
found.isRead = true;
diff --git a/packages/client/src/pages/messaging/messaging-room.form.vue b/packages/client/src/pages/messaging/messaging-room.form.vue
index 35cb75743f..ad8aaae6b7 100644
--- a/packages/client/src/pages/messaging/messaging-room.form.vue
+++ b/packages/client/src/pages/messaging/messaging-room.form.vue
@@ -59,7 +59,7 @@ export default defineComponent({
return this.user ? 'user:' + this.user.id : 'group:' + this.group.id;
},
canSend(): boolean {
- return (this.text != null && this.text != '') || this.file != null;
+ return (this.text != null && this.text !== '') || this.file != null;
},
room(): any {
return this.$parent;
@@ -88,12 +88,11 @@ export default defineComponent({
}
},
methods: {
- async onPaste(e: ClipboardEvent) {
- const data = e.clipboardData;
- const items = data.items;
+ async onPaste(evt: ClipboardEvent) {
+ const items = evt.clipboardData.items;
- if (items.length == 1) {
- if (items[0].kind == 'file') {
+ if (items.length === 1) {
+ if (items[0].kind === 'file') {
const file = items[0].getAsFile();
const lio = file.name.lastIndexOf('.');
const ext = lio >= 0 ? file.name.slice(lio) : '';
@@ -101,7 +100,7 @@ export default defineComponent({
if (formatted) this.upload(file, formatted);
}
} else {
- if (items[0].kind == 'file') {
+ if (items[0].kind === 'file') {
os.alert({
type: 'error',
text: this.$ts.onlyOneFileCanBeAttached
@@ -110,23 +109,23 @@ export default defineComponent({
}
},
- onDragover(e) {
- const isFile = e.dataTransfer.items[0].kind == 'file';
- const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
+ onDragover(evt) {
+ const isFile = evt.dataTransfer.items[0].kind === 'file';
+ const isDriveFile = evt.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
if (isFile || isDriveFile) {
- e.preventDefault();
- e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+ evt.preventDefault();
+ evt.dataTransfer.dropEffect = evt.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
}
},
- onDrop(e): void {
+ onDrop(evt): void {
// ファイルだったら
- if (e.dataTransfer.files.length == 1) {
- e.preventDefault();
- this.upload(e.dataTransfer.files[0]);
+ if (evt.dataTransfer.files.length === 1) {
+ evt.preventDefault();
+ this.upload(evt.dataTransfer.files[0]);
return;
- } else if (e.dataTransfer.files.length > 1) {
- e.preventDefault();
+ } else if (evt.dataTransfer.files.length > 1) {
+ evt.preventDefault();
os.alert({
type: 'error',
text: this.$ts.onlyOneFileCanBeAttached
@@ -135,17 +134,17 @@ export default defineComponent({
}
//#region ドライブのファイル
- const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile != '') {
+ const driveFile = evt.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
+ if (driveFile != null && driveFile !== '') {
this.file = JSON.parse(driveFile);
- e.preventDefault();
+ evt.preventDefault();
}
//#endregion
},
- onKeydown(e) {
+ onKeydown(evt) {
this.typing();
- if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && this.canSend) {
+ if ((evt.which === 10 || evt.which === 13) && (evt.ctrlKey || evt.metaKey) && this.canSend) {
this.send();
}
},
@@ -154,8 +153,8 @@ export default defineComponent({
this.typing();
},
- chooseFile(e) {
- selectFile(e.currentTarget ?? e.target, this.$ts.selectFile).then(file => {
+ chooseFile(evt) {
+ selectFile(evt.currentTarget ?? evt.target, this.$ts.selectFile).then(file => {
this.file = file;
});
},
@@ -193,9 +192,9 @@ export default defineComponent({
},
saveDraft() {
- const data = JSON.parse(localStorage.getItem('message_drafts') || '{}');
+ const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}');
- data[this.draftKey] = {
+ drafts[this.draftKey] = {
updatedAt: new Date(),
data: {
text: this.text,
@@ -203,15 +202,15 @@ export default defineComponent({
}
}
- localStorage.setItem('message_drafts', JSON.stringify(data));
+ localStorage.setItem('message_drafts', JSON.stringify(drafts));
},
deleteDraft() {
- const data = JSON.parse(localStorage.getItem('message_drafts') || '{}');
+ const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}');
- delete data[this.draftKey];
+ delete drafts[this.draftKey];
- localStorage.setItem('message_drafts', JSON.stringify(data));
+ localStorage.setItem('message_drafts', JSON.stringify(drafts));
},
async insertEmoji(ev) {
diff --git a/packages/client/src/pages/messaging/messaging-room.vue b/packages/client/src/pages/messaging/messaging-room.vue
index 2ecc68eb54..fd1962218a 100644
--- a/packages/client/src/pages/messaging/messaging-room.vue
+++ b/packages/client/src/pages/messaging/messaging-room.vue
@@ -166,23 +166,23 @@ const Component = defineComponent({
});
},
- onDragover(e) {
- const isFile = e.dataTransfer.items[0].kind == 'file';
- const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
+ onDragover(evt) {
+ const isFile = evt.dataTransfer.items[0].kind === 'file';
+ const isDriveFile = evt.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
if (isFile || isDriveFile) {
- e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
+ evt.dataTransfer.dropEffect = evt.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
} else {
- e.dataTransfer.dropEffect = 'none';
+ evt.dataTransfer.dropEffect = 'none';
}
},
- onDrop(e): void {
+ onDrop(evt): void {
// ファイルだったら
- if (e.dataTransfer.files.length == 1) {
- this.form.upload(e.dataTransfer.files[0]);
+ if (evt.dataTransfer.files.length === 1) {
+ this.form.upload(evt.dataTransfer.files[0]);
return;
- } else if (e.dataTransfer.files.length > 1) {
+ } else if (evt.dataTransfer.files.length > 1) {
os.alert({
type: 'error',
text: this.$ts.onlyOneFileCanBeAttached
@@ -191,8 +191,8 @@ const Component = defineComponent({
}
//#region ドライブのファイル
- const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile != '') {
+ const driveFile = evt.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
+ if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile);
this.form.file = file;
}
@@ -209,7 +209,7 @@ const Component = defineComponent({
limit: max + 1,
untilId: this.existMoreMessages ? this.messages[0].id : undefined
}).then(messages => {
- if (messages.length == max + 1) {
+ if (messages.length === max + 1) {
this.existMoreMessages = true;
messages.pop();
} else {
@@ -235,7 +235,7 @@ const Component = defineComponent({
const _isBottom = isBottom(this.$el, 64);
this.messages.push(message);
- if (message.userId != this.$i.id && !document.hidden) {
+ if (message.userId !== this.$i.id && !document.hidden) {
this.connection.send('read', {
id: message.id
});
@@ -246,7 +246,7 @@ const Component = defineComponent({
this.$nextTick(() => {
this.scrollToBottom();
});
- } else if (message.userId != this.$i.id) {
+ } else if (message.userId !== this.$i.id) {
// Notify
this.notifyNewMessage();
}
@@ -256,7 +256,7 @@ const Component = defineComponent({
if (this.user) {
if (!Array.isArray(x)) x = [x];
for (const id of x) {
- if (this.messages.some(x => x.id == id)) {
+ if (this.messages.some(x => x.id === id)) {
const exist = this.messages.map(x => x.id).indexOf(id);
this.messages[exist] = {
...this.messages[exist],
@@ -266,7 +266,7 @@ const Component = defineComponent({
}
} else if (this.group) {
for (const id of x.ids) {
- if (this.messages.some(x => x.id == id)) {
+ if (this.messages.some(x => x.id === id)) {
const exist = this.messages.map(x => x.id).indexOf(id);
this.messages[exist] = {
...this.messages[exist],
diff --git a/packages/client/src/pages/mfm-cheat-sheet.vue b/packages/client/src/pages/mfm-cheat-sheet.vue
index 83ae5741c3..aa35ec2158 100644
--- a/packages/client/src/pages/mfm-cheat-sheet.vue
+++ b/packages/client/src/pages/mfm-cheat-sheet.vue
@@ -325,20 +325,20 @@ export default defineComponent({
preview_inlineMath: '\\(x= \\frac{-b\' \\pm \\sqrt{(b\')^2-ac}}{a}\\)',
preview_quote: `> ${this.$ts._mfm.dummy}`,
preview_search: `${this.$ts._mfm.dummy} 検索`,
- preview_jelly: `$[jelly 🍮]`,
- preview_tada: `$[tada 🍮]`,
- preview_jump: `$[jump 🍮]`,
- preview_bounce: `$[bounce 🍮]`,
- preview_shake: `$[shake 🍮]`,
- preview_twitch: `$[twitch 🍮]`,
- preview_spin: `$[spin 🍮] $[spin.left 🍮] $[spin.alternate 🍮]\n$[spin.x 🍮] $[spin.x,left 🍮] $[spin.x,alternate 🍮]\n$[spin.y 🍮] $[spin.y,left 🍮] $[spin.y,alternate 🍮]`,
+ preview_jelly: `$[jelly 🍮] $[jelly.speed=5s 🍮]`,
+ preview_tada: `$[tada 🍮] $[tada.speed=5s 🍮]`,
+ preview_jump: `$[jump 🍮] $[jump.speed=5s 🍮]`,
+ preview_bounce: `$[bounce 🍮] $[bounce.speed=5s 🍮]`,
+ preview_shake: `$[shake 🍮] $[shake.speed=5s 🍮]`,
+ preview_twitch: `$[twitch 🍮] $[twitch.speed=5s 🍮]`,
+ preview_spin: `$[spin 🍮] $[spin.left 🍮] $[spin.alternate 🍮]\n$[spin.x 🍮] $[spin.x,left 🍮] $[spin.x,alternate 🍮]\n$[spin.y 🍮] $[spin.y,left 🍮] $[spin.y,alternate 🍮]\n\n$[spin.speed=5s 🍮]`,
preview_flip: `$[flip ${this.$ts._mfm.dummy}]\n$[flip.v ${this.$ts._mfm.dummy}]\n$[flip.h,v ${this.$ts._mfm.dummy}]`,
preview_font: `$[font.serif ${this.$ts._mfm.dummy}]\n$[font.monospace ${this.$ts._mfm.dummy}]\n$[font.cursive ${this.$ts._mfm.dummy}]\n$[font.fantasy ${this.$ts._mfm.dummy}]`,
preview_x2: `$[x2 🍮]`,
preview_x3: `$[x3 🍮]`,
preview_x4: `$[x4 🍮]`,
preview_blur: `$[blur ${this.$ts._mfm.dummy}]`,
- preview_rainbow: `$[rainbow 🍮]`,
+ preview_rainbow: `$[rainbow 🍮] $[rainbow.speed=5s 🍮]`,
preview_sparkle: `$[sparkle 🍮]`,
preview_rotate: `$[rotate 🍮]`,
}
diff --git a/packages/client/src/pages/my-antennas/edit.vue b/packages/client/src/pages/my-antennas/edit.vue
index 04928c81a3..38e56ce35d 100644
--- a/packages/client/src/pages/my-antennas/edit.vue
+++ b/packages/client/src/pages/my-antennas/edit.vue
@@ -4,49 +4,34 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-import MkButton from '@/components/ui/button.vue';
+<script lang="ts" setup>
+import { watch } from 'vue';
import XAntenna from './editor.vue';
import * as symbols from '@/symbols';
import * as os from '@/os';
+import { MisskeyNavigator } from '@/scripts/navigate';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- XAntenna,
- },
+const nav = new MisskeyNavigator();
- props: {
- antennaId: {
- type: String,
- required: true,
- }
- },
+let antenna: any = $ref(null);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.manageAntennas,
- icon: 'fas fa-satellite',
- },
- antenna: null,
- };
- },
+const props = defineProps<{
+ antennaId: string
+}>();
- watch: {
- antennaId: {
- async handler() {
- this.antenna = await os.api('antennas/show', { antennaId: this.antennaId });
- },
- immediate: true,
- }
- },
+function onAntennaUpdated() {
+ nav.push('/my/antennas');
+}
- methods: {
- onAntennaUpdated() {
- this.$router.push('/my/antennas');
- },
+os.api('antennas/show', { antennaId: props.antennaId }).then((antennaResponse) => {
+ antenna = antennaResponse;
+});
+
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.manageAntennas,
+ icon: 'fas fa-satellite',
}
});
</script>
diff --git a/packages/client/src/pages/my-antennas/editor.vue b/packages/client/src/pages/my-antennas/editor.vue
index 8c1d6148fe..6f3c4afbfe 100644
--- a/packages/client/src/pages/my-antennas/editor.vue
+++ b/packages/client/src/pages/my-antennas/editor.vue
@@ -44,135 +44,100 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { watch } from 'vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkTextarea from '@/components/form/textarea.vue';
import MkSelect from '@/components/form/select.vue';
import MkSwitch from '@/components/form/switch.vue';
-import * as Acct from 'misskey-js/built/acct';
import * as os from '@/os';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton, MkInput, MkTextarea, MkSelect, MkSwitch
- },
+const props = defineProps<{
+ antenna: any
+}>();
- props: {
- antenna: {
- type: Object,
- required: true
- }
- },
+const emit = defineEmits<{
+ (ev: 'created'): void,
+ (ev: 'updated'): void,
+ (ev: 'deleted'): void,
+}>();
- data() {
- return {
- name: '',
- src: '',
- userListId: null,
- userGroupId: null,
- users: '',
- keywords: '',
- excludeKeywords: '',
- caseSensitive: false,
- withReplies: false,
- withFile: false,
- notify: false,
- userLists: null,
- userGroups: null,
- };
- },
+let name: string = $ref(props.antenna.name);
+let src: string = $ref(props.antenna.src);
+let userListId: any = $ref(props.antenna.userListId);
+let userGroupId: any = $ref(props.antenna.userGroupId);
+let users: string = $ref(props.antenna.users.join('\n'));
+let keywords: string = $ref(props.antenna.keywords.map(x => x.join(' ')).join('\n'));
+let excludeKeywords: string = $ref(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n'));
+let caseSensitive: boolean = $ref(props.antenna.caseSensitive);
+let withReplies: boolean = $ref(props.antenna.withReplies);
+let withFile: boolean = $ref(props.antenna.withFile);
+let notify: boolean = $ref(props.antenna.notify);
+let userLists: any = $ref(null);
+let userGroups: any = $ref(null);
- watch: {
- async src() {
- if (this.src === 'list' && this.userLists === null) {
- this.userLists = await os.api('users/lists/list');
- }
+watch(() => src, async () => {
+ if (src === 'list' && userLists === null) {
+ userLists = await os.api('users/lists/list');
+ }
- if (this.src === 'group' && this.userGroups === null) {
- const groups1 = await os.api('users/groups/owned');
- const groups2 = await os.api('users/groups/joined');
+ if (src === 'group' && userGroups === null) {
+ const groups1 = await os.api('users/groups/owned');
+ const groups2 = await os.api('users/groups/joined');
- this.userGroups = [...groups1, ...groups2];
- }
- }
- },
+ userGroups = [...groups1, ...groups2];
+ }
+});
- created() {
- this.name = this.antenna.name;
- this.src = this.antenna.src;
- this.userListId = this.antenna.userListId;
- this.userGroupId = this.antenna.userGroupId;
- this.users = this.antenna.users.join('\n');
- this.keywords = this.antenna.keywords.map(x => x.join(' ')).join('\n');
- this.excludeKeywords = this.antenna.excludeKeywords.map(x => x.join(' ')).join('\n');
- this.caseSensitive = this.antenna.caseSensitive;
- this.withReplies = this.antenna.withReplies;
- this.withFile = this.antenna.withFile;
- this.notify = this.antenna.notify;
- },
+async function saveAntenna() {
+ const antennaData = {
+ name,
+ src,
+ userListId,
+ userGroupId,
+ withReplies,
+ withFile,
+ notify,
+ caseSensitive,
+ users: users.trim().split('\n').map(x => x.trim()),
+ keywords: keywords.trim().split('\n').map(x => x.trim().split(' ')),
+ excludeKeywords: excludeKeywords.trim().split('\n').map(x => x.trim().split(' ')),
+ };
- methods: {
- async saveAntenna() {
- if (this.antenna.id == null) {
- await os.apiWithDialog('antennas/create', {
- name: this.name,
- src: this.src,
- userListId: this.userListId,
- userGroupId: this.userGroupId,
- withReplies: this.withReplies,
- withFile: this.withFile,
- notify: this.notify,
- caseSensitive: this.caseSensitive,
- users: this.users.trim().split('\n').map(x => x.trim()),
- keywords: this.keywords.trim().split('\n').map(x => x.trim().split(' ')),
- excludeKeywords: this.excludeKeywords.trim().split('\n').map(x => x.trim().split(' ')),
- });
- this.$emit('created');
- } else {
- await os.apiWithDialog('antennas/update', {
- antennaId: this.antenna.id,
- name: this.name,
- src: this.src,
- userListId: this.userListId,
- userGroupId: this.userGroupId,
- withReplies: this.withReplies,
- withFile: this.withFile,
- notify: this.notify,
- caseSensitive: this.caseSensitive,
- users: this.users.trim().split('\n').map(x => x.trim()),
- keywords: this.keywords.trim().split('\n').map(x => x.trim().split(' ')),
- excludeKeywords: this.excludeKeywords.trim().split('\n').map(x => x.trim().split(' ')),
- });
- this.$emit('updated');
- }
- },
+ if (props.antenna.id == null) {
+ await os.apiWithDialog('antennas/create', antennaData);
+ emit('created');
+ } else {
+ antennaData['antennaId'] = props.antenna.id;
+ await os.apiWithDialog('antennas/update', antennaData);
+ emit('updated');
+ }
+}
- async deleteAntenna() {
- const { canceled } = await os.confirm({
- type: 'warning',
- text: this.$t('removeAreYouSure', { x: this.antenna.name }),
- });
- if (canceled) return;
+async function deleteAntenna() {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.t('removeAreYouSure', { x: props.antenna.name }),
+ });
+ if (canceled) return;
- await os.api('antennas/delete', {
- antennaId: this.antenna.id,
- });
+ await os.api('antennas/delete', {
+ antennaId: props.antenna.id,
+ });
- os.success();
- this.$emit('deleted');
- },
+ os.success();
+ emit('deleted');
+}
- addUser() {
- os.selectUser().then(user => {
- this.users = this.users.trim();
- this.users += '\n@' + Acct.toString(user);
- this.users = this.users.trim();
- });
- }
- }
-});
+function addUser() {
+ os.selectUser().then(user => {
+ users = users.trim();
+ users += '\n@' + Acct.toString(user as any);
+ users = users.trim();
+ });
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/pages/my-antennas/index.vue b/packages/client/src/pages/my-antennas/index.vue
index 7138d269a9..9f1e01f11d 100644
--- a/packages/client/src/pages/my-antennas/index.vue
+++ b/packages/client/src/pages/my-antennas/index.vue
@@ -1,7 +1,7 @@
<template>
<MkSpacer :content-max="700">
<div class="ieepwinx">
- <MkButton :link="true" to="/my/antennas/create" primary class="add"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
+ <MkButton :link="true" to="/my/antennas/create" primary class="add"><i class="fas fa-plus"></i> {{ i18n.ts.add }}</MkButton>
<div class="">
<MkPagination v-slot="{items}" ref="list" :pagination="pagination">
@@ -14,36 +14,25 @@
</MkSpacer>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
import MkPagination from '@/components/ui/pagination.vue';
import MkButton from '@/components/ui/button.vue';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkPagination,
- MkButton,
- },
+const pagination = {
+ endpoint: 'antennas/list' as const,
+ limit: 10,
+};
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.manageAntennas,
- icon: 'fas fa-satellite',
- bg: 'var(--bg)',
- action: {
- icon: 'fas fa-plus',
- handler: this.create
- }
- },
- pagination: {
- endpoint: 'antennas/list' as const,
- limit: 10,
- },
- };
- },
-});
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.manageAntennas,
+ icon: 'fas fa-satellite',
+ bg: 'var(--bg)'
+ }
+})
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/pages/note.vue b/packages/client/src/pages/note.vue
index 29261ec484..f0a18ecc36 100644
--- a/packages/client/src/pages/note.vue
+++ b/packages/client/src/pages/note.vue
@@ -108,6 +108,10 @@ export default defineComponent({
},
methods: {
fetch() {
+ this.hasPrev = false;
+ this.hasNext = false;
+ this.showPrev = false;
+ this.showNext = false;
this.note = null;
os.api('notes/show', {
noteId: this.noteId
@@ -132,8 +136,8 @@ export default defineComponent({
this.hasPrev = prev.length !== 0;
this.hasNext = next.length !== 0;
});
- }).catch(e => {
- this.error = e;
+ }).catch(err => {
+ this.error = err;
});
}
}
diff --git a/packages/client/src/pages/page.vue b/packages/client/src/pages/page.vue
index b2c039a269..5bca971438 100644
--- a/packages/client/src/pages/page.vue
+++ b/packages/client/src/pages/page.vue
@@ -139,8 +139,8 @@ export default defineComponent({
username: this.username,
}).then(page => {
this.page = page;
- }).catch(e => {
- this.error = e;
+ }).catch(err => {
+ this.error = err;
});
},
diff --git a/packages/client/src/pages/reset-password.vue b/packages/client/src/pages/reset-password.vue
index 7d008ae75c..b3e2ca8d6f 100644
--- a/packages/client/src/pages/reset-password.vue
+++ b/packages/client/src/pages/reset-password.vue
@@ -12,7 +12,7 @@
</template>
<script lang="ts" setup>
-import { onMounted } from 'vue';
+import { defineAsyncComponent, onMounted } from 'vue';
import FormInput from '@/components/form/input.vue';
import FormButton from '@/components/ui/button.vue';
import * as os from '@/os';
@@ -36,7 +36,7 @@ async function save() {
onMounted(() => {
if (props.token == null) {
- os.popup(import('@/components/forgot-password.vue'), {}, {}, 'closed');
+ os.popup(defineAsyncComponent(() => import('@/components/forgot-password.vue')), {}, {}, 'closed');
router.push('/');
}
});
diff --git a/packages/client/src/pages/settings/2fa.vue b/packages/client/src/pages/settings/2fa.vue
index 10599d99ff..be464f040d 100644
--- a/packages/client/src/pages/settings/2fa.vue
+++ b/packages/client/src/pages/settings/2fa.vue
@@ -1,49 +1,49 @@
<template>
<div>
- <MkButton v-if="!data && !$i.twoFactorEnabled" @click="register">{{ $ts._2fa.registerDevice }}</MkButton>
+ <MkButton v-if="!twoFactorData && !$i.twoFactorEnabled" @click="register">{{ i18n.ts._2fa.registerDevice }}</MkButton>
<template v-if="$i.twoFactorEnabled">
- <p>{{ $ts._2fa.alreadyRegistered }}</p>
- <MkButton @click="unregister">{{ $ts.unregister }}</MkButton>
+ <p>{{ i18n.ts._2fa.alreadyRegistered }}</p>
+ <MkButton @click="unregister">{{ i18n.ts.unregister }}</MkButton>
<template v-if="supportsCredentials">
<hr class="totp-method-sep">
- <h2 class="heading">{{ $ts.securityKey }}</h2>
- <p>{{ $ts._2fa.securityKeyInfo }}</p>
+ <h2 class="heading">{{ i18n.ts.securityKey }}</h2>
+ <p>{{ i18n.ts._2fa.securityKeyInfo }}</p>
<div class="key-list">
<div v-for="key in $i.securityKeysList" class="key">
<h3>{{ key.name }}</h3>
- <div class="last-used">{{ $ts.lastUsed }}<MkTime :time="key.lastUsed"/></div>
- <MkButton @click="unregisterKey(key)">{{ $ts.unregister }}</MkButton>
+ <div class="last-used">{{ i18n.ts.lastUsed }}<MkTime :time="key.lastUsed"/></div>
+ <MkButton @click="unregisterKey(key)">{{ i18n.ts.unregister }}</MkButton>
</div>
</div>
- <MkSwitch v-if="$i.securityKeysList.length > 0" v-model="usePasswordLessLogin" @update:modelValue="updatePasswordLessLogin">{{ $ts.passwordLessLogin }}</MkSwitch>
+ <MkSwitch v-if="$i.securityKeysList.length > 0" v-model="usePasswordLessLogin" @update:modelValue="updatePasswordLessLogin">{{ i18n.ts.passwordLessLogin }}</MkSwitch>
- <MkInfo v-if="registration && registration.error" warn>{{ $ts.error }} {{ registration.error }}</MkInfo>
- <MkButton v-if="!registration || registration.error" @click="addSecurityKey">{{ $ts._2fa.registerKey }}</MkButton>
+ <MkInfo v-if="registration && registration.error" warn>{{ i18n.ts.error }} {{ registration.error }}</MkInfo>
+ <MkButton v-if="!registration || registration.error" @click="addSecurityKey">{{ i18n.ts._2fa.registerKey }}</MkButton>
<ol v-if="registration && !registration.error">
<li v-if="registration.stage >= 0">
- {{ $ts.tapSecurityKey }}
+ {{ i18n.ts.tapSecurityKey }}
<i v-if="registration.saving && registration.stage == 0" class="fas fa-spinner fa-pulse fa-fw"></i>
</li>
<li v-if="registration.stage >= 1">
<MkForm :disabled="registration.stage != 1 || registration.saving">
<MkInput v-model="keyName" :max="30">
- <template #label>{{ $ts.securityKeyName }}</template>
+ <template #label>{{ i18n.ts.securityKeyName }}</template>
</MkInput>
- <MkButton :disabled="keyName.length == 0" @click="registerKey">{{ $ts.registerSecurityKey }}</MkButton>
+ <MkButton :disabled="keyName.length == 0" @click="registerKey">{{ i18n.ts.registerSecurityKey }}</MkButton>
<i v-if="registration.saving && registration.stage == 1" class="fas fa-spinner fa-pulse fa-fw"></i>
</MkForm>
</li>
</ol>
</template>
</template>
- <div v-if="data && !$i.twoFactorEnabled">
+ <div v-if="twoFactorData && !$i.twoFactorEnabled">
<ol style="margin: 0; padding: 0 0 0 1em;">
<li>
- <I18n :src="$ts._2fa.step1" tag="span">
+ <I18n :src="i18n.ts._2fa.step1" tag="span">
<template #a>
<a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a>
</template>
@@ -52,19 +52,20 @@
</template>
</I18n>
</li>
- <li>{{ $ts._2fa.step2 }}<br><img :src="data.qr"></li>
- <li>{{ $ts._2fa.step3 }}<br>
- <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false"><template #label>{{ $ts.token }}</template></MkInput>
- <MkButton primary @click="submit">{{ $ts.done }}</MkButton>
+ <li>{{ i18n.ts._2fa.step2 }}<br><img :src="twoFactorData.qr"><p>{{ $ts._2fa.step2Url }}<br>{{ twoFactorData.url }}</p></li>
+ <li>
+ {{ i18n.ts._2fa.step3 }}<br>
+ <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false"><template #label>{{ i18n.ts.token }}</template></MkInput>
+ <MkButton primary @click="submit">{{ i18n.ts.done }}</MkButton>
</li>
</ol>
- <MkInfo>{{ $ts._2fa.step4 }}</MkInfo>
+ <MkInfo>{{ i18n.ts._2fa.step4 }}</MkInfo>
</div>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { ref } from 'vue';
import { hostname } from '@/config';
import { byteify, hexify, stringify } from '@/scripts/2fa';
import MkButton from '@/components/ui/button.vue';
@@ -72,155 +73,144 @@ import MkInfo from '@/components/ui/info.vue';
import MkInput from '@/components/form/input.vue';
import MkSwitch from '@/components/form/switch.vue';
import * as os from '@/os';
-import * as symbols from '@/symbols';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton, MkInfo, MkInput, MkSwitch
- },
+const twoFactorData = ref<any>(null);
+const supportsCredentials = ref(!!navigator.credentials);
+const usePasswordLessLogin = ref($i!.usePasswordLessLogin);
+const registration = ref<any>(null);
+const keyName = ref('');
+const token = ref(null);
- data() {
- return {
- data: null,
- supportsCredentials: !!navigator.credentials,
- usePasswordLessLogin: this.$i.usePasswordLessLogin,
- registration: null,
- keyName: '',
- token: null,
- };
- },
+function register() {
+ os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ }).then(({ canceled, result: password }) => {
+ if (canceled) return;
+ os.api('i/2fa/register', {
+ password: password
+ }).then(data => {
+ twoFactorData.value = data;
+ });
+ });
+}
- methods: {
- register() {
- os.inputText({
- title: this.$ts.password,
- type: 'password'
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- os.api('i/2fa/register', {
- password: password
- }).then(data => {
- this.data = data;
- });
- });
- },
+function unregister() {
+ os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ }).then(({ canceled, result: password }) => {
+ if (canceled) return;
+ os.api('i/2fa/unregister', {
+ password: password
+ }).then(() => {
+ usePasswordLessLogin.value = false;
+ updatePasswordLessLogin();
+ }).then(() => {
+ os.success();
+ $i!.twoFactorEnabled = false;
+ });
+ });
+}
- unregister() {
- os.inputText({
- title: this.$ts.password,
- type: 'password'
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- os.api('i/2fa/unregister', {
- password: password
- }).then(() => {
- this.usePasswordLessLogin = false;
- this.updatePasswordLessLogin();
- }).then(() => {
- os.success();
- this.$i.twoFactorEnabled = false;
- });
- });
- },
+function submit() {
+ os.api('i/2fa/done', {
+ token: token.value
+ }).then(() => {
+ os.success();
+ $i!.twoFactorEnabled = true;
+ }).catch(err => {
+ os.alert({
+ type: 'error',
+ text: err,
+ });
+ });
+}
- submit() {
- os.api('i/2fa/done', {
- token: this.token
- }).then(() => {
- os.success();
- this.$i.twoFactorEnabled = true;
- }).catch(e => {
- os.alert({
- type: 'error',
- text: e
- });
- });
- },
+function registerKey() {
+ registration.value.saving = true;
+ os.api('i/2fa/key-done', {
+ password: registration.value.password,
+ name: keyName.value,
+ challengeId: registration.value.challengeId,
+ // we convert each 16 bits to a string to serialise
+ clientDataJSON: stringify(registration.value.credential.response.clientDataJSON),
+ attestationObject: hexify(registration.value.credential.response.attestationObject)
+ }).then(key => {
+ registration.value = null;
+ key.lastUsed = new Date();
+ os.success();
+ })
+}
- registerKey() {
- this.registration.saving = true;
- os.api('i/2fa/key-done', {
- password: this.registration.password,
- name: this.keyName,
- challengeId: this.registration.challengeId,
- // we convert each 16 bits to a string to serialise
- clientDataJSON: stringify(this.registration.credential.response.clientDataJSON),
- attestationObject: hexify(this.registration.credential.response.attestationObject)
- }).then(key => {
- this.registration = null;
- key.lastUsed = new Date();
- os.success();
- })
- },
+function unregisterKey(key) {
+ os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ }).then(({ canceled, result: password }) => {
+ if (canceled) return;
+ return os.api('i/2fa/remove-key', {
+ password,
+ credentialId: key.id
+ }).then(() => {
+ usePasswordLessLogin.value = false;
+ updatePasswordLessLogin();
+ }).then(() => {
+ os.success();
+ });
+ });
+}
- unregisterKey(key) {
- os.inputText({
- title: this.$ts.password,
- type: 'password'
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- return os.api('i/2fa/remove-key', {
- password,
- credentialId: key.id
- }).then(() => {
- this.usePasswordLessLogin = false;
- this.updatePasswordLessLogin();
- }).then(() => {
- os.success();
- });
+function addSecurityKey() {
+ os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ }).then(({ canceled, result: password }) => {
+ if (canceled) return;
+ os.api('i/2fa/register-key', {
+ password
+ }).then(reg => {
+ registration.value = {
+ password,
+ challengeId: reg!.challengeId,
+ stage: 0,
+ publicKeyOptions: {
+ challenge: byteify(reg!.challenge, 'base64'),
+ rp: {
+ id: hostname,
+ name: 'Misskey'
+ },
+ user: {
+ id: byteify($i!.id, 'ascii'),
+ name: $i!.username,
+ displayName: $i!.name,
+ },
+ pubKeyCredParams: [{ alg: -7, type: 'public-key' }],
+ timeout: 60000,
+ attestation: 'direct'
+ },
+ saving: true
+ };
+ return navigator.credentials.create({
+ publicKey: registration.value.publicKeyOptions
});
- },
+ }).then(credential => {
+ registration.value.credential = credential;
+ registration.value.saving = false;
+ registration.value.stage = 1;
+ }).catch(err => {
+ console.warn('Error while registering?', err);
+ registration.value.error = err.message;
+ registration.value.stage = -1;
+ });
+ });
+}
- addSecurityKey() {
- os.inputText({
- title: this.$ts.password,
- type: 'password'
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- os.api('i/2fa/register-key', {
- password
- }).then(registration => {
- this.registration = {
- password,
- challengeId: registration.challengeId,
- stage: 0,
- publicKeyOptions: {
- challenge: byteify(registration.challenge, 'base64'),
- rp: {
- id: hostname,
- name: 'Misskey'
- },
- user: {
- id: byteify(this.$i.id, 'ascii'),
- name: this.$i.username,
- displayName: this.$i.name,
- },
- pubKeyCredParams: [{ alg: -7, type: 'public-key' }],
- timeout: 60000,
- attestation: 'direct'
- },
- saving: true
- };
- return navigator.credentials.create({
- publicKey: this.registration.publicKeyOptions
- });
- }).then(credential => {
- this.registration.credential = credential;
- this.registration.saving = false;
- this.registration.stage = 1;
- }).catch(err => {
- console.warn('Error while registering?', err);
- this.registration.error = err.message;
- this.registration.stage = -1;
- });
- });
- },
-
- updatePasswordLessLogin() {
- os.api('i/2fa/password-less', {
- value: !!this.usePasswordLessLogin
- });
- }
- }
-});
+async function updatePasswordLessLogin() {
+ await os.api('i/2fa/password-less', {
+ value: !!usePasswordLessLogin.value
+ });
+}
</script>
diff --git a/packages/client/src/pages/settings/account-info.vue b/packages/client/src/pages/settings/account-info.vue
index c98ad056f6..12142b4dc1 100644
--- a/packages/client/src/pages/settings/account-info.vue
+++ b/packages/client/src/pages/settings/account-info.vue
@@ -7,163 +7,150 @@
<FormSection>
<MkKeyValue>
- <template #key>{{ $ts.registeredDate }}</template>
+ <template #key>{{ i18n.ts.registeredDate }}</template>
<template #value><MkTime :time="$i.createdAt" mode="detail"/></template>
</MkKeyValue>
</FormSection>
<FormSection v-if="stats">
- <template #label>{{ $ts.statistics }}</template>
+ <template #label>{{ i18n.ts.statistics }}</template>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.notesCount }}</template>
+ <template #key>{{ i18n.ts.notesCount }}</template>
<template #value>{{ number(stats.notesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.repliesCount }}</template>
+ <template #key>{{ i18n.ts.repliesCount }}</template>
<template #value>{{ number(stats.repliesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.renotesCount }}</template>
+ <template #key>{{ i18n.ts.renotesCount }}</template>
<template #value>{{ number(stats.renotesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.repliedCount }}</template>
+ <template #key>{{ i18n.ts.repliedCount }}</template>
<template #value>{{ number(stats.repliedCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.renotedCount }}</template>
+ <template #key>{{ i18n.ts.renotedCount }}</template>
<template #value>{{ number(stats.renotedCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.pollVotesCount }}</template>
+ <template #key>{{ i18n.ts.pollVotesCount }}</template>
<template #value>{{ number(stats.pollVotesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.pollVotedCount }}</template>
+ <template #key>{{ i18n.ts.pollVotedCount }}</template>
<template #value>{{ number(stats.pollVotedCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.sentReactionsCount }}</template>
+ <template #key>{{ i18n.ts.sentReactionsCount }}</template>
<template #value>{{ number(stats.sentReactionsCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.receivedReactionsCount }}</template>
+ <template #key>{{ i18n.ts.receivedReactionsCount }}</template>
<template #value>{{ number(stats.receivedReactionsCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.noteFavoritesCount }}</template>
+ <template #key>{{ i18n.ts.noteFavoritesCount }}</template>
<template #value>{{ number(stats.noteFavoritesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.followingCount }}</template>
+ <template #key>{{ i18n.ts.followingCount }}</template>
<template #value>{{ number(stats.followingCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.followingCount }} ({{ $ts.local }})</template>
+ <template #key>{{ i18n.ts.followingCount }} ({{ i18n.ts.local }})</template>
<template #value>{{ number(stats.localFollowingCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.followingCount }} ({{ $ts.remote }})</template>
+ <template #key>{{ i18n.ts.followingCount }} ({{ i18n.ts.remote }})</template>
<template #value>{{ number(stats.remoteFollowingCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.followersCount }}</template>
+ <template #key>{{ i18n.ts.followersCount }}</template>
<template #value>{{ number(stats.followersCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.followersCount }} ({{ $ts.local }})</template>
+ <template #key>{{ i18n.ts.followersCount }} ({{ i18n.ts.local }})</template>
<template #value>{{ number(stats.localFollowersCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.followersCount }} ({{ $ts.remote }})</template>
+ <template #key>{{ i18n.ts.followersCount }} ({{ i18n.ts.remote }})</template>
<template #value>{{ number(stats.remoteFollowersCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.pageLikesCount }}</template>
+ <template #key>{{ i18n.ts.pageLikesCount }}</template>
<template #value>{{ number(stats.pageLikesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.pageLikedCount }}</template>
+ <template #key>{{ i18n.ts.pageLikedCount }}</template>
<template #value>{{ number(stats.pageLikedCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.driveFilesCount }}</template>
+ <template #key>{{ i18n.ts.driveFilesCount }}</template>
<template #value>{{ number(stats.driveFilesCount) }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
- <template #key>{{ $ts.driveUsage }}</template>
+ <template #key>{{ i18n.ts.driveUsage }}</template>
<template #value>{{ bytes(stats.driveUsage) }}</template>
</MkKeyValue>
</FormSection>
<FormSection>
- <template #label>{{ $ts.other }}</template>
+ <template #label>{{ i18n.ts.other }}</template>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>emailVerified</template>
- <template #value>{{ $i.emailVerified ? $ts.yes : $ts.no }}</template>
+ <template #value>{{ $i.emailVerified ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>twoFactorEnabled</template>
- <template #value>{{ $i.twoFactorEnabled ? $ts.yes : $ts.no }}</template>
+ <template #value>{{ $i.twoFactorEnabled ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>securityKeys</template>
- <template #value>{{ $i.securityKeys ? $ts.yes : $ts.no }}</template>
+ <template #value>{{ $i.securityKeys ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>usePasswordLessLogin</template>
- <template #value>{{ $i.usePasswordLessLogin ? $ts.yes : $ts.no }}</template>
+ <template #value>{{ $i.usePasswordLessLogin ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>isModerator</template>
- <template #value>{{ $i.isModerator ? $ts.yes : $ts.no }}</template>
+ <template #value>{{ $i.isModerator ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0;">
<template #key>isAdmin</template>
- <template #value>{{ $i.isAdmin ? $ts.yes : $ts.no }}</template>
+ <template #value>{{ $i.isAdmin ? i18n.ts.yes : i18n.ts.no }}</template>
</MkKeyValue>
</FormSection>
</div>
</template>
-<script lang="ts">
-import { defineAsyncComponent, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, onMounted, ref } from 'vue';
import FormSection from '@/components/form/section.vue';
import MkKeyValue from '@/components/key-value.vue';
import * as os from '@/os';
import number from '@/filters/number';
import bytes from '@/filters/bytes';
import * as symbols from '@/symbols';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSection,
- MkKeyValue,
- },
+const stats = ref<any>({});
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.accountInfo,
- icon: 'fas fa-info-circle'
- },
- stats: null
- }
- },
-
- mounted() {
- os.api('users/stats', {
- userId: this.$i.id
- }).then(stats => {
- this.stats = stats;
- });
- },
+onMounted(() => {
+ os.api('users/stats', {
+ userId: $i!.id
+ }).then(response => {
+ stats.value = response;
+ });
+});
- methods: {
- number,
- bytes,
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.accountInfo,
+ icon: 'fas fa-info-circle'
}
});
</script>
diff --git a/packages/client/src/pages/settings/accounts.vue b/packages/client/src/pages/settings/accounts.vue
index a744a031d4..ecb2d036f2 100644
--- a/packages/client/src/pages/settings/accounts.vue
+++ b/packages/client/src/pages/settings/accounts.vue
@@ -1,7 +1,7 @@
<template>
<div class="_formRoot">
<FormSuspense :p="init">
- <FormButton primary @click="addAccount"><i class="fas fa-plus"></i> {{ $ts.addAccount }}</FormButton>
+ <FormButton primary @click="addAccount"><i class="fas fa-plus"></i> {{ i18n.ts.addAccount }}</FormButton>
<div v-for="account in accounts" :key="account.id" class="_panel _button lcjjdxlm" @click="menu(account, $event)">
<div class="avatar">
@@ -20,90 +20,89 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent, defineExpose, ref } from 'vue';
import FormSuspense from '@/components/form/suspense.vue';
import FormButton from '@/components/ui/button.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
-import { getAccounts, addAccount, login } from '@/account';
+import { getAccounts, addAccount as addAccounts, login, $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSuspense,
- FormButton,
- },
+const storedAccounts = ref<any>(null);
+const accounts = ref<any>(null);
- emits: ['info'],
+const init = async () => {
+ getAccounts().then(accounts => {
+ storedAccounts.value = accounts.filter(x => x.id !== $i!.id);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.accounts,
- icon: 'fas fa-users',
- bg: 'var(--bg)',
- },
- storedAccounts: getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id)),
- accounts: null,
- init: async () => os.api('users/show', {
- userIds: (await this.storedAccounts).map(x => x.id)
- }).then(accounts => {
- this.accounts = accounts;
- }),
- };
- },
+ console.log(storedAccounts.value);
- methods: {
- menu(account, ev) {
- os.popupMenu([{
- text: this.$ts.switch,
- icon: 'fas fa-exchange-alt',
- action: () => this.switchAccount(account),
- }, {
- text: this.$ts.remove,
- icon: 'fas fa-trash-alt',
- danger: true,
- action: () => this.removeAccount(account),
- }], ev.currentTarget ?? ev.target);
- },
+ return os.api('users/show', {
+ userIds: storedAccounts.value.map(x => x.id)
+ });
+ }).then(response => {
+ accounts.value = response;
+ console.log(accounts.value);
+ });
+}
- addAccount(ev) {
- os.popupMenu([{
- text: this.$ts.existingAccount,
- action: () => { this.addExistingAccount(); },
- }, {
- text: this.$ts.createAccount,
- action: () => { this.createAccount(); },
- }], ev.currentTarget ?? ev.target);
- },
+function menu(account, ev) {
+ os.popupMenu([{
+ text: i18n.ts.switch,
+ icon: 'fas fa-exchange-alt',
+ action: () => switchAccount(account),
+ }, {
+ text: i18n.ts.remove,
+ icon: 'fas fa-trash-alt',
+ danger: true,
+ action: () => removeAccount(account),
+ }], ev.currentTarget ?? ev.target);
+}
- addExistingAccount() {
- os.popup(import('@/components/signin-dialog.vue'), {}, {
- done: res => {
- addAccount(res.id, res.i);
- os.success();
- },
- }, 'closed');
- },
+function addAccount(ev) {
+ os.popupMenu([{
+ text: i18n.ts.existingAccount,
+ action: () => { addExistingAccount(); },
+ }, {
+ text: i18n.ts.createAccount,
+ action: () => { createAccount(); },
+ }], ev.currentTarget ?? ev.target);
+}
- createAccount() {
- os.popup(import('@/components/signup-dialog.vue'), {}, {
- done: res => {
- addAccount(res.id, res.i);
- this.switchAccountWithToken(res.i);
- },
- }, 'closed');
+function addExistingAccount() {
+ os.popup(defineAsyncComponent(() => import('@/components/signin-dialog.vue')), {}, {
+ done: res => {
+ addAccounts(res.id, res.i);
+ os.success();
},
+ }, 'closed');
+}
- async switchAccount(account: any) {
- const storedAccounts = await getAccounts();
- const token = storedAccounts.find(x => x.id === account.id).token;
- this.switchAccountWithToken(token);
+function createAccount() {
+ os.popup(defineAsyncComponent(() => import('@/components/signup-dialog.vue')), {}, {
+ done: res => {
+ addAccounts(res.id, res.i);
+ switchAccountWithToken(res.i);
},
+ }, 'closed');
+}
- switchAccountWithToken(token: string) {
- login(token);
- },
+async function switchAccount(account: any) {
+ const fetchedAccounts: any[] = await getAccounts();
+ const token = fetchedAccounts.find(x => x.id === account.id).token;
+ switchAccountWithToken(token);
+}
+
+function switchAccountWithToken(token: string) {
+ login(token);
+}
+
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.accounts,
+ icon: 'fas fa-users',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/api.vue b/packages/client/src/pages/settings/api.vue
index 20ff2a8d96..e6375763f1 100644
--- a/packages/client/src/pages/settings/api.vue
+++ b/packages/client/src/pages/settings/api.vue
@@ -1,56 +1,45 @@
<template>
<div class="_formRoot">
- <FormButton primary class="_formBlock" @click="generateToken">{{ $ts.generateAccessToken }}</FormButton>
- <FormLink to="/settings/apps" class="_formBlock">{{ $ts.manageAccessTokens }}</FormLink>
+ <FormButton primary class="_formBlock" @click="generateToken">{{ i18n.ts.generateAccessToken }}</FormButton>
+ <FormLink to="/settings/apps" class="_formBlock">{{ i18n.ts.manageAccessTokens }}</FormLink>
<FormLink to="/api-console" :behavior="isDesktop ? 'window' : null" class="_formBlock">API console</FormLink>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent, defineExpose, ref } from 'vue';
import FormLink from '@/components/form/link.vue';
import FormButton from '@/components/ui/button.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormButton,
- FormLink,
- },
+const isDesktop = ref(window.innerWidth >= 1100);
- emits: ['info'],
+function generateToken() {
+ os.popup(defineAsyncComponent(() => import('@/components/token-generate-window.vue')), {}, {
+ done: async result => {
+ const { name, permissions } = result;
+ const { token } = await os.api('miauth/gen-token', {
+ session: null,
+ name: name,
+ permission: permissions,
+ });
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: 'API',
- icon: 'fas fa-key',
- bg: 'var(--bg)',
- },
- isDesktop: window.innerWidth >= 1100,
- };
- },
-
- methods: {
- generateToken() {
- os.popup(import('@/components/token-generate-window.vue'), {}, {
- done: async result => {
- const { name, permissions } = result;
- const { token } = await os.api('miauth/gen-token', {
- session: null,
- name: name,
- permission: permissions,
- });
-
- os.alert({
- type: 'success',
- title: this.$ts.token,
- text: token
- });
- },
- }, 'closed');
+ os.alert({
+ type: 'success',
+ title: i18n.ts.token,
+ text: token
+ });
},
+ }, 'closed');
+}
+
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: 'API',
+ icon: 'fas fa-key',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/apps.vue b/packages/client/src/pages/settings/apps.vue
index 9c0fa8a54d..f3b251d9b2 100644
--- a/packages/client/src/pages/settings/apps.vue
+++ b/packages/client/src/pages/settings/apps.vue
@@ -4,7 +4,7 @@
<template #empty>
<div class="_fullinfo">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
- <div>{{ $ts.nothing }}</div>
+ <div>{{ i18n.ts.nothing }}</div>
</div>
</template>
<template v-slot="{items}">
@@ -14,18 +14,18 @@
<div class="name">{{ token.name }}</div>
<div class="description">{{ token.description }}</div>
<div class="_keyValue">
- <div>{{ $ts.installedDate }}:</div>
+ <div>{{ i18n.ts.installedDate }}:</div>
<div><MkTime :time="token.createdAt"/></div>
</div>
<div class="_keyValue">
- <div>{{ $ts.lastUsedDate }}:</div>
+ <div>{{ i18n.ts.lastUsedDate }}:</div>
<div><MkTime :time="token.lastUsedAt"/></div>
</div>
<div class="actions">
<button class="_button" @click="revoke(token)"><i class="fas fa-trash-alt"></i></button>
</div>
<details>
- <summary>{{ $ts.details }}</summary>
+ <summary>{{ i18n.ts.details }}</summary>
<ul>
<li v-for="p in token.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
</ul>
@@ -37,42 +37,34 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, ref } from 'vue';
import FormPagination from '@/components/ui/pagination.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormPagination,
- },
+const list = ref<any>(null);
- emits: ['info'],
+const pagination = {
+ endpoint: 'i/apps' as const,
+ limit: 100,
+ params: {
+ sort: '+lastUsedAt'
+ }
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.installedApps,
- icon: 'fas fa-plug',
- bg: 'var(--bg)',
- },
- pagination: {
- endpoint: 'i/apps' as const,
- limit: 100,
- params: {
- sort: '+lastUsedAt'
- }
- },
- };
- },
+function revoke(token) {
+ os.api('i/revoke-token', { tokenId: token.id }).then(() => {
+ list.value.reload();
+ });
+}
- methods: {
- revoke(token) {
- os.api('i/revoke-token', { tokenId: token.id }).then(() => {
- this.$refs.list.reload();
- });
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.installedApps,
+ icon: 'fas fa-plug',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/custom-css.vue b/packages/client/src/pages/settings/custom-css.vue
index 556ee30c1d..20db077ceb 100644
--- a/packages/client/src/pages/settings/custom-css.vue
+++ b/packages/client/src/pages/settings/custom-css.vue
@@ -1,6 +1,6 @@
<template>
<div class="_formRoot">
- <FormInfo warn class="_formBlock">{{ $ts.customCssWarn }}</FormInfo>
+ <FormInfo warn class="_formBlock">{{ i18n.ts.customCssWarn }}</FormInfo>
<FormTextarea v-model="localCustomCss" manual-save tall class="_monospace _formBlock" style="tab-size: 2;">
<template #label>CSS</template>
@@ -8,50 +8,38 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, ref, watch } from 'vue';
import FormTextarea from '@/components/form/textarea.vue';
import FormInfo from '@/components/ui/info.vue';
import * as os from '@/os';
import { unisonReload } from '@/scripts/unison-reload';
import * as symbols from '@/symbols';
-import { defaultStore } from '@/store';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormTextarea,
- FormInfo,
- },
+const localCustomCss = ref(localStorage.getItem('customCss') ?? '');
- emits: ['info'],
+async function apply() {
+ localStorage.setItem('customCss', localCustomCss.value);
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.customCss,
- icon: 'fas fa-code',
- bg: 'var(--bg)',
- },
- localCustomCss: localStorage.getItem('customCss')
- }
- },
+ const { canceled } = await os.confirm({
+ type: 'info',
+ text: i18n.ts.reloadToApplySetting,
+ });
+ if (canceled) return;
- mounted() {
- this.$watch('localCustomCss', this.apply);
- },
+ unisonReload();
+}
- methods: {
- async apply() {
- localStorage.setItem('customCss', this.localCustomCss);
-
- const { canceled } = await os.confirm({
- type: 'info',
- text: this.$ts.reloadToApplySetting,
- });
- if (canceled) return;
+watch(localCustomCss, async () => {
+ await apply();
+});
- unisonReload();
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.customCss,
+ icon: 'fas fa-code',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/deck.vue b/packages/client/src/pages/settings/deck.vue
index 46b90d3d1a..2d868aa0a7 100644
--- a/packages/client/src/pages/settings/deck.vue
+++ b/packages/client/src/pages/settings/deck.vue
@@ -1,36 +1,36 @@
<template>
<div class="_formRoot">
<FormGroup>
- <template #label>{{ $ts.defaultNavigationBehaviour }}</template>
- <FormSwitch v-model="navWindow">{{ $ts.openInWindow }}</FormSwitch>
+ <template #label>{{ i18n.ts.defaultNavigationBehaviour }}</template>
+ <FormSwitch v-model="navWindow">{{ i18n.ts.openInWindow }}</FormSwitch>
</FormGroup>
- <FormSwitch v-model="alwaysShowMainColumn" class="_formBlock">{{ $ts._deck.alwaysShowMainColumn }}</FormSwitch>
+ <FormSwitch v-model="alwaysShowMainColumn" class="_formBlock">{{ i18n.ts._deck.alwaysShowMainColumn }}</FormSwitch>
<FormRadios v-model="columnAlign" class="_formBlock">
- <template #label>{{ $ts._deck.columnAlign }}</template>
- <option value="left">{{ $ts.left }}</option>
- <option value="center">{{ $ts.center }}</option>
+ <template #label>{{ i18n.ts._deck.columnAlign }}</template>
+ <option value="left">{{ i18n.ts.left }}</option>
+ <option value="center">{{ i18n.ts.center }}</option>
</FormRadios>
<FormRadios v-model="columnHeaderHeight" class="_formBlock">
- <template #label>{{ $ts._deck.columnHeaderHeight }}</template>
- <option :value="42">{{ $ts.narrow }}</option>
- <option :value="45">{{ $ts.medium }}</option>
- <option :value="48">{{ $ts.wide }}</option>
+ <template #label>{{ i18n.ts._deck.columnHeaderHeight }}</template>
+ <option :value="42">{{ i18n.ts.narrow }}</option>
+ <option :value="45">{{ i18n.ts.medium }}</option>
+ <option :value="48">{{ i18n.ts.wide }}</option>
</FormRadios>
<FormInput v-model="columnMargin" type="number" class="_formBlock">
- <template #label>{{ $ts._deck.columnMargin }}</template>
+ <template #label>{{ i18n.ts._deck.columnMargin }}</template>
<template #suffix>px</template>
</FormInput>
- <FormLink class="_formBlock" @click="setProfile">{{ $ts._deck.profile }}<template #suffix>{{ profile }}</template></FormLink>
+ <FormLink class="_formBlock" @click="setProfile">{{ i18n.ts._deck.profile }}<template #suffix>{{ profile }}</template></FormLink>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed, defineExpose, watch } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormLink from '@/components/form/link.vue';
import FormRadios from '@/components/form/radios.vue';
@@ -40,59 +40,41 @@ import { deckStore } from '@/ui/deck/deck-store';
import * as os from '@/os';
import { unisonReload } from '@/scripts/unison-reload';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSwitch,
- FormLink,
- FormInput,
- FormRadios,
- FormGroup,
- },
+const navWindow = computed(deckStore.makeGetterSetter('navWindow'));
+const alwaysShowMainColumn = computed(deckStore.makeGetterSetter('alwaysShowMainColumn'));
+const columnAlign = computed(deckStore.makeGetterSetter('columnAlign'));
+const columnMargin = computed(deckStore.makeGetterSetter('columnMargin'));
+const columnHeaderHeight = computed(deckStore.makeGetterSetter('columnHeaderHeight'));
+const profile = computed(deckStore.makeGetterSetter('profile'));
- emits: ['info'],
+watch(navWindow, async () => {
+ const { canceled } = await os.confirm({
+ type: 'info',
+ text: i18n.ts.reloadToApplySetting,
+ });
+ if (canceled) return;
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.deck,
- icon: 'fas fa-columns',
- bg: 'var(--bg)',
- },
- }
- },
-
- computed: {
- navWindow: deckStore.makeGetterSetter('navWindow'),
- alwaysShowMainColumn: deckStore.makeGetterSetter('alwaysShowMainColumn'),
- columnAlign: deckStore.makeGetterSetter('columnAlign'),
- columnMargin: deckStore.makeGetterSetter('columnMargin'),
- columnHeaderHeight: deckStore.makeGetterSetter('columnHeaderHeight'),
- profile: deckStore.makeGetterSetter('profile'),
- },
-
- watch: {
- async navWindow() {
- const { canceled } = await os.confirm({
- type: 'info',
- text: this.$ts.reloadToApplySetting,
- });
- if (canceled) return;
+ unisonReload();
+});
- unisonReload();
- }
- },
+async function setProfile() {
+ const { canceled, result: name } = await os.inputText({
+ title: i18n.ts._deck.profile,
+ allowEmpty: false
+ });
+ if (canceled) return;
+
+ profile.value = name;
+ unisonReload();
+}
- methods: {
- async setProfile() {
- const { canceled, result: name } = await os.inputText({
- title: this.$ts._deck.profile,
- allowEmpty: false
- });
- if (canceled) return;
- this.profile = name;
- unisonReload();
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.deck,
+ icon: 'fas fa-columns',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/delete-account.vue b/packages/client/src/pages/settings/delete-account.vue
index 7edc81a309..e9f19aaf0b 100644
--- a/packages/client/src/pages/settings/delete-account.vue
+++ b/packages/client/src/pages/settings/delete-account.vue
@@ -1,64 +1,52 @@
<template>
<div class="_formRoot">
- <FormInfo warn class="_formBlock">{{ $ts._accountDelete.mayTakeTime }}</FormInfo>
- <FormInfo class="_formBlock">{{ $ts._accountDelete.sendEmail }}</FormInfo>
- <FormButton v-if="!$i.isDeleted" danger class="_formBlock" @click="deleteAccount">{{ $ts._accountDelete.requestAccountDelete }}</FormButton>
- <FormButton v-else disabled>{{ $ts._accountDelete.inProgress }}</FormButton>
+ <FormInfo warn class="_formBlock">{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo>
+ <FormInfo class="_formBlock">{{ i18n.ts._accountDelete.sendEmail }}</FormInfo>
+ <FormButton v-if="!$i.isDeleted" danger class="_formBlock" @click="deleteAccount">{{ i18n.ts._accountDelete.requestAccountDelete }}</FormButton>
+ <FormButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</FormButton>
</div>
</template>
-<script lang="ts">
-import { defineAsyncComponent, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose } from 'vue';
import FormInfo from '@/components/ui/info.vue';
import FormButton from '@/components/ui/button.vue';
import * as os from '@/os';
import { signout } from '@/account';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormButton,
- FormInfo,
- },
-
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts._accountDelete.accountDelete,
- icon: 'fas fa-exclamation-triangle',
- bg: 'var(--bg)',
- },
- }
- },
+async function deleteAccount() {
+ {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.ts.deleteAccountConfirm,
+ });
+ if (canceled) return;
+ }
- methods: {
- async deleteAccount() {
- {
- const { canceled } = await os.confirm({
- type: 'warning',
- text: this.$ts.deleteAccountConfirm,
- });
- if (canceled) return;
- }
+ const { canceled, result: password } = await os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ });
+ if (canceled) return;
- const { canceled, result: password } = await os.inputText({
- title: this.$ts.password,
- type: 'password'
- });
- if (canceled) return;
+ await os.apiWithDialog('i/delete-account', {
+ password: password
+ });
- await os.apiWithDialog('i/delete-account', {
- password: password
- });
+ await os.alert({
+ title: i18n.ts._accountDelete.started,
+ });
- await os.alert({
- title: this.$ts._accountDelete.started,
- });
+ await signout();
+}
- signout();
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts._accountDelete.accountDelete,
+ icon: 'fas fa-exclamation-triangle',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/drive.vue b/packages/client/src/pages/settings/drive.vue
index 9309eb5ec7..f235ace7ca 100644
--- a/packages/client/src/pages/settings/drive.vue
+++ b/packages/client/src/pages/settings/drive.vue
@@ -1,40 +1,40 @@
<template>
<div class="_formRoot">
<FormSection v-if="!fetching">
- <template #label>{{ $ts.usageAmount }}</template>
+ <template #label>{{ i18n.ts.usageAmount }}</template>
<div class="_formBlock uawsfosz">
<div class="meter"><div :style="meterStyle"></div></div>
</div>
<FormSplit>
<MkKeyValue class="_formBlock">
- <template #key>{{ $ts.capacity }}</template>
+ <template #key>{{ i18n.ts.capacity }}</template>
<template #value>{{ bytes(capacity, 1) }}</template>
</MkKeyValue>
<MkKeyValue class="_formBlock">
- <template #key>{{ $ts.inUse }}</template>
+ <template #key>{{ i18n.ts.inUse }}</template>
<template #value>{{ bytes(usage, 1) }}</template>
</MkKeyValue>
</FormSplit>
</FormSection>
<FormSection>
- <template #label>{{ $ts.statistics }}</template>
+ <template #label>{{ i18n.ts.statistics }}</template>
<MkChart src="per-user-drive" :args="{ user: $i }" span="day" :limit="7 * 5" :bar="true" :stacked="true" :detailed="false" :aspect-ratio="6"/>
</FormSection>
<FormSection>
<FormLink @click="chooseUploadFolder()">
- {{ $ts.uploadFolder }}
+ {{ i18n.ts.uploadFolder }}
<template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template>
<template #suffixIcon><i class="fas fa-folder-open"></i></template>
</FormLink>
- <FormSwitch v-model="keepOriginalUploading" class="_formBlock">{{ $ts.keepOriginalUploading }}<template #caption>{{ $ts.keepOriginalUploadingDescription }}</template></FormSwitch>
+ <FormSwitch v-model="keepOriginalUploading" class="_formBlock">{{ i18n.ts.keepOriginalUploading }}<template #caption>{{ i18n.ts.keepOriginalUploadingDescription }}</template></FormSwitch>
</FormSection>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed, defineExpose, ref } from 'vue';
import * as tinycolor from 'tinycolor2';
import FormLink from '@/components/form/link.vue';
import FormSwitch from '@/components/form/switch.vue';
@@ -46,80 +46,59 @@ import bytes from '@/filters/bytes';
import * as symbols from '@/symbols';
import { defaultStore } from '@/store';
import MkChart from '@/components/chart.vue';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormLink,
- FormSwitch,
- FormSection,
- MkKeyValue,
- FormSplit,
- MkChart,
- },
+const fetching = ref(true);
+const usage = ref<any>(null);
+const capacity = ref<any>(null);
+const uploadFolder = ref<any>(null);
- emits: ['info'],
+const meterStyle = computed(() => {
+ return {
+ width: `${usage.value / capacity.value * 100}%`,
+ background: tinycolor({
+ h: 180 - (usage.value / capacity.value * 180),
+ s: 0.7,
+ l: 0.5
+ })
+ };
+});
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.drive,
- icon: 'fas fa-cloud',
- bg: 'var(--bg)',
- },
- fetching: true,
- usage: null,
- capacity: null,
- uploadFolder: null,
- }
- },
+const keepOriginalUploading = computed(defaultStore.makeGetterSetter('keepOriginalUploading'));
- computed: {
- meterStyle(): any {
- return {
- width: `${this.usage / this.capacity * 100}%`,
- background: tinycolor({
- h: 180 - (this.usage / this.capacity * 180),
- s: 0.7,
- l: 0.5
- })
- };
- },
- keepOriginalUploading: defaultStore.makeGetterSetter('keepOriginalUploading'),
- },
+os.api('drive').then(info => {
+ capacity.value = info.capacity;
+ usage.value = info.usage;
+ fetching.value = false;
+});
- async created() {
- os.api('drive').then(info => {
- this.capacity = info.capacity;
- this.usage = info.usage;
- this.fetching = false;
- this.$nextTick(() => {
- this.renderChart();
- });
- });
+if (defaultStore.state.uploadFolder) {
+ os.api('drive/folders/show', {
+ folderId: defaultStore.state.uploadFolder
+ }).then(response => {
+ uploadFolder.value = response;
+ });
+}
- if (this.$store.state.uploadFolder) {
- this.uploadFolder = await os.api('drive/folders/show', {
- folderId: this.$store.state.uploadFolder
+function chooseUploadFolder() {
+ os.selectDriveFolder(false).then(async folder => {
+ defaultStore.set('uploadFolder', folder ? folder.id : null);
+ os.success();
+ if (defaultStore.state.uploadFolder) {
+ uploadFolder.value = await os.api('drive/folders/show', {
+ folderId: defaultStore.state.uploadFolder
});
+ } else {
+ uploadFolder.value = null;
}
- },
-
- methods: {
- chooseUploadFolder() {
- os.selectDriveFolder(false).then(async folder => {
- this.$store.set('uploadFolder', folder ? folder.id : null);
- os.success();
- if (this.$store.state.uploadFolder) {
- this.uploadFolder = await os.api('drive/folders/show', {
- folderId: this.$store.state.uploadFolder
- });
- } else {
- this.uploadFolder = null;
- }
- });
- },
+ });
+}
- bytes
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.drive,
+ icon: 'fas fa-cloud',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/email.vue b/packages/client/src/pages/settings/email.vue
index 4697fec9b7..37f14068e2 100644
--- a/packages/client/src/pages/settings/email.vue
+++ b/packages/client/src/pages/settings/email.vue
@@ -39,8 +39,8 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent, onMounted, ref, watch } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, onMounted, ref, watch } from 'vue';
import FormSection from '@/components/form/section.vue';
import FormInput from '@/components/form/input.vue';
import FormSwitch from '@/components/form/switch.vue';
@@ -49,79 +49,62 @@ import * as symbols from '@/symbols';
import { $i } from '@/account';
import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSection,
- FormSwitch,
- FormInput,
- },
+const emailAddress = ref($i!.email);
- emits: ['info'],
+const onChangeReceiveAnnouncementEmail = (v) => {
+ os.api('i/update', {
+ receiveAnnouncementEmail: v
+ });
+};
- setup(props, context) {
- const emailAddress = ref($i.email);
-
- const INFO = {
- title: i18n.ts.email,
- icon: 'fas fa-envelope',
- bg: 'var(--bg)',
- };
-
- const onChangeReceiveAnnouncementEmail = (v) => {
- os.api('i/update', {
- receiveAnnouncementEmail: v
- });
- };
-
- const saveEmailAddress = () => {
- os.inputText({
- title: i18n.ts.password,
- type: 'password'
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- os.apiWithDialog('i/update-email', {
- password: password,
- email: emailAddress.value,
- });
- });
- };
+const saveEmailAddress = () => {
+ os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ }).then(({ canceled, result: password }) => {
+ if (canceled) return;
+ os.apiWithDialog('i/update-email', {
+ password: password,
+ email: emailAddress.value,
+ });
+ });
+};
- const emailNotification_mention = ref($i.emailNotificationTypes.includes('mention'));
- const emailNotification_reply = ref($i.emailNotificationTypes.includes('reply'));
- const emailNotification_quote = ref($i.emailNotificationTypes.includes('quote'));
- const emailNotification_follow = ref($i.emailNotificationTypes.includes('follow'));
- const emailNotification_receiveFollowRequest = ref($i.emailNotificationTypes.includes('receiveFollowRequest'));
- const emailNotification_groupInvited = ref($i.emailNotificationTypes.includes('groupInvited'));
+const emailNotification_mention = ref($i!.emailNotificationTypes.includes('mention'));
+const emailNotification_reply = ref($i!.emailNotificationTypes.includes('reply'));
+const emailNotification_quote = ref($i!.emailNotificationTypes.includes('quote'));
+const emailNotification_follow = ref($i!.emailNotificationTypes.includes('follow'));
+const emailNotification_receiveFollowRequest = ref($i!.emailNotificationTypes.includes('receiveFollowRequest'));
+const emailNotification_groupInvited = ref($i!.emailNotificationTypes.includes('groupInvited'));
- const saveNotificationSettings = () => {
- os.api('i/update', {
- emailNotificationTypes: [
- ...[emailNotification_mention.value ? 'mention' : null],
- ...[emailNotification_reply.value ? 'reply' : null],
- ...[emailNotification_quote.value ? 'quote' : null],
- ...[emailNotification_follow.value ? 'follow' : null],
- ...[emailNotification_receiveFollowRequest.value ? 'receiveFollowRequest' : null],
- ...[emailNotification_groupInvited.value ? 'groupInvited' : null],
- ].filter(x => x != null)
- });
- };
+const saveNotificationSettings = () => {
+ os.api('i/update', {
+ emailNotificationTypes: [
+ ...[emailNotification_mention.value ? 'mention' : null],
+ ...[emailNotification_reply.value ? 'reply' : null],
+ ...[emailNotification_quote.value ? 'quote' : null],
+ ...[emailNotification_follow.value ? 'follow' : null],
+ ...[emailNotification_receiveFollowRequest.value ? 'receiveFollowRequest' : null],
+ ...[emailNotification_groupInvited.value ? 'groupInvited' : null],
+ ].filter(x => x != null)
+ });
+};
- watch([emailNotification_mention, emailNotification_reply, emailNotification_quote, emailNotification_follow, emailNotification_receiveFollowRequest, emailNotification_groupInvited], () => {
- saveNotificationSettings();
- });
+watch([emailNotification_mention, emailNotification_reply, emailNotification_quote, emailNotification_follow, emailNotification_receiveFollowRequest, emailNotification_groupInvited], () => {
+ saveNotificationSettings();
+});
- onMounted(() => {
- watch(emailAddress, () => {
- saveEmailAddress();
- });
- });
+onMounted(() => {
+ watch(emailAddress, () => {
+ saveEmailAddress();
+ });
+});
- return {
- [symbols.PAGE_INFO]: INFO,
- emailAddress,
- onChangeReceiveAnnouncementEmail,
- emailNotification_mention, emailNotification_reply, emailNotification_quote, emailNotification_follow, emailNotification_receiveFollowRequest, emailNotification_groupInvited,
- };
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.email,
+ icon: 'fas fa-envelope',
+ bg: 'var(--bg)',
+ }
});
</script>
diff --git a/packages/client/src/pages/settings/general.vue b/packages/client/src/pages/settings/general.vue
index c8f6f58322..64b8cc3106 100644
--- a/packages/client/src/pages/settings/general.vue
+++ b/packages/client/src/pages/settings/general.vue
@@ -1,10 +1,10 @@
<template>
<div class="_formRoot">
<FormSelect v-model="lang" class="_formBlock">
- <template #label>{{ $ts.uiLanguage }}</template>
+ <template #label>{{ i18n.ts.uiLanguage }}</template>
<option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option>
<template #caption>
- <I18n :src="$ts.i18nInfo" tag="span">
+ <I18n :src="i18n.ts.i18nInfo" tag="span">
<template #link>
<MkLink url="https://crowdin.com/project/misskey">Crowdin</MkLink>
</template>
@@ -13,48 +13,48 @@
</FormSelect>
<FormRadios v-model="overridedDeviceKind" class="_formBlock">
- <template #label>{{ $ts.overridedDeviceKind }}</template>
- <option :value="null">{{ $ts.auto }}</option>
- <option value="smartphone"><i class="fas fa-mobile-alt"/> {{ $ts.smartphone }}</option>
- <option value="tablet"><i class="fas fa-tablet-alt"/> {{ $ts.tablet }}</option>
- <option value="desktop"><i class="fas fa-desktop"/> {{ $ts.desktop }}</option>
+ <template #label>{{ i18n.ts.overridedDeviceKind }}</template>
+ <option :value="null">{{ i18n.ts.auto }}</option>
+ <option value="smartphone"><i class="fas fa-mobile-alt"/> {{ i18n.ts.smartphone }}</option>
+ <option value="tablet"><i class="fas fa-tablet-alt"/> {{ i18n.ts.tablet }}</option>
+ <option value="desktop"><i class="fas fa-desktop"/> {{ i18n.ts.desktop }}</option>
</FormRadios>
- <FormSwitch v-model="showFixedPostForm" class="_formBlock">{{ $ts.showFixedPostForm }}</FormSwitch>
+ <FormSwitch v-model="showFixedPostForm" class="_formBlock">{{ i18n.ts.showFixedPostForm }}</FormSwitch>
<FormSection>
- <template #label>{{ $ts.behavior }}</template>
- <FormSwitch v-model="imageNewTab" class="_formBlock">{{ $ts.openImageInNewTab }}</FormSwitch>
- <FormSwitch v-model="enableInfiniteScroll" class="_formBlock">{{ $ts.enableInfiniteScroll }}</FormSwitch>
- <FormSwitch v-model="useReactionPickerForContextMenu" class="_formBlock">{{ $ts.useReactionPickerForContextMenu }}</FormSwitch>
- <FormSwitch v-model="disablePagesScript" class="_formBlock">{{ $ts.disablePagesScript }}</FormSwitch>
+ <template #label>{{ i18n.ts.behavior }}</template>
+ <FormSwitch v-model="imageNewTab" class="_formBlock">{{ i18n.ts.openImageInNewTab }}</FormSwitch>
+ <FormSwitch v-model="enableInfiniteScroll" class="_formBlock">{{ i18n.ts.enableInfiniteScroll }}</FormSwitch>
+ <FormSwitch v-model="useReactionPickerForContextMenu" class="_formBlock">{{ i18n.ts.useReactionPickerForContextMenu }}</FormSwitch>
+ <FormSwitch v-model="disablePagesScript" class="_formBlock">{{ i18n.ts.disablePagesScript }}</FormSwitch>
<FormSelect v-model="serverDisconnectedBehavior" class="_formBlock">
- <template #label>{{ $ts.whenServerDisconnected }}</template>
- <option value="reload">{{ $ts._serverDisconnectedBehavior.reload }}</option>
- <option value="dialog">{{ $ts._serverDisconnectedBehavior.dialog }}</option>
- <option value="quiet">{{ $ts._serverDisconnectedBehavior.quiet }}</option>
+ <template #label>{{ i18n.ts.whenServerDisconnected }}</template>
+ <option value="reload">{{ i18n.ts._serverDisconnectedBehavior.reload }}</option>
+ <option value="dialog">{{ i18n.ts._serverDisconnectedBehavior.dialog }}</option>
+ <option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option>
</FormSelect>
</FormSection>
<FormSection>
- <template #label>{{ $ts.appearance }}</template>
- <FormSwitch v-model="disableAnimatedMfm" class="_formBlock">{{ $ts.disableAnimatedMfm }}</FormSwitch>
- <FormSwitch v-model="reduceAnimation" class="_formBlock">{{ $ts.reduceUiAnimation }}</FormSwitch>
- <FormSwitch v-model="useBlurEffect" class="_formBlock">{{ $ts.useBlurEffect }}</FormSwitch>
- <FormSwitch v-model="useBlurEffectForModal" class="_formBlock">{{ $ts.useBlurEffectForModal }}</FormSwitch>
- <FormSwitch v-model="showGapBetweenNotesInTimeline" class="_formBlock">{{ $ts.showGapBetweenNotesInTimeline }}</FormSwitch>
- <FormSwitch v-model="loadRawImages" class="_formBlock">{{ $ts.loadRawImages }}</FormSwitch>
- <FormSwitch v-model="disableShowingAnimatedImages" class="_formBlock">{{ $ts.disableShowingAnimatedImages }}</FormSwitch>
- <FormSwitch v-model="squareAvatars" class="_formBlock">{{ $ts.squareAvatars }}</FormSwitch>
- <FormSwitch v-model="useSystemFont" class="_formBlock">{{ $ts.useSystemFont }}</FormSwitch>
- <FormSwitch v-model="useOsNativeEmojis" class="_formBlock">{{ $ts.useOsNativeEmojis }}
+ <template #label>{{ i18n.ts.appearance }}</template>
+ <FormSwitch v-model="disableAnimatedMfm" class="_formBlock">{{ i18n.ts.disableAnimatedMfm }}</FormSwitch>
+ <FormSwitch v-model="reduceAnimation" class="_formBlock">{{ i18n.ts.reduceUiAnimation }}</FormSwitch>
+ <FormSwitch v-model="useBlurEffect" class="_formBlock">{{ i18n.ts.useBlurEffect }}</FormSwitch>
+ <FormSwitch v-model="useBlurEffectForModal" class="_formBlock">{{ i18n.ts.useBlurEffectForModal }}</FormSwitch>
+ <FormSwitch v-model="showGapBetweenNotesInTimeline" class="_formBlock">{{ i18n.ts.showGapBetweenNotesInTimeline }}</FormSwitch>
+ <FormSwitch v-model="loadRawImages" class="_formBlock">{{ i18n.ts.loadRawImages }}</FormSwitch>
+ <FormSwitch v-model="disableShowingAnimatedImages" class="_formBlock">{{ i18n.ts.disableShowingAnimatedImages }}</FormSwitch>
+ <FormSwitch v-model="squareAvatars" class="_formBlock">{{ i18n.ts.squareAvatars }}</FormSwitch>
+ <FormSwitch v-model="useSystemFont" class="_formBlock">{{ i18n.ts.useSystemFont }}</FormSwitch>
+ <FormSwitch v-model="useOsNativeEmojis" class="_formBlock">{{ i18n.ts.useOsNativeEmojis }}
<div><Mfm :key="useOsNativeEmojis" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div>
</FormSwitch>
- <FormSwitch v-model="disableDrawer" class="_formBlock">{{ $ts.disableDrawer }}</FormSwitch>
+ <FormSwitch v-model="disableDrawer" class="_formBlock">{{ i18n.ts.disableDrawer }}</FormSwitch>
<FormRadios v-model="fontSize" class="_formBlock">
- <template #label>{{ $ts.fontSize }}</template>
+ <template #label>{{ i18n.ts.fontSize }}</template>
<option value="small"><span style="font-size: 14px;">Aa</span></option>
<option :value="null"><span style="font-size: 16px;">Aa</span></option>
<option value="large"><span style="font-size: 18px;">Aa</span></option>
@@ -63,36 +63,36 @@
</FormSection>
<FormSection>
- <FormSwitch v-model="aiChanMode">{{ $ts.aiChanMode }}</FormSwitch>
+ <FormSwitch v-model="aiChanMode">{{ i18n.ts.aiChanMode }}</FormSwitch>
</FormSection>
<FormSelect v-model="instanceTicker" class="_formBlock">
- <template #label>{{ $ts.instanceTicker }}</template>
- <option value="none">{{ $ts._instanceTicker.none }}</option>
- <option value="remote">{{ $ts._instanceTicker.remote }}</option>
- <option value="always">{{ $ts._instanceTicker.always }}</option>
+ <template #label>{{ i18n.ts.instanceTicker }}</template>
+ <option value="none">{{ i18n.ts._instanceTicker.none }}</option>
+ <option value="remote">{{ i18n.ts._instanceTicker.remote }}</option>
+ <option value="always">{{ i18n.ts._instanceTicker.always }}</option>
</FormSelect>
<FormSelect v-model="nsfw" class="_formBlock">
- <template #label>{{ $ts.nsfw }}</template>
- <option value="respect">{{ $ts._nsfw.respect }}</option>
- <option value="ignore">{{ $ts._nsfw.ignore }}</option>
- <option value="force">{{ $ts._nsfw.force }}</option>
+ <template #label>{{ i18n.ts.nsfw }}</template>
+ <option value="respect">{{ i18n.ts._nsfw.respect }}</option>
+ <option value="ignore">{{ i18n.ts._nsfw.ignore }}</option>
+ <option value="force">{{ i18n.ts._nsfw.force }}</option>
</FormSelect>
<FormGroup>
- <template #label>{{ $ts.defaultNavigationBehaviour }}</template>
- <FormSwitch v-model="defaultSideView">{{ $ts.openInSideView }}</FormSwitch>
+ <template #label>{{ i18n.ts.defaultNavigationBehaviour }}</template>
+ <FormSwitch v-model="defaultSideView">{{ i18n.ts.openInSideView }}</FormSwitch>
</FormGroup>
- <FormLink to="/settings/deck" class="_formBlock">{{ $ts.deck }}</FormLink>
+ <FormLink to="/settings/deck" class="_formBlock">{{ i18n.ts.deck }}</FormLink>
- <FormLink to="/settings/custom-css" class="_formBlock"><template #icon><i class="fas fa-code"></i></template>{{ $ts.customCss }}</FormLink>
+ <FormLink to="/settings/custom-css" class="_formBlock"><template #icon><i class="fas fa-code"></i></template>{{ i18n.ts.customCss }}</FormLink>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed, defineExpose, ref, watch } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
import FormRadios from '@/components/form/radios.vue';
@@ -102,122 +102,87 @@ import FormLink from '@/components/form/link.vue';
import MkLink from '@/components/link.vue';
import { langs } from '@/config';
import { defaultStore } from '@/store';
-import { ColdDeviceStorage } from '@/store';
import * as os from '@/os';
import { unisonReload } from '@/scripts/unison-reload';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkLink,
- FormSwitch,
- FormSelect,
- FormRadios,
- FormGroup,
- FormLink,
- FormSection,
- },
+const lang = ref(localStorage.getItem('lang'));
+const fontSize = ref(localStorage.getItem('fontSize'));
+const useSystemFont = ref(localStorage.getItem('useSystemFont') != null);
- emits: ['info'],
+async function reloadAsk() {
+ const { canceled } = await os.confirm({
+ type: 'info',
+ text: i18n.ts.reloadToApplySetting,
+ });
+ if (canceled) return;
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.general,
- icon: 'fas fa-cogs',
- bg: 'var(--bg)'
- },
- langs,
- lang: localStorage.getItem('lang'),
- fontSize: localStorage.getItem('fontSize'),
- useSystemFont: localStorage.getItem('useSystemFont') != null,
- }
- },
+ unisonReload();
+}
- computed: {
- overridedDeviceKind: defaultStore.makeGetterSetter('overridedDeviceKind'),
- serverDisconnectedBehavior: defaultStore.makeGetterSetter('serverDisconnectedBehavior'),
- reduceAnimation: defaultStore.makeGetterSetter('animation', v => !v, v => !v),
- useBlurEffectForModal: defaultStore.makeGetterSetter('useBlurEffectForModal'),
- useBlurEffect: defaultStore.makeGetterSetter('useBlurEffect'),
- showGapBetweenNotesInTimeline: defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'),
- disableAnimatedMfm: defaultStore.makeGetterSetter('animatedMfm', v => !v, v => !v),
- useOsNativeEmojis: defaultStore.makeGetterSetter('useOsNativeEmojis'),
- disableDrawer: defaultStore.makeGetterSetter('disableDrawer'),
- disableShowingAnimatedImages: defaultStore.makeGetterSetter('disableShowingAnimatedImages'),
- loadRawImages: defaultStore.makeGetterSetter('loadRawImages'),
- imageNewTab: defaultStore.makeGetterSetter('imageNewTab'),
- nsfw: defaultStore.makeGetterSetter('nsfw'),
- disablePagesScript: defaultStore.makeGetterSetter('disablePagesScript'),
- showFixedPostForm: defaultStore.makeGetterSetter('showFixedPostForm'),
- defaultSideView: defaultStore.makeGetterSetter('defaultSideView'),
- instanceTicker: defaultStore.makeGetterSetter('instanceTicker'),
- enableInfiniteScroll: defaultStore.makeGetterSetter('enableInfiniteScroll'),
- useReactionPickerForContextMenu: defaultStore.makeGetterSetter('useReactionPickerForContextMenu'),
- squareAvatars: defaultStore.makeGetterSetter('squareAvatars'),
- aiChanMode: defaultStore.makeGetterSetter('aiChanMode'),
- },
+const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind'));
+const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior'));
+const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v));
+const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal'));
+const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect'));
+const showGapBetweenNotesInTimeline = computed(defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'));
+const disableAnimatedMfm = computed(defaultStore.makeGetterSetter('animatedMfm', v => !v, v => !v));
+const useOsNativeEmojis = computed(defaultStore.makeGetterSetter('useOsNativeEmojis'));
+const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer'));
+const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages'));
+const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages'));
+const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab'));
+const nsfw = computed(defaultStore.makeGetterSetter('nsfw'));
+const disablePagesScript = computed(defaultStore.makeGetterSetter('disablePagesScript'));
+const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm'));
+const defaultSideView = computed(defaultStore.makeGetterSetter('defaultSideView'));
+const instanceTicker = computed(defaultStore.makeGetterSetter('instanceTicker'));
+const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfiniteScroll'));
+const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu'));
+const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars'));
+const aiChanMode = computed(defaultStore.makeGetterSetter('aiChanMode'));
- watch: {
- lang() {
- localStorage.setItem('lang', this.lang);
- localStorage.removeItem('locale');
- this.reloadAsk();
- },
-
- fontSize() {
- if (this.fontSize == null) {
- localStorage.removeItem('fontSize');
- } else {
- localStorage.setItem('fontSize', this.fontSize);
- }
- this.reloadAsk();
- },
-
- useSystemFont() {
- if (this.useSystemFont) {
- localStorage.setItem('useSystemFont', 't');
- } else {
- localStorage.removeItem('useSystemFont');
- }
- this.reloadAsk();
- },
-
- enableInfiniteScroll() {
- this.reloadAsk();
- },
-
- squareAvatars() {
- this.reloadAsk();
- },
-
- aiChanMode() {
- this.reloadAsk();
- },
-
- showGapBetweenNotesInTimeline() {
- this.reloadAsk();
- },
+watch(lang, () => {
+ localStorage.setItem('lang', lang.value as string);
+ localStorage.removeItem('locale');
+});
- instanceTicker() {
- this.reloadAsk();
- },
+watch(fontSize, () => {
+ if (fontSize.value == null) {
+ localStorage.removeItem('fontSize');
+ } else {
+ localStorage.setItem('fontSize', fontSize.value);
+ }
+});
- overridedDeviceKind() {
- this.reloadAsk();
- },
- },
+watch(useSystemFont, () => {
+ if (useSystemFont.value) {
+ localStorage.setItem('useSystemFont', 't');
+ } else {
+ localStorage.removeItem('useSystemFont');
+ }
+});
- methods: {
- async reloadAsk() {
- const { canceled } = await os.confirm({
- type: 'info',
- text: this.$ts.reloadToApplySetting,
- });
- if (canceled) return;
+watch([
+ lang,
+ fontSize,
+ useSystemFont,
+ enableInfiniteScroll,
+ squareAvatars,
+ aiChanMode,
+ showGapBetweenNotesInTimeline,
+ instanceTicker,
+ overridedDeviceKind
+], async () => {
+ await reloadAsk();
+});
- unisonReload();
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.general,
+ icon: 'fas fa-cogs',
+ bg: 'var(--bg)'
}
});
</script>
diff --git a/packages/client/src/pages/settings/menu.vue b/packages/client/src/pages/settings/menu.vue
index 6e38cd5dfe..2288c3f718 100644
--- a/packages/client/src/pages/settings/menu.vue
+++ b/packages/client/src/pages/settings/menu.vue
@@ -1,24 +1,24 @@
<template>
<div class="_formRoot">
<FormTextarea v-model="items" tall manual-save class="_formBlock">
- <template #label>{{ $ts.menu }}</template>
- <template #caption><button class="_textButton" @click="addItem">{{ $ts.addItem }}</button></template>
+ <template #label>{{ i18n.ts.menu }}</template>
+ <template #caption><button class="_textButton" @click="addItem">{{ i18n.ts.addItem }}</button></template>
</FormTextarea>
<FormRadios v-model="menuDisplay" class="_formBlock">
- <template #label>{{ $ts.display }}</template>
- <option value="sideFull">{{ $ts._menuDisplay.sideFull }}</option>
- <option value="sideIcon">{{ $ts._menuDisplay.sideIcon }}</option>
- <option value="top">{{ $ts._menuDisplay.top }}</option>
- <!-- <MkRadio v-model="menuDisplay" value="hide" disabled>{{ $ts._menuDisplay.hide }}</MkRadio>--> <!-- TODO: サイドバーを完全に隠せるようにすると、別途ハンバーガーボタンのようなものをUIに表示する必要があり面倒 -->
+ <template #label>{{ i18n.ts.display }}</template>
+ <option value="sideFull">{{ i18n.ts._menuDisplay.sideFull }}</option>
+ <option value="sideIcon">{{ i18n.ts._menuDisplay.sideIcon }}</option>
+ <option value="top">{{ i18n.ts._menuDisplay.top }}</option>
+ <!-- <MkRadio v-model="menuDisplay" value="hide" disabled>{{ i18n.ts._menuDisplay.hide }}</MkRadio>--> <!-- TODO: サイドバーを完全に隠せるようにすると、別途ハンバーガーボタンのようなものをUIに表示する必要があり面倒 -->
</FormRadios>
- <FormButton danger class="_formBlock" @click="reset()"><i class="fas fa-redo"></i> {{ $ts.default }}</FormButton>
+ <FormButton danger class="_formBlock" @click="reset()"><i class="fas fa-redo"></i> {{ i18n.ts.default }}</FormButton>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed, defineExpose, ref, watch } from 'vue';
import FormTextarea from '@/components/form/textarea.vue';
import FormRadios from '@/components/form/radios.vue';
import FormButton from '@/components/ui/button.vue';
@@ -27,81 +27,60 @@ import { menuDef } from '@/menu';
import { defaultStore } from '@/store';
import * as symbols from '@/symbols';
import { unisonReload } from '@/scripts/unison-reload';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormButton,
- FormTextarea,
- FormRadios,
- },
+const items = ref(defaultStore.state.menu.join('\n'));
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.menu,
- icon: 'fas fa-list-ul',
- bg: 'var(--bg)',
- },
- menuDef: menuDef,
- items: defaultStore.state.menu.join('\n'),
- }
- },
+const split = computed(() => items.value.trim().split('\n').filter(x => x.trim() !== ''));
+const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay'));
- computed: {
- splited(): string[] {
- return this.items.trim().split('\n').filter(x => x.trim() !== '');
- },
+async function reloadAsk() {
+ const { canceled } = await os.confirm({
+ type: 'info',
+ text: i18n.ts.reloadToApplySetting
+ });
+ if (canceled) return;
- menuDisplay: defaultStore.makeGetterSetter('menuDisplay')
- },
+ unisonReload();
+}
- watch: {
- menuDisplay() {
- this.reloadAsk();
- },
+async function addItem() {
+ const menu = Object.keys(menuDef).filter(k => !defaultStore.state.menu.includes(k));
+ const { canceled, result: item } = await os.select({
+ title: i18n.ts.addItem,
+ items: [...menu.map(k => ({
+ value: k, text: i18n.ts[menuDef[k].title]
+ })), {
+ value: '-', text: i18n.ts.divider
+ }]
+ });
+ if (canceled) return;
+ items.value = [...split.value, item].join('\n');
+}
- items() {
- this.save();
- },
- },
+async function save() {
+ defaultStore.set('menu', split.value);
+ await reloadAsk();
+}
- methods: {
- async addItem() {
- const menu = Object.keys(this.menuDef).filter(k => !this.$store.state.menu.includes(k));
- const { canceled, result: item } = await os.select({
- title: this.$ts.addItem,
- items: [...menu.map(k => ({
- value: k, text: this.$ts[this.menuDef[k].title]
- })), ...[{
- value: '-', text: this.$ts.divider
- }]]
- });
- if (canceled) return;
- this.items = [...this.splited, item].join('\n');
- },
+function reset() {
+ defaultStore.reset('menu');
+ items.value = defaultStore.state.menu.join('\n');
+}
- save() {
- this.$store.set('menu', this.splited);
- this.reloadAsk();
- },
-
- reset() {
- this.$store.reset('menu');
- this.items = this.$store.state.menu.join('\n');
- },
+watch(items, async () => {
+ await save();
+});
- async reloadAsk() {
- const { canceled } = await os.confirm({
- type: 'info',
- text: this.$ts.reloadToApplySetting,
- showCancelButton: true
- });
- if (canceled) return;
+watch(menuDisplay, async () => {
+ await reloadAsk();
+});
- unisonReload();
- }
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.menu,
+ icon: 'fas fa-list-ul',
+ bg: 'var(--bg)',
+ }
});
</script>
diff --git a/packages/client/src/pages/settings/notifications.vue b/packages/client/src/pages/settings/notifications.vue
index 12171530bb..b8fff95a8d 100644
--- a/packages/client/src/pages/settings/notifications.vue
+++ b/packages/client/src/pages/settings/notifications.vue
@@ -1,71 +1,59 @@
<template>
<div class="_formRoot">
- <FormLink class="_formBlock" @click="configure"><template #icon><i class="fas fa-cog"></i></template>{{ $ts.notificationSetting }}</FormLink>
+ <FormLink class="_formBlock" @click="configure"><template #icon><i class="fas fa-cog"></i></template>{{ i18n.ts.notificationSetting }}</FormLink>
<FormSection>
- <FormLink class="_formBlock" @click="readAllNotifications">{{ $ts.markAsReadAllNotifications }}</FormLink>
- <FormLink class="_formBlock" @click="readAllUnreadNotes">{{ $ts.markAsReadAllUnreadNotes }}</FormLink>
- <FormLink class="_formBlock" @click="readAllMessagingMessages">{{ $ts.markAsReadAllTalkMessages }}</FormLink>
+ <FormLink class="_formBlock" @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink>
+ <FormLink class="_formBlock" @click="readAllUnreadNotes">{{ i18n.ts.markAsReadAllUnreadNotes }}</FormLink>
+ <FormLink class="_formBlock" @click="readAllMessagingMessages">{{ i18n.ts.markAsReadAllTalkMessages }}</FormLink>
</FormSection>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent, defineExpose } from 'vue';
import FormButton from '@/components/ui/button.vue';
import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
import { notificationTypes } from 'misskey-js';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormLink,
- FormButton,
- FormSection,
- },
+async function readAllUnreadNotes() {
+ await os.api('i/read-all-unread-notes');
+}
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.notifications,
- icon: 'fas fa-bell',
- bg: 'var(--bg)',
- },
- }
- },
-
- methods: {
- readAllUnreadNotes() {
- os.api('i/read-all-unread-notes');
- },
+async function readAllMessagingMessages() {
+ await os.api('i/read-all-messaging-messages');
+}
- readAllMessagingMessages() {
- os.api('i/read-all-messaging-messages');
- },
+async function readAllNotifications() {
+ await os.api('notifications/mark-all-as-read');
+}
- readAllNotifications() {
- os.api('notifications/mark-all-as-read');
- },
+function configure() {
+ const includingTypes = notificationTypes.filter(x => !$i!.mutingNotificationTypes.includes(x));
+ os.popup(defineAsyncComponent(() => import('@/components/notification-setting-window.vue')), {
+ includingTypes,
+ showGlobalToggle: false,
+ }, {
+ done: async (res) => {
+ const { includingTypes: value } = res;
+ await os.apiWithDialog('i/update', {
+ mutingNotificationTypes: notificationTypes.filter(x => !value.includes(x)),
+ }).then(i => {
+ $i!.mutingNotificationTypes = i.mutingNotificationTypes;
+ });
+ }
+ }, 'closed');
+}
- configure() {
- const includingTypes = notificationTypes.filter(x => !this.$i.mutingNotificationTypes.includes(x));
- os.popup(import('@/components/notification-setting-window.vue'), {
- includingTypes,
- showGlobalToggle: false,
- }, {
- done: async (res) => {
- const { includingTypes: value } = res;
- await os.apiWithDialog('i/update', {
- mutingNotificationTypes: notificationTypes.filter(x => !value.includes(x)),
- }).then(i => {
- this.$i.mutingNotificationTypes = i.mutingNotificationTypes;
- });
- }
- }, 'closed');
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.notifications,
+ icon: 'fas fa-bell',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/other.vue b/packages/client/src/pages/settings/other.vue
index a9903acc7e..82e174a5b4 100644
--- a/packages/client/src/pages/settings/other.vue
+++ b/packages/client/src/pages/settings/other.vue
@@ -1,66 +1,44 @@
<template>
<div class="_formRoot">
- <FormSwitch :value="$i.injectFeaturedNote" class="_formBlock" @update:modelValue="onChangeInjectFeaturedNote">
- {{ $ts.showFeaturedNotesInTimeline }}
+ <FormSwitch v-model="$i.injectFeaturedNote" class="_formBlock" @update:modelValue="onChangeInjectFeaturedNote">
+ {{ i18n.ts.showFeaturedNotesInTimeline }}
</FormSwitch>
<!--
- <FormSwitch v-model="reportError" class="_formBlock">{{ $ts.sendErrorReports }}<template #caption>{{ $ts.sendErrorReportsDescription }}</template></FormSwitch>
+ <FormSwitch v-model="reportError" class="_formBlock">{{ i18n.ts.sendErrorReports }}<template #caption>{{ i18n.ts.sendErrorReportsDescription }}</template></FormSwitch>
-->
- <FormLink to="/settings/account-info" class="_formBlock">{{ $ts.accountInfo }}</FormLink>
+ <FormLink to="/settings/account-info" class="_formBlock">{{ i18n.ts.accountInfo }}</FormLink>
- <FormLink to="/settings/delete-account" class="_formBlock"><template #icon><i class="fas fa-exclamation-triangle"></i></template>{{ $ts.closeAccount }}</FormLink>
+ <FormLink to="/settings/delete-account" class="_formBlock"><template #icon><i class="fas fa-exclamation-triangle"></i></template>{{ i18n.ts.closeAccount }}</FormLink>
</div>
</template>
-<script lang="ts">
-import { defineAsyncComponent, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed, defineExpose } from 'vue';
import FormSwitch from '@/components/form/switch.vue';
-import FormSection from '@/components/form/section.vue';
import FormLink from '@/components/form/link.vue';
import * as os from '@/os';
-import { debug } from '@/config';
import { defaultStore } from '@/store';
-import { unisonReload } from '@/scripts/unison-reload';
import * as symbols from '@/symbols';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSection,
- FormSwitch,
- FormLink,
- },
+const reportError = computed(defaultStore.makeGetterSetter('reportError'));
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.other,
- icon: 'fas fa-ellipsis-h',
- bg: 'var(--bg)',
- },
- debug,
- }
- },
+function onChangeInjectFeaturedNote(v) {
+ os.api('i/update', {
+ injectFeaturedNote: v
+ }).then((i) => {
+ $i!.injectFeaturedNote = i.injectFeaturedNote;
+ });
+}
- computed: {
- reportError: defaultStore.makeGetterSetter('reportError'),
- },
-
- methods: {
- changeDebug(v) {
- console.log(v);
- localStorage.setItem('debug', v.toString());
- unisonReload();
- },
-
- onChangeInjectFeaturedNote(v) {
- os.api('i/update', {
- injectFeaturedNote: v
- });
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.other,
+ icon: 'fas fa-ellipsis-h',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/plugin.install.vue b/packages/client/src/pages/settings/plugin.install.vue
index d35d20d17a..96c0abfd99 100644
--- a/packages/client/src/pages/settings/plugin.install.vue
+++ b/packages/client/src/pages/settings/plugin.install.vue
@@ -1,19 +1,19 @@
<template>
<div class="_formRoot">
- <FormInfo warn class="_formBlock">{{ $ts._plugin.installWarn }}</FormInfo>
+ <FormInfo warn class="_formBlock">{{ i18n.ts._plugin.installWarn }}</FormInfo>
<FormTextarea v-model="code" tall class="_formBlock">
- <template #label>{{ $ts.code }}</template>
+ <template #label>{{ i18n.ts.code }}</template>
</FormTextarea>
<div class="_formBlock">
- <FormButton :disabled="code == null" primary inline @click="install"><i class="fas fa-check"></i> {{ $ts.install }}</FormButton>
+ <FormButton :disabled="code == null" primary inline @click="install"><i class="fas fa-check"></i> {{ i18n.ts.install }}</FormButton>
</div>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, defineAsyncComponent, nextTick, ref } from 'vue';
import { AiScript, parse } from '@syuilo/aiscript';
import { serialize } from '@syuilo/aiscript/built/serializer';
import { v4 as uuid } from 'uuid';
@@ -23,111 +23,101 @@ import FormInfo from '@/components/ui/info.vue';
import * as os from '@/os';
import { ColdDeviceStorage } from '@/store';
import { unisonReload } from '@/scripts/unison-reload';
+import { i18n } from '@/i18n';
import * as symbols from '@/symbols';
-export default defineComponent({
- components: {
- FormTextarea,
- FormButton,
- FormInfo,
- },
+const code = ref(null);
- emits: ['info'],
+function installPlugin({ id, meta, ast, token }) {
+ ColdDeviceStorage.set('plugins', ColdDeviceStorage.get('plugins').concat({
+ ...meta,
+ id,
+ active: true,
+ configData: {},
+ token: token,
+ ast: ast
+ }));
+}
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts._plugin.install,
- icon: 'fas fa-download',
- bg: 'var(--bg)',
- },
- code: null,
- }
- },
+async function install() {
+ let ast;
+ try {
+ ast = parse(code.value);
+ } catch (err) {
+ os.alert({
+ type: 'error',
+ text: 'Syntax error :('
+ });
+ return;
+ }
- methods: {
- installPlugin({ id, meta, ast, token }) {
- ColdDeviceStorage.set('plugins', ColdDeviceStorage.get('plugins').concat({
- ...meta,
- id,
- active: true,
- configData: {},
- token: token,
- ast: ast
- }));
- },
+ const meta = AiScript.collectMetadata(ast);
+ if (meta == null) {
+ os.alert({
+ type: 'error',
+ text: 'No metadata found :('
+ });
+ return;
+ }
- async install() {
- let ast;
- try {
- ast = parse(this.code);
- } catch (e) {
- os.alert({
- type: 'error',
- text: 'Syntax error :('
- });
- return;
- }
- const meta = AiScript.collectMetadata(ast);
- if (meta == null) {
- os.alert({
- type: 'error',
- text: 'No metadata found :('
- });
- return;
- }
- const data = meta.get(null);
- if (data == null) {
- os.alert({
- type: 'error',
- text: 'No metadata found :('
- });
- return;
- }
- const { name, version, author, description, permissions, config } = data;
- if (name == null || version == null || author == null) {
- os.alert({
- type: 'error',
- text: 'Required property not found :('
+ const metadata = meta.get(null);
+ if (metadata == null) {
+ os.alert({
+ type: 'error',
+ text: 'No metadata found :('
+ });
+ return;
+ }
+
+ const { name, version, author, description, permissions, config } = metadata;
+ if (name == null || version == null || author == null) {
+ os.alert({
+ type: 'error',
+ text: 'Required property not found :('
+ });
+ return;
+ }
+
+ const token = permissions == null || permissions.length === 0 ? null : await new Promise((res, rej) => {
+ os.popup(defineAsyncComponent(() => import('@/components/token-generate-window.vue')), {
+ title: i18n.ts.tokenRequested,
+ information: i18n.ts.pluginTokenRequestedDescription,
+ initialName: name,
+ initialPermissions: permissions
+ }, {
+ done: async result => {
+ const { name, permissions } = result;
+ const { token } = await os.api('miauth/gen-token', {
+ session: null,
+ name: name,
+ permission: permissions,
});
- return;
+ res(token);
}
+ }, 'closed');
+ });
- const token = permissions == null || permissions.length === 0 ? null : await new Promise((res, rej) => {
- os.popup(import('@/components/token-generate-window.vue'), {
- title: this.$ts.tokenRequested,
- information: this.$ts.pluginTokenRequestedDescription,
- initialName: name,
- initialPermissions: permissions
- }, {
- done: async result => {
- const { name, permissions } = result;
- const { token } = await os.api('miauth/gen-token', {
- session: null,
- name: name,
- permission: permissions,
- });
-
- res(token);
- }
- }, 'closed');
- });
+ installPlugin({
+ id: uuid(),
+ meta: {
+ name, version, author, description, permissions, config
+ },
+ token,
+ ast: serialize(ast)
+ });
- this.installPlugin({
- id: uuid(),
- meta: {
- name, version, author, description, permissions, config
- },
- token,
- ast: serialize(ast)
- });
+ os.success();
- os.success();
+ nextTick(() => {
+ unisonReload();
+ });
+}
- this.$nextTick(() => {
- unisonReload();
- });
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts._plugin.install,
+ icon: 'fas fa-download',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/plugin.vue b/packages/client/src/pages/settings/plugin.vue
index 7a3ab9d152..873a022cbc 100644
--- a/packages/client/src/pages/settings/plugin.vue
+++ b/packages/client/src/pages/settings/plugin.vue
@@ -1,38 +1,38 @@
<template>
<div class="_formRoot">
- <FormLink to="/settings/plugin/install"><template #icon><i class="fas fa-download"></i></template>{{ $ts._plugin.install }}</FormLink>
+ <FormLink to="/settings/plugin/install"><template #icon><i class="fas fa-download"></i></template>{{ i18n.ts._plugin.install }}</FormLink>
<FormSection>
- <template #label>{{ $ts.manage }}</template>
+ <template #label>{{ i18n.ts.manage }}</template>
<div v-for="plugin in plugins" :key="plugin.id" class="_formBlock _panel" style="padding: 20px;">
<span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span>
- <FormSwitch class="_formBlock" :modelValue="plugin.active" @update:modelValue="changeActive(plugin, $event)">{{ $ts.makeActive }}</FormSwitch>
+ <FormSwitch class="_formBlock" :modelValue="plugin.active" @update:modelValue="changeActive(plugin, $event)">{{ i18n.ts.makeActive }}</FormSwitch>
<MkKeyValue class="_formBlock">
- <template #key>{{ $ts.author }}</template>
+ <template #key>{{ i18n.ts.author }}</template>
<template #value>{{ plugin.author }}</template>
</MkKeyValue>
<MkKeyValue class="_formBlock">
- <template #key>{{ $ts.description }}</template>
+ <template #key>{{ i18n.ts.description }}</template>
<template #value>{{ plugin.description }}</template>
</MkKeyValue>
<MkKeyValue class="_formBlock">
- <template #key>{{ $ts.permission }}</template>
+ <template #key>{{ i18n.ts.permission }}</template>
<template #value>{{ plugin.permission }}</template>
</MkKeyValue>
<div style="display: flex; gap: var(--margin); flex-wrap: wrap;">
- <MkButton v-if="plugin.config" inline @click="config(plugin)"><i class="fas fa-cog"></i> {{ $ts.settings }}</MkButton>
- <MkButton inline danger @click="uninstall(plugin)"><i class="fas fa-trash-alt"></i> {{ $ts.uninstall }}</MkButton>
+ <MkButton v-if="plugin.config" inline @click="config(plugin)"><i class="fas fa-cog"></i> {{ i18n.ts.settings }}</MkButton>
+ <MkButton inline danger @click="uninstall(plugin)"><i class="fas fa-trash-alt"></i> {{ i18n.ts.uninstall }}</MkButton>
</div>
</div>
</FormSection>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, nextTick, ref } from 'vue';
import FormLink from '@/components/form/link.vue';
import FormSwitch from '@/components/form/switch.vue';
import FormSection from '@/components/form/section.vue';
@@ -41,67 +41,54 @@ import MkKeyValue from '@/components/key-value.vue';
import * as os from '@/os';
import { ColdDeviceStorage } from '@/store';
import * as symbols from '@/symbols';
+import { unisonReload } from '@/scripts/unison-reload';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormLink,
- FormSwitch,
- FormSection,
- MkButton,
- MkKeyValue,
- },
+const plugins = ref(ColdDeviceStorage.get('plugins'));
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.plugins,
- icon: 'fas fa-plug',
- bg: 'var(--bg)',
- },
- plugins: ColdDeviceStorage.get('plugins'),
- }
- },
+function uninstall(plugin) {
+ ColdDeviceStorage.set('plugins', plugins.value.filter(x => x.id !== plugin.id));
+ os.success();
+ nextTick(() => {
+ unisonReload();
+ });
+}
- methods: {
- uninstall(plugin) {
- ColdDeviceStorage.set('plugins', this.plugins.filter(x => x.id !== plugin.id));
- os.success();
- this.$nextTick(() => {
- unisonReload();
- });
- },
+// TODO: この処理をstore側にactionとして移動し、設定画面を開くAiScriptAPIを実装できるようにする
+async function config(plugin) {
+ const config = plugin.config;
+ for (const key in plugin.configData) {
+ config[key].default = plugin.configData[key];
+ }
- // TODO: この処理をstore側にactionとして移動し、設定画面を開くAiScriptAPIを実装できるようにする
- async config(plugin) {
- const config = plugin.config;
- for (const key in plugin.configData) {
- config[key].default = plugin.configData[key];
- }
+ const { canceled, result } = await os.form(plugin.name, config);
+ if (canceled) return;
- const { canceled, result } = await os.form(plugin.name, config);
- if (canceled) return;
+ const coldPlugins = ColdDeviceStorage.get('plugins');
+ coldPlugins.find(p => p.id === plugin.id)!.configData = result;
+ ColdDeviceStorage.set('plugins', coldPlugins);
- const plugins = ColdDeviceStorage.get('plugins');
- plugins.find(p => p.id === plugin.id).configData = result;
- ColdDeviceStorage.set('plugins', plugins);
+ nextTick(() => {
+ location.reload();
+ });
+}
- this.$nextTick(() => {
- location.reload();
- });
- },
+function changeActive(plugin, active) {
+ const coldPlugins = ColdDeviceStorage.get('plugins');
+ coldPlugins.find(p => p.id === plugin.id)!.active = active;
+ ColdDeviceStorage.set('plugins', coldPlugins);
- changeActive(plugin, active) {
- const plugins = ColdDeviceStorage.get('plugins');
- plugins.find(p => p.id === plugin.id).active = active;
- ColdDeviceStorage.set('plugins', plugins);
+ nextTick(() => {
+ location.reload();
+ });
+}
- this.$nextTick(() => {
- location.reload();
- });
- }
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.plugins,
+ icon: 'fas fa-plug',
+ bg: 'var(--bg)',
+ }
});
</script>
diff --git a/packages/client/src/pages/settings/reaction.vue b/packages/client/src/pages/settings/reaction.vue
index a188ba353d..963ac81dfa 100644
--- a/packages/client/src/pages/settings/reaction.vue
+++ b/packages/client/src/pages/settings/reaction.vue
@@ -54,7 +54,7 @@
</template>
<script lang="ts" setup>
-import { watch } from 'vue';
+import { defineAsyncComponent, watch } from 'vue';
import XDraggable from 'vuedraggable';
import FormInput from '@/components/form/input.vue';
import FormRadios from '@/components/form/radios.vue';
@@ -88,7 +88,7 @@ function remove(reaction, ev: MouseEvent) {
}
function preview(ev: MouseEvent) {
- os.popup(import('@/components/emoji-picker-dialog.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/emoji-picker-dialog.vue')), {
asReactionPicker: true,
src: ev.currentTarget ?? ev.target,
}, {}, 'closed');
diff --git a/packages/client/src/pages/settings/security.vue b/packages/client/src/pages/settings/security.vue
index 6fb3f1c413..401648790a 100644
--- a/packages/client/src/pages/settings/security.vue
+++ b/packages/client/src/pages/settings/security.vue
@@ -1,17 +1,17 @@
<template>
<div class="_formRoot">
<FormSection>
- <template #label>{{ $ts.password }}</template>
- <FormButton primary @click="change()">{{ $ts.changePassword }}</FormButton>
+ <template #label>{{ i18n.ts.password }}</template>
+ <FormButton primary @click="change()">{{ i18n.ts.changePassword }}</FormButton>
</FormSection>
<FormSection>
- <template #label>{{ $ts.twoStepAuthentication }}</template>
+ <template #label>{{ i18n.ts.twoStepAuthentication }}</template>
<X2fa/>
</FormSection>
<FormSection>
- <template #label>{{ $ts.signinHistory }}</template>
+ <template #label>{{ i18n.ts.signinHistory }}</template>
<MkPagination :pagination="pagination">
<template v-slot="{items}">
<div>
@@ -30,15 +30,15 @@
<FormSection>
<FormSlot>
- <FormButton danger @click="regenerateToken"><i class="fas fa-sync-alt"></i> {{ $ts.regenerateLoginToken }}</FormButton>
- <template #caption>{{ $ts.regenerateLoginTokenDescription }}</template>
+ <FormButton danger @click="regenerateToken"><i class="fas fa-sync-alt"></i> {{ i18n.ts.regenerateLoginToken }}</FormButton>
+ <template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template>
</FormSlot>
</FormSection>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose } from 'vue';
import FormSection from '@/components/form/section.vue';
import FormSlot from '@/components/form/slot.vue';
import FormButton from '@/components/ui/button.vue';
@@ -46,77 +46,63 @@ import MkPagination from '@/components/ui/pagination.vue';
import X2fa from './2fa.vue';
import * as os from '@/os';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormSection,
- FormButton,
- MkPagination,
- FormSlot,
- X2fa,
- },
-
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.security,
- icon: 'fas fa-lock',
- bg: 'var(--bg)',
- },
- pagination: {
- endpoint: 'i/signin-history' as const,
- limit: 5,
- },
- }
- },
+const pagination = {
+ endpoint: 'i/signin-history' as const,
+ limit: 5,
+};
- methods: {
- async change() {
- const { canceled: canceled1, result: currentPassword } = await os.inputText({
- title: this.$ts.currentPassword,
- type: 'password'
- });
- if (canceled1) return;
+async function change() {
+ const { canceled: canceled1, result: currentPassword } = await os.inputText({
+ title: i18n.ts.currentPassword,
+ type: 'password'
+ });
+ if (canceled1) return;
- const { canceled: canceled2, result: newPassword } = await os.inputText({
- title: this.$ts.newPassword,
- type: 'password'
- });
- if (canceled2) return;
+ const { canceled: canceled2, result: newPassword } = await os.inputText({
+ title: i18n.ts.newPassword,
+ type: 'password'
+ });
+ if (canceled2) return;
- const { canceled: canceled3, result: newPassword2 } = await os.inputText({
- title: this.$ts.newPasswordRetype,
- type: 'password'
- });
- if (canceled3) return;
+ const { canceled: canceled3, result: newPassword2 } = await os.inputText({
+ title: i18n.ts.newPasswordRetype,
+ type: 'password'
+ });
+ if (canceled3) return;
- if (newPassword !== newPassword2) {
- os.alert({
- type: 'error',
- text: this.$ts.retypedNotMatch
- });
- return;
- }
-
- os.apiWithDialog('i/change-password', {
- currentPassword,
- newPassword
- });
- },
+ if (newPassword !== newPassword2) {
+ os.alert({
+ type: 'error',
+ text: i18n.ts.retypedNotMatch
+ });
+ return;
+ }
+
+ os.apiWithDialog('i/change-password', {
+ currentPassword,
+ newPassword
+ });
+}
+
+function regenerateToken() {
+ os.inputText({
+ title: i18n.ts.password,
+ type: 'password'
+ }).then(({ canceled, result: password }) => {
+ if (canceled) return;
+ os.api('i/regenerate_token', {
+ password: password
+ });
+ });
+}
- regenerateToken() {
- os.inputText({
- title: this.$ts.password,
- type: 'password'
- }).then(({ canceled, result: password }) => {
- if (canceled) return;
- os.api('i/regenerate_token', {
- password: password
- });
- });
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.security,
+ icon: 'fas fa-lock',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/sounds.vue b/packages/client/src/pages/settings/sounds.vue
index 490a1b5514..d01e87c1f8 100644
--- a/packages/client/src/pages/settings/sounds.vue
+++ b/packages/client/src/pages/settings/sounds.vue
@@ -1,24 +1,24 @@
<template>
<div class="_formRoot">
<FormRange v-model="masterVolume" :min="0" :max="1" :step="0.05" :text-converter="(v) => `${Math.floor(v * 100)}%`" class="_formBlock">
- <template #label>{{ $ts.masterVolume }}</template>
+ <template #label>{{ i18n.ts.masterVolume }}</template>
</FormRange>
<FormSection>
- <template #label>{{ $ts.sounds }}</template>
+ <template #label>{{ i18n.ts.sounds }}</template>
<FormLink v-for="type in Object.keys(sounds)" :key="type" style="margin-bottom: 8px;" @click="edit(type)">
{{ $t('_sfx.' + type) }}
- <template #suffix>{{ sounds[type].type || $ts.none }}</template>
+ <template #suffix>{{ sounds[type].type || i18n.ts.none }}</template>
<template #suffixIcon><i class="fas fa-chevron-down"></i></template>
</FormLink>
</FormSection>
- <FormButton danger class="_formBlock" @click="reset()"><i class="fas fa-redo"></i> {{ $ts.default }}</FormButton>
+ <FormButton danger class="_formBlock" @click="reset()"><i class="fas fa-redo"></i> {{ i18n.ts.default }}</FormButton>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { computed, defineExpose, ref } from 'vue';
import FormRange from '@/components/form/range.vue';
import FormButton from '@/components/ui/button.vue';
import FormLink from '@/components/form/link.vue';
@@ -27,6 +27,28 @@ import * as os from '@/os';
import { ColdDeviceStorage } from '@/store';
import { playFile } from '@/scripts/sound';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
+
+const masterVolume = computed({
+ get: () => {
+ return ColdDeviceStorage.get('sound_masterVolume');
+ },
+ set: (value) => {
+ ColdDeviceStorage.set('sound_masterVolume', value);
+ }
+});
+
+const volumeIcon = computed(() => masterVolume.value === 0 ? 'fas fa-volume-mute' : 'fas fa-volume-up');
+
+const sounds = ref({
+ note: ColdDeviceStorage.get('sound_note'),
+ noteMy: ColdDeviceStorage.get('sound_noteMy'),
+ notification: ColdDeviceStorage.get('sound_notification'),
+ chat: ColdDeviceStorage.get('sound_chat'),
+ chatBg: ColdDeviceStorage.get('sound_chatBg'),
+ antenna: ColdDeviceStorage.get('sound_antenna'),
+ channel: ColdDeviceStorage.get('sound_channel'),
+});
const soundsTypes = [
null,
@@ -55,94 +77,58 @@ const soundsTypes = [
'noizenecio/kick_gaba2',
];
-export default defineComponent({
- components: {
- FormLink,
- FormButton,
- FormRange,
- FormSection,
- },
-
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.sounds,
- icon: 'fas fa-music',
- bg: 'var(--bg)',
- },
- sounds: {},
- }
- },
-
- computed: {
- masterVolume: { // TODO: (外部)関数にcomputedを使うのはアレなので直す
- get() { return ColdDeviceStorage.get('sound_masterVolume'); },
- set(value) { ColdDeviceStorage.set('sound_masterVolume', value); }
+async function edit(type) {
+ const { canceled, result } = await os.form(i18n.t('_sfx.' + type), {
+ type: {
+ type: 'enum',
+ enum: soundsTypes.map(x => ({
+ value: x,
+ label: x == null ? i18n.ts.none : x,
+ })),
+ label: i18n.ts.sound,
+ default: sounds.value[type].type,
+ },
+ volume: {
+ type: 'range',
+ mim: 0,
+ max: 1,
+ step: 0.05,
+ textConverter: (v) => `${Math.floor(v * 100)}%`,
+ label: i18n.ts.volume,
+ default: sounds.value[type].volume
},
- volumeIcon() {
- return this.masterVolume === 0 ? 'fas fa-volume-mute' : 'fas fa-volume-up';
+ listen: {
+ type: 'button',
+ content: i18n.ts.listen,
+ action: (_, values) => {
+ playFile(values.type, values.volume);
+ }
}
- },
+ });
+ if (canceled) return;
- created() {
- this.sounds.note = ColdDeviceStorage.get('sound_note');
- this.sounds.noteMy = ColdDeviceStorage.get('sound_noteMy');
- this.sounds.notification = ColdDeviceStorage.get('sound_notification');
- this.sounds.chat = ColdDeviceStorage.get('sound_chat');
- this.sounds.chatBg = ColdDeviceStorage.get('sound_chatBg');
- this.sounds.antenna = ColdDeviceStorage.get('sound_antenna');
- this.sounds.channel = ColdDeviceStorage.get('sound_channel');
- },
+ const v = {
+ type: result.type,
+ volume: result.volume,
+ };
- methods: {
- async edit(type) {
- const { canceled, result } = await os.form(this.$t('_sfx.' + type), {
- type: {
- type: 'enum',
- enum: soundsTypes.map(x => ({
- value: x,
- label: x == null ? this.$ts.none : x,
- })),
- label: this.$ts.sound,
- default: this.sounds[type].type,
- },
- volume: {
- type: 'range',
- mim: 0,
- max: 1,
- step: 0.05,
- textConverter: (v) => `${Math.floor(v * 100)}%`,
- label: this.$ts.volume,
- default: this.sounds[type].volume
- },
- listen: {
- type: 'button',
- content: this.$ts.listen,
- action: (_, values) => {
- playFile(values.type, values.volume);
- }
- }
- });
- if (canceled) return;
+ ColdDeviceStorage.set('sound_' + type, v);
+ sounds.value[type] = v;
+}
- const v = {
- type: result.type,
- volume: result.volume,
- };
-
- ColdDeviceStorage.set('sound_' + type, v);
- this.sounds[type] = v;
- },
+function reset() {
+ for (const sound of Object.keys(sounds.value)) {
+ const v = ColdDeviceStorage.default['sound_' + sound];
+ ColdDeviceStorage.set('sound_' + sound, v);
+ sounds.value[sound] = v;
+ }
+}
- reset() {
- for (const sound of Object.keys(this.sounds)) {
- const v = ColdDeviceStorage.default['sound_' + sound];
- ColdDeviceStorage.set('sound_' + sound, v);
- this.sounds[sound] = v;
- }
- }
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.sounds,
+ icon: 'fas fa-music',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/theme.install.vue b/packages/client/src/pages/settings/theme.install.vue
index 2d3514342e..25fa6c012b 100644
--- a/packages/client/src/pages/settings/theme.install.vue
+++ b/packages/client/src/pages/settings/theme.install.vue
@@ -13,7 +13,7 @@
<script lang="ts" setup>
import { } from 'vue';
-import * as JSON5 from 'json5';
+import JSON5 from 'json5';
import FormTextarea from '@/components/form/textarea.vue';
import FormButton from '@/components/ui/button.vue';
import { applyTheme, validateTheme } from '@/scripts/theme';
@@ -29,7 +29,7 @@ function parseThemeCode(code: string) {
try {
theme = JSON5.parse(code);
- } catch (e) {
+ } catch (err) {
os.alert({
type: 'error',
text: i18n.ts._theme.invalid
diff --git a/packages/client/src/pages/settings/theme.manage.vue b/packages/client/src/pages/settings/theme.manage.vue
index a1e849b540..7da439f9c0 100644
--- a/packages/client/src/pages/settings/theme.manage.vue
+++ b/packages/client/src/pages/settings/theme.manage.vue
@@ -1,33 +1,33 @@
<template>
<div class="_formRoot">
<FormSelect v-model="selectedThemeId" class="_formBlock">
- <template #label>{{ $ts.theme }}</template>
- <optgroup :label="$ts._theme.installedThemes">
+ <template #label>{{ i18n.ts.theme }}</template>
+ <optgroup :label="i18n.ts._theme.installedThemes">
<option v-for="x in installedThemes" :key="x.id" :value="x.id">{{ x.name }}</option>
</optgroup>
- <optgroup :label="$ts._theme.builtinThemes">
+ <optgroup :label="i18n.ts._theme.builtinThemes">
<option v-for="x in builtinThemes" :key="x.id" :value="x.id">{{ x.name }}</option>
</optgroup>
</FormSelect>
<template v-if="selectedTheme">
<FormInput readonly :modelValue="selectedTheme.author" class="_formBlock">
- <template #label>{{ $ts.author }}</template>
+ <template #label>{{ i18n.ts.author }}</template>
</FormInput>
<FormTextarea v-if="selectedTheme.desc" readonly :modelValue="selectedTheme.desc" class="_formBlock">
- <template #label>{{ $ts._theme.description }}</template>
+ <template #label>{{ i18n.ts._theme.description }}</template>
</FormTextarea>
<FormTextarea readonly tall :modelValue="selectedThemeCode" class="_formBlock">
- <template #label>{{ $ts._theme.code }}</template>
- <template #caption><button class="_textButton" @click="copyThemeCode()">{{ $ts.copy }}</button></template>
+ <template #label>{{ i18n.ts._theme.code }}</template>
+ <template #caption><button class="_textButton" @click="copyThemeCode()">{{ i18n.ts.copy }}</button></template>
</FormTextarea>
- <FormButton v-if="!builtinThemes.some(t => t.id == selectedTheme.id)" class="_formBlock" danger @click="uninstall()"><i class="fas fa-trash-alt"></i> {{ $ts.uninstall }}</FormButton>
+ <FormButton v-if="!builtinThemes.some(t => t.id == selectedTheme.id)" class="_formBlock" danger @click="uninstall()"><i class="fas fa-trash-alt"></i> {{ i18n.ts.uninstall }}</FormButton>
</template>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-import * as JSON5 from 'json5';
+<script lang="ts" setup>
+import { computed, defineExpose, ref } from 'vue';
+import JSON5 from 'json5';
import FormTextarea from '@/components/form/textarea.vue';
import FormSelect from '@/components/form/select.vue';
import FormInput from '@/components/form/input.vue';
@@ -35,61 +35,42 @@ import FormButton from '@/components/ui/button.vue';
import { Theme, builtinThemes } from '@/scripts/theme';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import * as os from '@/os';
-import { ColdDeviceStorage } from '@/store';
import { getThemes, removeTheme } from '@/theme-store';
import * as symbols from '@/symbols';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- FormTextarea,
- FormSelect,
- FormInput,
- FormButton,
- },
+const installedThemes = ref(getThemes());
+const selectedThemeId = ref(null);
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts._theme.manage,
- icon: 'fas fa-folder-open',
- bg: 'var(--bg)',
- },
- installedThemes: getThemes(),
- builtinThemes,
- selectedThemeId: null,
- }
- },
+const themes = computed(() => builtinThemes.concat(installedThemes.value));
- computed: {
- themes(): Theme[] {
- return this.builtinThemes.concat(this.installedThemes);
- },
-
- selectedTheme() {
- if (this.selectedThemeId == null) return null;
- return this.themes.find(x => x.id === this.selectedThemeId);
- },
+const selectedTheme = computed(() => {
+ if (selectedThemeId.value == null) return null;
+ return themes.value.find(x => x.id === selectedThemeId.value);
+});
+
+const selectedThemeCode = computed(() => {
+ if (selectedTheme.value == null) return null;
+ return JSON5.stringify(selectedTheme.value, null, '\t');
+});
- selectedThemeCode() {
- if (this.selectedTheme == null) return null;
- return JSON5.stringify(this.selectedTheme, null, '\t');
- },
- },
+function copyThemeCode() {
+ copyToClipboard(selectedThemeCode.value);
+ os.success();
+}
- methods: {
- copyThemeCode() {
- copyToClipboard(this.selectedThemeCode);
- os.success();
- },
+function uninstall() {
+ removeTheme(selectedTheme.value as Theme);
+ installedThemes.value = installedThemes.value.filter(t => t.id !== selectedThemeId.value);
+ selectedThemeId.value = null;
+ os.success();
+}
- uninstall() {
- removeTheme(this.selectedTheme);
- this.installedThemes = this.installedThemes.filter(t => t.id !== this.selectedThemeId);
- this.selectedThemeId = null;
- os.success();
- },
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts._theme.manage,
+ icon: 'fas fa-folder-open',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/theme.vue b/packages/client/src/pages/settings/theme.vue
index d134a092b6..64b384bdcd 100644
--- a/packages/client/src/pages/settings/theme.vue
+++ b/packages/client/src/pages/settings/theme.vue
@@ -85,12 +85,11 @@
</div>
</template>
-<script lang="ts">
-import { computed, defineComponent, onActivated, onMounted, ref, watch } from 'vue';
-import * as JSON5 from 'json5';
+<script lang="ts" setup>
+import { computed, onActivated, ref, watch } from 'vue';
+import JSON5 from 'json5';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
-import FormGroup from '@/components/form/group.vue';
import FormSection from '@/components/form/section.vue';
import FormLink from '@/components/form/link.vue';
import FormButton from '@/components/ui/button.vue';
@@ -101,100 +100,78 @@ import { ColdDeviceStorage } from '@/store';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
import { instance } from '@/instance';
-import { concat, uniqueBy } from '@/scripts/array';
+import { uniqueBy } from '@/scripts/array';
import { fetchThemes, getThemes } from '@/theme-store';
import * as symbols from '@/symbols';
-export default defineComponent({
- components: {
- FormSwitch,
- FormSelect,
- FormGroup,
- FormSection,
- FormLink,
- FormButton,
- },
+const installedThemes = ref(getThemes());
+const instanceThemes = [];
- emits: ['info'],
+if (instance.defaultLightTheme != null) instanceThemes.push(JSON5.parse(instance.defaultLightTheme));
+if (instance.defaultDarkTheme != null) instanceThemes.push(JSON5.parse(instance.defaultDarkTheme));
- setup(props, { emit }) {
- const INFO = {
- title: i18n.ts.theme,
- icon: 'fas fa-palette',
- bg: 'var(--bg)',
- };
+const themes = computed(() => uniqueBy(instanceThemes.concat(builtinThemes.concat(installedThemes.value)), theme => theme.id));
+const darkThemes = computed(() => themes.value.filter(t => t.base === 'dark' || t.kind === 'dark'));
+const lightThemes = computed(() => themes.value.filter(t => t.base === 'light' || t.kind === 'light'));
+const darkTheme = ColdDeviceStorage.ref('darkTheme');
+const darkThemeId = computed({
+ get() {
+ return darkTheme.value.id;
+ },
+ set(id) {
+ ColdDeviceStorage.set('darkTheme', themes.value.find(x => x.id === id))
+ }
+});
+const lightTheme = ColdDeviceStorage.ref('lightTheme');
+const lightThemeId = computed({
+ get() {
+ return lightTheme.value.id;
+ },
+ set(id) {
+ ColdDeviceStorage.set('lightTheme', themes.value.find(x => x.id === id))
+ }
+});
+const darkMode = computed(defaultStore.makeGetterSetter('darkMode'));
+const syncDeviceDarkMode = computed(ColdDeviceStorage.makeGetterSetter('syncDeviceDarkMode'));
+const wallpaper = ref(localStorage.getItem('wallpaper'));
+const themesCount = installedThemes.value.length;
- const installedThemes = ref(getThemes());
- const instanceThemes = [];
- if (instance.defaultLightTheme != null) instanceThemes.push(JSON5.parse(instance.defaultLightTheme));
- if (instance.defaultDarkTheme != null) instanceThemes.push(JSON5.parse(instance.defaultDarkTheme));
- const themes = computed(() => uniqueBy(instanceThemes.concat(builtinThemes.concat(installedThemes.value)), theme => theme.id));
- const darkThemes = computed(() => themes.value.filter(t => t.base === 'dark' || t.kind === 'dark'));
- const lightThemes = computed(() => themes.value.filter(t => t.base === 'light' || t.kind === 'light'));
- const darkTheme = ColdDeviceStorage.ref('darkTheme');
- const darkThemeId = computed({
- get() {
- return darkTheme.value.id;
- },
- set(id) {
- ColdDeviceStorage.set('darkTheme', themes.value.find(x => x.id === id))
- }
- });
- const lightTheme = ColdDeviceStorage.ref('lightTheme');
- const lightThemeId = computed({
- get() {
- return lightTheme.value.id;
- },
- set(id) {
- ColdDeviceStorage.set('lightTheme', themes.value.find(x => x.id === id))
- }
- });
- const darkMode = computed(defaultStore.makeGetterSetter('darkMode'));
- const syncDeviceDarkMode = computed(ColdDeviceStorage.makeGetterSetter('syncDeviceDarkMode'));
- const wallpaper = ref(localStorage.getItem('wallpaper'));
- const themesCount = installedThemes.value.length;
+watch(syncDeviceDarkMode, () => {
+ if (syncDeviceDarkMode.value) {
+ defaultStore.set('darkMode', isDeviceDarkmode());
+ }
+});
- watch(syncDeviceDarkMode, () => {
- if (syncDeviceDarkMode.value) {
- defaultStore.set('darkMode', isDeviceDarkmode());
- }
- });
+watch(wallpaper, () => {
+ if (wallpaper.value == null) {
+ localStorage.removeItem('wallpaper');
+ } else {
+ localStorage.setItem('wallpaper', wallpaper.value);
+ }
+ location.reload();
+});
- watch(wallpaper, () => {
- if (wallpaper.value == null) {
- localStorage.removeItem('wallpaper');
- } else {
- localStorage.setItem('wallpaper', wallpaper.value);
- }
- location.reload();
- });
+onActivated(() => {
+ fetchThemes().then(() => {
+ installedThemes.value = getThemes();
+ });
+});
- onActivated(() => {
- fetchThemes().then(() => {
- installedThemes.value = getThemes();
- });
- });
+fetchThemes().then(() => {
+ installedThemes.value = getThemes();
+});
- fetchThemes().then(() => {
- installedThemes.value = getThemes();
- });
+function setWallpaper(event) {
+ selectFile(event.currentTarget ?? event.target, null).then(file => {
+ wallpaper.value = file.url;
+ });
+}
- return {
- [symbols.PAGE_INFO]: INFO,
- darkThemes,
- lightThemes,
- darkThemeId,
- lightThemeId,
- darkMode,
- syncDeviceDarkMode,
- themesCount,
- wallpaper,
- setWallpaper(e) {
- selectFile(e.currentTarget ?? e.target, null).then(file => {
- wallpaper.value = file.url;
- });
- },
- };
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.theme,
+ icon: 'fas fa-palette',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/settings/webhook.edit.vue b/packages/client/src/pages/settings/webhook.edit.vue
index bb3a25407e..3690526b41 100644
--- a/packages/client/src/pages/settings/webhook.edit.vue
+++ b/packages/client/src/pages/settings/webhook.edit.vue
@@ -43,6 +43,14 @@ import * as os from '@/os';
import * as symbols from '@/symbols';
import { i18n } from '@/i18n';
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: 'Edit webhook',
+ icon: 'fas fa-bolt',
+ bg: 'var(--bg)',
+ },
+});
+
const webhook = await os.api('i/webhooks/show', {
webhookId: new URLSearchParams(window.location.search).get('id')
});
@@ -78,12 +86,4 @@ async function save(): Promise<void> {
active,
});
}
-
-defineExpose({
- [symbols.PAGE_INFO]: {
- title: 'Edit webhook',
- icon: 'fas fa-bolt',
- bg: 'var(--bg)',
- },
-});
</script>
diff --git a/packages/client/src/pages/settings/word-mute.vue b/packages/client/src/pages/settings/word-mute.vue
index c11707b6cf..48fcb362b9 100644
--- a/packages/client/src/pages/settings/word-mute.vue
+++ b/packages/client/src/pages/settings/word-mute.vue
@@ -1,35 +1,35 @@
<template>
<div class="_formRoot">
<MkTab v-model="tab" class="_formBlock">
- <option value="soft">{{ $ts._wordMute.soft }}</option>
- <option value="hard">{{ $ts._wordMute.hard }}</option>
+ <option value="soft">{{ i18n.ts._wordMute.soft }}</option>
+ <option value="hard">{{ i18n.ts._wordMute.hard }}</option>
</MkTab>
<div class="_formBlock">
<div v-show="tab === 'soft'">
- <MkInfo class="_formBlock">{{ $ts._wordMute.softDescription }}</MkInfo>
+ <MkInfo class="_formBlock">{{ i18n.ts._wordMute.softDescription }}</MkInfo>
<FormTextarea v-model="softMutedWords" class="_formBlock">
- <span>{{ $ts._wordMute.muteWords }}</span>
- <template #caption>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template>
+ <span>{{ i18n.ts._wordMute.muteWords }}</span>
+ <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template>
</FormTextarea>
</div>
<div v-show="tab === 'hard'">
- <MkInfo class="_formBlock">{{ $ts._wordMute.hardDescription }} {{ $ts.reflectMayTakeTime }}</MkInfo>
+ <MkInfo class="_formBlock">{{ i18n.ts._wordMute.hardDescription }} {{ i18n.ts.reflectMayTakeTime }}</MkInfo>
<FormTextarea v-model="hardMutedWords" class="_formBlock">
- <span>{{ $ts._wordMute.muteWords }}</span>
- <template #caption>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template>
+ <span>{{ i18n.ts._wordMute.muteWords }}</span>
+ <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template>
</FormTextarea>
<MkKeyValue v-if="hardWordMutedNotesCount != null" class="_formBlock">
- <template #key>{{ $ts._wordMute.mutedNotes }}</template>
+ <template #key>{{ i18n.ts._wordMute.mutedNotes }}</template>
<template #value>{{ number(hardWordMutedNotesCount) }}</template>
</MkKeyValue>
</div>
</div>
- <MkButton primary inline :disabled="!changed" @click="save()"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
+ <MkButton primary inline :disabled="!changed" @click="save()"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { defineExpose, ref, watch } from 'vue';
import FormTextarea from '@/components/form/textarea.vue';
import MkKeyValue from '@/components/key-value.vue';
import MkButton from '@/components/ui/button.vue';
@@ -38,114 +38,90 @@ import MkTab from '@/components/tab.vue';
import * as os from '@/os';
import number from '@/filters/number';
import * as symbols from '@/symbols';
+import { defaultStore } from '@/store';
+import { $i } from '@/account';
+import { i18n } from '@/i18n';
-export default defineComponent({
- components: {
- MkButton,
- FormTextarea,
- MkKeyValue,
- MkTab,
- MkInfo,
- },
-
- emits: ['info'],
-
- data() {
- return {
- [symbols.PAGE_INFO]: {
- title: this.$ts.wordMute,
- icon: 'fas fa-comment-slash',
- bg: 'var(--bg)',
- },
- tab: 'soft',
- softMutedWords: '',
- hardMutedWords: '',
- hardWordMutedNotesCount: null,
- changed: false,
- }
- },
+const render = (mutedWords) => mutedWords.map(x => {
+ if (Array.isArray(x)) {
+ return x.join(' ');
+ } else {
+ return x;
+ }
+}).join('\n');
- watch: {
- softMutedWords: {
- handler() {
- this.changed = true;
- },
- deep: true
- },
- hardMutedWords: {
- handler() {
- this.changed = true;
- },
- deep: true
- },
- },
+const tab = ref('soft');
+const softMutedWords = ref(render(defaultStore.state.mutedWords));
+const hardMutedWords = ref(render($i!.mutedWords));
+const hardWordMutedNotesCount = ref(null);
+const changed = ref(false);
- async created() {
- const render = (mutedWords) => mutedWords.map(x => {
- if (Array.isArray(x)) {
- return x.join(' ');
- } else {
- return x;
- }
- }).join('\n');
+os.api('i/get-word-muted-notes-count', {}).then(response => {
+ hardWordMutedNotesCount.value = response?.count;
+});
- this.softMutedWords = render(this.$store.state.mutedWords);
- this.hardMutedWords = render(this.$i.mutedWords);
+watch(softMutedWords, () => {
+ changed.value = true;
+});
- this.hardWordMutedNotesCount = (await os.api('i/get-word-muted-notes-count', {})).count;
- },
+watch(hardMutedWords, () => {
+ changed.value = true;
+});
- methods: {
- async save() {
- const parseMutes = (mutes, tab) => {
- // split into lines, remove empty lines and unnecessary whitespace
- let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line != '');
+async function save() {
+ const parseMutes = (mutes, tab) => {
+ // split into lines, remove empty lines and unnecessary whitespace
+ let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line !== '');
- // check each line if it is a RegExp or not
- for (let i = 0; i < lines.length; i++) {
- const line = lines[i]
- const regexp = line.match(/^\/(.+)\/(.*)$/);
- if (regexp) {
- // check that the RegExp is valid
- try {
- new RegExp(regexp[1], regexp[2]);
- // note that regex lines will not be split by spaces!
- } catch (err) {
- // invalid syntax: do not save, do not reset changed flag
- os.alert({
- type: 'error',
- title: this.$ts.regexpError,
- text: this.$t('regexpErrorDescription', { tab, line: i + 1 }) + "\n" + err.toString()
- });
- // re-throw error so these invalid settings are not saved
- throw err;
- }
- } else {
- lines[i] = line.split(' ');
- }
+ // check each line if it is a RegExp or not
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i]
+ const regexp = line.match(/^\/(.+)\/(.*)$/);
+ if (regexp) {
+ // check that the RegExp is valid
+ try {
+ new RegExp(regexp[1], regexp[2]);
+ // note that regex lines will not be split by spaces!
+ } catch (err: any) {
+ // invalid syntax: do not save, do not reset changed flag
+ os.alert({
+ type: 'error',
+ title: i18n.ts.regexpError,
+ text: i18n.t('regexpErrorDescription', { tab, line: i + 1 }) + "\n" + err.toString()
+ });
+ // re-throw error so these invalid settings are not saved
+ throw err;
}
+ } else {
+ lines[i] = line.split(' ');
+ }
+ }
- return lines;
- };
+ return lines;
+ };
- let softMutes, hardMutes;
- try {
- softMutes = parseMutes(this.softMutedWords, this.$ts._wordMute.soft);
- hardMutes = parseMutes(this.hardMutedWords, this.$ts._wordMute.hard);
- } catch (err) {
- // already displayed error message in parseMutes
- return;
- }
+ let softMutes, hardMutes;
+ try {
+ softMutes = parseMutes(softMutedWords.value, i18n.ts._wordMute.soft);
+ hardMutes = parseMutes(hardMutedWords.value, i18n.ts._wordMute.hard);
+ } catch (err) {
+ // already displayed error message in parseMutes
+ return;
+ }
- this.$store.set('mutedWords', softMutes);
- await os.api('i/update', {
- mutedWords: hardMutes,
- });
+ defaultStore.set('mutedWords', softMutes);
+ await os.api('i/update', {
+ mutedWords: hardMutes,
+ });
- this.changed = false;
- },
+ changed.value = false;
+}
- number
+defineExpose({
+ [symbols.PAGE_INFO]: {
+ title: i18n.ts.wordMute,
+ icon: 'fas fa-comment-slash',
+ bg: 'var(--bg)',
}
});
</script>
diff --git a/packages/client/src/pages/share.vue b/packages/client/src/pages/share.vue
index 4d77de5819..b08ac2b237 100644
--- a/packages/client/src/pages/share.vue
+++ b/packages/client/src/pages/share.vue
@@ -153,11 +153,11 @@ export default defineComponent({
);
}
//#endregion
- } catch (e) {
+ } catch (err) {
os.alert({
type: 'error',
- title: e.message,
- text: e.name
+ title: err.message,
+ text: err.name
});
}
diff --git a/packages/client/src/pages/theme-editor.vue b/packages/client/src/pages/theme-editor.vue
index a53e23c1c5..4250673d91 100644
--- a/packages/client/src/pages/theme-editor.vue
+++ b/packages/client/src/pages/theme-editor.vue
@@ -67,9 +67,9 @@
<script lang="ts" setup>
import { watch } from 'vue';
import { toUnicode } from 'punycode/';
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
import { v4 as uuid} from 'uuid';
-import * as JSON5 from 'json5';
+import JSON5 from 'json5';
import FormButton from '@/components/ui/button.vue';
import FormTextarea from '@/components/form/textarea.vue';
@@ -128,7 +128,7 @@ function showPreview() {
}
function setBgColor(color: typeof bgColors[number]) {
- if (theme.base != color.kind) {
+ if (theme.base !== color.kind) {
const base = color.kind === 'dark' ? darkTheme : lightTheme;
for (const prop of Object.keys(base.props)) {
if (prop === 'accent') continue;
diff --git a/packages/client/src/pages/user-info.vue b/packages/client/src/pages/user-info.vue
index 516ab4d440..1b2682ed29 100644
--- a/packages/client/src/pages/user-info.vue
+++ b/packages/client/src/pages/user-info.vue
@@ -232,10 +232,10 @@ export default defineComponent({
await os.api('admin/delete-all-files-of-a-user', { userId: this.user.id });
os.success();
};
- await process().catch(e => {
+ await process().catch(err => {
os.alert({
type: 'error',
- text: e.toString()
+ text: err.toString(),
});
});
await this.refreshUser();
diff --git a/packages/client/src/pages/user/index.vue b/packages/client/src/pages/user/index.vue
index 405494ec23..a024dd28bc 100644
--- a/packages/client/src/pages/user/index.vue
+++ b/packages/client/src/pages/user/index.vue
@@ -125,7 +125,7 @@
<script lang="ts">
import { defineComponent, defineAsyncComponent, computed } from 'vue';
-import * as age from 's-age';
+import age from 's-age';
import XUserTimeline from './index.timeline.vue';
import XNote from '@/components/note.vue';
import MkFollowButton from '@/components/follow-button.vue';
@@ -260,8 +260,8 @@ export default defineComponent({
this.user = null;
os.api('users/show', Acct.parse(this.acct)).then(user => {
this.user = user;
- }).catch(e => {
- this.error = e;
+ }).catch(err => {
+ this.error = err;
});
},
diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts
index 839841f0fe..db39dd741c 100644
--- a/packages/client/src/router.ts
+++ b/packages/client/src/router.ts
@@ -1,4 +1,4 @@
-import { defineAsyncComponent, markRaw } from 'vue';
+import { AsyncComponentLoader, defineAsyncComponent, markRaw } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import MkLoading from '@/pages/_loading_.vue';
import MkError from '@/pages/_error_.vue';
@@ -6,8 +6,9 @@ import MkTimeline from '@/pages/timeline.vue';
import { $i, iAmModerator } from './account';
import { ui } from '@/config';
-const page = (path: string, ui?: string) => defineAsyncComponent({
- loader: ui ? () => import(`./ui/${ui}/pages/${path}.vue`) : () => import(`./pages/${path}.vue`),
+// pathに/が入るとrollupが解決してくれないので、() => import('*.vue')を指定すること
+const page = (path: string | AsyncComponentLoader<any>, uiName?: string) => defineAsyncComponent({
+ loader: typeof path === 'string' ? uiName ? () => import(`./ui/${ui}/pages/${path}.vue`) : () => import(`./pages/${path}.vue`) : path,
loadingComponent: MkLoading,
errorComponent: MkError,
});
@@ -17,10 +18,10 @@ let indexScrollPos = 0;
const defaultRoutes = [
// NOTE: MkTimelineをdynamic importするとAsyncComponentWrapperが間に入るせいでkeep-aliveのコンポーネント指定が効かなくなる
{ path: '/', name: 'index', component: $i ? MkTimeline : page('welcome') },
- { path: '/@:acct/:page?', name: 'user', component: page('user/index'), props: route => ({ acct: route.params.acct, page: route.params.page || 'index' }) },
+ { path: '/@:acct/:page?', name: 'user', component: page(() => import('./pages/user/index.vue')), props: route => ({ acct: route.params.acct, page: route.params.page || 'index' }) },
{ path: '/@:user/pages/:page', component: page('page'), props: route => ({ pageName: route.params.page, username: route.params.user }) },
- { path: '/@:user/pages/:pageName/view-source', component: page('page-editor/page-editor'), props: route => ({ initUser: route.params.user, initPageName: route.params.pageName }) },
- { path: '/settings/:page(.*)?', name: 'settings', component: page('settings/index'), props: route => ({ initialPage: route.params.page || null }) },
+ { path: '/@:user/pages/:pageName/view-source', component: page(() => import('./pages/page-editor/page-editor.vue')), props: route => ({ initUser: route.params.user, initPageName: route.params.pageName }) },
+ { path: '/settings/:page(.*)?', name: 'settings', component: page(() => import('./pages/settings/index.vue')), props: route => ({ initialPage: route.params.page || null }) },
{ path: '/reset-password/:token?', component: page('reset-password'), props: route => ({ token: route.params.token }) },
{ path: '/signup-complete/:code', component: page('signup-complete'), props: route => ({ code: route.params.code }) },
{ path: '/announcements', component: page('announcements') },
@@ -35,12 +36,12 @@ const defaultRoutes = [
{ path: '/emojis', component: page('emojis') },
{ path: '/search', component: page('search'), props: route => ({ query: route.query.q, channel: route.query.channel }) },
{ path: '/pages', name: 'pages', component: page('pages') },
- { path: '/pages/new', component: page('page-editor/page-editor') },
- { path: '/pages/edit/:pageId', component: page('page-editor/page-editor'), props: route => ({ initPageId: route.params.pageId }) },
- { path: '/gallery', component: page('gallery/index') },
- { path: '/gallery/new', component: page('gallery/edit') },
- { path: '/gallery/:postId/edit', component: page('gallery/edit'), props: route => ({ postId: route.params.postId }) },
- { path: '/gallery/:postId', component: page('gallery/post'), props: route => ({ postId: route.params.postId }) },
+ { path: '/pages/new', component: page(() => import('./pages/page-editor/page-editor.vue')) },
+ { path: '/pages/edit/:pageId', component: page(() => import('./pages/page-editor/page-editor.vue')), props: route => ({ initPageId: route.params.pageId }) },
+ { path: '/gallery', component: page(() => import('./pages/gallery/index.vue')) },
+ { path: '/gallery/new', component: page(() => import('./pages/gallery/edit.vue')) },
+ { path: '/gallery/:postId/edit', component: page(() => import('./pages/gallery/edit.vue')), props: route => ({ postId: route.params.postId }) },
+ { path: '/gallery/:postId', component: page(() => import('./pages/gallery/edit.vue')), props: route => ({ postId: route.params.postId }) },
{ path: '/channels', component: page('channels') },
{ path: '/channels/new', component: page('channel-editor') },
{ path: '/channels/:channelId/edit', component: page('channel-editor'), props: true },
@@ -52,23 +53,23 @@ const defaultRoutes = [
{ path: '/my/favorites', component: page('favorites') },
{ path: '/my/messages', component: page('messages') },
{ path: '/my/mentions', component: page('mentions') },
- { path: '/my/messaging', name: 'messaging', component: page('messaging/index') },
- { path: '/my/messaging/:user', component: page('messaging/messaging-room'), props: route => ({ userAcct: route.params.user }) },
- { path: '/my/messaging/group/:group', component: page('messaging/messaging-room'), props: route => ({ groupId: route.params.group }) },
+ { path: '/my/messaging', name: 'messaging', component: page(() => import('./pages/messaging/index.vue')) },
+ { path: '/my/messaging/:user', component: page(() => import('./pages/messaging/messaging-room.vue')), props: route => ({ userAcct: route.params.user }) },
+ { path: '/my/messaging/group/:group', component: page(() => import('./pages/messaging/messaging-room.vue')), props: route => ({ groupId: route.params.group }) },
{ path: '/my/drive', name: 'drive', component: page('drive') },
{ path: '/my/drive/folder/:folder', component: page('drive') },
{ path: '/my/follow-requests', component: page('follow-requests') },
- { path: '/my/lists', component: page('my-lists/index') },
- { path: '/my/lists/:list', component: page('my-lists/list') },
- { path: '/my/groups', component: page('my-groups/index') },
- { path: '/my/groups/:group', component: page('my-groups/group'), props: route => ({ groupId: route.params.group }) },
- { path: '/my/antennas', component: page('my-antennas/index') },
- { path: '/my/antennas/create', component: page('my-antennas/create') },
- { path: '/my/antennas/:antennaId', component: page('my-antennas/edit'), props: true },
- { path: '/my/clips', component: page('my-clips/index') },
+ { path: '/my/lists', component: page(() => import('./pages/my-lists/index.vue')) },
+ { path: '/my/lists/:list', component: page(() => import('./pages/my-lists/list.vue')) },
+ { path: '/my/groups', component: page(() => import('./pages/my-groups/index.vue')) },
+ { path: '/my/groups/:group', component: page(() => import('./pages/my-groups/group.vue')), props: route => ({ groupId: route.params.group }) },
+ { path: '/my/antennas', component: page(() => import('./pages/my-antennas/index.vue')) },
+ { path: '/my/antennas/create', component: page(() => import('./pages/my-antennas/create.vue')) },
+ { path: '/my/antennas/:antennaId', component: page(() => import('./pages/my-antennas/edit.vue')), props: true },
+ { path: '/my/clips', component: page(() => import('./pages/my-clips/index.vue')) },
{ path: '/scratchpad', component: page('scratchpad') },
- { path: '/admin/:page(.*)?', component: iAmModerator ? page('admin/index') : page('not-found'), props: route => ({ initialPage: route.params.page || null }) },
- { path: '/admin', component: iAmModerator ? page('admin/index') : page('not-found') },
+ { path: '/admin/:page(.*)?', component: iAmModerator ? page(() => import('./pages/admin/index.vue')) : page('not-found'), props: route => ({ initialPage: route.params.page || null }) },
+ { path: '/admin', component: iAmModerator ? page(() => import('./pages/admin/index.vue')) : page('not-found') },
{ path: '/notes/:note', name: 'note', component: page('note'), props: route => ({ noteId: route.params.note }) },
{ path: '/tags/:tag', component: page('tag'), props: route => ({ tag: route.params.tag }) },
{ path: '/user-info/:user', component: page('user-info'), props: route => ({ userId: route.params.user }) },
diff --git a/packages/client/src/scripts/2fa.ts b/packages/client/src/scripts/2fa.ts
index 00363cffa6..d1b9581e72 100644
--- a/packages/client/src/scripts/2fa.ts
+++ b/packages/client/src/scripts/2fa.ts
@@ -1,11 +1,11 @@
-export function byteify(data: string, encoding: 'ascii' | 'base64' | 'hex') {
+export function byteify(string: string, encoding: 'ascii' | 'base64' | 'hex') {
switch (encoding) {
case 'ascii':
- return Uint8Array.from(data, c => c.charCodeAt(0));
+ return Uint8Array.from(string, c => c.charCodeAt(0));
case 'base64':
return Uint8Array.from(
atob(
- data
+ string
.replace(/-/g, '+')
.replace(/_/g, '/')
),
@@ -13,7 +13,7 @@ export function byteify(data: string, encoding: 'ascii' | 'base64' | 'hex') {
);
case 'hex':
return new Uint8Array(
- data
+ string
.match(/.{1,2}/g)
.map(byte => parseInt(byte, 16))
);
diff --git a/packages/client/src/scripts/autocomplete.ts b/packages/client/src/scripts/autocomplete.ts
index f4a3a4c0fc..8d9bdee8f5 100644
--- a/packages/client/src/scripts/autocomplete.ts
+++ b/packages/client/src/scripts/autocomplete.ts
@@ -1,5 +1,5 @@
-import { nextTick, Ref, ref } from 'vue';
-import * as getCaretCoordinates from 'textarea-caret';
+import { nextTick, Ref, ref, defineAsyncComponent } from 'vue';
+import getCaretCoordinates from 'textarea-caret';
import { toASCII } from 'punycode/';
import { popup } from '@/os';
@@ -74,21 +74,21 @@ export class Autocomplete {
emojiIndex,
mfmTagIndex);
- if (max == -1) {
+ if (max === -1) {
this.close();
return;
}
- const isMention = mentionIndex != -1;
- const isHashtag = hashtagIndex != -1;
- const isMfmTag = mfmTagIndex != -1;
- const isEmoji = emojiIndex != -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
+ const isMention = mentionIndex !== -1;
+ const isHashtag = hashtagIndex !== -1;
+ const isMfmTag = mfmTagIndex !== -1;
+ const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
let opened = false;
if (isMention) {
const username = text.substr(mentionIndex + 1);
- if (username != '' && username.match(/^[a-zA-Z0-9_]+$/)) {
+ if (username !== '' && username.match(/^[a-zA-Z0-9_]+$/)) {
this.open('user', username);
opened = true;
} else if (username === '') {
@@ -130,7 +130,7 @@ export class Autocomplete {
* サジェストを提示します。
*/
private async open(type: string, q: string | null) {
- if (type != this.currentType) {
+ if (type !== this.currentType) {
this.close();
}
if (this.opening) return;
@@ -157,7 +157,7 @@ export class Autocomplete {
const _y = ref(y);
const _q = ref(q);
- const { dispose } = await popup(import('@/components/autocomplete.vue'), {
+ const { dispose } = await popup(defineAsyncComponent(() => import('@/components/autocomplete.vue')), {
textarea: this.textarea,
close: this.close,
type: type,
@@ -201,7 +201,7 @@ export class Autocomplete {
const caret = this.textarea.selectionStart;
- if (type == 'user') {
+ if (type === 'user') {
const source = this.text;
const before = source.substr(0, caret);
@@ -219,7 +219,7 @@ export class Autocomplete {
const pos = trimmedBefore.length + (acct.length + 2);
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type == 'hashtag') {
+ } else if (type === 'hashtag') {
const source = this.text;
const before = source.substr(0, caret);
@@ -235,7 +235,7 @@ export class Autocomplete {
const pos = trimmedBefore.length + (value.length + 2);
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type == 'emoji') {
+ } else if (type === 'emoji') {
const source = this.text;
const before = source.substr(0, caret);
@@ -251,7 +251,7 @@ export class Autocomplete {
const pos = trimmedBefore.length + value.length;
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type == 'mfmTag') {
+ } else if (type === 'mfmTag') {
const source = this.text;
const before = source.substr(0, caret);
diff --git a/packages/client/src/scripts/contains.ts b/packages/client/src/scripts/contains.ts
index 770bda63bb..256e09d293 100644
--- a/packages/client/src/scripts/contains.ts
+++ b/packages/client/src/scripts/contains.ts
@@ -2,7 +2,7 @@ export default (parent, child, checkSame = true) => {
if (checkSame && parent === child) return true;
let node = child.parentNode;
while (node) {
- if (node == parent) return true;
+ if (node === parent) return true;
node = node.parentNode;
}
return false;
diff --git a/packages/client/src/scripts/emojilist.ts b/packages/client/src/scripts/emojilist.ts
index bd8689e4f8..4196170d24 100644
--- a/packages/client/src/scripts/emojilist.ts
+++ b/packages/client/src/scripts/emojilist.ts
@@ -8,4 +8,4 @@ export type UnicodeEmojiDef = {
}
// initial converted from https://github.com/muan/emojilib/commit/242fe68be86ed6536843b83f7e32f376468b38fb
-export const emojilist = require('../emojilist.json') as UnicodeEmojiDef[];
+export const emojilist = (await import('../emojilist.json')).default as UnicodeEmojiDef[];
diff --git a/packages/client/src/scripts/extract-avg-color-from-blurhash.ts b/packages/client/src/scripts/extract-avg-color-from-blurhash.ts
index 123ab7a06d..af517f2672 100644
--- a/packages/client/src/scripts/extract-avg-color-from-blurhash.ts
+++ b/packages/client/src/scripts/extract-avg-color-from-blurhash.ts
@@ -1,5 +1,5 @@
export function extractAvgColorFromBlurhash(hash: string) {
- return typeof hash == 'string'
+ return typeof hash === 'string'
? '#' + [...hash.slice(2, 6)]
.map(x => '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~'.indexOf(x))
.reduce((a, c) => a * 83 + c, 0)
diff --git a/packages/client/src/scripts/get-account-from-id.ts b/packages/client/src/scripts/get-account-from-id.ts
index ba3adceecc..1da897f176 100644
--- a/packages/client/src/scripts/get-account-from-id.ts
+++ b/packages/client/src/scripts/get-account-from-id.ts
@@ -3,5 +3,5 @@ import { get } from '@/scripts/idb-proxy';
export async function getAccountFromId(id: string) {
const accounts = await get('accounts') as { token: string; id: string; }[];
if (!accounts) console.log('Accounts are not recorded');
- return accounts.find(e => e.id === id);
+ return accounts.find(account => account.id === id);
}
diff --git a/packages/client/src/scripts/get-md5.ts b/packages/client/src/scripts/get-md5.ts
deleted file mode 100644
index b002d762b1..0000000000
--- a/packages/client/src/scripts/get-md5.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-// スクリプトサイズがデカい
-//import * as crypto from 'crypto';
-
-export default (data: ArrayBuffer) => {
- //const buf = new Buffer(data);
- //const hash = crypto.createHash('md5');
- //hash.update(buf);
- //return hash.digest('hex');
- return '';
-};
diff --git a/packages/client/src/scripts/get-note-menu.ts b/packages/client/src/scripts/get-note-menu.ts
index b19656d3cc..aeb09ef97a 100644
--- a/packages/client/src/scripts/get-note-menu.ts
+++ b/packages/client/src/scripts/get-note-menu.ts
@@ -1,4 +1,4 @@
-import { Ref } from 'vue';
+import { defineAsyncComponent, Ref } from 'vue';
import * as misskey from 'misskey-js';
import { $i } from '@/account';
import { i18n } from '@/i18n';
@@ -83,8 +83,8 @@ export function getNoteMenu(props: {
function togglePin(pin: boolean): void {
os.apiWithDialog(pin ? 'i/pin' : 'i/unpin', {
noteId: appearNote.id
- }, undefined, null, e => {
- if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
+ }, undefined, null, res => {
+ if (res.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
os.alert({
type: 'error',
text: i18n.ts.pinLimitExceeded
@@ -209,7 +209,7 @@ export function getNoteMenu(props: {
text: i18n.ts.clip,
action: () => clip()
},
- (appearNote.userId != $i.id) ? statePromise.then(state => state.isWatching ? {
+ (appearNote.userId !== $i.id) ? statePromise.then(state => state.isWatching ? {
icon: 'fas fa-eye-slash',
text: i18n.ts.unwatch,
action: () => toggleWatch(false)
@@ -227,7 +227,7 @@ export function getNoteMenu(props: {
text: i18n.ts.muteThread,
action: () => toggleThreadMute(true)
}),
- appearNote.userId == $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? {
+ appearNote.userId === $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? {
icon: 'fas fa-thumbtack',
text: i18n.ts.unpin,
action: () => togglePin(false)
@@ -246,14 +246,14 @@ export function getNoteMenu(props: {
}]
: []
),*/
- ...(appearNote.userId != $i.id ? [
+ ...(appearNote.userId !== $i.id ? [
null,
{
icon: 'fas fa-exclamation-circle',
text: i18n.ts.reportAbuse,
action: () => {
const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`;
- os.popup(import('@/components/abuse-report-window.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/abuse-report-window.vue')), {
user: appearNote.user,
initialComment: `Note: ${u}\n-----\n`
}, {}, 'closed');
@@ -261,9 +261,9 @@ export function getNoteMenu(props: {
}]
: []
),
- ...(appearNote.userId == $i.id || $i.isModerator || $i.isAdmin ? [
+ ...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [
null,
- appearNote.userId == $i.id ? {
+ appearNote.userId === $i.id ? {
icon: 'fas fa-edit',
text: i18n.ts.deleteAndEdit,
action: delEdit
diff --git a/packages/client/src/scripts/get-note-summary.ts b/packages/client/src/scripts/get-note-summary.ts
index 54b8d109d6..d57e1c3029 100644
--- a/packages/client/src/scripts/get-note-summary.ts
+++ b/packages/client/src/scripts/get-note-summary.ts
@@ -24,7 +24,7 @@ export const getNoteSummary = (note: misskey.entities.Note): string => {
}
// ファイルが添付されているとき
- if ((note.files || []).length != 0) {
+ if ((note.files || []).length !== 0) {
summary += ` (${i18n.t('withNFiles', { n: note.files.length })})`;
}
diff --git a/packages/client/src/scripts/get-user-menu.ts b/packages/client/src/scripts/get-user-menu.ts
index 192d14b83e..1d2b761117 100644
--- a/packages/client/src/scripts/get-user-menu.ts
+++ b/packages/client/src/scripts/get-user-menu.ts
@@ -6,6 +6,7 @@ import * as os from '@/os';
import { userActions } from '@/store';
import { router } from '@/router';
import { $i, iAmModerator } from '@/account';
+import { defineAsyncComponent } from 'vue';
export function getUserMenu(user) {
const meId = $i ? $i.id : null;
@@ -127,7 +128,7 @@ export function getUserMenu(user) {
}
function reportAbuse() {
- os.popup(import('@/components/abuse-report-window.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/abuse-report-window.vue')), {
user: user,
}, {}, 'closed');
}
@@ -168,7 +169,7 @@ export function getUserMenu(user) {
action: () => {
os.post({ specified: user });
}
- }, meId != user.id ? {
+ }, meId !== user.id ? {
type: 'link',
icon: 'fas fa-comments',
text: i18n.ts.startMessaging,
@@ -177,13 +178,13 @@ export function getUserMenu(user) {
icon: 'fas fa-list-ul',
text: i18n.ts.addToList,
action: pushList
- }, meId != user.id ? {
+ }, meId !== user.id ? {
icon: 'fas fa-users',
text: i18n.ts.inviteToGroup,
action: inviteGroup
} : undefined] as any;
- if ($i && meId != user.id) {
+ if ($i && meId !== user.id) {
menu = menu.concat([null, {
icon: user.isMuted ? 'fas fa-eye' : 'fas fa-eye-slash',
text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute,
diff --git a/packages/client/src/scripts/hotkey.ts b/packages/client/src/scripts/hotkey.ts
index 2b3f491fd8..fd9c74f6c8 100644
--- a/packages/client/src/scripts/hotkey.ts
+++ b/packages/client/src/scripts/hotkey.ts
@@ -53,34 +53,34 @@ const parseKeymap = (keymap: Keymap) => Object.entries(keymap).map(([patterns, c
const ignoreElemens = ['input', 'textarea'];
-function match(e: KeyboardEvent, patterns: Action['patterns']): boolean {
- const key = e.code.toLowerCase();
+function match(ev: KeyboardEvent, patterns: Action['patterns']): boolean {
+ const key = ev.code.toLowerCase();
return patterns.some(pattern => pattern.which.includes(key) &&
- pattern.ctrl === e.ctrlKey &&
- pattern.shift === e.shiftKey &&
- pattern.alt === e.altKey &&
- !e.metaKey
+ pattern.ctrl === ev.ctrlKey &&
+ pattern.shift === ev.shiftKey &&
+ pattern.alt === ev.altKey &&
+ !ev.metaKey
);
}
export const makeHotkey = (keymap: Keymap) => {
const actions = parseKeymap(keymap);
- return (e: KeyboardEvent) => {
+ return (ev: KeyboardEvent) => {
if (document.activeElement) {
if (ignoreElemens.some(el => document.activeElement!.matches(el))) return;
if (document.activeElement.attributes['contenteditable']) return;
}
for (const action of actions) {
- const matched = match(e, action.patterns);
+ const matched = match(ev, action.patterns);
if (matched) {
- if (!action.allowRepeat && e.repeat) return;
+ if (!action.allowRepeat && ev.repeat) return;
- e.preventDefault();
- e.stopPropagation();
- action.callback(e);
+ ev.preventDefault();
+ ev.stopPropagation();
+ action.callback(ev);
break;
}
}
diff --git a/packages/client/src/scripts/hpml/evaluator.ts b/packages/client/src/scripts/hpml/evaluator.ts
index 6329c0860e..0469a31cbb 100644
--- a/packages/client/src/scripts/hpml/evaluator.ts
+++ b/packages/client/src/scripts/hpml/evaluator.ts
@@ -85,7 +85,7 @@ export class Hpml {
public eval() {
try {
this.vars.value = this.evaluateVars();
- } catch (e) {
+ } catch (err) {
//this.onError(e);
}
}
@@ -103,7 +103,7 @@ export class Hpml {
public callAiScript(fn: string) {
try {
if (this.aiscript) this.aiscript.execFn(this.aiscript.scope.get(fn), []);
- } catch (e) {}
+ } catch (err) {}
}
@autobind
@@ -185,7 +185,7 @@ export class Hpml {
if (this.aiscript) {
try {
return utils.valToJs(this.aiscript.scope.get(expr.value));
- } catch (e) {
+ } catch (err) {
return null;
}
} else {
@@ -194,7 +194,7 @@ export class Hpml {
}
// Define user function
- if (expr.type == 'fn') {
+ if (expr.type === 'fn') {
return {
slots: expr.value.slots.map(x => x.name),
exec: (slotArg: Record<string, any>) => {
diff --git a/packages/client/src/scripts/hpml/lib.ts b/packages/client/src/scripts/hpml/lib.ts
index 2a1ac73a40..01a44ffcdf 100644
--- a/packages/client/src/scripts/hpml/lib.ts
+++ b/packages/client/src/scripts/hpml/lib.ts
@@ -1,9 +1,9 @@
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
import { Hpml } from './evaluator';
import { values, utils } from '@syuilo/aiscript';
import { Fn, HpmlScope } from '.';
import { Expr } from './expr';
-import * as seedrandom from 'seedrandom';
+import seedrandom from 'seedrandom';
/* TODO: https://www.chartjs.org/docs/latest/configuration/canvas-background.html#color
// https://stackoverflow.com/questions/38493564/chart-area-background-color-chartjs
diff --git a/packages/client/src/scripts/idb-proxy.ts b/packages/client/src/scripts/idb-proxy.ts
index 5f76ae30bb..d462a0d7ce 100644
--- a/packages/client/src/scripts/idb-proxy.ts
+++ b/packages/client/src/scripts/idb-proxy.ts
@@ -13,8 +13,8 @@ let idbAvailable = typeof window !== 'undefined' ? !!window.indexedDB : true;
if (idbAvailable) {
try {
await iset('idb-test', 'test');
- } catch (e) {
- console.error('idb error', e);
+ } catch (err) {
+ console.error('idb error', err);
idbAvailable = false;
}
}
diff --git a/packages/client/src/scripts/lookup-user.ts b/packages/client/src/scripts/lookup-user.ts
index 8de5c84ce8..2d00e51621 100644
--- a/packages/client/src/scripts/lookup-user.ts
+++ b/packages/client/src/scripts/lookup-user.ts
@@ -25,12 +25,12 @@ export async function lookupUser() {
_notFound = true;
}
};
- usernamePromise.then(show).catch(e => {
- if (e.code === 'NO_SUCH_USER') {
+ usernamePromise.then(show).catch(err => {
+ if (err.code === 'NO_SUCH_USER') {
notFound();
}
});
- idPromise.then(show).catch(e => {
+ idPromise.then(show).catch(err => {
notFound();
});
}
diff --git a/packages/client/src/scripts/please-login.ts b/packages/client/src/scripts/please-login.ts
index aeaafa124b..e21a6d2ed3 100644
--- a/packages/client/src/scripts/please-login.ts
+++ b/packages/client/src/scripts/please-login.ts
@@ -1,14 +1,21 @@
+import { defineAsyncComponent } from 'vue';
import { $i } from '@/account';
import { i18n } from '@/i18n';
-import { alert } from '@/os';
+import { popup } from '@/os';
-export function pleaseLogin() {
+export function pleaseLogin(path?: string) {
if ($i) return;
- alert({
- title: i18n.ts.signinRequired,
- text: null
- });
+ popup(defineAsyncComponent(() => import('@/components/signin-dialog.vue')), {
+ autoSet: true,
+ message: i18n.ts.signinRequired
+ }, {
+ cancelled: () => {
+ if (path) {
+ window.location.href = path;
+ }
+ },
+ }, 'closed');
throw new Error('signin required');
}
diff --git a/packages/client/src/scripts/reaction-picker.ts b/packages/client/src/scripts/reaction-picker.ts
index 3ac1f63430..b7699cae4a 100644
--- a/packages/client/src/scripts/reaction-picker.ts
+++ b/packages/client/src/scripts/reaction-picker.ts
@@ -1,4 +1,4 @@
-import { Ref, ref } from 'vue';
+import { defineAsyncComponent, Ref, ref } from 'vue';
import { popup } from '@/os';
class ReactionPicker {
@@ -12,7 +12,7 @@ class ReactionPicker {
}
public async init() {
- await popup(import('@/components/emoji-picker-dialog.vue'), {
+ await popup(defineAsyncComponent(() => import('@/components/emoji-picker-dialog.vue')), {
src: this.src,
asReactionPicker: true,
manualShowing: this.manualShowing
diff --git a/packages/client/src/scripts/select-file.ts b/packages/client/src/scripts/select-file.ts
index 49a46f0bb2..461d613b42 100644
--- a/packages/client/src/scripts/select-file.ts
+++ b/packages/client/src/scripts/select-file.ts
@@ -19,10 +19,10 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv
Promise.all(promises).then(driveFiles => {
res(multiple ? driveFiles : driveFiles[0]);
- }).catch(e => {
+ }).catch(err => {
os.alert({
type: 'error',
- text: e
+ text: err
});
});
@@ -54,9 +54,9 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv
const marker = Math.random().toString(); // TODO: UUIDとか使う
const connection = stream.useChannel('main');
- connection.on('urlUploadFinished', data => {
- if (data.marker === marker) {
- res(multiple ? [data.file] : data.file);
+ connection.on('urlUploadFinished', urlResponse => {
+ if (urlResponse.marker === marker) {
+ res(multiple ? [urlResponse.file] : urlResponse.file);
connection.dispose();
}
});
diff --git a/packages/client/src/scripts/theme.ts b/packages/client/src/scripts/theme.ts
index 2cb78fae5c..b61b1684a8 100644
--- a/packages/client/src/scripts/theme.ts
+++ b/packages/client/src/scripts/theme.ts
@@ -1,5 +1,5 @@
import { globalEvents } from '@/events';
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
export type Theme = {
id: string;
@@ -10,29 +10,29 @@ export type Theme = {
props: Record<string, string>;
};
-export const lightTheme: Theme = require('@/themes/_light.json5');
-export const darkTheme: Theme = require('@/themes/_dark.json5');
+export const lightTheme: Theme = await import('@/themes/_light.json5');
+export const darkTheme: Theme = await import('@/themes/_dark.json5');
export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X'));
export const builtinThemes = [
- require('@/themes/l-light.json5'),
- require('@/themes/l-coffee.json5'),
- require('@/themes/l-apricot.json5'),
- require('@/themes/l-rainy.json5'),
- require('@/themes/l-vivid.json5'),
- require('@/themes/l-cherry.json5'),
- require('@/themes/l-sushi.json5'),
+ await import('@/themes/l-light.json5'),
+ await import('@/themes/l-coffee.json5'),
+ await import('@/themes/l-apricot.json5'),
+ await import('@/themes/l-rainy.json5'),
+ await import('@/themes/l-vivid.json5'),
+ await import('@/themes/l-cherry.json5'),
+ await import('@/themes/l-sushi.json5'),
- require('@/themes/d-dark.json5'),
- require('@/themes/d-persimmon.json5'),
- require('@/themes/d-astro.json5'),
- require('@/themes/d-future.json5'),
- require('@/themes/d-botanical.json5'),
- require('@/themes/d-cherry.json5'),
- require('@/themes/d-ice.json5'),
- require('@/themes/d-pumpkin.json5'),
- require('@/themes/d-black.json5'),
+ await import('@/themes/d-dark.json5'),
+ await import('@/themes/d-persimmon.json5'),
+ await import('@/themes/d-astro.json5'),
+ await import('@/themes/d-future.json5'),
+ await import('@/themes/d-botanical.json5'),
+ await import('@/themes/d-cherry.json5'),
+ await import('@/themes/d-ice.json5'),
+ await import('@/themes/d-pumpkin.json5'),
+ await import('@/themes/d-black.json5'),
] as Theme[];
let timeout = null;
diff --git a/packages/client/src/scripts/upload.ts b/packages/client/src/scripts/upload.ts
index 7e4f793b44..2f7b30b58d 100644
--- a/packages/client/src/scripts/upload.ts
+++ b/packages/client/src/scripts/upload.ts
@@ -33,13 +33,13 @@ export function uploadFile(
name?: string,
keepOriginal: boolean = defaultStore.state.keepOriginalUploading
): Promise<Misskey.entities.DriveFile> {
- if (folder && typeof folder == 'object') folder = folder.id;
+ if (folder && typeof folder === 'object') folder = folder.id;
return new Promise((resolve, reject) => {
const id = Math.random().toString();
const reader = new FileReader();
- reader.onload = async (e) => {
+ reader.onload = async (ev) => {
const ctx = reactive<Uploading>({
id: id,
name: name || file.name || 'untitled',
@@ -64,24 +64,24 @@ export function uploadFile(
try {
resizedImage = await readAndCompressImage(file, config);
ctx.name = file.type !== imgConfig.mimeType ? `${ctx.name}.${mimeTypeMap[compressTypeMap[file.type].mimeType]}` : ctx.name;
- } catch (e) {
- console.error('Failed to resize image', e);
+ } catch (err) {
+ console.error('Failed to resize image', err);
}
}
- const data = new FormData();
- data.append('i', $i.token);
- data.append('force', 'true');
- data.append('file', resizedImage || file);
- data.append('name', ctx.name);
- if (folder) data.append('folderId', folder);
+ const formData = new FormData();
+ formData.append('i', $i.token);
+ formData.append('force', 'true');
+ formData.append('file', resizedImage || file);
+ formData.append('name', ctx.name);
+ if (folder) formData.append('folderId', folder);
const xhr = new XMLHttpRequest();
xhr.open('POST', apiUrl + '/drive/files/create', true);
xhr.onload = (ev) => {
if (xhr.status !== 200 || ev.target == null || ev.target.response == null) {
// TODO: 消すのではなくて再送できるようにしたい
- uploads.value = uploads.value.filter(x => x.id != id);
+ uploads.value = uploads.value.filter(x => x.id !== id);
alert({
type: 'error',
@@ -97,17 +97,17 @@ export function uploadFile(
resolve(driveFile);
- uploads.value = uploads.value.filter(x => x.id != id);
+ uploads.value = uploads.value.filter(x => x.id !== id);
};
- xhr.upload.onprogress = e => {
- if (e.lengthComputable) {
- ctx.progressMax = e.total;
- ctx.progressValue = e.loaded;
+ xhr.upload.onprogress = ev => {
+ if (ev.lengthComputable) {
+ ctx.progressMax = ev.total;
+ ctx.progressValue = ev.loaded;
}
};
- xhr.send(data);
+ xhr.send(formData);
};
reader.readAsArrayBuffer(file);
});
diff --git a/packages/client/src/scripts/url.ts b/packages/client/src/scripts/url.ts
index c7f2b7c1e7..542b00e0f0 100644
--- a/packages/client/src/scripts/url.ts
+++ b/packages/client/src/scripts/url.ts
@@ -4,7 +4,7 @@ export function query(obj: {}): string {
.reduce((a, [k, v]) => (a[k] = v, a), {} as Record<string, any>);
return Object.entries(params)
- .map((e) => `${e[0]}=${encodeURIComponent(e[1])}`)
+ .map((p) => `${p[0]}=${encodeURIComponent(p[1])}`)
.join('&');
}
diff --git a/packages/client/src/scripts/use-note-capture.ts b/packages/client/src/scripts/use-note-capture.ts
index b2a96062c7..f1f976693e 100644
--- a/packages/client/src/scripts/use-note-capture.ts
+++ b/packages/client/src/scripts/use-note-capture.ts
@@ -11,8 +11,8 @@ export function useNoteCapture(props: {
const note = props.note;
const connection = $i ? stream : null;
- function onStreamNoteUpdated(data): void {
- const { type, id, body } = data;
+ function onStreamNoteUpdated(noteData): void {
+ const { type, id, body } = noteData;
if (id !== note.value.id) return;
diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts
index 296eaa2068..bf4fb6f710 100644
--- a/packages/client/src/store.ts
+++ b/packages/client/src/store.ts
@@ -255,10 +255,13 @@ type Plugin = {
/**
* 常にメモリにロードしておく必要がないような設定情報を保管するストレージ(非リアクティブ)
*/
+import lightTheme from '@/themes/l-light.json5';
+import darkTheme from '@/themes/d-dark.json5'
+
export class ColdDeviceStorage {
public static default = {
- lightTheme: require('@/themes/l-light.json5') as Theme,
- darkTheme: require('@/themes/d-dark.json5') as Theme,
+ lightTheme,
+ darkTheme,
syncDeviceDarkMode: true,
plugins: [] as Plugin[],
mediaVolume: 0.5,
diff --git a/packages/client/src/theme-store.ts b/packages/client/src/theme-store.ts
index e7962e7e8e..fdc92ed793 100644
--- a/packages/client/src/theme-store.ts
+++ b/packages/client/src/theme-store.ts
@@ -14,9 +14,9 @@ export async function fetchThemes(): Promise<void> {
try {
const themes = await api('i/registry/get', { scope: ['client'], key: 'themes' });
localStorage.setItem(lsCacheKey, JSON.stringify(themes));
- } catch (e) {
- if (e.code === 'NO_SUCH_KEY') return;
- throw e;
+ } catch (err) {
+ if (err.code === 'NO_SUCH_KEY') return;
+ throw err;
}
}
@@ -28,7 +28,7 @@ export async function addTheme(theme: Theme): Promise<void> {
}
export async function removeTheme(theme: Theme): Promise<void> {
- const themes = getThemes().filter(t => t.id != theme.id);
+ const themes = getThemes().filter(t => t.id !== theme.id);
await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes });
localStorage.setItem(lsCacheKey, JSON.stringify(themes));
}
diff --git a/packages/client/src/ui/_common_/common.vue b/packages/client/src/ui/_common_/common.vue
index 62e97a11e1..9f7388db53 100644
--- a/packages/client/src/ui/_common_/common.vue
+++ b/packages/client/src/ui/_common_/common.vue
@@ -39,7 +39,7 @@ export default defineComponent({
id: notification.id
});
- popup(import('@/components/notification-toast.vue'), {
+ popup(defineAsyncComponent(() => import('@/components/notification-toast.vue')), {
notification
}, {}, 'closed');
}
diff --git a/packages/client/src/ui/_common_/sidebar-for-mobile.vue b/packages/client/src/ui/_common_/sidebar-for-mobile.vue
index afcc50725b..064a63bf29 100644
--- a/packages/client/src/ui/_common_/sidebar-for-mobile.vue
+++ b/packages/client/src/ui/_common_/sidebar-for-mobile.vue
@@ -33,7 +33,7 @@
</template>
<script lang="ts">
-import { computed, defineComponent, ref, toRef, watch } from 'vue';
+import { computed, defineAsyncComponent, defineComponent, ref, toRef, watch } from 'vue';
import { host } from '@/config';
import { search } from '@/scripts/search';
import * as os from '@/os';
@@ -67,7 +67,7 @@ export default defineComponent({
}, ev);
},
more: () => {
- os.popup(import('@/components/launch-pad.vue'), {}, {
+ os.popup(defineAsyncComponent(() => import('@/components/launch-pad.vue')), {}, {
}, 'closed');
},
};
diff --git a/packages/client/src/ui/_common_/sidebar.vue b/packages/client/src/ui/_common_/sidebar.vue
index a23b7d4152..d65e776d86 100644
--- a/packages/client/src/ui/_common_/sidebar.vue
+++ b/packages/client/src/ui/_common_/sidebar.vue
@@ -33,7 +33,7 @@
</template>
<script lang="ts" setup>
-import { computed, ref, watch } from 'vue';
+import { computed, defineAsyncComponent, ref, watch } from 'vue';
import * as os from '@/os';
import { menuDef } from '@/menu';
import { $i, openAccountMenu as openAccountMenu_ } from '@/account';
@@ -69,7 +69,7 @@ function openAccountMenu(ev: MouseEvent) {
}
function more(ev: MouseEvent) {
- os.popup(import('@/components/launch-pad.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/launch-pad.vue')), {
src: ev.currentTarget ?? ev.target,
}, {
}, 'closed');
diff --git a/packages/client/src/ui/_common_/sw-inject.ts b/packages/client/src/ui/_common_/sw-inject.ts
index e3e2ddd7e6..371f80ca15 100644
--- a/packages/client/src/ui/_common_/sw-inject.ts
+++ b/packages/client/src/ui/_common_/sw-inject.ts
@@ -14,30 +14,29 @@ export function swInject() {
console.log('sw msg', ev.data);
}
- const data = ev.data; // as SwMessage
- if (data.type !== 'order') return;
+ if (ev.data.type !== 'order') return;
- if (data.loginId !== $i?.id) {
- return getAccountFromId(data.loginId).then(account => {
+ if (ev.data.loginId !== $i?.id) {
+ return getAccountFromId(ev.data.loginId).then(account => {
if (!account) return;
- return login(account.token, data.url);
+ return login(account.token, ev.data.url);
});
}
- switch (data.order) {
+ switch (ev.data.order) {
case 'post':
- return post(data.options);
+ return post(ev.data.options);
case 'push':
- if (router.currentRoute.value.path === data.url) {
+ if (router.currentRoute.value.path === ev.data.url) {
return window.scroll({ top: 0, behavior: 'smooth' });
}
if (navHook) {
- return navHook(data.url);
+ return navHook(ev.data.url);
}
- if (sideViewHook && defaultStore.state.defaultSideView && data.url !== '/') {
- return sideViewHook(data.url);
+ if (sideViewHook && defaultStore.state.defaultSideView && ev.data.url !== '/') {
+ return sideViewHook(ev.data.url);
}
- return router.push(data.url);
+ return router.push(ev.data.url);
default:
return;
}
diff --git a/packages/client/src/ui/classic.header.vue b/packages/client/src/ui/classic.header.vue
index c7fddbc491..57008aeaed 100644
--- a/packages/client/src/ui/classic.header.vue
+++ b/packages/client/src/ui/classic.header.vue
@@ -39,7 +39,7 @@
</template>
<script lang="ts">
-import { defineComponent } from 'vue';
+import { defineAsyncComponent, defineComponent } from 'vue';
import { host } from '@/config';
import { search } from '@/scripts/search';
import * as os from '@/os';
@@ -101,7 +101,7 @@ export default defineComponent({
},
more(ev) {
- os.popup(import('@/components/launch-pad.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/launch-pad.vue')), {
src: ev.currentTarget ?? ev.target,
anchor: { x: 'center', y: 'bottom' },
}, {
diff --git a/packages/client/src/ui/classic.sidebar.vue b/packages/client/src/ui/classic.sidebar.vue
index 3364ee39be..ad11c3ebd5 100644
--- a/packages/client/src/ui/classic.sidebar.vue
+++ b/packages/client/src/ui/classic.sidebar.vue
@@ -41,7 +41,7 @@
</template>
<script lang="ts">
-import { defineComponent } from 'vue';
+import { defineAsyncComponent, defineComponent } from 'vue';
import { host } from '@/config';
import { search } from '@/scripts/search';
import * as os from '@/os';
@@ -121,7 +121,7 @@ export default defineComponent({
},
more(ev) {
- os.popup(import('@/components/launch-pad.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/launch-pad.vue')), {
src: ev.currentTarget ?? ev.target,
}, {}, 'closed');
},
diff --git a/packages/client/src/ui/classic.widgets.vue b/packages/client/src/ui/classic.widgets.vue
index f42f27e926..6f9d18bde5 100644
--- a/packages/client/src/ui/classic.widgets.vue
+++ b/packages/client/src/ui/classic.widgets.vue
@@ -44,13 +44,13 @@ export default defineComponent({
},
removeWidget(widget) {
- this.$store.set('widgets', this.$store.state.widgets.filter(w => w.id != widget.id));
+ this.$store.set('widgets', this.$store.state.widgets.filter(w => w.id !== widget.id));
},
updateWidget({ id, data }) {
this.$store.set('widgets', this.$store.state.widgets.map(w => w.id === id ? {
...w,
- data: data
+ data,
} : w));
},
diff --git a/packages/client/src/ui/deck/antenna-column.vue b/packages/client/src/ui/deck/antenna-column.vue
index e0f56c2800..f12f5c6b25 100644
--- a/packages/client/src/ui/deck/antenna-column.vue
+++ b/packages/client/src/ui/deck/antenna-column.vue
@@ -22,8 +22,8 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'loaded'): void;
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'loaded'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
let timeline = $ref<InstanceType<typeof XTimeline>>();
diff --git a/packages/client/src/ui/deck/column-core.vue b/packages/client/src/ui/deck/column-core.vue
index 485e89a062..2667b6d745 100644
--- a/packages/client/src/ui/deck/column-core.vue
+++ b/packages/client/src/ui/deck/column-core.vue
@@ -29,7 +29,7 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
/*
diff --git a/packages/client/src/ui/deck/column.vue b/packages/client/src/ui/deck/column.vue
index 5f8da8cf8f..fbaea64f56 100644
--- a/packages/client/src/ui/deck/column.vue
+++ b/packages/client/src/ui/deck/column.vue
@@ -61,8 +61,8 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
- (e: 'change-active-state', v: boolean): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'change-active-state', v: boolean): void;
}>();
let body = $ref<HTMLDivElement>();
@@ -193,9 +193,9 @@ function goTop() {
});
}
-function onDragstart(e) {
- e.dataTransfer.effectAllowed = 'move';
- e.dataTransfer.setData(_DATA_TRANSFER_DECK_COLUMN_, props.column.id);
+function onDragstart(ev) {
+ ev.dataTransfer.effectAllowed = 'move';
+ ev.dataTransfer.setData(_DATA_TRANSFER_DECK_COLUMN_, props.column.id);
// Chromeのバグで、Dragstartハンドラ内ですぐにDOMを変更する(=リアクティブなプロパティを変更する)とDragが終了してしまう
// SEE: https://stackoverflow.com/questions/19639969/html5-dragend-event-firing-immediately
@@ -204,21 +204,21 @@ function onDragstart(e) {
}, 10);
}
-function onDragend(e) {
+function onDragend(ev) {
dragging = false;
}
-function onDragover(e) {
+function onDragover(ev) {
// 自分自身がドラッグされている場合
if (dragging) {
// 自分自身にはドロップさせない
- e.dataTransfer.dropEffect = 'none';
+ ev.dataTransfer.dropEffect = 'none';
return;
}
- const isDeckColumn = e.dataTransfer.types[0] == _DATA_TRANSFER_DECK_COLUMN_;
+ const isDeckColumn = ev.dataTransfer.types[0] === _DATA_TRANSFER_DECK_COLUMN_;
- e.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';
+ ev.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';
if (!dragging && isDeckColumn) draghover = true;
}
@@ -227,12 +227,12 @@ function onDragleave() {
draghover = false;
}
-function onDrop(e) {
+function onDrop(ev) {
draghover = false;
os.deckGlobalEvents.emit('column.dragEnd');
- const id = e.dataTransfer.getData(_DATA_TRANSFER_DECK_COLUMN_);
- if (id != null && id != '') {
+ const id = ev.dataTransfer.getData(_DATA_TRANSFER_DECK_COLUMN_);
+ if (id != null && id !== '') {
swapColumn(props.column.id, id);
}
}
diff --git a/packages/client/src/ui/deck/deck-store.ts b/packages/client/src/ui/deck/deck-store.ts
index f7c39ad8fd..c847bf2b43 100644
--- a/packages/client/src/ui/deck/deck-store.ts
+++ b/packages/client/src/ui/deck/deck-store.ts
@@ -72,8 +72,8 @@ export const loadDeck = async () => {
scope: ['client', 'deck', 'profiles'],
key: deckStore.state.profile,
});
- } catch (e) {
- if (e.code === 'NO_SUCH_KEY') {
+ } catch (err) {
+ if (err.code === 'NO_SUCH_KEY') {
// 後方互換性のため
if (deckStore.state.profile === 'default') {
saveDeck();
@@ -94,7 +94,7 @@ export const loadDeck = async () => {
deckStore.set('layout', [['a'], ['b']]);
return;
}
- throw e;
+ throw err;
}
deckStore.set('columns', deck.columns);
@@ -114,7 +114,7 @@ export const saveDeck = throttle(1000, () => {
});
export function addColumn(column: Column) {
- if (column.name == undefined) column.name = null;
+ if (column.name === undefined) column.name = null;
deckStore.push('columns', column);
deckStore.push('layout', [column.id]);
saveDeck();
@@ -129,10 +129,10 @@ export function removeColumn(id: Column['id']) {
}
export function swapColumn(a: Column['id'], b: Column['id']) {
- const aX = deckStore.state.layout.findIndex(ids => ids.indexOf(a) != -1);
- const aY = deckStore.state.layout[aX].findIndex(id => id == a);
- const bX = deckStore.state.layout.findIndex(ids => ids.indexOf(b) != -1);
- const bY = deckStore.state.layout[bX].findIndex(id => id == b);
+ const aX = deckStore.state.layout.findIndex(ids => ids.indexOf(a) !== -1);
+ const aY = deckStore.state.layout[aX].findIndex(id => id === a);
+ const bX = deckStore.state.layout.findIndex(ids => ids.indexOf(b) !== -1);
+ const bY = deckStore.state.layout[bX].findIndex(id => id === b);
const layout = copy(deckStore.state.layout);
layout[aX][aY] = b;
layout[bX][bY] = a;
@@ -259,7 +259,7 @@ export function removeColumnWidget(id: Column['id'], widget: ColumnWidget) {
const columnIndex = deckStore.state.columns.findIndex(c => c.id === id);
const column = copy(deckStore.state.columns[columnIndex]);
if (column == null) return;
- column.widgets = column.widgets.filter(w => w.id != widget.id);
+ column.widgets = column.widgets.filter(w => w.id !== widget.id);
columns[columnIndex] = column;
deckStore.set('columns', columns);
saveDeck();
@@ -276,14 +276,14 @@ export function setColumnWidgets(id: Column['id'], widgets: ColumnWidget[]) {
saveDeck();
}
-export function updateColumnWidget(id: Column['id'], widgetId: string, data: any) {
+export function updateColumnWidget(id: Column['id'], widgetId: string, WidgetData: any) {
const columns = copy(deckStore.state.columns);
const columnIndex = deckStore.state.columns.findIndex(c => c.id === id);
const column = copy(deckStore.state.columns[columnIndex]);
if (column == null) return;
column.widgets = column.widgets.map(w => w.id === widgetId ? {
...w,
- data: data
+ data: widgetData,
} : w);
columns[columnIndex] = column;
deckStore.set('columns', columns);
diff --git a/packages/client/src/ui/deck/direct-column.vue b/packages/client/src/ui/deck/direct-column.vue
index ebaba574f4..4837c0ce38 100644
--- a/packages/client/src/ui/deck/direct-column.vue
+++ b/packages/client/src/ui/deck/direct-column.vue
@@ -18,7 +18,7 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
const pagination = {
diff --git a/packages/client/src/ui/deck/list-column.vue b/packages/client/src/ui/deck/list-column.vue
index b990516d05..843a3bd1cb 100644
--- a/packages/client/src/ui/deck/list-column.vue
+++ b/packages/client/src/ui/deck/list-column.vue
@@ -22,8 +22,8 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'loaded'): void;
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'loaded'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
let timeline = $ref<InstanceType<typeof XTimeline>>();
diff --git a/packages/client/src/ui/deck/main-column.vue b/packages/client/src/ui/deck/main-column.vue
index 57caab44cb..3c97cd4867 100644
--- a/packages/client/src/ui/deck/main-column.vue
+++ b/packages/client/src/ui/deck/main-column.vue
@@ -35,7 +35,7 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
let pageInfo = $ref<Record<string, any> | null>(null);
diff --git a/packages/client/src/ui/deck/mentions-column.vue b/packages/client/src/ui/deck/mentions-column.vue
index a7a012a7fb..0b6ca3a239 100644
--- a/packages/client/src/ui/deck/mentions-column.vue
+++ b/packages/client/src/ui/deck/mentions-column.vue
@@ -18,7 +18,7 @@ defineProps<{
}>();
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
const pagination = {
diff --git a/packages/client/src/ui/deck/notifications-column.vue b/packages/client/src/ui/deck/notifications-column.vue
index 50ee12a275..6dd040cb8d 100644
--- a/packages/client/src/ui/deck/notifications-column.vue
+++ b/packages/client/src/ui/deck/notifications-column.vue
@@ -7,7 +7,7 @@
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { defineAsyncComponent } from 'vue';
import XColumn from './column.vue';
import XNotifications from '@/components/notifications.vue';
import * as os from '@/os';
@@ -20,11 +20,11 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
function func() {
- os.popup(import('@/components/notification-setting-window.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/notification-setting-window.vue')), {
includingTypes: props.column.includingTypes,
}, {
done: async (res) => {
diff --git a/packages/client/src/ui/deck/tl-column.vue b/packages/client/src/ui/deck/tl-column.vue
index 02b9ef83a1..f3ecda5aa4 100644
--- a/packages/client/src/ui/deck/tl-column.vue
+++ b/packages/client/src/ui/deck/tl-column.vue
@@ -35,8 +35,8 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'loaded'): void;
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'loaded'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
let disabled = $ref(false);
diff --git a/packages/client/src/ui/deck/widgets-column.vue b/packages/client/src/ui/deck/widgets-column.vue
index a2edc38357..10c6f5adf6 100644
--- a/packages/client/src/ui/deck/widgets-column.vue
+++ b/packages/client/src/ui/deck/widgets-column.vue
@@ -3,7 +3,7 @@
<template #header><i class="fas fa-window-maximize" style="margin-right: 8px;"></i>{{ column.name }}</template>
<div class="wtdtxvec">
- <XWidgets v-if="column.widgets" :edit="edit" :widgets="column.widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="edit = false"/>
+ <XWidgets :edit="edit" :widgets="column.widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="edit = false"/>
</div>
</XColumn>
</template>
@@ -20,7 +20,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (e: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
+ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void;
}>();
let edit = $ref(false);
diff --git a/packages/client/src/ui/universal.widgets.vue b/packages/client/src/ui/universal.widgets.vue
index 2660e80368..a42c085690 100644
--- a/packages/client/src/ui/universal.widgets.vue
+++ b/packages/client/src/ui/universal.widgets.vue
@@ -14,7 +14,7 @@ import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
const emit = defineEmits<{
- (e: 'mounted', el: Element): void;
+ (ev: 'mounted', el: Element): void;
}>();
let editMode = $ref(false);
@@ -32,13 +32,13 @@ function addWidget(widget) {
}
function removeWidget(widget) {
- defaultStore.set('widgets', defaultStore.state.widgets.filter(w => w.id != widget.id));
+ defaultStore.set('widgets', defaultStore.state.widgets.filter(w => w.id !== widget.id));
}
function updateWidget({ id, data }) {
defaultStore.set('widgets', defaultStore.state.widgets.map(w => w.id === id ? {
...w,
- data: data
+ data,
} : w));
}
diff --git a/packages/client/src/widgets/activity.calendar.vue b/packages/client/src/widgets/activity.calendar.vue
index b833bd65ca..33b95b00db 100644
--- a/packages/client/src/widgets/activity.calendar.vue
+++ b/packages/client/src/widgets/activity.calendar.vue
@@ -1,13 +1,13 @@
<template>
<svg viewBox="0 0 21 7">
- <rect v-for="record in data" class="day"
+ <rect v-for="record in activity" class="day"
width="1" height="1"
:x="record.x" :y="record.date.weekday"
rx="1" ry="1"
fill="transparent">
<title>{{ record.date.year }}/{{ record.date.month + 1 }}/{{ record.date.day }}</title>
</rect>
- <rect v-for="record in data" class="day"
+ <rect v-for="record in activity" class="day"
:width="record.v" :height="record.v"
:x="record.x + ((1 - record.v) / 2)" :y="record.date.weekday + ((1 - record.v) / 2)"
rx="1" ry="1"
@@ -15,7 +15,7 @@
style="pointer-events: none;"/>
<rect class="today"
width="1" height="1"
- :x="data[0].x" :y="data[0].date.weekday"
+ :x="activity[0].x" :y="activity[0].date.weekday"
rx="1" ry="1"
fill="none"
stroke-width="0.1"
@@ -23,45 +23,41 @@
</svg>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-import * as os from '@/os';
+<script lang="ts" setup>
+const props = defineProps<{
+ activity: any[]
+}>();
-export default defineComponent({
- props: ['data'],
- created() {
- for (const d of this.data) {
- d.total = d.notes + d.replies + d.renotes;
- }
- const peak = Math.max.apply(null, this.data.map(d => d.total));
+for (const d of props.activity) {
+ d.total = d.notes + d.replies + d.renotes;
+}
+const peak = Math.max(...props.activity.map(d => d.total));
- const now = new Date();
- const year = now.getFullYear();
- const month = now.getMonth();
- const day = now.getDate();
+const now = new Date();
+const year = now.getFullYear();
+const month = now.getMonth();
+const day = now.getDate();
- let x = 20;
- this.data.slice().forEach((d, i) => {
- d.x = x;
+let x = 20;
+props.activity.slice().forEach((d, i) => {
+ d.x = x;
- const date = new Date(year, month, day - i);
- d.date = {
- year: date.getFullYear(),
- month: date.getMonth(),
- day: date.getDate(),
- weekday: date.getDay()
- };
+ const date = new Date(year, month, day - i);
+ d.date = {
+ year: date.getFullYear(),
+ month: date.getMonth(),
+ day: date.getDate(),
+ weekday: date.getDay()
+ };
- d.v = peak === 0 ? 0 : d.total / (peak / 2);
- if (d.v > 1) d.v = 1;
- const ch = d.date.weekday === 0 || d.date.weekday === 6 ? 275 : 170;
- const cs = d.v * 100;
- const cl = 15 + ((1 - d.v) * 80);
- d.color = `hsl(${ch}, ${cs}%, ${cl}%)`;
+ d.v = peak === 0 ? 0 : d.total / (peak / 2);
+ if (d.v > 1) d.v = 1;
+ const ch = d.date.weekday === 0 || d.date.weekday === 6 ? 275 : 170;
+ const cs = d.v * 100;
+ const cl = 15 + ((1 - d.v) * 80);
+ d.color = `hsl(${ch}, ${cs}%, ${cl}%)`;
- if (d.date.weekday === 0) x--;
- });
- }
+ if (d.date.weekday === 0) x--;
});
</script>
diff --git a/packages/client/src/widgets/activity.chart.vue b/packages/client/src/widgets/activity.chart.vue
index 9702d66663..b7db2af580 100644
--- a/packages/client/src/widgets/activity.chart.vue
+++ b/packages/client/src/widgets/activity.chart.vue
@@ -24,9 +24,19 @@
</svg>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
-import * as os from '@/os';
+<script lang="ts" setup>
+const props = defineProps<{
+ activity: any[]
+}>();
+
+let viewBoxX: number = $ref(147);
+let viewBoxY: number = $ref(60);
+let zoom: number = $ref(1);
+let pos: number = $ref(0);
+let pointsNote: any = $ref(null);
+let pointsReply: any = $ref(null);
+let pointsRenote: any = $ref(null);
+let pointsTotal: any = $ref(null);
function dragListen(fn) {
window.addEventListener('mousemove', fn);
@@ -40,60 +50,35 @@ function dragClear(fn) {
window.removeEventListener('mouseup', dragClear);
}
-export default defineComponent({
- props: ['data'],
- data() {
- return {
- viewBoxX: 147,
- viewBoxY: 60,
- zoom: 1,
- pos: 0,
- pointsNote: null,
- pointsReply: null,
- pointsRenote: null,
- pointsTotal: null
- };
- },
- created() {
- for (const d of this.data) {
- d.total = d.notes + d.replies + d.renotes;
- }
+function onMousedown(ev) {
+ const clickX = ev.clientX;
+ const clickY = ev.clientY;
+ const baseZoom = zoom;
+ const basePos = pos;
- this.render();
- },
- methods: {
- render() {
- const peak = Math.max.apply(null, this.data.map(d => d.total));
- if (peak != 0) {
- const data = this.data.slice().reverse();
- this.pointsNote = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.notes / peak)) * this.viewBoxY}`).join(' ');
- this.pointsReply = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' ');
- this.pointsRenote = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.renotes / peak)) * this.viewBoxY}`).join(' ');
- this.pointsTotal = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ');
- }
- },
- onMousedown(e) {
- const clickX = e.clientX;
- const clickY = e.clientY;
- const baseZoom = this.zoom;
- const basePos = this.pos;
+ // 動かした時
+ dragListen(me => {
+ let moveLeft = me.clientX - clickX;
+ let moveTop = me.clientY - clickY;
- // 動かした時
- dragListen(me => {
- let moveLeft = me.clientX - clickX;
- let moveTop = me.clientY - clickY;
+ zoom = Math.max(1, baseZoom + (-moveTop / 20));
+ pos = Math.min(0, basePos + moveLeft);
+ if (pos < -(((props.activity.length - 1) * zoom) - viewBoxX)) pos = -(((props.activity.length - 1) * zoom) - viewBoxX);
- this.zoom = baseZoom + (-moveTop / 20);
- this.pos = basePos + moveLeft;
- if (this.zoom < 1) this.zoom = 1;
- if (this.pos > 0) this.pos = 0;
- if (this.pos < -(((this.data.length - 1) * this.zoom) - this.viewBoxX)) this.pos = -(((this.data.length - 1) * this.zoom) - this.viewBoxX);
+ render();
+ });
+}
- this.render();
- });
- }
+function render() {
+ const peak = Math.max(...props.activity.map(d => d.total));
+ if (peak !== 0) {
+ const activity = props.activity.slice().reverse();
+ pointsNote = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.notes / peak)) * viewBoxY}`).join(' ');
+ pointsReply = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.replies / peak)) * viewBoxY}`).join(' ');
+ pointsRenote = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.renotes / peak)) * viewBoxY}`).join(' ');
+ pointsTotal = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.total / peak)) * viewBoxY}`).join(' ');
}
-});
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/widgets/activity.vue b/packages/client/src/widgets/activity.vue
index acbbb7a97a..631beceb72 100644
--- a/packages/client/src/widgets/activity.vue
+++ b/packages/client/src/widgets/activity.vue
@@ -6,8 +6,8 @@
<div>
<MkLoading v-if="fetching"/>
<template v-else>
- <XCalendar v-show="widgetProps.view === 0" :data="[].concat(activity)"/>
- <XChart v-show="widgetProps.view === 1" :data="[].concat(activity)"/>
+ <XCalendar v-show="widgetProps.view === 0" :activity="[].concat(activity)"/>
+ <XChart v-show="widgetProps.view === 1" :activity="[].concat(activity)"/>
</template>
</div>
</MkContainer>
@@ -47,7 +47,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure, save } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/aichan.vue b/packages/client/src/widgets/aichan.vue
index 03e394b976..70e47f2af1 100644
--- a/packages/client/src/widgets/aichan.vue
+++ b/packages/client/src/widgets/aichan.vue
@@ -24,7 +24,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/aiscript.vue b/packages/client/src/widgets/aiscript.vue
index 0a5c0d614d..b74e2258a9 100644
--- a/packages/client/src/widgets/aiscript.vue
+++ b/packages/client/src/widgets/aiscript.vue
@@ -43,7 +43,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
@@ -94,7 +94,7 @@ const run = async () => {
let ast;
try {
ast = parse(widgetProps.script);
- } catch (e) {
+ } catch (err) {
os.alert({
type: 'error',
text: 'Syntax error :(',
@@ -103,10 +103,10 @@ const run = async () => {
}
try {
await aiscript.exec(ast);
- } catch (e) {
+ } catch (err) {
os.alert({
type: 'error',
- text: e,
+ text: err,
});
}
};
diff --git a/packages/client/src/widgets/button.vue b/packages/client/src/widgets/button.vue
index a33afd6e7a..ee4e9c6423 100644
--- a/packages/client/src/widgets/button.vue
+++ b/packages/client/src/widgets/button.vue
@@ -40,7 +40,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
@@ -73,7 +73,7 @@ const run = async () => {
let ast;
try {
ast = parse(widgetProps.script);
- } catch (e) {
+ } catch (err) {
os.alert({
type: 'error',
text: 'Syntax error :(',
@@ -82,10 +82,10 @@ const run = async () => {
}
try {
await aiscript.exec(ast);
- } catch (e) {
+ } catch (err) {
os.alert({
type: 'error',
- text: e,
+ text: err,
});
}
};
diff --git a/packages/client/src/widgets/calendar.vue b/packages/client/src/widgets/calendar.vue
index c6a69b3fb8..2a2b035541 100644
--- a/packages/client/src/widgets/calendar.vue
+++ b/packages/client/src/widgets/calendar.vue
@@ -53,7 +53,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/clock.vue b/packages/client/src/widgets/clock.vue
index 6acb10d74d..0a35c4c5ab 100644
--- a/packages/client/src/widgets/clock.vue
+++ b/packages/client/src/widgets/clock.vue
@@ -39,7 +39,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/digital-clock.vue b/packages/client/src/widgets/digital-clock.vue
index 62f052a692..a17ed040c9 100644
--- a/packages/client/src/widgets/digital-clock.vue
+++ b/packages/client/src/widgets/digital-clock.vue
@@ -41,7 +41,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/federation.vue b/packages/client/src/widgets/federation.vue
index 5f1131dce1..1bfb068a2f 100644
--- a/packages/client/src/widgets/federation.vue
+++ b/packages/client/src/widgets/federation.vue
@@ -41,7 +41,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps> & { foldable?: boolean; scrollable?: boolean; }>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; foldable?: boolean; scrollable?: boolean; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/job-queue.vue b/packages/client/src/widgets/job-queue.vue
index 4a2a3cf233..8897f240bd 100644
--- a/packages/client/src/widgets/job-queue.vue
+++ b/packages/client/src/widgets/job-queue.vue
@@ -73,7 +73,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/memo.vue b/packages/client/src/widgets/memo.vue
index 450598f65a..f2d1bbc047 100644
--- a/packages/client/src/widgets/memo.vue
+++ b/packages/client/src/widgets/memo.vue
@@ -32,7 +32,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/notifications.vue b/packages/client/src/widgets/notifications.vue
index 8cf29c9271..f51e983a0e 100644
--- a/packages/client/src/widgets/notifications.vue
+++ b/packages/client/src/widgets/notifications.vue
@@ -15,6 +15,7 @@ import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExp
import MkContainer from '@/components/ui/container.vue';
import XNotifications from '@/components/notifications.vue';
import * as os from '@/os';
+import { defineAsyncComponent } from 'vue';
const name = 'notifications';
@@ -40,7 +41,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure, save } = useWidgetPropsManager(name,
widgetPropsDef,
@@ -49,7 +50,7 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
);
const configureNotification = () => {
- os.popup(import('@/components/notification-setting-window.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/notification-setting-window.vue')), {
includingTypes: widgetProps.includingTypes,
}, {
done: async (res) => {
diff --git a/packages/client/src/widgets/online-users.vue b/packages/client/src/widgets/online-users.vue
index 1746a8314e..eb3184fe9d 100644
--- a/packages/client/src/widgets/online-users.vue
+++ b/packages/client/src/widgets/online-users.vue
@@ -27,7 +27,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/photos.vue b/packages/client/src/widgets/photos.vue
index 8f948dc643..8e30765290 100644
--- a/packages/client/src/widgets/photos.vue
+++ b/packages/client/src/widgets/photos.vue
@@ -43,7 +43,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/post-form.vue b/packages/client/src/widgets/post-form.vue
index 51aa8fcf6b..5b74602c85 100644
--- a/packages/client/src/widgets/post-form.vue
+++ b/packages/client/src/widgets/post-form.vue
@@ -19,7 +19,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/rss.vue b/packages/client/src/widgets/rss.vue
index 9e2e503602..6b057cdd06 100644
--- a/packages/client/src/widgets/rss.vue
+++ b/packages/client/src/widgets/rss.vue
@@ -38,7 +38,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/server-metric/cpu-mem.vue b/packages/client/src/widgets/server-metric/cpu-mem.vue
index ad9e6a8b0f..00c3a10c9b 100644
--- a/packages/client/src/widgets/server-metric/cpu-mem.vue
+++ b/packages/client/src/widgets/server-metric/cpu-mem.vue
@@ -69,79 +69,72 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onBeforeUnmount } from 'vue';
import { v4 as uuid } from 'uuid';
-export default defineComponent({
- props: {
- connection: {
- required: true,
- },
- meta: {
- required: true,
- }
- },
- data() {
- return {
- viewBoxX: 50,
- viewBoxY: 30,
- stats: [],
- cpuGradientId: uuid(),
- cpuMaskId: uuid(),
- memGradientId: uuid(),
- memMaskId: uuid(),
- cpuPolylinePoints: '',
- memPolylinePoints: '',
- cpuPolygonPoints: '',
- memPolygonPoints: '',
- cpuHeadX: null,
- cpuHeadY: null,
- memHeadX: null,
- memHeadY: null,
- cpuP: '',
- memP: ''
- };
- },
- mounted() {
- this.connection.on('stats', this.onStats);
- this.connection.on('statsLog', this.onStatsLog);
- this.connection.send('requestLog', {
- id: Math.random().toString().substr(2, 8)
- });
- },
- beforeUnmount() {
- this.connection.off('stats', this.onStats);
- this.connection.off('statsLog', this.onStatsLog);
- },
- methods: {
- onStats(stats) {
- this.stats.push(stats);
- if (this.stats.length > 50) this.stats.shift();
+const props = defineProps<{
+ connection: any,
+ meta: any
+}>();
- const cpuPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - s.cpu) * this.viewBoxY]);
- const memPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.mem.active / this.meta.mem.total)) * this.viewBoxY]);
- this.cpuPolylinePoints = cpuPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
- this.memPolylinePoints = memPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+let viewBoxX: number = $ref(50);
+let viewBoxY: number = $ref(30);
+let stats: any[] = $ref([]);
+const cpuGradientId = uuid();
+const cpuMaskId = uuid();
+const memGradientId = uuid();
+const memMaskId = uuid();
+let cpuPolylinePoints: string = $ref('');
+let memPolylinePoints: string = $ref('');
+let cpuPolygonPoints: string = $ref('');
+let memPolygonPoints: string = $ref('');
+let cpuHeadX: any = $ref(null);
+let cpuHeadY: any = $ref(null);
+let memHeadX: any = $ref(null);
+let memHeadY: any = $ref(null);
+let cpuP: string = $ref('');
+let memP: string = $ref('');
- this.cpuPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.cpuPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
- this.memPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.memPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
+onMounted(() => {
+ props.connection.on('stats', onStats);
+ props.connection.on('statsLog', onStatsLog);
+ props.connection.send('requestLog', {
+ id: Math.random().toString().substr(2, 8)
+ });
+});
- this.cpuHeadX = cpuPolylinePoints[cpuPolylinePoints.length - 1][0];
- this.cpuHeadY = cpuPolylinePoints[cpuPolylinePoints.length - 1][1];
- this.memHeadX = memPolylinePoints[memPolylinePoints.length - 1][0];
- this.memHeadY = memPolylinePoints[memPolylinePoints.length - 1][1];
+onBeforeUnmount(() => {
+ props.connection.off('stats', onStats);
+ props.connection.off('statsLog', onStatsLog);
+});
- this.cpuP = (stats.cpu * 100).toFixed(0);
- this.memP = (stats.mem.active / this.meta.mem.total * 100).toFixed(0);
- },
- onStatsLog(statsLog) {
- for (const stats of [...statsLog].reverse()) {
- this.onStats(stats);
- }
- }
+function onStats(connStats) {
+ stats.push(connStats);
+ if (stats.length > 50) stats.shift();
+
+ let cpuPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - s.cpu) * viewBoxY]);
+ let memPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - (s.mem.active / props.meta.mem.total)) * viewBoxY]);
+ cpuPolylinePoints = cpuPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+ memPolylinePoints = memPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+
+ cpuPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${cpuPolylinePoints} ${viewBoxX},${viewBoxY}`;
+ memPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${memPolylinePoints} ${viewBoxX},${viewBoxY}`;
+
+ cpuHeadX = cpuPolylinePointsStats[cpuPolylinePointsStats.length - 1][0];
+ cpuHeadY = cpuPolylinePointsStats[cpuPolylinePointsStats.length - 1][1];
+ memHeadX = memPolylinePointsStats[memPolylinePointsStats.length - 1][0];
+ memHeadY = memPolylinePointsStats[memPolylinePointsStats.length - 1][1];
+
+ cpuP = (connStats.cpu * 100).toFixed(0);
+ memP = (connStats.mem.active / props.meta.mem.total * 100).toFixed(0);
+}
+
+function onStatsLog(statsLog) {
+ for (const revStats of [...statsLog].reverse()) {
+ onStats(revStats);
}
-});
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/widgets/server-metric/cpu.vue b/packages/client/src/widgets/server-metric/cpu.vue
index 4478ee3065..baf802cb8f 100644
--- a/packages/client/src/widgets/server-metric/cpu.vue
+++ b/packages/client/src/widgets/server-metric/cpu.vue
@@ -9,38 +9,27 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onBeforeUnmount } from 'vue';
import XPie from './pie.vue';
-export default defineComponent({
- components: {
- XPie
- },
- props: {
- connection: {
- required: true,
- },
- meta: {
- required: true,
- }
- },
- data() {
- return {
- usage: 0,
- };
- },
- mounted() {
- this.connection.on('stats', this.onStats);
- },
- beforeUnmount() {
- this.connection.off('stats', this.onStats);
- },
- methods: {
- onStats(stats) {
- this.usage = stats.cpu;
- }
- }
+const props = defineProps<{
+ connection: any,
+ meta: any
+}>();
+
+let usage: number = $ref(0);
+
+function onStats(stats) {
+ usage = stats.cpu;
+}
+
+onMounted(() => {
+ props.connection.on('stats', onStats);
+});
+
+onBeforeUnmount(() => {
+ props.connection.off('stats', onStats);
});
</script>
diff --git a/packages/client/src/widgets/server-metric/index.vue b/packages/client/src/widgets/server-metric/index.vue
index 2caa73fa74..9e86b811d1 100644
--- a/packages/client/src/widgets/server-metric/index.vue
+++ b/packages/client/src/widgets/server-metric/index.vue
@@ -50,7 +50,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure, save } = useWidgetPropsManager(name,
widgetPropsDef,
@@ -65,7 +65,7 @@ os.api('server-info', {}).then(res => {
});
const toggleView = () => {
- if (widgetProps.view == 4) {
+ if (widgetProps.view === 4) {
widgetProps.view = 0;
} else {
widgetProps.view++;
diff --git a/packages/client/src/widgets/server-metric/mem.vue b/packages/client/src/widgets/server-metric/mem.vue
index a6ca7b1175..6018eb4265 100644
--- a/packages/client/src/widgets/server-metric/mem.vue
+++ b/packages/client/src/widgets/server-metric/mem.vue
@@ -10,46 +10,34 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onBeforeUnmount } from 'vue';
import XPie from './pie.vue';
import bytes from '@/filters/bytes';
-export default defineComponent({
- components: {
- XPie
- },
- props: {
- connection: {
- required: true,
- },
- meta: {
- required: true,
- }
- },
- data() {
- return {
- usage: 0,
- total: 0,
- used: 0,
- free: 0,
- };
- },
- mounted() {
- this.connection.on('stats', this.onStats);
- },
- beforeUnmount() {
- this.connection.off('stats', this.onStats);
- },
- methods: {
- onStats(stats) {
- this.usage = stats.mem.active / this.meta.mem.total;
- this.total = this.meta.mem.total;
- this.used = stats.mem.active;
- this.free = this.meta.mem.total - stats.mem.active;
- },
- bytes
- }
+const props = defineProps<{
+ connection: any,
+ meta: any
+}>();
+
+let usage: number = $ref(0);
+let total: number = $ref(0);
+let used: number = $ref(0);
+let free: number = $ref(0);
+
+function onStats(stats) {
+ usage = stats.mem.active / props.meta.mem.total;
+ total = props.meta.mem.total;
+ used = stats.mem.active;
+ free = total - used;
+}
+
+onMounted(() => {
+ props.connection.on('stats', onStats);
+});
+
+onBeforeUnmount(() => {
+ props.connection.off('stats', onStats);
});
</script>
diff --git a/packages/client/src/widgets/server-metric/net.vue b/packages/client/src/widgets/server-metric/net.vue
index 23c148eeb6..82b3a67d76 100644
--- a/packages/client/src/widgets/server-metric/net.vue
+++ b/packages/client/src/widgets/server-metric/net.vue
@@ -43,79 +43,71 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { onMounted, onBeforeUnmount } from 'vue';
import bytes from '@/filters/bytes';
-export default defineComponent({
- props: {
- connection: {
- required: true,
- },
- meta: {
- required: true,
- }
- },
- data() {
- return {
- viewBoxX: 50,
- viewBoxY: 30,
- stats: [],
- inPolylinePoints: '',
- outPolylinePoints: '',
- inPolygonPoints: '',
- outPolygonPoints: '',
- inHeadX: null,
- inHeadY: null,
- outHeadX: null,
- outHeadY: null,
- inRecent: 0,
- outRecent: 0
- };
- },
- mounted() {
- this.connection.on('stats', this.onStats);
- this.connection.on('statsLog', this.onStatsLog);
- this.connection.send('requestLog', {
- id: Math.random().toString().substr(2, 8)
- });
- },
- beforeUnmount() {
- this.connection.off('stats', this.onStats);
- this.connection.off('statsLog', this.onStatsLog);
- },
- methods: {
- onStats(stats) {
- this.stats.push(stats);
- if (this.stats.length > 50) this.stats.shift();
+const props = defineProps<{
+ connection: any,
+ meta: any
+}>();
+
+let viewBoxX: number = $ref(50);
+let viewBoxY: number = $ref(30);
+let stats: any[] = $ref([]);
+let inPolylinePoints: string = $ref('');
+let outPolylinePoints: string = $ref('');
+let inPolygonPoints: string = $ref('');
+let outPolygonPoints: string = $ref('');
+let inHeadX: any = $ref(null);
+let inHeadY: any = $ref(null);
+let outHeadX: any = $ref(null);
+let outHeadY: any = $ref(null);
+let inRecent: number = $ref(0);
+let outRecent: number = $ref(0);
+
+onMounted(() => {
+ props.connection.on('stats', onStats);
+ props.connection.on('statsLog', onStatsLog);
+ props.connection.send('requestLog', {
+ id: Math.random().toString().substr(2, 8)
+ });
+});
- const inPeak = Math.max(1024 * 64, Math.max(...this.stats.map(s => s.net.rx)));
- const outPeak = Math.max(1024 * 64, Math.max(...this.stats.map(s => s.net.tx)));
+onBeforeUnmount(() => {
+ props.connection.off('stats', onStats);
+ props.connection.off('statsLog', onStatsLog);
+});
- const inPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.net.rx / inPeak)) * this.viewBoxY]);
- const outPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.net.tx / outPeak)) * this.viewBoxY]);
- this.inPolylinePoints = inPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
- this.outPolylinePoints = outPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+function onStats(connStats) {
+ stats.push(connStats);
+ if (stats.length > 50) stats.shift();
- this.inPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.inPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
- this.outPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.outPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
+ const inPeak = Math.max(1024 * 64, Math.max(...stats.map(s => s.net.rx)));
+ const outPeak = Math.max(1024 * 64, Math.max(...stats.map(s => s.net.tx)));
- this.inHeadX = inPolylinePoints[inPolylinePoints.length - 1][0];
- this.inHeadY = inPolylinePoints[inPolylinePoints.length - 1][1];
- this.outHeadX = outPolylinePoints[outPolylinePoints.length - 1][0];
- this.outHeadY = outPolylinePoints[outPolylinePoints.length - 1][1];
+ let inPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - (s.net.rx / inPeak)) * viewBoxY]);
+ let outPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - (s.net.tx / outPeak)) * viewBoxY]);
+ inPolylinePoints = inPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+ outPolylinePoints = outPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
- this.inRecent = stats.net.rx;
- this.outRecent = stats.net.tx;
- },
- onStatsLog(statsLog) {
- for (const stats of [...statsLog].reverse()) {
- this.onStats(stats);
- }
- },
- bytes
+ inPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${inPolylinePoints} ${viewBoxX},${viewBoxY}`;
+ outPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${outPolylinePoints} ${viewBoxX},${viewBoxY}`;
+
+ inHeadX = inPolylinePoints[inPolylinePoints.length - 1][0];
+ inHeadY = inPolylinePoints[inPolylinePoints.length - 1][1];
+ outHeadX = outPolylinePoints[outPolylinePoints.length - 1][0];
+ outHeadY = outPolylinePoints[outPolylinePoints.length - 1][1];
+
+ inRecent = connStats.net.rx;
+ outRecent = connStats.net.tx;
+}
+
+function onStatsLog(statsLog) {
+ for (const revStats of [...statsLog].reverse()) {
+ onStats(revStats);
}
-});
+}
</script>
<style lang="scss" scoped>
diff --git a/packages/client/src/widgets/slideshow.vue b/packages/client/src/widgets/slideshow.vue
index 7b2e539685..1b6c2d766d 100644
--- a/packages/client/src/widgets/slideshow.vue
+++ b/packages/client/src/widgets/slideshow.vue
@@ -37,7 +37,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure, save } = useWidgetPropsManager(name,
widgetPropsDef,
@@ -51,7 +51,7 @@ const slideA = ref<HTMLElement>();
const slideB = ref<HTMLElement>();
const change = () => {
- if (images.value.length == 0) return;
+ if (images.value.length === 0) return;
const index = Math.floor(Math.random() * images.value.length);
const img = `url(${ images.value[index].url })`;
diff --git a/packages/client/src/widgets/timeline.vue b/packages/client/src/widgets/timeline.vue
index 34e3b20e36..c9a9e68bb9 100644
--- a/packages/client/src/widgets/timeline.vue
+++ b/packages/client/src/widgets/timeline.vue
@@ -63,7 +63,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure, save } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/trends.vue b/packages/client/src/widgets/trends.vue
index a34710eae7..34bbc16a8b 100644
--- a/packages/client/src/widgets/trends.vue
+++ b/packages/client/src/widgets/trends.vue
@@ -40,7 +40,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
-const emit = defineEmits<{ (e: 'updateProps', props: WidgetProps); }>();
+const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
diff --git a/packages/client/src/widgets/widget.ts b/packages/client/src/widgets/widget.ts
index 81239bfb3b..db164c2bc3 100644
--- a/packages/client/src/widgets/widget.ts
+++ b/packages/client/src/widgets/widget.ts
@@ -13,7 +13,7 @@ export type WidgetComponentProps<P extends Record<string, unknown>> = {
};
export type WidgetComponentEmits<P extends Record<string, unknown>> = {
- (e: 'updateProps', props: P);
+ (ev: 'updateProps', props: P);
};
export type WidgetComponentExpose = {