summaryrefslogtreecommitdiff
path: root/packages/frontend/src/pages/admin
diff options
context:
space:
mode:
authorMarie <Marie@kaifa.ch>2023-12-23 02:09:23 +0100
committerMarie <Marie@kaifa.ch>2023-12-23 02:09:23 +0100
commit5db583a3eb61d50de14d875ebf7ecef20490e313 (patch)
tree783dd43d2ac660c32e745a4485d499e9ddc43324 /packages/frontend/src/pages/admin
parentadd: Custom MOTDs (diff)
parentUpdate CHANGELOG.md (diff)
downloadsharkey-5db583a3eb61d50de14d875ebf7ecef20490e313.tar.gz
sharkey-5db583a3eb61d50de14d875ebf7ecef20490e313.tar.bz2
sharkey-5db583a3eb61d50de14d875ebf7ecef20490e313.zip
merge: upstream
Diffstat (limited to 'packages/frontend/src/pages/admin')
-rw-r--r--packages/frontend/src/pages/admin/_header_.vue8
-rw-r--r--packages/frontend/src/pages/admin/abuses.vue26
-rw-r--r--packages/frontend/src/pages/admin/ads.vue74
-rw-r--r--packages/frontend/src/pages/admin/announcements.vue24
-rw-r--r--packages/frontend/src/pages/admin/approvals.vue4
-rw-r--r--packages/frontend/src/pages/admin/bot-protection.vue48
-rw-r--r--packages/frontend/src/pages/admin/branding.vue85
-rw-r--r--packages/frontend/src/pages/admin/database.vue6
-rw-r--r--packages/frontend/src/pages/admin/email-settings.vue46
-rw-r--r--packages/frontend/src/pages/admin/external-services.vue19
-rw-r--r--packages/frontend/src/pages/admin/federation.vue32
-rw-r--r--packages/frontend/src/pages/admin/files.vue24
-rw-r--r--packages/frontend/src/pages/admin/index.vue109
-rw-r--r--packages/frontend/src/pages/admin/instance-block.vue19
-rw-r--r--packages/frontend/src/pages/admin/invites.vue8
-rw-r--r--packages/frontend/src/pages/admin/moderation.vue66
-rw-r--r--packages/frontend/src/pages/admin/modlog.ModLog.vue7
-rw-r--r--packages/frontend/src/pages/admin/modlog.vue16
-rw-r--r--packages/frontend/src/pages/admin/object-storage.vue82
-rw-r--r--packages/frontend/src/pages/admin/other-settings.vue42
-rw-r--r--packages/frontend/src/pages/admin/overview.active-users.vue10
-rw-r--r--packages/frontend/src/pages/admin/overview.ap-requests.vue14
-rw-r--r--packages/frontend/src/pages/admin/overview.federation.vue30
-rw-r--r--packages/frontend/src/pages/admin/overview.heatmap.vue3
-rw-r--r--packages/frontend/src/pages/admin/overview.moderators.vue10
-rw-r--r--packages/frontend/src/pages/admin/overview.queue.vue26
-rw-r--r--packages/frontend/src/pages/admin/overview.stats.vue22
-rw-r--r--packages/frontend/src/pages/admin/overview.users.vue9
-rw-r--r--packages/frontend/src/pages/admin/overview.vue44
-rw-r--r--packages/frontend/src/pages/admin/proxy-account.vue22
-rw-r--r--packages/frontend/src/pages/admin/queue.chart.vue26
-rw-r--r--packages/frontend/src/pages/admin/queue.vue11
-rw-r--r--packages/frontend/src/pages/admin/relays.vue10
-rw-r--r--packages/frontend/src/pages/admin/roles.edit.vue28
-rw-r--r--packages/frontend/src/pages/admin/roles.editor.vue76
-rw-r--r--packages/frontend/src/pages/admin/roles.role.vue16
-rw-r--r--packages/frontend/src/pages/admin/roles.vue15
-rw-r--r--packages/frontend/src/pages/admin/security.vue49
-rw-r--r--packages/frontend/src/pages/admin/server-rules.vue10
-rw-r--r--packages/frontend/src/pages/admin/settings.vue118
-rw-r--r--packages/frontend/src/pages/admin/users.vue30
41 files changed, 693 insertions, 631 deletions
diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue
index 43bfbeb870..353030b1b9 100644
--- a/packages/frontend/src/pages/admin/_header_.vue
+++ b/packages/frontend/src/pages/admin/_header_.vue
@@ -69,7 +69,7 @@ const metadata = injectPageMetadata();
const el = shallowRef<HTMLElement>(null);
const tabRefs = {};
-const tabHighlightEl = $shallowRef<HTMLElement | null>(null);
+const tabHighlightEl = shallowRef<HTMLElement | null>(null);
const bg = ref(null);
const height = ref(0);
const hasTabs = computed(() => {
@@ -131,13 +131,13 @@ onMounted(() => {
watch(() => [props.tab, props.tabs], () => {
nextTick(() => {
const tabEl = tabRefs[props.tab];
- if (tabEl && tabHighlightEl) {
+ if (tabEl && tabHighlightEl.value) {
// offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
const parentRect = tabEl.parentElement.getBoundingClientRect();
const rect = tabEl.getBoundingClientRect();
- tabHighlightEl.style.width = rect.width + 'px';
- tabHighlightEl.style.left = (rect.left - parentRect.left) + 'px';
+ tabHighlightEl.value.style.width = rect.width + 'px';
+ tabHighlightEl.value.style.left = (rect.left - parentRect.left) + 'px';
}
});
}, {
diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue
index d670cc7913..92688989d2 100644
--- a/packages/frontend/src/pages/admin/abuses.vue
+++ b/packages/frontend/src/pages/admin/abuses.vue
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, shallowRef, ref } from 'vue';
import XHeader from './_header_.vue';
import MkSelect from '@/components/MkSelect.vue';
@@ -61,31 +61,31 @@ import XAbuseReport from '@/components/MkAbuseReport.vue';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-let reports = $shallowRef<InstanceType<typeof MkPagination>>();
+const reports = shallowRef<InstanceType<typeof MkPagination>>();
-let state = $ref('unresolved');
-let reporterOrigin = $ref('combined');
-let targetUserOrigin = $ref('combined');
-let searchUsername = $ref('');
-let searchHost = $ref('');
+const state = ref('unresolved');
+const reporterOrigin = ref('combined');
+const targetUserOrigin = ref('combined');
+const searchUsername = ref('');
+const searchHost = ref('');
const pagination = {
endpoint: 'admin/abuse-user-reports' as const,
limit: 10,
params: computed(() => ({
- state,
- reporterOrigin,
- targetUserOrigin,
+ state: state.value,
+ reporterOrigin: reporterOrigin.value,
+ targetUserOrigin: targetUserOrigin.value,
})),
};
function resolved(reportId) {
- reports.removeItem(reportId);
+ reports.value.removeItem(reportId);
}
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.abuseReports,
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index c45d0f8b04..9de9da7d98 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -9,12 +9,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<XHeader :actions="headerActions" :tabs="headerTabs"/>
</template>
<MkSpacer :contentMax="900">
- <MkSwitch :modelValue="publishing" @update:modelValue="onChangePublishing">
- {{ i18n.ts.publishing }}
- </MkSwitch>
+ <MkSelect v-model="filterType" :class="$style.input" @update:modelValue="filterItems">
+ <template #label>{{ i18n.ts.state }}</template>
+ <option value="all">{{ i18n.ts.all }}</option>
+ <option value="publishing">{{ i18n.ts.publishing }}</option>
+ <option value="expired">{{ i18n.ts.expired }}</option>
+ </MkSelect>
<div>
<div v-for="ad in ads" class="_panel _gaps_m" :class="$style.ad">
- <MkAd v-if="ad.url" :specify="ad"/>
+ <MkAd v-if="ad.url" :key="ad.id" :specify="ad"/>
<MkInput v-model="ad.url" type="url">
<template #label>URL</template>
</MkInput>
@@ -82,43 +85,53 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkRadios from '@/components/MkRadios.vue';
import MkFolder from '@/components/MkFolder.vue';
-import MkSwitch from '@/components/MkSwitch.vue';
+import MkSelect from '@/components/MkSelect.vue';
import FormSplit from '@/components/form/split.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-let ads: any[] = $ref([]);
+const ads = ref<any[]>([]);
// ISO形式はTZがUTCになってしまうので、TZ分ずらして時間を初期化
const localTime = new Date();
const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000;
const daysOfWeek: string[] = [i18n.ts._weekday.sunday, i18n.ts._weekday.monday, i18n.ts._weekday.tuesday, i18n.ts._weekday.wednesday, i18n.ts._weekday.thursday, i18n.ts._weekday.friday, i18n.ts._weekday.saturday];
-let publishing = false;
+const filterType = ref('all');
+let publishing: boolean | null = null;
os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
- ads = adsResponse.map(r => {
- const exdate = new Date(r.expiresAt);
- const stdate = new Date(r.startsAt);
- exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
- stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
- return {
- ...r,
- expiresAt: exdate.toISOString().slice(0, 16),
- startsAt: stdate.toISOString().slice(0, 16),
- };
- });
+ if (adsResponse != null) {
+ ads.value = adsResponse.map(r => {
+ const exdate = new Date(r.expiresAt);
+ const stdate = new Date(r.startsAt);
+ exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
+ stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
+ return {
+ ...r,
+ expiresAt: exdate.toISOString().slice(0, 16),
+ startsAt: stdate.toISOString().slice(0, 16),
+ };
+ });
+ }
});
-const onChangePublishing = (v) => {
- publishing = v;
+const filterItems = (v) => {
+ if (v === 'publishing') {
+ publishing = true;
+ } else if (v === 'expired') {
+ publishing = false;
+ } else {
+ publishing = null;
+ }
+
refresh();
};
@@ -128,7 +141,7 @@ function toggleDayOfWeek(ad, index) {
}
function add() {
- ads.unshift({
+ ads.value.unshift({
id: null,
memo: '',
place: 'square',
@@ -148,7 +161,7 @@ function remove(ad) {
text: i18n.t('removeAreYouSure', { x: ad.url }),
}).then(({ canceled }) => {
if (canceled) return;
- ads = ads.filter(x => x !== ad);
+ ads.value = ads.value.filter(x => x !== ad);
if (ad.id == null) return;
os.apiWithDialog('admin/ad/delete', {
id: ad.id,
@@ -196,8 +209,9 @@ function save(ad) {
}
function more() {
- os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
- ads = ads.concat(adsResponse.map(r => {
+ os.api('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
+ if (adsResponse == null) return;
+ ads.value = ads.value.concat(adsResponse.map(r => {
const exdate = new Date(r.expiresAt);
const stdate = new Date(r.startsAt);
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
@@ -213,7 +227,8 @@ function more() {
function refresh() {
os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
- ads = adsResponse.map(r => {
+ if (adsResponse == null) return;
+ ads.value = adsResponse.map(r => {
const exdate = new Date(r.expiresAt);
const stdate = new Date(r.startsAt);
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
@@ -229,14 +244,14 @@ function refresh() {
refresh();
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
asFullButton: true,
icon: 'ph-plus ph-bold ph-lg',
text: i18n.ts.add,
handler: add,
}]);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.ads,
@@ -252,4 +267,7 @@ definePageMetadata({
margin-bottom: var(--margin);
}
}
+.input {
+ margin-bottom: 32px;
+}
</style>
diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue
index 77649c6c4a..931bd9bbc8 100644
--- a/packages/frontend/src/pages/admin/announcements.vue
+++ b/packages/frontend/src/pages/admin/announcements.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInput v-model="announcement.title">
<template #label>{{ i18n.ts.title }}</template>
</MkInput>
- <MkTextarea v-model="announcement.text">
+ <MkTextarea v-model="announcement.text" mfmAutocomplete :mfmPreview="true">
<template #label>{{ i18n.ts.text }}</template>
</MkTextarea>
<MkInput v-model="announcement.imageUrl" type="url">
@@ -71,11 +71,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
-import MkTextarea from '@/components/MkTextarea.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkRadios from '@/components/MkRadios.vue';
import MkInfo from '@/components/MkInfo.vue';
@@ -83,15 +82,16 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkFolder from '@/components/MkFolder.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
-let announcements: any[] = $ref([]);
+const announcements = ref<any[]>([]);
os.api('admin/announcements/list').then(announcementResponse => {
- announcements = announcementResponse;
+ announcements.value = announcementResponse;
});
function add() {
- announcements.unshift({
+ announcements.value.unshift({
_id: Math.random().toString(36),
id: null,
title: 'New announcement',
@@ -111,7 +111,7 @@ function del(announcement) {
text: i18n.t('deleteAreYouSure', { x: announcement.title }),
}).then(({ canceled }) => {
if (canceled) return;
- announcements = announcements.filter(x => x !== announcement);
+ announcements.value = announcements.value.filter(x => x !== announcement);
os.api('admin/announcements/delete', announcement);
});
}
@@ -134,27 +134,27 @@ async function save(announcement) {
}
function more() {
- os.api('admin/announcements/list', { untilId: announcements.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => {
- announcements = announcements.concat(announcementResponse);
+ os.api('admin/announcements/list', { untilId: announcements.value.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => {
+ announcements.value = announcements.value.concat(announcementResponse);
});
}
function refresh() {
os.api('admin/announcements/list').then(announcementResponse => {
- announcements = announcementResponse;
+ announcements.value = announcementResponse;
});
}
refresh();
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
asFullButton: true,
icon: 'ph-plus ph-bold ph-lg',
text: i18n.ts.add,
handler: add,
}]);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.announcements,
diff --git a/packages/frontend/src/pages/admin/approvals.vue b/packages/frontend/src/pages/admin/approvals.vue
index 5d9c9de03b..7d0535bd7f 100644
--- a/packages/frontend/src/pages/admin/approvals.vue
+++ b/packages/frontend/src/pages/admin/approvals.vue
@@ -44,9 +44,9 @@ function deleted(id: string) {
}
}
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
title: i18n.ts.approvals,
diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue
index 450c0ec663..034a6fdcc5 100644
--- a/packages/frontend/src/pages/admin/bot-protection.vue
+++ b/packages/frontend/src/pages/admin/bot-protection.vue
@@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
+import { defineAsyncComponent, ref } from 'vue';
import MkRadios from '@/components/MkRadios.vue';
import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue';
@@ -76,37 +76,37 @@ import { i18n } from '@/i18n.js';
const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
-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);
-let turnstileSiteKey: string | null = $ref(null);
-let turnstileSecretKey: string | null = $ref(null);
+const provider = ref(null);
+const hcaptchaSiteKey = ref<string | null>(null);
+const hcaptchaSecretKey = ref<string | null>(null);
+const recaptchaSiteKey = ref<string | null>(null);
+const recaptchaSecretKey = ref<string | null>(null);
+const turnstileSiteKey = ref<string | null>(null);
+const turnstileSecretKey = ref<string | null>(null);
async function init() {
const meta = await os.api('admin/meta');
- hcaptchaSiteKey = meta.hcaptchaSiteKey;
- hcaptchaSecretKey = meta.hcaptchaSecretKey;
- recaptchaSiteKey = meta.recaptchaSiteKey;
- recaptchaSecretKey = meta.recaptchaSecretKey;
- turnstileSiteKey = meta.turnstileSiteKey;
- turnstileSecretKey = meta.turnstileSecretKey;
+ hcaptchaSiteKey.value = meta.hcaptchaSiteKey;
+ hcaptchaSecretKey.value = meta.hcaptchaSecretKey;
+ recaptchaSiteKey.value = meta.recaptchaSiteKey;
+ recaptchaSecretKey.value = meta.recaptchaSecretKey;
+ turnstileSiteKey.value = meta.turnstileSiteKey;
+ turnstileSecretKey.value = meta.turnstileSecretKey;
- provider = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : meta.enableTurnstile ? 'turnstile' : null;
+ provider.value = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : meta.enableTurnstile ? 'turnstile' : null;
}
function save() {
os.apiWithDialog('admin/update-meta', {
- enableHcaptcha: provider === 'hcaptcha',
- hcaptchaSiteKey,
- hcaptchaSecretKey,
- enableRecaptcha: provider === 'recaptcha',
- recaptchaSiteKey,
- recaptchaSecretKey,
- enableTurnstile: provider === 'turnstile',
- turnstileSiteKey,
- turnstileSecretKey,
+ enableHcaptcha: provider.value === 'hcaptcha',
+ hcaptchaSiteKey: hcaptchaSiteKey.value,
+ hcaptchaSecretKey: hcaptchaSecretKey.value,
+ enableRecaptcha: provider.value === 'recaptcha',
+ recaptchaSiteKey: recaptchaSiteKey.value,
+ recaptchaSecretKey: recaptchaSecretKey.value,
+ enableTurnstile: provider.value === 'turnstile',
+ turnstileSiteKey: turnstileSiteKey.value,
+ turnstileSecretKey: turnstileSecretKey.value,
}).then(() => {
fetchInstance();
});
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index 96a2d8a300..3750a84aac 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -101,14 +101,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
import JSON5 from 'json5';
import XHeader from './_header_.vue';
-import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
-import FormSection from '@/components/form/section.vue';
-import FormSplit from '@/components/form/split.vue';
import FromSlot from '@/components/form/slot.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js';
@@ -119,51 +116,51 @@ import MkButton from '@/components/MkButton.vue';
import MkColorInput from '@/components/MkColorInput.vue';
import { host } from '@/config.js';
-let iconUrl: string | null = $ref(null);
-let app192IconUrl: string | null = $ref(null);
-let app512IconUrl: 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 defaultLike: any = $ref(null);
-let serverErrorImageUrl: string | null = $ref(null);
-let infoImageUrl: string | null = $ref(null);
-let notFoundImageUrl: string | null = $ref(null);
-let manifestJsonOverride: string = $ref('{}');
+const iconUrl = ref<string | null>(null);
+const app192IconUrl = ref<string | null>(null);
+const app512IconUrl = ref<string | null>(null);
+const bannerUrl = ref<string | null>(null);
+const backgroundImageUrl = ref<string | null>(null);
+const themeColor = ref<any>(null);
+const defaultLightTheme = ref<any>(null);
+const defaultDarkTheme = ref<any>(null);
+const defaultLike = ref<string>('');
+const serverErrorImageUrl = ref<string | null>(null);
+const infoImageUrl = ref<string | null>(null);
+const notFoundImageUrl = ref<string | null>(null);
+const manifestJsonOverride = ref<string>('{}');
async function init() {
const meta = await os.api('admin/meta');
- iconUrl = meta.iconUrl;
- app192IconUrl = meta.app192IconUrl;
- app512IconUrl = meta.app512IconUrl;
- bannerUrl = meta.bannerUrl;
- backgroundImageUrl = meta.backgroundImageUrl;
- themeColor = meta.themeColor;
- defaultLightTheme = meta.defaultLightTheme;
- defaultDarkTheme = meta.defaultDarkTheme;
- defaultLike = meta.defaultLike;
- serverErrorImageUrl = meta.serverErrorImageUrl;
- infoImageUrl = meta.infoImageUrl;
- notFoundImageUrl = meta.notFoundImageUrl;
- manifestJsonOverride = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
+ iconUrl.value = meta.iconUrl;
+ app192IconUrl.value = meta.app192IconUrl;
+ app512IconUrl.value = meta.app512IconUrl;
+ bannerUrl.value = meta.bannerUrl;
+ backgroundImageUrl.value = meta.backgroundImageUrl;
+ themeColor.value = meta.themeColor;
+ defaultLightTheme.value = meta.defaultLightTheme;
+ defaultDarkTheme.value = meta.defaultDarkTheme;
+ defaultLike.value = meta.defaultLike;
+ serverErrorImageUrl.value = meta.serverErrorImageUrl;
+ infoImageUrl.value = meta.infoImageUrl;
+ notFoundImageUrl.value = meta.notFoundImageUrl;
+ manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
}
function save() {
os.apiWithDialog('admin/update-meta', {
- iconUrl,
- app192IconUrl,
- app512IconUrl,
- bannerUrl,
- backgroundImageUrl,
- themeColor: themeColor === '' ? null : themeColor,
- defaultLightTheme: defaultLightTheme === '' ? null : defaultLightTheme,
- defaultDarkTheme: defaultDarkTheme === '' ? null : defaultDarkTheme,
- infoImageUrl,
- notFoundImageUrl,
- serverErrorImageUrl,
- manifestJsonOverride: manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride)),
+ iconUrl: iconUrl.value,
+ app192IconUrl: app192IconUrl.value,
+ app512IconUrl: app512IconUrl.value,
+ bannerUrl: bannerUrl.value,
+ backgroundImageUrl: backgroundImageUrl.value,
+ themeColor: themeColor.value === '' ? null : themeColor.value,
+ defaultLightTheme: defaultLightTheme.value === '' ? null : defaultLightTheme.value,
+ defaultDarkTheme: defaultDarkTheme.value === '' ? null : defaultDarkTheme.value,
+ infoImageUrl: infoImageUrl.value,
+ notFoundImageUrl: notFoundImageUrl.value,
+ serverErrorImageUrl: serverErrorImageUrl.value,
+ manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)),
}).then(() => {
fetchInstance();
});
@@ -177,12 +174,12 @@ function chooseNewLike(ev: MouseEvent) {
defaultLike: emoji,
}).then(() => {
fetchInstance();
- defaultLike = emoji;
+ defaultLike.value = emoji as string;
});
});
}
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.branding,
diff --git a/packages/frontend/src/pages/admin/database.vue b/packages/frontend/src/pages/admin/database.vue
index 0c14fc9b5f..d9fc672fbf 100644
--- a/packages/frontend/src/pages/admin/database.vue
+++ b/packages/frontend/src/pages/admin/database.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { computed } from 'vue';
import FormSuspense from '@/components/form/suspense.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import * as os from '@/os.js';
@@ -29,9 +29,9 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size));
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.database,
diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue
index 8dcb5f4726..3fee3d553a 100644
--- a/packages/frontend/src/pages/admin/email-settings.vue
+++ b/packages/frontend/src/pages/admin/email-settings.vue
@@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
import XHeader from './_header_.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
@@ -78,23 +78,23 @@ import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkButton from '@/components/MkButton.vue';
-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('');
+const enableEmail = ref<boolean>(false);
+const email = ref<any>(null);
+const smtpSecure = ref<boolean>(false);
+const smtpHost = ref<string>('');
+const smtpPort = ref<number>(0);
+const smtpUser = ref<string>('');
+const smtpPass = ref<string>('');
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;
+ enableEmail.value = meta.enableEmail;
+ email.value = meta.email;
+ smtpSecure.value = meta.smtpSecure;
+ smtpHost.value = meta.smtpHost;
+ smtpPort.value = meta.smtpPort;
+ smtpUser.value = meta.smtpUser;
+ smtpPass.value = meta.smtpPass;
}
async function testEmail() {
@@ -115,19 +115,19 @@ async function testEmail() {
function save() {
os.apiWithDialog('admin/update-meta', {
- enableEmail,
- email,
- smtpSecure,
- smtpHost,
- smtpPort,
- smtpUser,
- smtpPass,
+ enableEmail: enableEmail.value,
+ email: email.value,
+ smtpSecure: smtpSecure.value,
+ smtpHost: smtpHost.value,
+ smtpPort: smtpPort.value,
+ smtpUser: smtpUser.value,
+ smtpPass: smtpPass.value,
}).then(() => {
fetchInstance();
});
}
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.emailServer,
diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue
index d1967c4bff..f4359270b6 100644
--- a/packages/frontend/src/pages/admin/external-services.vue
+++ b/packages/frontend/src/pages/admin/external-services.vue
@@ -34,10 +34,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
import XHeader from './_header_.vue';
import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
import FormSuspense from '@/components/form/suspense.vue';
import FormSection from '@/components/form/section.vue';
import * as os from '@/os.js';
@@ -45,27 +46,27 @@ import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-let deeplAuthKey: string = $ref('');
-let deeplIsPro: boolean = $ref(false);
+const deeplAuthKey = ref<string>('');
+const deeplIsPro = ref<boolean>(false);
async function init() {
const meta = await os.api('admin/meta');
- deeplAuthKey = meta.deeplAuthKey;
- deeplIsPro = meta.deeplIsPro;
+ deeplAuthKey.value = meta.deeplAuthKey;
+ deeplIsPro.value = meta.deeplIsPro;
}
function save() {
os.apiWithDialog('admin/update-meta', {
- deeplAuthKey,
- deeplIsPro,
+ deeplAuthKey: deeplAuthKey.value,
+ deeplIsPro: deeplIsPro.value,
}).then(() => {
fetchInstance();
});
}
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.externalServices,
diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue
index e09d5181c5..1888a0eb16 100644
--- a/packages/frontend/src/pages/admin/federation.vue
+++ b/packages/frontend/src/pages/admin/federation.vue
@@ -59,7 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
import XHeader from './_header_.vue';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
@@ -69,25 +69,25 @@ import FormSplit from '@/components/form/split.vue';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-let host = $ref('');
-let state = $ref('federating');
-let sort = $ref('+pubSub');
+const host = ref('');
+const state = ref('federating');
+const sort = ref('+pubSub');
const pagination = {
endpoint: 'federation/instances' as const,
limit: 10,
offsetMode: true,
params: computed(() => ({
- sort: sort,
- host: host !== '' ? host : null,
+ sort: sort.value,
+ host: host.value !== '' ? host.value : null,
...(
- state === 'federating' ? { federating: true } :
- state === 'subscribing' ? { subscribing: true } :
- state === 'publishing' ? { publishing: true } :
- state === 'suspended' ? { suspended: true } :
- state === 'blocked' ? { blocked: true } :
- state === 'silenced' ? { silenced: true } :
- state === 'notResponding' ? { notResponding: true } :
- state === 'nsfw' ? { nsfw: true } :
+ state.value === 'federating' ? { federating: true } :
+ state.value === 'subscribing' ? { subscribing: true } :
+ state.value === 'publishing' ? { publishing: true } :
+ state.value === 'suspended' ? { suspended: true } :
+ state.value === 'blocked' ? { blocked: true } :
+ state.value === 'silenced' ? { silenced: true } :
+ state.value === 'notResponding' ? { notResponding: true } :
+ state.value === 'nsfw' ? { nsfw: true } :
{}),
})),
};
@@ -101,9 +101,9 @@ function getStatus(instance) {
return 'Alive';
}
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
title: i18n.ts.federation,
diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue
index 8935cd96b8..9a355865a4 100644
--- a/packages/frontend/src/pages/admin/files.vue
+++ b/packages/frontend/src/pages/admin/files.vue
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
import XHeader from './_header_.vue';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
@@ -45,19 +45,19 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-let origin = $ref('local');
-let type = $ref(null);
-let searchHost = $ref('');
-let userId = $ref('');
-let viewMode = $ref('grid');
+const origin = ref('local');
+const type = ref(null);
+const searchHost = ref('');
+const userId = ref('');
+const viewMode = ref('grid');
const pagination = {
endpoint: 'admin/drive/files' as const,
limit: 10,
params: computed(() => ({
- type: (type && type !== '') ? type : null,
- userId: (userId && userId !== '') ? userId : null,
- origin: origin,
- hostname: (searchHost && searchHost !== '') ? searchHost : null,
+ type: (type.value && type.value !== '') ? type.value : null,
+ userId: (userId.value && userId.value !== '') ? userId.value : null,
+ origin: origin.value,
+ hostname: (searchHost.value && searchHost.value !== '') ? searchHost.value : null,
})),
};
@@ -95,7 +95,7 @@ async function find() {
});
}
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
text: i18n.ts.lookup,
icon: 'ph-magnifying-glass ph-bold ph-lg',
handler: find,
@@ -105,7 +105,7 @@ const headerActions = $computed(() => [{
handler: clear,
}]);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
title: i18n.ts.files,
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index cf62767f05..3aa74b1b91 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { onActivated, onMounted, onUnmounted, provide, watch } from 'vue';
+import { onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue';
import { i18n } from '@/i18n.js';
import MkSuperMenu from '@/components/MkSuperMenu.vue';
import MkInfo from '@/components/MkInfo.vue';
@@ -51,24 +51,24 @@ const indexInfo = {
provide('shouldOmitHeaderTitle', false);
-let INFO = $ref(indexInfo);
-let childInfo = $ref(null);
-let narrow = $ref(false);
-let view = $ref(null);
-let el = $ref(null);
-let pageProps = $ref({});
+const INFO = ref(indexInfo);
+const childInfo = ref(null);
+const narrow = ref(false);
+const view = ref(null);
+const el = ref(null);
+const pageProps = ref({});
let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile;
let noEmailServer = !instance.enableEmail;
-let thereIsUnresolvedAbuseReport = $ref(false);
-let pendingUserApprovals = $ref(false);
-let currentPage = $computed(() => router.currentRef.value.child);
+const thereIsUnresolvedAbuseReport = ref(false);
+const pendingUserApprovals = ref(false);
+const currentPage = computed(() => router.currentRef.value.child);
os.api('admin/abuse-user-reports', {
state: 'unresolved',
limit: 1,
}).then(reports => {
- if (reports.length > 0) thereIsUnresolvedAbuseReport = true;
+ if (reports.length > 0) thereIsUnresolvedAbuseReport.value = true;
});
os.api('admin/show-users', {
@@ -76,16 +76,16 @@ os.api('admin/show-users', {
origin: 'local',
limit: 1,
}).then(approvals => {
- if (approvals.length > 0) pendingUserApprovals = true;
+ if (approvals.length > 0) pendingUserApprovals.value = true;
});
const NARROW_THRESHOLD = 600;
const ro = new ResizeObserver((entries, observer) => {
if (entries.length === 0) return;
- narrow = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
+ narrow.value = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
});
-const menuDef = $computed(() => [{
+const menuDef = computed(() => [{
title: i18n.ts.quickAction,
items: [{
type: 'button',
@@ -104,72 +104,72 @@ const menuDef = $computed(() => [{
icon: 'ph-gauge ph-bold ph-lg',
text: i18n.ts.dashboard,
to: '/admin/overview',
- active: currentPage?.route.name === 'overview',
+ active: currentPage.value?.route.name === 'overview',
}, {
icon: 'ph-users ph-bold ph-lg',
text: i18n.ts.users,
to: '/admin/users',
- active: currentPage?.route.name === 'users',
+ active: currentPage.value?.route.name === 'users',
}, {
icon: 'ph-user-plus ph-bold ph-lg',
text: i18n.ts.invite,
to: '/admin/invites',
- active: currentPage?.route.name === 'invites',
+ active: currentPage.value?.route.name === 'invites',
}, {
icon: 'ph-chalkboard-teacher ph-bold ph-lg',
text: i18n.ts.approvals,
to: '/admin/approvals',
- active: currentPage?.route.name === 'approvals',
+ active: currentPage.value?.route.name === 'approvals',
}, {
icon: 'ph-seal-check ph-bold ph-lg',
text: i18n.ts.roles,
to: '/admin/roles',
- active: currentPage?.route.name === 'roles',
+ active: currentPage.value?.route.name === 'roles',
}, {
icon: 'ph-smiley ph-bold ph-lg',
text: i18n.ts.customEmojis,
to: '/admin/emojis',
- active: currentPage?.route.name === 'emojis',
+ active: currentPage.value?.route.name === 'emojis',
}, {
icon: 'ph-sparkle ph-bold ph-lg',
text: i18n.ts.avatarDecorations,
to: '/admin/avatar-decorations',
- active: currentPage?.route.name === 'avatarDecorations',
+ active: currentPage.value?.route.name === 'avatarDecorations',
}, {
icon: 'ph-globe-hemisphere-west ph-bold ph-lg',
text: i18n.ts.federation,
to: '/admin/federation',
- active: currentPage?.route.name === 'federation',
+ active: currentPage.value?.route.name === 'federation',
}, {
icon: 'ph-clock ph-bold ph-lg-play',
text: i18n.ts.jobQueue,
to: '/admin/queue',
- active: currentPage?.route.name === 'queue',
+ active: currentPage.value?.route.name === 'queue',
}, {
icon: 'ph-cloud ph-bold ph-lg',
text: i18n.ts.files,
to: '/admin/files',
- active: currentPage?.route.name === 'files',
+ active: currentPage.value?.route.name === 'files',
}, {
icon: 'ph-megaphone ph-bold ph-lg',
text: i18n.ts.announcements,
to: '/admin/announcements',
- active: currentPage?.route.name === 'announcements',
+ active: currentPage.value?.route.name === 'announcements',
}, {
icon: 'ph-flag ph-bold ph-lg',
text: i18n.ts.ads,
to: '/admin/ads',
- active: currentPage?.route.name === 'ads',
+ active: currentPage.value?.route.name === 'ads',
}, {
icon: 'ph-warning-circle ph-bold ph-lg',
text: i18n.ts.abuseReports,
to: '/admin/abuses',
- active: currentPage?.route.name === 'abuses',
+ active: currentPage.value?.route.name === 'abuses',
}, {
icon: 'ph-list ph-bold ph-lg-search',
text: i18n.ts.moderationLogs,
to: '/admin/modlog',
- active: currentPage?.route.name === 'modlog',
+ active: currentPage.value?.route.name === 'modlog',
}],
}, {
title: i18n.ts.settings,
@@ -177,57 +177,57 @@ const menuDef = $computed(() => [{
icon: 'ph-gear ph-bold ph-lg',
text: i18n.ts.general,
to: '/admin/settings',
- active: currentPage?.route.name === 'settings',
+ active: currentPage.value?.route.name === 'settings',
}, {
icon: 'ph-paint-roller ph-bold ph-lg',
text: i18n.ts.branding,
to: '/admin/branding',
- active: currentPage?.route.name === 'branding',
+ active: currentPage.value?.route.name === 'branding',
}, {
icon: 'ph-shield ph-bold ph-lg',
text: i18n.ts.moderation,
to: '/admin/moderation',
- active: currentPage?.route.name === 'moderation',
+ active: currentPage.value?.route.name === 'moderation',
}, {
icon: 'ph-envelope ph-bold ph-lg',
text: i18n.ts.emailServer,
to: '/admin/email-settings',
- active: currentPage?.route.name === 'email-settings',
+ active: currentPage.value?.route.name === 'email-settings',
}, {
icon: 'ph-cloud ph-bold ph-lg',
text: i18n.ts.objectStorage,
to: '/admin/object-storage',
- active: currentPage?.route.name === 'object-storage',
+ active: currentPage.value?.route.name === 'object-storage',
}, {
icon: 'ph-lock ph-bold ph-lg',
text: i18n.ts.security,
to: '/admin/security',
- active: currentPage?.route.name === 'security',
+ active: currentPage.value?.route.name === 'security',
}, {
icon: 'ph-planet ph-bold ph-lg',
text: i18n.ts.relays,
to: '/admin/relays',
- active: currentPage?.route.name === 'relays',
+ active: currentPage.value?.route.name === 'relays',
}, {
icon: 'ph-prohibit ph-bold ph-lg',
text: i18n.ts.instanceBlocking,
to: '/admin/instance-block',
- active: currentPage?.route.name === 'instance-block',
+ active: currentPage.value?.route.name === 'instance-block',
}, {
icon: 'ph-ghost ph-bold ph-lg',
text: i18n.ts.proxyAccount,
to: '/admin/proxy-account',
- active: currentPage?.route.name === 'proxy-account',
+ active: currentPage.value?.route.name === 'proxy-account',
}, {
icon: 'ph-arrow-square-out ph-bold ph-lg',
text: i18n.ts.externalServices,
to: '/admin/external-services',
- active: currentPage?.route.name === 'external-services',
+ active: currentPage.value?.route.name === 'external-services',
}, {
icon: 'ph-faders ph-bold ph-lg',
text: i18n.ts.other,
to: '/admin/other-settings',
- active: currentPage?.route.name === 'other-settings',
+ active: currentPage.value?.route.name === 'other-settings',
}],
}, {
title: i18n.ts.info,
@@ -235,28 +235,28 @@ const menuDef = $computed(() => [{
icon: 'ph-database ph-bold ph-lg',
text: i18n.ts.database,
to: '/admin/database',
- active: currentPage?.route.name === 'database',
+ active: currentPage.value?.route.name === 'database',
}],
}]);
-watch(narrow, () => {
- if (currentPage?.route.name == null && !narrow) {
+watch(narrow.value, () => {
+ if (currentPage.value?.route.name == null && !narrow.value) {
router.push('/admin/overview');
}
});
onMounted(() => {
- ro.observe(el);
+ ro.observe(el.value);
- narrow = el.offsetWidth < NARROW_THRESHOLD;
- if (currentPage?.route.name == null && !narrow) {
+ narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
+ if (currentPage.value?.route.name == null && !narrow.value) {
router.push('/admin/overview');
}
});
onActivated(() => {
- narrow = el.offsetWidth < NARROW_THRESHOLD;
- if (currentPage?.route.name == null && !narrow) {
+ narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
+ if (currentPage.value?.route.name == null && !narrow.value) {
router.push('/admin/overview');
}
});
@@ -266,16 +266,17 @@ onUnmounted(() => {
});
watch(router.currentRef, (to) => {
- if (to.route.path === '/admin' && to.child?.route.name == null && !narrow) {
+ if (to.route.path === '/admin' && to.child?.route.name == null && !narrow.value) {
router.replace('/admin/overview');
}
});
provideMetadataReceiver((info) => {
if (info == null) {
- childInfo = null;
+ childInfo.value = null;
} else {
- childInfo = info;
+ childInfo.value = info;
+ INFO.value.needWideArea = info.value.needWideArea ?? undefined;
}
});
@@ -283,7 +284,7 @@ function invite() {
os.api('admin/invite/create').then(x => {
os.alert({
type: 'info',
- text: x?.[0].code,
+ text: x[0].code,
});
}).catch(err => {
os.alert({
@@ -327,11 +328,11 @@ function lookup(ev: MouseEvent) {
}], ev.currentTarget ?? ev.target);
}
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
-definePageMetadata(INFO);
+definePageMetadata(INFO.value);
defineExpose({
header: {
diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue
index 6024ed6b8f..e54f6dc065 100644
--- a/packages/frontend/src/pages/admin/instance-block.vue
+++ b/packages/frontend/src/pages/admin/instance-block.vue
@@ -23,6 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
+import { ref, computed } from 'vue';
import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue';
import MkTextarea from '@/components/MkTextarea.vue';
@@ -32,29 +33,29 @@ import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-let blockedHosts: string = $ref('');
-let silencedHosts: string = $ref('');
-let tab = $ref('block');
+const blockedHosts = ref<string>('');
+const silencedHosts = ref<string>('');
+const tab = ref('block');
async function init() {
const meta = await os.api('admin/meta');
- blockedHosts = meta.blockedHosts.join('\n');
- silencedHosts = meta.silencedHosts.join('\n');
+ blockedHosts.value = meta.blockedHosts.join('\n');
+ silencedHosts.value = meta.silencedHosts.join('\n');
}
function save() {
os.apiWithDialog('admin/update-meta', {
- blockedHosts: blockedHosts.split('\n') || [],
- silencedHosts: silencedHosts.split('\n') || [],
+ blockedHosts: blockedHosts.value.split('\n') || [],
+ silencedHosts: silencedHosts.value.split('\n') || [],
}).then(() => {
fetchInstance();
});
}
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
key: 'block',
title: i18n.ts.block,
icon: 'ph-prohibit ph-bold ph-lg',
diff --git a/packages/frontend/src/pages/admin/invites.vue b/packages/frontend/src/pages/admin/invites.vue
index 406317f660..6314d0ce4e 100644
--- a/packages/frontend/src/pages/admin/invites.vue
+++ b/packages/frontend/src/pages/admin/invites.vue
@@ -70,8 +70,8 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
-let type = ref('all');
-let sort = ref('+createdAt');
+const type = ref('all');
+const sort = ref('+createdAt');
const pagination: Paging = {
endpoint: 'admin/invite/list' as const,
@@ -109,8 +109,8 @@ function deleted(id: string) {
}
}
-const headerActions = $computed(() => []);
-const headerTabs = $computed(() => []);
+const headerActions = computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.invite,
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index cacb3254a8..9539611f76 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -48,6 +48,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.sensitiveWords }}</template>
<template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template>
</MkTextarea>
+
+ <MkTextarea v-model="hiddenTags">
+ <template #label>{{ i18n.ts.hiddenTags }}</template>
+ <template #caption>{{ i18n.ts.hiddenTagsDescription }}</template>
+ </MkTextarea>
</div>
</FormSuspense>
</MkSpacer>
@@ -63,13 +68,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
import XHeader from './_header_.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
-import FormSection from '@/components/form/section.vue';
-import FormSplit from '@/components/form/split.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js';
import { fetchInstance } from '@/instance.js';
@@ -78,45 +81,48 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkButton from '@/components/MkButton.vue';
import FormLink from '@/components/form/link.vue';
-let enableRegistration: boolean = $ref(false);
-let emailRequiredForSignup: boolean = $ref(false);
-let approvalRequiredForSignup: boolean = $ref(false);
-let bubbleTimelineEnabled: boolean = $ref(false);
-let sensitiveWords: string = $ref('');
-let preservedUsernames: string = $ref('');
-let bubbleTimeline: string = $ref('');
-let tosUrl: string | null = $ref(null);
-let privacyPolicyUrl: string | null = $ref(null);
+const enableRegistration = ref<boolean>(false);
+const emailRequiredForSignup = ref<boolean>(false);
+const approvalRequiredForSignup = ref<boolean>(false);
+const bubbleTimelineEnabled = ref<boolean>(false);
+const sensitiveWords = ref<string>('');
+const hiddenTags = ref<string>('');
+const preservedUsernames = ref<string>('');
+const bubbleTimeline = ref<string>('');
+const tosUrl = ref<string | null>(null);
+const privacyPolicyUrl = ref<string | null>(null);
async function init() {
const meta = await os.api('admin/meta');
- enableRegistration = !meta.disableRegistration;
- emailRequiredForSignup = meta.emailRequiredForSignup;
- approvalRequiredForSignup = meta.approvalRequiredForSignup;
- sensitiveWords = meta.sensitiveWords.join('\n');
- preservedUsernames = meta.preservedUsernames.join('\n');
- tosUrl = meta.tosUrl;
- privacyPolicyUrl = meta.privacyPolicyUrl;
- bubbleTimeline = meta.bubbleInstances.join('\n');
- bubbleTimelineEnabled = meta.policies.btlAvailable;
+ enableRegistration.value = !meta.disableRegistration;
+ emailRequiredForSignup.value = meta.emailRequiredForSignup;
+ approvalRequiredForSignup.value = meta.approvalRequiredForSignup;
+ sensitiveWords.value = meta.sensitiveWords.join('\n');
+ hiddenTags.value = meta.hiddenTags.join('\n');
+ preservedUsernames.value = meta.preservedUsernames.join('\n');
+ tosUrl.value = meta.tosUrl;
+ privacyPolicyUrl.value = meta.privacyPolicyUrl;
+ bubbleTimeline.value = meta.bubbleInstances.join('\n');
+ bubbleTimelineEnabled.value = meta.policies.btlAvailable;
}
function save() {
os.apiWithDialog('admin/update-meta', {
- disableRegistration: !enableRegistration,
- emailRequiredForSignup,
- approvalRequiredForSignup,
- tosUrl,
- privacyPolicyUrl,
- sensitiveWords: sensitiveWords.split('\n'),
- preservedUsernames: preservedUsernames.split('\n'),
- bubbleInstances: bubbleTimeline.split('\n'),
+ disableRegistration: !enableRegistration.value,
+ emailRequiredForSignup: emailRequiredForSignup.value,
+ approvalRequiredForSignup: approvalRequiredForSignup.value,
+ tosUrl: tosUrl.value,
+ privacyPolicyUrl: privacyPolicyUrl.value,
+ sensitiveWords: sensitiveWords.value.split('\n'),
+ hiddenTags: hiddenTags.value.split('\n'),
+ preservedUsernames: preservedUsernames.value.split('\n'),
+ bubbleInstances: bubbleTimeline.value.split('\n'),
}).then(() => {
fetchInstance();
});
}
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.moderation,
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index a6612f17d1..4436fbac89 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkTime :time="log.createdAt"/>
</template>
- <div :class="$style.root">
+ <div>
<div style="display: flex; gap: var(--margin); flex-wrap: wrap;">
<div style="flex: 1;">{{ i18n.ts.moderator }}: <MkA :to="`/admin/user/${log.userId}`" class="_link">@{{ log.user?.username }}</MkA></div>
<div style="flex: 1;">{{ i18n.ts.dateAndTime }}: <MkTime :time="log.createdAt" mode="detail"/></div>
@@ -127,9 +127,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import * as Misskey from 'misskey-js';
import { CodeDiff } from 'v-code-diff';
import JSON5 from 'json5';
-import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
-import { dateString } from '@/filters/date.js';
import MkFolder from '@/components/MkFolder.vue';
const props = defineProps<{
@@ -138,9 +136,6 @@ const props = defineProps<{
</script>
<style lang="scss" module>
-.root {
-}
-
.avatar {
width: 18px;
height: 18px;
diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue
index bdbed67ad1..a50d401ba2 100644
--- a/packages/frontend/src/pages/admin/modlog.vue
+++ b/packages/frontend/src/pages/admin/modlog.vue
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, shallowRef, ref } from 'vue';
import * as Misskey from 'misskey-js';
import XHeader from './_header_.vue';
import XModLog from './modlog.ModLog.vue';
@@ -40,25 +40,25 @@ import MkPagination from '@/components/MkPagination.vue';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-let logs = $shallowRef<InstanceType<typeof MkPagination>>();
+const logs = shallowRef<InstanceType<typeof MkPagination>>();
-let type = $ref(null);
-let moderatorId = $ref('');
+const type = ref(null);
+const moderatorId = ref('');
const pagination = {
endpoint: 'admin/show-moderation-logs' as const,
limit: 30,
params: computed(() => ({
- type,
- userId: moderatorId === '' ? null : moderatorId,
+ type: type.value,
+ userId: moderatorId.value === '' ? null : moderatorId.value,
})),
};
console.log(Misskey);
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.moderationLogs,
diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue
index cd1f4ca601..e71e53c942 100644
--- a/packages/frontend/src/pages/admin/object-storage.vue
+++ b/packages/frontend/src/pages/admin/object-storage.vue
@@ -83,7 +83,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
import XHeader from './_header_.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
@@ -95,58 +95,58 @@ import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkButton from '@/components/MkButton.vue';
-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);
+const useObjectStorage = ref<boolean>(false);
+const objectStorageBaseUrl = ref<string | null>(null);
+const objectStorageBucket = ref<string | null>(null);
+const objectStoragePrefix = ref<string | null>(null);
+const objectStorageEndpoint = ref<string | null>(null);
+const objectStorageRegion = ref<string | null>(null);
+const objectStoragePort = ref<number | null>(null);
+const objectStorageAccessKey = ref<string | null>(null);
+const objectStorageSecretKey = ref<string | null>(null);
+const objectStorageUseSSL = ref<boolean>(false);
+const objectStorageUseProxy = ref<boolean>(false);
+const objectStorageSetPublicRead = ref<boolean>(false);
+const objectStorageS3ForcePathStyle = ref<boolean>(true);
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;
+ useObjectStorage.value = meta.useObjectStorage;
+ objectStorageBaseUrl.value = meta.objectStorageBaseUrl;
+ objectStorageBucket.value = meta.objectStorageBucket;
+ objectStoragePrefix.value = meta.objectStoragePrefix;
+ objectStorageEndpoint.value = meta.objectStorageEndpoint;
+ objectStorageRegion.value = meta.objectStorageRegion;
+ objectStoragePort.value = meta.objectStoragePort;
+ objectStorageAccessKey.value = meta.objectStorageAccessKey;
+ objectStorageSecretKey.value = meta.objectStorageSecretKey;
+ objectStorageUseSSL.value = meta.objectStorageUseSSL;
+ objectStorageUseProxy.value = meta.objectStorageUseProxy;
+ objectStorageSetPublicRead.value = meta.objectStorageSetPublicRead;
+ objectStorageS3ForcePathStyle.value = meta.objectStorageS3ForcePathStyle;
}
function save() {
os.apiWithDialog('admin/update-meta', {
- useObjectStorage,
- objectStorageBaseUrl,
- objectStorageBucket,
- objectStoragePrefix,
- objectStorageEndpoint,
- objectStorageRegion,
- objectStoragePort,
- objectStorageAccessKey,
- objectStorageSecretKey,
- objectStorageUseSSL,
- objectStorageUseProxy,
- objectStorageSetPublicRead,
- objectStorageS3ForcePathStyle,
+ useObjectStorage: useObjectStorage.value,
+ objectStorageBaseUrl: objectStorageBaseUrl.value,
+ objectStorageBucket: objectStorageBucket.value,
+ objectStoragePrefix: objectStoragePrefix.value,
+ objectStorageEndpoint: objectStorageEndpoint.value,
+ objectStorageRegion: objectStorageRegion.value,
+ objectStoragePort: objectStoragePort.value,
+ objectStorageAccessKey: objectStorageAccessKey.value,
+ objectStorageSecretKey: objectStorageSecretKey.value,
+ objectStorageUseSSL: objectStorageUseSSL.value,
+ objectStorageUseProxy: objectStorageUseProxy.value,
+ objectStorageSetPublicRead: objectStorageSetPublicRead.value,
+ objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value,
}).then(() => {
fetchInstance();
});
}
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.objectStorage,
diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue
index d1bc420db8..6523676a18 100644
--- a/packages/frontend/src/pages/admin/other-settings.vue
+++ b/packages/frontend/src/pages/admin/other-settings.vue
@@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
import XHeader from './_header_.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js';
@@ -66,44 +66,44 @@ import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkSwitch from '@/components/MkSwitch.vue';
-let enableServerMachineStats: boolean = $ref(false);
-let enableAchievements: boolean = $ref(false);
-let enableBotTrending: boolean = $ref(false);
-let enableIdenticonGeneration: boolean = $ref(false);
-let enableChartsForRemoteUser: boolean = $ref(false);
-let enableChartsForFederatedInstances: boolean = $ref(false);
+const enableServerMachineStats = ref<boolean>(false);
+const enableAchievements = ref<boolean>(false);
+const enableBotTrending = ref<boolean>(false);
+const enableIdenticonGeneration = ref<boolean>(false);
+const enableChartsForRemoteUser = ref<boolean>(false);
+const enableChartsForFederatedInstances = ref<boolean>(false);
async function init() {
const meta = await os.api('admin/meta');
- enableServerMachineStats = meta.enableServerMachineStats;
- enableAchievements = meta.enableAchievements;
- enableBotTrending = meta.enableBotTrending;
- enableIdenticonGeneration = meta.enableIdenticonGeneration;
- enableChartsForRemoteUser = meta.enableChartsForRemoteUser;
- enableChartsForFederatedInstances = meta.enableChartsForFederatedInstances;
+ enableServerMachineStats.value = meta.enableServerMachineStats;
+ enableAchievements.value = meta.enableAchievements;
+ enableBotTrending.value = meta.enableBotTrending;
+ enableIdenticonGeneration.value = meta.enableIdenticonGeneration;
+ enableChartsForRemoteUser.value = meta.enableChartsForRemoteUser;
+ enableChartsForFederatedInstances.value = meta.enableChartsForFederatedInstances;
}
function save() {
os.apiWithDialog('admin/update-meta', {
- enableServerMachineStats,
- enableAchievements,
- enableBotTrending,
- enableIdenticonGeneration,
- enableChartsForRemoteUser,
- enableChartsForFederatedInstances,
+ enableServerMachineStats: enableServerMachineStats.value,
+ enableAchievements: enableAchievements.value,
+ enableBotTrending: enableBotTrending.value,
+ enableIdenticonGeneration: enableIdenticonGeneration.value,
+ enableChartsForRemoteUser: enableChartsForRemoteUser.value,
+ enableChartsForFederatedInstances: enableChartsForFederatedInstances.value,
}).then(() => {
fetchInstance();
});
}
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
asFullButton: true,
icon: 'ph-check ph-bold ph-lg',
text: i18n.ts.save,
handler: save,
}]);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.other,
diff --git a/packages/frontend/src/pages/admin/overview.active-users.vue b/packages/frontend/src/pages/admin/overview.active-users.vue
index 8426c463d2..5e67370c2b 100644
--- a/packages/frontend/src/pages/admin/overview.active-users.vue
+++ b/packages/frontend/src/pages/admin/overview.active-users.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef, ref } from 'vue';
import { Chart } from 'chart.js';
import gradient from 'chartjs-plugin-gradient';
import * as os from '@/os.js';
@@ -24,11 +24,11 @@ import { initChart } from '@/scripts/init-chart.js';
initChart();
-const chartEl = $shallowRef<HTMLCanvasElement>(null);
+const chartEl = shallowRef<HTMLCanvasElement>(null);
const now = new Date();
let chartInstance: Chart = null;
const chartLimit = 7;
-let fetching = $ref(true);
+const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip();
@@ -61,7 +61,7 @@ async function renderChart() {
const max = Math.max(...raw.read);
- chartInstance = new Chart(chartEl, {
+ chartInstance = new Chart(chartEl.value, {
type: 'bar',
data: {
datasets: [{
@@ -155,7 +155,7 @@ async function renderChart() {
plugins: [chartVLine(vLineColor)],
});
- fetching = false;
+ fetching.value = false;
}
onMounted(async () => {
diff --git a/packages/frontend/src/pages/admin/overview.ap-requests.vue b/packages/frontend/src/pages/admin/overview.ap-requests.vue
index cd54041c34..0de62fadea 100644
--- a/packages/frontend/src/pages/admin/overview.ap-requests.vue
+++ b/packages/frontend/src/pages/admin/overview.ap-requests.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef, ref } from 'vue';
import { Chart } from 'chart.js';
import gradient from 'chartjs-plugin-gradient';
import * as os from '@/os.js';
@@ -33,9 +33,9 @@ import { initChart } from '@/scripts/init-chart.js';
initChart();
const chartLimit = 50;
-const chartEl = $shallowRef<HTMLCanvasElement>();
-const chartEl2 = $shallowRef<HTMLCanvasElement>();
-let fetching = $ref(true);
+const chartEl = shallowRef<HTMLCanvasElement>();
+const chartEl2 = shallowRef<HTMLCanvasElement>();
+const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip();
const { handler: externalTooltipHandler2 } = useChartTooltip();
@@ -74,7 +74,7 @@ onMounted(async () => {
const succMax = Math.max(...raw.deliverSucceeded);
const failMax = Math.max(...raw.deliverFailed);
- new Chart(chartEl, {
+ new Chart(chartEl.value, {
type: 'line',
data: {
datasets: [{
@@ -178,7 +178,7 @@ onMounted(async () => {
plugins: [chartVLine(vLineColor)],
});
- new Chart(chartEl2, {
+ new Chart(chartEl2.value, {
type: 'bar',
data: {
datasets: [{
@@ -265,7 +265,7 @@ onMounted(async () => {
plugins: [chartVLine(vLineColor)],
});
- fetching = false;
+ fetching.value = false;
});
</script>
diff --git a/packages/frontend/src/pages/admin/overview.federation.vue b/packages/frontend/src/pages/admin/overview.federation.vue
index 33dbbd0d84..03e33e57c4 100644
--- a/packages/frontend/src/pages/admin/overview.federation.vue
+++ b/packages/frontend/src/pages/admin/overview.federation.vue
@@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
import XPie from './overview.pie.vue';
import * as os from '@/os.js';
import number from '@/filters/number.js';
@@ -54,25 +54,25 @@ import MkNumberDiff from '@/components/MkNumberDiff.vue';
import { i18n } from '@/i18n.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
-let topSubInstancesForPie: any = $ref(null);
-let topPubInstancesForPie: any = $ref(null);
-let federationPubActive = $ref<number | null>(null);
-let federationPubActiveDiff = $ref<number | null>(null);
-let federationSubActive = $ref<number | null>(null);
-let federationSubActiveDiff = $ref<number | null>(null);
-let fetching = $ref(true);
+const topSubInstancesForPie = ref<any>(null);
+const topPubInstancesForPie = ref<any>(null);
+const federationPubActive = ref<number | null>(null);
+const federationPubActiveDiff = ref<number | null>(null);
+const federationSubActive = ref<number | null>(null);
+const federationSubActiveDiff = ref<number | null>(null);
+const fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip();
onMounted(async () => {
const chart = await os.apiGet('charts/federation', { limit: 2, span: 'day' });
- federationPubActive = chart.pubActive[0];
- federationPubActiveDiff = chart.pubActive[0] - chart.pubActive[1];
- federationSubActive = chart.subActive[0];
- federationSubActiveDiff = chart.subActive[0] - chart.subActive[1];
+ federationPubActive.value = chart.pubActive[0];
+ federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1];
+ federationSubActive.value = chart.subActive[0];
+ federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
os.apiGet('federation/stats', { limit: 10 }).then(res => {
- topSubInstancesForPie = res.topSubInstances.map(x => ({
+ topSubInstancesForPie.value = res.topSubInstances.map(x => ({
name: x.host,
color: x.themeColor,
value: x.followersCount,
@@ -80,7 +80,7 @@ onMounted(async () => {
os.pageWindow(`/instance-info/${x.host}`);
},
})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowersCount }]);
- topPubInstancesForPie = res.topPubInstances.map(x => ({
+ topPubInstancesForPie.value = res.topPubInstances.map(x => ({
name: x.host,
color: x.themeColor,
value: x.followingCount,
@@ -90,7 +90,7 @@ onMounted(async () => {
})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowingCount }]);
});
- fetching = false;
+ fetching.value = false;
});
</script>
diff --git a/packages/frontend/src/pages/admin/overview.heatmap.vue b/packages/frontend/src/pages/admin/overview.heatmap.vue
index 4d09f183bf..8e3c809353 100644
--- a/packages/frontend/src/pages/admin/overview.heatmap.vue
+++ b/packages/frontend/src/pages/admin/overview.heatmap.vue
@@ -17,10 +17,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
+import { ref } from 'vue';
import MkHeatmap from '@/components/MkHeatmap.vue';
import MkSelect from '@/components/MkSelect.vue';
-let src = $ref('active-users');
+const src = ref('active-users');
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/pages/admin/overview.moderators.vue b/packages/frontend/src/pages/admin/overview.moderators.vue
index 4086ca51f0..c6e81b4a18 100644
--- a/packages/frontend/src/pages/admin/overview.moderators.vue
+++ b/packages/frontend/src/pages/admin/overview.moderators.vue
@@ -17,21 +17,21 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
import * as os from '@/os.js';
import { defaultStore } from '@/store.js';
-let moderators: any = $ref(null);
-let fetching = $ref(true);
+const moderators = ref<any>(null);
+const fetching = ref(true);
onMounted(async () => {
- moderators = await os.api('admin/show-users', {
+ moderators.value = await os.api('admin/show-users', {
sort: '+lastActiveDate',
state: 'adminOrModerator',
limit: 30,
});
- fetching = false;
+ fetching.value = false;
});
</script>
diff --git a/packages/frontend/src/pages/admin/overview.queue.vue b/packages/frontend/src/pages/admin/overview.queue.vue
index 1af9d89f62..b6b3bf194a 100644
--- a/packages/frontend/src/pages/admin/overview.queue.vue
+++ b/packages/frontend/src/pages/admin/overview.queue.vue
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { markRaw, onMounted, onUnmounted, ref } from 'vue';
+import { markRaw, onMounted, onUnmounted, ref, shallowRef } from 'vue';
import XChart from './overview.queue.chart.vue';
import number from '@/filters/number.js';
import { useStream } from '@/stream.js';
@@ -46,10 +46,10 @@ const activeSincePrevTick = ref(0);
const active = ref(0);
const delayed = ref(0);
const waiting = ref(0);
-let chartProcess = $shallowRef<InstanceType<typeof XChart>>();
-let chartActive = $shallowRef<InstanceType<typeof XChart>>();
-let chartDelayed = $shallowRef<InstanceType<typeof XChart>>();
-let chartWaiting = $shallowRef<InstanceType<typeof XChart>>();
+const chartProcess = shallowRef<InstanceType<typeof XChart>>();
+const chartActive = shallowRef<InstanceType<typeof XChart>>();
+const chartDelayed = shallowRef<InstanceType<typeof XChart>>();
+const chartWaiting = shallowRef<InstanceType<typeof XChart>>();
const props = defineProps<{
domain: string;
@@ -61,10 +61,10 @@ const onStats = (stats) => {
delayed.value = stats[props.domain].delayed;
waiting.value = stats[props.domain].waiting;
- chartProcess.pushData(stats[props.domain].activeSincePrevTick);
- chartActive.pushData(stats[props.domain].active);
- chartDelayed.pushData(stats[props.domain].delayed);
- chartWaiting.pushData(stats[props.domain].waiting);
+ chartProcess.value.pushData(stats[props.domain].activeSincePrevTick);
+ chartActive.value.pushData(stats[props.domain].active);
+ chartDelayed.value.pushData(stats[props.domain].delayed);
+ chartWaiting.value.pushData(stats[props.domain].waiting);
};
const onStatsLog = (statsLog) => {
@@ -80,10 +80,10 @@ const onStatsLog = (statsLog) => {
dataWaiting.push(stats[props.domain].waiting);
}
- chartProcess.setData(dataProcess);
- chartActive.setData(dataActive);
- chartDelayed.setData(dataDelayed);
- chartWaiting.setData(dataWaiting);
+ chartProcess.value.setData(dataProcess);
+ chartActive.value.setData(dataActive);
+ chartDelayed.value.setData(dataDelayed);
+ chartWaiting.value.setData(dataWaiting);
};
onMounted(() => {
diff --git a/packages/frontend/src/pages/admin/overview.stats.vue b/packages/frontend/src/pages/admin/overview.stats.vue
index 23ed736c94..b853aee55d 100644
--- a/packages/frontend/src/pages/admin/overview.stats.vue
+++ b/packages/frontend/src/pages/admin/overview.stats.vue
@@ -61,7 +61,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
import * as os from '@/os.js';
import MkNumberDiff from '@/components/MkNumberDiff.vue';
import MkNumber from '@/components/MkNumber.vue';
@@ -69,29 +69,29 @@ import { i18n } from '@/i18n.js';
import { customEmojis } from '@/custom-emojis.js';
import { defaultStore } from '@/store.js';
-let stats: any = $ref(null);
-let usersComparedToThePrevDay = $ref<number>();
-let notesComparedToThePrevDay = $ref<number>();
-let onlineUsersCount = $ref(0);
-let fetching = $ref(true);
+const stats = ref<any>(null);
+const usersComparedToThePrevDay = ref<number>();
+const notesComparedToThePrevDay = ref<number>();
+const onlineUsersCount = ref(0);
+const fetching = ref(true);
onMounted(async () => {
const [_stats, _onlineUsersCount] = await Promise.all([
os.api('stats', {}),
os.apiGet('get-online-users-count').then(res => res.count),
]);
- stats = _stats;
- onlineUsersCount = _onlineUsersCount;
+ stats.value = _stats;
+ onlineUsersCount.value = _onlineUsersCount;
os.apiGet('charts/users', { limit: 2, span: 'day' }).then(chart => {
- usersComparedToThePrevDay = stats.originalUsersCount - chart.local.total[1];
+ usersComparedToThePrevDay.value = stats.value.originalUsersCount - chart.local.total[1];
});
os.apiGet('charts/notes', { limit: 2, span: 'day' }).then(chart => {
- notesComparedToThePrevDay = stats.originalNotesCount - chart.local.total[1];
+ notesComparedToThePrevDay.value = stats.value.originalNotesCount - chart.local.total[1];
});
- fetching = false;
+ fetching.value = false;
});
</script>
diff --git a/packages/frontend/src/pages/admin/overview.users.vue b/packages/frontend/src/pages/admin/overview.users.vue
index 6ee83c51e7..6b8dd90747 100644
--- a/packages/frontend/src/pages/admin/overview.users.vue
+++ b/packages/frontend/src/pages/admin/overview.users.vue
@@ -17,13 +17,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
+import { ref } from 'vue';
import * as os from '@/os.js';
import { useInterval } from '@/scripts/use-interval.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import { defaultStore } from '@/store.js';
-let newUsers = $ref(null);
-let fetching = $ref(true);
+const newUsers = ref(null);
+const fetching = ref(true);
const fetch = async () => {
const _newUsers = await os.api('admin/show-users', {
@@ -31,8 +32,8 @@ const fetch = async () => {
sort: '+createdAt',
origin: 'local',
});
- newUsers = _newUsers;
- fetching = false;
+ newUsers.value = _newUsers;
+ fetching.value = false;
};
useInterval(fetch, 1000 * 60, {
diff --git a/packages/frontend/src/pages/admin/overview.vue b/packages/frontend/src/pages/admin/overview.vue
index 7563ae9834..edeab30cbb 100644
--- a/packages/frontend/src/pages/admin/overview.vue
+++ b/packages/frontend/src/pages/admin/overview.vue
@@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { markRaw, onMounted, onBeforeUnmount, nextTick } from 'vue';
+import { markRaw, onMounted, onBeforeUnmount, nextTick, shallowRef, ref, computed } from 'vue';
import XFederation from './overview.federation.vue';
import XInstances from './overview.instances.vue';
import XQueue from './overview.queue.vue';
@@ -82,16 +82,16 @@ import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
-const rootEl = $shallowRef<HTMLElement>();
-let serverInfo: any = $ref(null);
-let topSubInstancesForPie: any = $ref(null);
-let topPubInstancesForPie: any = $ref(null);
-let federationPubActive = $ref<number | null>(null);
-let federationPubActiveDiff = $ref<number | null>(null);
-let federationSubActive = $ref<number | null>(null);
-let federationSubActiveDiff = $ref<number | null>(null);
-let newUsers = $ref(null);
-let activeInstances = $shallowRef(null);
+const rootEl = shallowRef<HTMLElement>();
+const serverInfo = ref<any>(null);
+const topSubInstancesForPie = ref<any>(null);
+const topPubInstancesForPie = ref<any>(null);
+const federationPubActive = ref<number | null>(null);
+const federationPubActiveDiff = ref<number | null>(null);
+const federationSubActive = ref<number | null>(null);
+const federationSubActiveDiff = ref<number | null>(null);
+const newUsers = ref(null);
+const activeInstances = shallowRef(null);
const queueStatsConnection = markRaw(useStream().useChannel('queueStats'));
const now = new Date();
const filesPagination = {
@@ -116,14 +116,14 @@ onMounted(async () => {
*/
os.apiGet('charts/federation', { limit: 2, span: 'day' }).then(chart => {
- federationPubActive = chart.pubActive[0];
- federationPubActiveDiff = chart.pubActive[0] - chart.pubActive[1];
- federationSubActive = chart.subActive[0];
- federationSubActiveDiff = chart.subActive[0] - chart.subActive[1];
+ federationPubActive.value = chart.pubActive[0];
+ federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1];
+ federationSubActive.value = chart.subActive[0];
+ federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
});
os.apiGet('federation/stats', { limit: 10 }).then(res => {
- topSubInstancesForPie = res.topSubInstances.map(x => ({
+ topSubInstancesForPie.value = res.topSubInstances.map(x => ({
name: x.host,
color: x.themeColor,
value: x.followersCount,
@@ -131,7 +131,7 @@ onMounted(async () => {
os.pageWindow(`/instance-info/${x.host}`);
},
})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowersCount }]);
- topPubInstancesForPie = res.topPubInstances.map(x => ({
+ topPubInstancesForPie.value = res.topPubInstances.map(x => ({
name: x.host,
color: x.themeColor,
value: x.followingCount,
@@ -142,21 +142,21 @@ onMounted(async () => {
});
os.api('admin/server-info').then(serverInfoResponse => {
- serverInfo = serverInfoResponse;
+ serverInfo.value = serverInfoResponse;
});
os.api('admin/show-users', {
limit: 5,
sort: '+createdAt',
}).then(res => {
- newUsers = res;
+ newUsers.value = res;
});
os.api('federation/instances', {
sort: '+latestRequestReceivedAt',
limit: 25,
}).then(res => {
- activeInstances = res;
+ activeInstances.value = res;
});
nextTick(() => {
@@ -171,9 +171,9 @@ onBeforeUnmount(() => {
queueStatsConnection.dispose();
});
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.dashboard,
diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue
index 3e2a8bdf38..d65e78afa5 100644
--- a/packages/frontend/src/pages/admin/proxy-account.vue
+++ b/packages/frontend/src/pages/admin/proxy-account.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import MkButton from '@/components/MkButton.vue';
import MkInfo from '@/components/MkInfo.vue';
@@ -31,36 +31,36 @@ import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-let proxyAccount: any = $ref(null);
-let proxyAccountId: any = $ref(null);
+const proxyAccount = ref<any>(null);
+const proxyAccountId = ref<any>(null);
async function init() {
const meta = await os.api('admin/meta');
- proxyAccountId = meta.proxyAccountId;
- if (proxyAccountId) {
- proxyAccount = await os.api('users/show', { userId: proxyAccountId });
+ proxyAccountId.value = meta.proxyAccountId;
+ if (proxyAccountId.value) {
+ proxyAccount.value = await os.api('users/show', { userId: proxyAccountId.value });
}
}
function chooseProxyAccount() {
os.selectUser().then(user => {
- proxyAccount = user;
- proxyAccountId = user.id;
+ proxyAccount.value = user;
+ proxyAccountId.value = user.id;
save();
});
}
function save() {
os.apiWithDialog('admin/update-meta', {
- proxyAccountId: proxyAccountId,
+ proxyAccountId: proxyAccountId.value,
}).then(() => {
fetchInstance();
});
}
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.proxyAccount,
diff --git a/packages/frontend/src/pages/admin/queue.chart.vue b/packages/frontend/src/pages/admin/queue.chart.vue
index 4746f9de1b..9612f78624 100644
--- a/packages/frontend/src/pages/admin/queue.chart.vue
+++ b/packages/frontend/src/pages/admin/queue.chart.vue
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { markRaw, onMounted, onUnmounted, ref } from 'vue';
+import { markRaw, onMounted, onUnmounted, ref, shallowRef } from 'vue';
import XChart from './queue.chart.chart.vue';
import number from '@/filters/number.js';
import * as os from '@/os.js';
@@ -63,10 +63,10 @@ const active = ref(0);
const delayed = ref(0);
const waiting = ref(0);
const jobs = ref([]);
-let chartProcess = $shallowRef<InstanceType<typeof XChart>>();
-let chartActive = $shallowRef<InstanceType<typeof XChart>>();
-let chartDelayed = $shallowRef<InstanceType<typeof XChart>>();
-let chartWaiting = $shallowRef<InstanceType<typeof XChart>>();
+const chartProcess = shallowRef<InstanceType<typeof XChart>>();
+const chartActive = shallowRef<InstanceType<typeof XChart>>();
+const chartDelayed = shallowRef<InstanceType<typeof XChart>>();
+const chartWaiting = shallowRef<InstanceType<typeof XChart>>();
const props = defineProps<{
domain: string;
@@ -78,10 +78,10 @@ const onStats = (stats) => {
delayed.value = stats[props.domain].delayed;
waiting.value = stats[props.domain].waiting;
- chartProcess.pushData(stats[props.domain].activeSincePrevTick);
- chartActive.pushData(stats[props.domain].active);
- chartDelayed.pushData(stats[props.domain].delayed);
- chartWaiting.pushData(stats[props.domain].waiting);
+ chartProcess.value.pushData(stats[props.domain].activeSincePrevTick);
+ chartActive.value.pushData(stats[props.domain].active);
+ chartDelayed.value.pushData(stats[props.domain].delayed);
+ chartWaiting.value.pushData(stats[props.domain].waiting);
};
const onStatsLog = (statsLog) => {
@@ -97,10 +97,10 @@ const onStatsLog = (statsLog) => {
dataWaiting.push(stats[props.domain].waiting);
}
- chartProcess.setData(dataProcess);
- chartActive.setData(dataActive);
- chartDelayed.setData(dataDelayed);
- chartWaiting.setData(dataWaiting);
+ chartProcess.value.setData(dataProcess);
+ chartActive.value.setData(dataActive);
+ chartDelayed.value.setData(dataDelayed);
+ chartWaiting.value.setData(dataWaiting);
};
onMounted(() => {
diff --git a/packages/frontend/src/pages/admin/queue.vue b/packages/frontend/src/pages/admin/queue.vue
index 62a8aa1e22..245c55f6e7 100644
--- a/packages/frontend/src/pages/admin/queue.vue
+++ b/packages/frontend/src/pages/admin/queue.vue
@@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
+import { ref, computed } from 'vue';
import XQueue from './queue.chart.vue';
import XHeader from './_header_.vue';
import * as os from '@/os.js';
@@ -24,7 +25,7 @@ import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkButton from '@/components/MkButton.vue';
-let tab = $ref('deliver');
+const tab = ref('deliver');
function clear() {
os.confirm({
@@ -46,20 +47,20 @@ function promoteAllQueues() {
}).then(({ canceled }) => {
if (canceled) return;
- os.apiWithDialog('admin/queue/promote', { type: tab });
+ os.apiWithDialog('admin/queue/promote', { type: tab.value });
});
}
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
asFullButton: true,
icon: 'ph-arrow-square-out ph-bold ph-lg',
text: i18n.ts.dashboard,
handler: () => {
- window.open(config.url + '/queue', '_blank');
+ window.open(config.url + '/queue', '_blank', 'noopener');
},
}]);
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
key: 'deliver',
title: 'Deliver',
}, {
diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue
index 9b4a84d12f..b4d6490e09 100644
--- a/packages/frontend/src/pages/admin/relays.vue
+++ b/packages/frontend/src/pages/admin/relays.vue
@@ -24,14 +24,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-let relays: any[] = $ref([]);
+const relays = ref<any[]>([]);
async function addRelay() {
const { canceled, result: inbox } = await os.inputText({
@@ -67,20 +67,20 @@ function remove(inbox: string) {
function refresh() {
os.api('admin/relays/list').then((relayList: any) => {
- relays = relayList;
+ relays.value = relayList;
});
}
refresh();
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
asFullButton: true,
icon: 'ph-plus ph-bold ph-lg',
text: i18n.ts.addRelay,
handler: addRelay,
}]);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.relays,
diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue
index a982b41e71..b3c06454ae 100644
--- a/packages/frontend/src/pages/admin/roles.edit.vue
+++ b/packages/frontend/src/pages/admin/roles.edit.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
import { v4 as uuid } from 'uuid';
import XHeader from './_header_.vue';
import XEditor from './roles.editor.vue';
@@ -39,17 +39,17 @@ const props = defineProps<{
id?: string;
}>();
-let role = $ref(null);
-let data = $ref(null);
+const role = ref(null);
+const data = ref(null);
if (props.id) {
- role = await os.api('admin/roles/show', {
+ role.value = await os.api('admin/roles/show', {
roleId: props.id,
});
- data = role;
+ data.value = role.value;
} else {
- data = {
+ data.value = {
name: 'New Role',
description: '',
isAdministrator: false,
@@ -69,24 +69,24 @@ if (props.id) {
async function save() {
rolesCache.delete();
- if (role) {
+ if (role.value) {
os.apiWithDialog('admin/roles/update', {
- roleId: role.id,
- ...data,
+ roleId: role.value.id,
+ ...data.value,
});
- router.push('/admin/roles/' + role.id);
+ router.push('/admin/roles/' + role.value.id);
} else {
const created = await os.apiWithDialog('admin/roles/create', {
- ...data,
+ ...data.value,
});
router.push('/admin/roles/' + created.id);
}
}
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
-definePageMetadata(computed(() => role ? {
- title: i18n.ts._role.edit + ': ' + role.name,
+definePageMetadata(computed(() => role.value ? {
+ title: i18n.ts._role.edit + ': ' + role.value.name,
icon: 'ph-seal-check ph-bold ph-lg',
} : {
title: i18n.ts._role.new,
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index efdf1ff4f8..164510fd24 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -318,7 +318,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkRange>
</div>
</MkFolder>
-
+
<MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])">
<template #label>{{ i18n.ts._role._options.canSearchNotes }}</template>
<template #suffix>
@@ -571,13 +571,33 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkRange>
</div>
</MkFolder>
+
+ <MkFolder v-if="matchQuery([i18n.ts._role._options.avatarDecorationLimit, 'avatarDecorationLimit'])">
+ <template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template>
+ <template #suffix>
+ <span v-if="role.policies.avatarDecorationLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+ <span v-else>{{ role.policies.avatarDecorationLimit.value }}</span>
+ <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.avatarDecorationLimit)"></i></span>
+ </template>
+ <div class="_gaps">
+ <MkSwitch v-model="role.policies.avatarDecorationLimit.useDefault" :readonly="readonly">
+ <template #label>{{ i18n.ts._role.useBaseValue }}</template>
+ </MkSwitch>
+ <MkInput v-model="role.policies.avatarDecorationLimit.value" type="number" :min="0">
+ <template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template>
+ </MkInput>
+ <MkRange v-model="role.policies.avatarDecorationLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+ <template #label>{{ i18n.ts._role.priority }}</template>
+ </MkRange>
+ </div>
+ </MkFolder>
</div>
</FormSlot>
</div>
</template>
<script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, ref, computed } from 'vue';
import { throttle } from 'throttle-debounce';
import RolesEditorFormula from './RolesEditorFormula.vue';
import MkInput from '@/components/MkInput.vue';
@@ -589,7 +609,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
import MkRange from '@/components/MkRange.vue';
import FormSlot from '@/components/form/slot.vue';
import { i18n } from '@/i18n.js';
-import { ROLE_POLICIES } from '@/const';
+import { ROLE_POLICIES } from '@/const.js';
import { instance } from '@/instance.js';
import { deepClone } from '@/scripts/clone.js';
@@ -602,12 +622,12 @@ const props = defineProps<{
readonly?: boolean;
}>();
-let role = $ref(deepClone(props.modelValue));
+const role = ref(deepClone(props.modelValue));
// fill missing policy
for (const ROLE_POLICY of ROLE_POLICIES) {
- if (role.policies[ROLE_POLICY] == null) {
- role.policies[ROLE_POLICY] = {
+ if (role.value.policies[ROLE_POLICY] == null) {
+ role.value.policies[ROLE_POLICY] = {
useDefault: true,
priority: 0,
value: instance.policies[ROLE_POLICY],
@@ -615,15 +635,15 @@ for (const ROLE_POLICY of ROLE_POLICIES) {
}
}
-let rolePermission = $computed({
- get: () => role.isAdministrator ? 'administrator' : role.isModerator ? 'moderator' : 'normal',
+const rolePermission = computed({
+ get: () => role.value.isAdministrator ? 'administrator' : role.value.isModerator ? 'moderator' : 'normal',
set: (val) => {
- role.isAdministrator = val === 'administrator';
- role.isModerator = val === 'moderator';
+ role.value.isAdministrator = val === 'administrator';
+ role.value.isModerator = val === 'moderator';
},
});
-let q = $ref('');
+const q = ref('');
function getPriorityIcon(option) {
if (option.priority === 2) return 'ph-arrow-up ph-bold ph-lg';
@@ -632,32 +652,32 @@ function getPriorityIcon(option) {
}
function matchQuery(keywords: string[]): boolean {
- if (q.trim().length === 0) return true;
- return keywords.some(keyword => keyword.toLowerCase().includes(q.toLowerCase()));
+ if (q.value.trim().length === 0) return true;
+ return keywords.some(keyword => keyword.toLowerCase().includes(q.value.toLowerCase()));
}
const save = throttle(100, () => {
const data = {
- name: role.name,
- description: role.description,
- color: role.color === '' ? null : role.color,
- iconUrl: role.iconUrl === '' ? null : role.iconUrl,
- displayOrder: role.displayOrder,
- target: role.target,
- condFormula: role.condFormula,
- isAdministrator: role.isAdministrator,
- isModerator: role.isModerator,
- isPublic: role.isPublic,
- isExplorable: role.isExplorable,
- asBadge: role.asBadge,
- canEditMembersByModerator: role.canEditMembersByModerator,
- policies: role.policies,
+ name: role.value.name,
+ description: role.value.description,
+ color: role.value.color === '' ? null : role.value.color,
+ iconUrl: role.value.iconUrl === '' ? null : role.value.iconUrl,
+ displayOrder: role.value.displayOrder,
+ target: role.value.target,
+ condFormula: role.value.condFormula,
+ isAdministrator: role.value.isAdministrator,
+ isModerator: role.value.isModerator,
+ isPublic: role.value.isPublic,
+ isExplorable: role.value.isExplorable,
+ asBadge: role.value.asBadge,
+ canEditMembersByModerator: role.value.canEditMembersByModerator,
+ policies: role.value.policies,
};
emit('update:modelValue', data);
});
-watch($$(role), save, { deep: true });
+watch(role, save, { deep: true });
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index 953db11a1a..92818cc3de 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed, reactive } from 'vue';
+import { computed, reactive, ref } from 'vue';
import XHeader from './_header_.vue';
import XEditor from './roles.editor.vue';
import MkFolder from '@/components/MkFolder.vue';
@@ -73,7 +73,7 @@ import { useRouter } from '@/router.js';
import MkButton from '@/components/MkButton.vue';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import MkInfo from '@/components/MkInfo.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
import { infoImageUrl } from '@/instance.js';
const router = useRouter();
@@ -90,7 +90,7 @@ const usersPagination = {
})),
};
-let expandedItems = $ref([]);
+const expandedItems = ref([]);
const role = reactive(await os.api('admin/roles/show', {
roleId: props.id,
@@ -160,16 +160,16 @@ async function unassign(user, ev) {
}
async function toggleItem(item) {
- if (expandedItems.includes(item.id)) {
- expandedItems = expandedItems.filter(x => x !== item.id);
+ if (expandedItems.value.includes(item.id)) {
+ expandedItems.value = expandedItems.value.filter(x => x !== item.id);
} else {
- expandedItems.push(item.id);
+ expandedItems.value.push(item.id);
}
}
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
title: i18n.ts.role + ': ' + role.name,
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 7fedb87d41..07cce6d9a5 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -211,19 +211,26 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch>
</MkFolder>
+ <MkFolder v-if="matchQuery([i18n.ts._role._options.avatarDecorationLimit, 'avatarDecorationLimit'])">
+ <template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template>
+ <template #suffix>{{ policies.avatarDecorationLimit }}</template>
+ <MkInput v-model="policies.avatarDecorationLimit" type="number" :min="0">
+ </MkInput>
+ </MkFolder>
+
<MkButton primary rounded @click="updateBaseRole">{{ i18n.ts.save }}</MkButton>
</div>
</MkFolder>
<MkButton primary rounded @click="create"><i class="ph-plus ph-bold ph-lg"></i> {{ i18n.ts._role.new }}</MkButton>
<div class="_gaps_s">
<MkFoldableSection>
- <template #header>Manual roles</template>
+ <template #header>{{ i18n.ts._role.manualRoles }}</template>
<div class="_gaps_s">
<MkRolePreview v-for="role in roles.filter(x => x.target === 'manual')" :key="role.id" :role="role" :forModeration="true"/>
</div>
</MkFoldableSection>
<MkFoldableSection>
- <template #header>Conditional roles</template>
+ <template #header>{{ i18n.ts._role.conditionalRoles }}</template>
<div class="_gaps_s">
<MkRolePreview v-for="role in roles.filter(x => x.target === 'conditional')" :key="role.id" :role="role" :forModeration="true"/>
</div>
@@ -278,9 +285,9 @@ function create() {
router.push('/admin/roles/new');
}
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
title: i18n.ts.roles,
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index f02fa1024d..95f4d2b20c 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -30,6 +30,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="enableActiveEmailValidation" @update:modelValue="save">
<template #label>Enable</template>
</MkSwitch>
+ <MkSwitch v-model="enableVerifymailApi" @update:modelValue="save">
+ <template #label>Use Verifymail API</template>
+ </MkSwitch>
+ <MkInput v-model="verifymailAuthKey" @update:modelValue="save">
+ <template #prefix><i class="ti ti-key"></i></template>
+ <template #label>Verifymail API Auth Key</template>
+ </MkInput>
</div>
</MkFolder>
@@ -64,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
import XBotProtection from './bot-protection.vue';
import XHeader from './_header_.vue';
import MkFolder from '@/components/MkFolder.vue';
@@ -79,36 +86,42 @@ import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-let summalyProxy: string = $ref('');
-let enableHcaptcha: boolean = $ref(false);
-let enableRecaptcha: boolean = $ref(false);
-let enableTurnstile: boolean = $ref(false);
-let enableIpLogging: boolean = $ref(false);
-let enableActiveEmailValidation: boolean = $ref(false);
+const summalyProxy = ref<string>('');
+const enableHcaptcha = ref<boolean>(false);
+const enableRecaptcha = ref<boolean>(false);
+const enableTurnstile = ref<boolean>(false);
+const enableIpLogging = ref<boolean>(false);
+const enableActiveEmailValidation = ref<boolean>(false);
+const enableVerifymailApi = ref<boolean>(false);
+const verifymailAuthKey = ref<string | null>(null);
async function init() {
const meta = await os.api('admin/meta');
- summalyProxy = meta.summalyProxy;
- enableHcaptcha = meta.enableHcaptcha;
- enableRecaptcha = meta.enableRecaptcha;
- enableTurnstile = meta.enableTurnstile;
- enableIpLogging = meta.enableIpLogging;
- enableActiveEmailValidation = meta.enableActiveEmailValidation;
+ summalyProxy.value = meta.summalyProxy;
+ enableHcaptcha.value = meta.enableHcaptcha;
+ enableRecaptcha.value = meta.enableRecaptcha;
+ enableTurnstile.value = meta.enableTurnstile;
+ enableIpLogging.value = meta.enableIpLogging;
+ enableActiveEmailValidation.value = meta.enableActiveEmailValidation;
+ enableVerifymailApi.value = meta.enableVerifymailApi;
+ verifymailAuthKey.value = meta.verifymailAuthKey;
}
function save() {
os.apiWithDialog('admin/update-meta', {
- summalyProxy,
- enableIpLogging,
- enableActiveEmailValidation,
+ summalyProxy: summalyProxy.value,
+ enableIpLogging: enableIpLogging.value,
+ enableActiveEmailValidation: enableActiveEmailValidation.value,
+ enableVerifymailApi: enableVerifymailApi.value,
+ verifymailAuthKey: verifymailAuthKey.value,
}).then(() => {
fetchInstance();
});
}
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.security,
diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue
index 4302bde91d..6aecb43399 100644
--- a/packages/frontend/src/pages/admin/server-rules.vue
+++ b/packages/frontend/src/pages/admin/server-rules.vue
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
+import { defineAsyncComponent, ref, computed } from 'vue';
import XHeader from './_header_.vue';
import * as os from '@/os.js';
import { fetchInstance, instance } from '@/instance.js';
@@ -52,20 +52,20 @@ import MkInput from '@/components/MkInput.vue';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
-let serverRules: string[] = $ref(instance.serverRules);
+const serverRules = ref<string[]>(instance.serverRules);
const save = async () => {
await os.apiWithDialog('admin/update-meta', {
- serverRules,
+ serverRules: serverRules.value,
});
fetchInstance();
};
const remove = (index: number): void => {
- serverRules.splice(index, 1);
+ serverRules.value.splice(index, 1);
};
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.serverRules,
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index db117d5061..07d7acf11c 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -148,7 +148,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
import XHeader from './_header_.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue';
@@ -163,76 +163,76 @@ import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkButton from '@/components/MkButton.vue';
-let name: string | null = $ref(null);
-let shortName: string | null = $ref(null);
-let description: string | null = $ref(null);
-let maintainerName: string | null = $ref(null);
-let maintainerEmail: string | null = $ref(null);
-let impressumUrl: string | null = $ref(null);
-let pinnedUsers: string = $ref('');
-let cacheRemoteFiles: boolean = $ref(false);
-let cacheRemoteSensitiveFiles: boolean = $ref(false);
-let enableServiceWorker: boolean = $ref(false);
-let swPublicKey: any = $ref(null);
-let swPrivateKey: any = $ref(null);
-let enableFanoutTimeline: boolean = $ref(false);
-let enableFanoutTimelineDbFallback: boolean = $ref(false);
-let perLocalUserUserTimelineCacheMax: number = $ref(0);
-let perRemoteUserUserTimelineCacheMax: number = $ref(0);
-let perUserHomeTimelineCacheMax: number = $ref(0);
-let perUserListTimelineCacheMax: number = $ref(0);
-let notesPerOneAd: number = $ref(0);
+const name = ref<string | null>(null);
+const shortName = ref<string | null>(null);
+const description = ref<string | null>(null);
+const maintainerName = ref<string | null>(null);
+const maintainerEmail = ref<string | null>(null);
+const impressumUrl = ref<string | null>(null);
+const pinnedUsers = ref<string>('');
+const cacheRemoteFiles = ref<boolean>(false);
+const cacheRemoteSensitiveFiles = ref<boolean>(false);
+const enableServiceWorker = ref<boolean>(false);
+const swPublicKey = ref<any>(null);
+const swPrivateKey = ref<any>(null);
+const enableFanoutTimeline = ref<boolean>(false);
+const enableFanoutTimelineDbFallback = ref<boolean>(false);
+const perLocalUserUserTimelineCacheMax = ref<number>(0);
+const perRemoteUserUserTimelineCacheMax = ref<number>(0);
+const perUserHomeTimelineCacheMax = ref<number>(0);
+const perUserListTimelineCacheMax = ref<number>(0);
+const notesPerOneAd = ref<number>(0);
async function init(): Promise<void> {
const meta = await os.api('admin/meta');
- name = meta.name;
- shortName = meta.shortName;
- description = meta.description;
- maintainerName = meta.maintainerName;
- maintainerEmail = meta.maintainerEmail;
- impressumUrl = meta.impressumUrl;
- pinnedUsers = meta.pinnedUsers.join('\n');
- cacheRemoteFiles = meta.cacheRemoteFiles;
- cacheRemoteSensitiveFiles = meta.cacheRemoteSensitiveFiles;
- enableServiceWorker = meta.enableServiceWorker;
- swPublicKey = meta.swPublickey;
- swPrivateKey = meta.swPrivateKey;
- enableFanoutTimeline = meta.enableFanoutTimeline;
- enableFanoutTimelineDbFallback = meta.enableFanoutTimelineDbFallback;
- perLocalUserUserTimelineCacheMax = meta.perLocalUserUserTimelineCacheMax;
- perRemoteUserUserTimelineCacheMax = meta.perRemoteUserUserTimelineCacheMax;
- perUserHomeTimelineCacheMax = meta.perUserHomeTimelineCacheMax;
- perUserListTimelineCacheMax = meta.perUserListTimelineCacheMax;
- notesPerOneAd = meta.notesPerOneAd;
+ name.value = meta.name;
+ shortName.value = meta.shortName;
+ description.value = meta.description;
+ maintainerName.value = meta.maintainerName;
+ maintainerEmail.value = meta.maintainerEmail;
+ impressumUrl.value = meta.impressumUrl;
+ pinnedUsers.value = meta.pinnedUsers.join('\n');
+ cacheRemoteFiles.value = meta.cacheRemoteFiles;
+ cacheRemoteSensitiveFiles.value = meta.cacheRemoteSensitiveFiles;
+ enableServiceWorker.value = meta.enableServiceWorker;
+ swPublicKey.value = meta.swPublickey;
+ swPrivateKey.value = meta.swPrivateKey;
+ enableFanoutTimeline.value = meta.enableFanoutTimeline;
+ enableFanoutTimelineDbFallback.value = meta.enableFanoutTimelineDbFallback;
+ perLocalUserUserTimelineCacheMax.value = meta.perLocalUserUserTimelineCacheMax;
+ perRemoteUserUserTimelineCacheMax.value = meta.perRemoteUserUserTimelineCacheMax;
+ perUserHomeTimelineCacheMax.value = meta.perUserHomeTimelineCacheMax;
+ perUserListTimelineCacheMax.value = meta.perUserListTimelineCacheMax;
+ notesPerOneAd.value = meta.notesPerOneAd;
}
async function save(): void {
await os.apiWithDialog('admin/update-meta', {
- name,
- shortName: shortName === '' ? null : shortName,
- description,
- maintainerName,
- maintainerEmail,
- impressumUrl,
- pinnedUsers: pinnedUsers.split('\n'),
- cacheRemoteFiles,
- cacheRemoteSensitiveFiles,
- enableServiceWorker,
- swPublicKey,
- swPrivateKey,
- enableFanoutTimeline,
- enableFanoutTimelineDbFallback,
- perLocalUserUserTimelineCacheMax,
- perRemoteUserUserTimelineCacheMax,
- perUserHomeTimelineCacheMax,
- perUserListTimelineCacheMax,
- notesPerOneAd,
+ name: name.value,
+ shortName: shortName.value === '' ? null : shortName.value,
+ description: description.value,
+ maintainerName: maintainerName.value,
+ maintainerEmail: maintainerEmail.value,
+ impressumUrl: impressumUrl.value,
+ pinnedUsers: pinnedUsers.value.split('\n'),
+ cacheRemoteFiles: cacheRemoteFiles.value,
+ cacheRemoteSensitiveFiles: cacheRemoteSensitiveFiles.value,
+ enableServiceWorker: enableServiceWorker.value,
+ swPublicKey: swPublicKey.value,
+ swPrivateKey: swPrivateKey.value,
+ enableFanoutTimeline: enableFanoutTimeline.value,
+ enableFanoutTimelineDbFallback: enableFanoutTimelineDbFallback.value,
+ perLocalUserUserTimelineCacheMax: perLocalUserUserTimelineCacheMax.value,
+ perRemoteUserUserTimelineCacheMax: perRemoteUserUserTimelineCacheMax.value,
+ perUserHomeTimelineCacheMax: perUserHomeTimelineCacheMax.value,
+ perUserListTimelineCacheMax: perUserListTimelineCacheMax.value,
+ notesPerOneAd: notesPerOneAd.value,
});
fetchInstance();
}
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata({
title: i18n.ts.general,
diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue
index 7f809ed9cc..1bc4eb4089 100644
--- a/packages/frontend/src/pages/admin/users.vue
+++ b/packages/frontend/src/pages/admin/users.vue
@@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, shallowRef, ref } from 'vue';
import XHeader from './_header_.vue';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
@@ -70,22 +70,22 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import { dateString } from '@/filters/date.js';
-let paginationComponent = $shallowRef<InstanceType<typeof MkPagination>>();
+const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
-let sort = $ref('+createdAt');
-let state = $ref('all');
-let origin = $ref('local');
-let searchUsername = $ref('');
-let searchHost = $ref('');
+const sort = ref('+createdAt');
+const state = ref('all');
+const origin = ref('local');
+const searchUsername = ref('');
+const searchHost = ref('');
const pagination = {
endpoint: 'admin/show-users' as const,
limit: 10,
params: computed(() => ({
- sort: sort,
- state: state,
- origin: origin,
- username: searchUsername,
- hostname: searchHost,
+ sort: sort.value,
+ state: state.value,
+ origin: origin.value,
+ username: searchUsername.value,
+ hostname: searchHost.value,
})),
offsetMode: true,
};
@@ -112,7 +112,7 @@ async function addUser() {
username: username,
password: password,
}).then(res => {
- paginationComponent.reload();
+ paginationComponent.value.reload();
});
}
@@ -120,7 +120,7 @@ function show(user) {
os.pageWindow(`/admin/user/${user.id}`);
}
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
icon: 'ph-magnifying-glass ph-bold ph-lg',
text: i18n.ts.search,
handler: searchUser,
@@ -136,7 +136,7 @@ const headerActions = $computed(() => [{
handler: lookupUser,
}]);
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
definePageMetadata(computed(() => ({
title: i18n.ts.users,