diff options
| author | Marie <Marie@kaifa.ch> | 2023-12-23 02:09:23 +0100 |
|---|---|---|
| committer | Marie <Marie@kaifa.ch> | 2023-12-23 02:09:23 +0100 |
| commit | 5db583a3eb61d50de14d875ebf7ecef20490e313 (patch) | |
| tree | 783dd43d2ac660c32e745a4485d499e9ddc43324 /packages/frontend/src/pages/admin | |
| parent | add: Custom MOTDs (diff) | |
| parent | Update CHANGELOG.md (diff) | |
| download | sharkey-5db583a3eb61d50de14d875ebf7ecef20490e313.tar.gz sharkey-5db583a3eb61d50de14d875ebf7ecef20490e313.tar.bz2 sharkey-5db583a3eb61d50de14d875ebf7ecef20490e313.zip | |
merge: upstream
Diffstat (limited to 'packages/frontend/src/pages/admin')
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, |