diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-04-19 14:42:09 +0900 |
|---|---|---|
| committer | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-04-19 14:42:09 +0900 |
| commit | 388ef3de08215f7265bb53cc7bae17849a830c65 (patch) | |
| tree | 6020c7dcce9b90d689b90d0367aaad1dba96212c /packages/frontend/src/pages/admin | |
| parent | 🎨 (diff) | |
| download | misskey-388ef3de08215f7265bb53cc7bae17849a830c65.tar.gz misskey-388ef3de08215f7265bb53cc7bae17849a830c65.tar.bz2 misskey-388ef3de08215f7265bb53cc7bae17849a830c65.zip | |
🎨
Diffstat (limited to 'packages/frontend/src/pages/admin')
25 files changed, 872 insertions, 1242 deletions
diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue deleted file mode 100644 index 529d438c4a..0000000000 --- a/packages/frontend/src/pages/admin/_header_.vue +++ /dev/null @@ -1,296 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and misskey-project -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<div ref="el" class="fdidabkc" :style="{ background: bg }" @click="onClick"> - <template v-if="pageMetadata"> - <div class="titleContainer" @click="showTabsPopup"> - <i v-if="pageMetadata.icon" class="icon" :class="pageMetadata.icon"></i> - - <div class="title"> - <div class="title">{{ pageMetadata.title }}</div> - </div> - </div> - <div class="tabs"> - <button v-for="tab in tabs" :ref="(el) => tabRefs[tab.key] = el" v-tooltip.noDelay="tab.title" class="tab _button" :class="{ active: tab.key != null && tab.key === props.tab }" @mousedown="(ev) => onTabMousedown(tab, ev)" @click="(ev) => onTabClick(tab, ev)"> - <i v-if="tab.icon" class="icon" :class="tab.icon"></i> - <span v-if="!tab.iconOnly" class="title">{{ tab.title }}</span> - </button> - <div ref="tabHighlightEl" class="highlight"></div> - </div> - </template> - <div class="buttons right"> - <template v-if="actions"> - <template v-for="action in actions"> - <MkButton v-if="action.asFullButton" class="fullButton" primary :disabled="action.disabled" @click.stop="action.handler"><i :class="action.icon" style="margin-right: 6px;"></i>{{ action.text }}</MkButton> - <button v-else v-tooltip.noDelay="action.text" class="_button button" :class="{ highlighted: action.highlighted }" :disabled="action.disabled" @click.stop="action.handler" @touchstart="preventDrag"><i :class="action.icon"></i></button> - </template> - </template> - </div> -</div> -</template> - -<script lang="ts" setup> -import { computed, onMounted, onUnmounted, ref, useTemplateRef, watch, nextTick, inject } from 'vue'; -import tinycolor from 'tinycolor2'; -import { scrollToTop } from '@@/js/scroll.js'; -import { popupMenu } from '@/os.js'; -import MkButton from '@/components/MkButton.vue'; -import { globalEvents } from '@/events.js'; -import { DI } from '@/di.js'; - -type Tab = { - key?: string | null; - title: string; - icon?: string; - iconOnly?: boolean; - onClick?: (ev: MouseEvent) => void; -}; - -const props = defineProps<{ - tabs?: Tab[]; - tab?: string; - actions?: { - text: string; - icon: string; - asFullButton?: boolean; - disabled?: boolean; - handler: (ev: MouseEvent) => void; - }[]; - thin?: boolean; -}>(); - -const emit = defineEmits<{ - (ev: 'update:tab', key: string); -}>(); - -const pageMetadata = inject(DI.pageMetadata, ref(null)); - -const el = useTemplateRef('el'); -const tabHighlightEl = useTemplateRef('tabHighlightEl'); -const tabRefs = {}; -const bg = ref<string | null>(null); -const height = ref(0); -const hasTabs = computed(() => { - return props.tabs && props.tabs.length > 0; -}); - -const showTabsPopup = (ev: MouseEvent) => { - if (!hasTabs.value) return; - ev.preventDefault(); - ev.stopPropagation(); - const menu = props.tabs.map(tab => ({ - text: tab.title, - icon: tab.icon, - active: tab.key != null && tab.key === props.tab, - action: (ev) => { - onTabClick(tab, ev); - }, - })); - popupMenu(menu, ev.currentTarget ?? ev.target); -}; - -const preventDrag = (ev: TouchEvent) => { - ev.stopPropagation(); -}; - -const onClick = () => { - scrollToTop(el.value, { behavior: 'smooth' }); -}; - -function onTabMousedown(tab: Tab, ev: MouseEvent): void { - // ユーザビリティの観点からmousedown時にはonClickは呼ばない - if (tab.key) { - emit('update:tab', tab.key); - } -} - -function onTabClick(tab: Tab, ev: MouseEvent): void { - if (tab.onClick) { - ev.preventDefault(); - ev.stopPropagation(); - tab.onClick(ev); - } - if (tab.key) { - emit('update:tab', tab.key); - } -} - -const calcBg = () => { - const rawBg = pageMetadata.value.bg ?? 'var(--MI_THEME-bg)'; - const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(window.document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); - tinyBg.setAlpha(0.85); - bg.value = tinyBg.toRgbString(); -}; - -onMounted(() => { - calcBg(); - globalEvents.on('themeChanging', calcBg); - - watch(() => [props.tab, props.tabs], () => { - nextTick(() => { - const tabEl = tabRefs[props.tab]; - 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.value.style.width = rect.width + 'px'; - tabHighlightEl.value.style.left = (rect.left - parentRect.left) + 'px'; - } - }); - }, { - immediate: true, - }); -}); - -onUnmounted(() => { - globalEvents.off('themeChanging', calcBg); -}); -</script> - -<style lang="scss" scoped> -.fdidabkc { - --height: 60px; - display: flex; - width: 100%; - -webkit-backdrop-filter: var(--MI-blur, blur(15px)); - backdrop-filter: var(--MI-blur, blur(15px)); - - > .buttons { - --margin: 8px; - display: flex; - align-items: center; - height: var(--height); - margin: 0 var(--margin); - - &.right { - margin-left: auto; - } - - &:empty { - width: var(--height); - } - - > .button { - display: flex; - align-items: center; - justify-content: center; - height: calc(var(--height) - (var(--margin) * 2)); - width: calc(var(--height) - (var(--margin) * 2)); - box-sizing: border-box; - position: relative; - border-radius: 5px; - - &:hover { - background: rgba(0, 0, 0, 0.05); - } - - &.highlighted { - color: var(--MI_THEME-accent); - } - } - - > .fullButton { - & + .fullButton { - margin-left: 12px; - } - } - } - - > .titleContainer { - display: flex; - align-items: center; - max-width: 400px; - overflow: auto; - white-space: nowrap; - text-align: left; - font-weight: bold; - flex-shrink: 0; - margin-left: 24px; - - > .avatar { - $size: 32px; - display: inline-block; - width: $size; - height: $size; - vertical-align: bottom; - margin: 0 8px; - pointer-events: none; - } - - > .icon { - margin-right: 8px; - width: 16px; - text-align: center; - } - - > .title { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - line-height: 1.1; - - > .subtitle { - opacity: 0.6; - font-size: 0.8em; - font-weight: normal; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - - &.activeTab { - text-align: center; - - > .chevron { - display: inline-block; - margin-left: 6px; - } - } - } - } - } - - > .tabs { - position: relative; - margin-left: 16px; - font-size: 0.8em; - overflow: auto; - white-space: nowrap; - - > .tab { - display: inline-block; - position: relative; - padding: 0 10px; - height: 100%; - font-weight: normal; - opacity: 0.7; - - &:hover { - opacity: 1; - } - - &.active { - opacity: 1; - } - - > .icon + .title { - margin-left: 8px; - } - } - - > .highlight { - position: absolute; - bottom: 0; - height: 3px; - background: var(--MI_THEME-accent); - border-radius: 999px; - transition: all 0.2s ease; - pointer-events: none; - } - } -} -</style> diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue index ee87fae606..a569ab7c33 100644 --- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue +++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue @@ -4,11 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header> - <XHeader :actions="headerActions" :tabs="headerTabs"/> - </template> - +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="900"> <div :class="$style.root" class="_gaps_m"> <div :class="$style.addButton"> @@ -41,14 +37,13 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script setup lang="ts"> import { entities } from 'misskey-js'; import { computed, defineAsyncComponent, onMounted, ref } from 'vue'; import XRecipient from './notification-recipient.item.vue'; -import XHeader from '@/pages/admin/_header_.vue'; import { misskeyApi } from '@/utility/misskey-api.js'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue index 08e06ce4b4..2335cc3db7 100644 --- a/packages/frontend/src/pages/admin/abuses.vue +++ b/packages/frontend/src/pages/admin/abuses.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="900"> <div :class="$style.root" class="_gaps"> <div :class="$style.subMenus" class="_gaps"> @@ -55,12 +54,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkPagination> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, useTemplateRef, ref } from 'vue'; -import XHeader from './_header_.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkPagination from '@/components/MkPagination.vue'; import XAbuseReport from '@/components/MkAbuseReport.vue'; diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index ebc3d23296..aa8ba2f7c3 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -4,10 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header> - <XHeader :actions="headerActions" :tabs="headerTabs"/> - </template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="900"> <MkSelect v-model="filterType" :class="$style.input" @update:modelValue="filterItems"> <template #label>{{ i18n.ts.state }}</template> @@ -81,13 +78,12 @@ SPDX-License-Identifier: AGPL-3.0-only </MkButton> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; -import XHeader from './_header_.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue index f6b331455f..ea7f0cc73d 100644 --- a/packages/frontend/src/pages/admin/announcements.vue +++ b/packages/frontend/src/pages/admin/announcements.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="900"> <div class="_gaps"> <MkInfo>{{ i18n.ts._announcement.shouldNotBeUsedToPresentPermanentInfo }}</MkInfo> @@ -81,12 +80,11 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed, watch } from 'vue'; -import XHeader from './_header_.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue index 0ac45914e8..2674879f90 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -4,109 +4,106 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> - <FormSuspense :p="init"> - <div class="_gaps_m"> - <MkInput v-model="iconUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts._serverSettings.iconUrl }}</template> - </MkInput> +<PageWithHeader :tabs="headerTabs"> + <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <FormSuspense :p="init"> + <div class="_gaps_m"> + <MkInput v-model="iconUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts._serverSettings.iconUrl }}</template> + </MkInput> - <MkInput v-model="app192IconUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</template> - <template #caption> - <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> - <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> - <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> - <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div> - </template> - </MkInput> + <MkInput v-model="app192IconUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</template> + <template #caption> + <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> + <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> + <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> + <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div> + </template> + </MkInput> - <MkInput v-model="app512IconUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</template> - <template #caption> - <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> - <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> - <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> - <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div> - </template> - </MkInput> + <MkInput v-model="app512IconUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</template> + <template #caption> + <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> + <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> + <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> + <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div> + </template> + </MkInput> - <MkInput v-model="bannerUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.bannerUrl }}</template> - </MkInput> + <MkInput v-model="bannerUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.bannerUrl }}</template> + </MkInput> - <MkInput v-model="backgroundImageUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.backgroundImageUrl }}</template> - </MkInput> + <MkInput v-model="backgroundImageUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.backgroundImageUrl }}</template> + </MkInput> - <MkInput v-model="notFoundImageUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.notFoundDescription }}</template> - </MkInput> + <MkInput v-model="notFoundImageUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.notFoundDescription }}</template> + </MkInput> - <MkInput v-model="infoImageUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.nothing }}</template> - </MkInput> + <MkInput v-model="infoImageUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.nothing }}</template> + </MkInput> - <MkInput v-model="serverErrorImageUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.somethingHappened }}</template> - </MkInput> + <MkInput v-model="serverErrorImageUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.somethingHappened }}</template> + </MkInput> - <MkColorInput v-model="themeColor"> - <template #label>{{ i18n.ts.themeColor }}</template> - </MkColorInput> + <MkColorInput v-model="themeColor"> + <template #label>{{ i18n.ts.themeColor }}</template> + </MkColorInput> - <MkTextarea v-model="defaultLightTheme"> - <template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template> - <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> - </MkTextarea> + <MkTextarea v-model="defaultLightTheme"> + <template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template> + <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> + </MkTextarea> - <MkTextarea v-model="defaultDarkTheme"> - <template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template> - <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> - </MkTextarea> + <MkTextarea v-model="defaultDarkTheme"> + <template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template> + <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> + </MkTextarea> - <MkInput v-model="repositoryUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.repositoryUrl }}</template> - </MkInput> + <MkInput v-model="repositoryUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.repositoryUrl }}</template> + </MkInput> - <MkInput v-model="feedbackUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.feedbackUrl }}</template> - </MkInput> + <MkInput v-model="feedbackUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.feedbackUrl }}</template> + </MkInput> - <MkTextarea v-model="manifestJsonOverride"> - <template #label>{{ i18n.ts._serverSettings.manifestJsonOverride }}</template> - </MkTextarea> - </div> - </FormSuspense> - </MkSpacer> - <template #footer> - <div :class="$style.footer"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16"> - <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> - </MkSpacer> + <MkTextarea v-model="manifestJsonOverride"> + <template #label>{{ i18n.ts._serverSettings.manifestJsonOverride }}</template> + </MkTextarea> </div> - </template> - </MkStickyContainer> -</div> + </FormSuspense> + </MkSpacer> + <template #footer> + <div :class="$style.footer"> + <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16"> + <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> + </MkSpacer> + </div> + </template> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; import JSON5 from 'json5'; -import XHeader from './_header_.vue'; +import { host } from '@@/js/config.js'; import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import FormSuspense from '@/components/form/suspense.vue'; @@ -117,7 +114,6 @@ import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import MkButton from '@/components/MkButton.vue'; import MkColorInput from '@/components/MkColorInput.vue'; -import { host } from '@@/js/config.js'; const iconUrl = ref<string | null>(null); const app192IconUrl = ref<string | null>(null); diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue index ab584ba9da..9f73808493 100644 --- a/packages/frontend/src/pages/admin/email-settings.vue +++ b/packages/frontend/src/pages/admin/email-settings.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> +<PageWithHeader :tabs="headerTabs"> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <FormSuspense :p="init"> <div class="_gaps_m"> @@ -60,12 +59,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSpacer> </div> </template> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; -import XHeader from './_header_.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; import FormInfo from '@/components/MkInfo.vue'; diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue index a6557114dc..cacf6273f9 100644 --- a/packages/frontend/src/pages/admin/external-services.vue +++ b/packages/frontend/src/pages/admin/external-services.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <FormSuspense :p="init"> <div class="_gaps_m"> @@ -38,12 +37,11 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </FormSuspense> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> 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'; diff --git a/packages/frontend/src/pages/admin/federation-job-queue.vue b/packages/frontend/src/pages/admin/federation-job-queue.vue index df976ba999..77e460d0eb 100644 --- a/packages/frontend/src/pages/admin/federation-job-queue.vue +++ b/packages/frontend/src/pages/admin/federation-job-queue.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="800"> <XQueue v-if="tab === 'deliver'" domain="deliver"/> <XQueue v-else-if="tab === 'inbox'" domain="inbox"/> @@ -15,13 +14,12 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton danger @click="clear"><i class="ti ti-trash"></i> {{ i18n.ts.clearQueue }}</MkButton> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; import XQueue from './federation-job-queue.chart.vue'; -import XHeader from './_header_.vue'; import type { Ref } from 'vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue index 7f6424225b..065a8021f1 100644 --- a/packages/frontend/src/pages/admin/federation.vue +++ b/packages/frontend/src/pages/admin/federation.vue @@ -4,63 +4,59 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :actions="headerActions"/></template> - <MkSpacer :contentMax="900"> - <div class="_gaps"> - <div> - <MkInput v-model="host" :debounce="true" class=""> - <template #prefix><i class="ti ti-search"></i></template> - <template #label>{{ i18n.ts.host }}</template> - </MkInput> - <FormSplit style="margin-top: var(--MI-margin);"> - <MkSelect v-model="state"> - <template #label>{{ i18n.ts.state }}</template> - <option value="all">{{ i18n.ts.all }}</option> - <option value="federating">{{ i18n.ts.federating }}</option> - <option value="subscribing">{{ i18n.ts.subscribing }}</option> - <option value="publishing">{{ i18n.ts.publishing }}</option> - <option value="suspended">{{ i18n.ts.suspended }}</option> - <option value="blocked">{{ i18n.ts.blocked }}</option> - <option value="silenced">{{ i18n.ts.silence }}</option> - <option value="notResponding">{{ i18n.ts.notResponding }}</option> - </MkSelect> - <MkSelect v-model="sort"> - <template #label>{{ i18n.ts.sort }}</template> - <option value="+pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+notes">{{ i18n.ts.notes }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-notes">{{ i18n.ts.notes }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+users">{{ i18n.ts.users }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-users">{{ i18n.ts.users }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+following">{{ i18n.ts.following }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-following">{{ i18n.ts.following }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+followers">{{ i18n.ts.followers }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-followers">{{ i18n.ts.followers }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+firstRetrievedAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-firstRetrievedAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.ascendingOrder }})</option> - </MkSelect> - </FormSplit> - </div> - - <MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination"> - <div :class="$style.instances"> - <MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Status: ${getStatus(instance)}`" :class="$style.instance" :to="`/instance-info/${instance.host}`"> - <MkInstanceCardMini :instance="instance"/> - </MkA> - </div> - </MkPagination> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> + <MkSpacer :contentMax="900"> + <div class="_gaps"> + <div> + <MkInput v-model="host" :debounce="true" class=""> + <template #prefix><i class="ti ti-search"></i></template> + <template #label>{{ i18n.ts.host }}</template> + </MkInput> + <FormSplit style="margin-top: var(--MI-margin);"> + <MkSelect v-model="state"> + <template #label>{{ i18n.ts.state }}</template> + <option value="all">{{ i18n.ts.all }}</option> + <option value="federating">{{ i18n.ts.federating }}</option> + <option value="subscribing">{{ i18n.ts.subscribing }}</option> + <option value="publishing">{{ i18n.ts.publishing }}</option> + <option value="suspended">{{ i18n.ts.suspended }}</option> + <option value="blocked">{{ i18n.ts.blocked }}</option> + <option value="silenced">{{ i18n.ts.silence }}</option> + <option value="notResponding">{{ i18n.ts.notResponding }}</option> + </MkSelect> + <MkSelect v-model="sort"> + <template #label>{{ i18n.ts.sort }}</template> + <option value="+pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+notes">{{ i18n.ts.notes }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-notes">{{ i18n.ts.notes }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+users">{{ i18n.ts.users }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-users">{{ i18n.ts.users }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+following">{{ i18n.ts.following }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-following">{{ i18n.ts.following }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+followers">{{ i18n.ts.followers }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-followers">{{ i18n.ts.followers }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+firstRetrievedAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-firstRetrievedAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.ascendingOrder }})</option> + </MkSelect> + </FormSplit> </div> - </MkSpacer> - </MkStickyContainer> -</div> + + <MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination"> + <div :class="$style.instances"> + <MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Status: ${getStatus(instance)}`" :class="$style.instance" :to="`/instance-info/${instance.host}`"> + <MkInstanceCardMini :instance="instance"/> + </MkA> + </div> + </MkPagination> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import * as Misskey from 'misskey-js'; import { computed, ref } from 'vue'; -import XHeader from './_header_.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkPagination from '@/components/MkPagination.vue'; diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue index e15724c2a7..12c633bf7f 100644 --- a/packages/frontend/src/pages/admin/files.vue +++ b/packages/frontend/src/pages/admin/files.vue @@ -4,40 +4,36 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :actions="headerActions"/></template> - <MkSpacer :contentMax="900"> - <div class="_gaps"> - <div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;"> - <MkSelect v-model="origin" style="margin: 0; flex: 1;"> - <template #label>{{ i18n.ts.instance }}</template> - <option value="combined">{{ i18n.ts.all }}</option> - <option value="local">{{ i18n.ts.local }}</option> - <option value="remote">{{ i18n.ts.remote }}</option> - </MkSelect> - <MkInput v-model="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="pagination.params.origin === 'local'"> - <template #label>{{ i18n.ts.host }}</template> - </MkInput> - </div> - <div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;"> - <MkInput v-model="userId" :debounce="true" type="search" style="margin: 0; flex: 1;"> - <template #label>User ID</template> - </MkInput> - <MkInput v-model="type" :debounce="true" type="search" style="margin: 0; flex: 1;"> - <template #label>MIME type</template> - </MkInput> - </div> - <MkFileListForAdmin :pagination="pagination" :viewMode="viewMode"/> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> + <MkSpacer :contentMax="900"> + <div class="_gaps"> + <div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;"> + <MkSelect v-model="origin" style="margin: 0; flex: 1;"> + <template #label>{{ i18n.ts.instance }}</template> + <option value="combined">{{ i18n.ts.all }}</option> + <option value="local">{{ i18n.ts.local }}</option> + <option value="remote">{{ i18n.ts.remote }}</option> + </MkSelect> + <MkInput v-model="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="pagination.params.origin === 'local'"> + <template #label>{{ i18n.ts.host }}</template> + </MkInput> </div> - </MkSpacer> - </MkStickyContainer> -</div> + <div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;"> + <MkInput v-model="userId" :debounce="true" type="search" style="margin: 0; flex: 1;"> + <template #label>User ID</template> + </MkInput> + <MkInput v-model="type" :debounce="true" type="search" style="margin: 0; flex: 1;"> + <template #label>MIME type</template> + </MkInput> + </div> + <MkFileListForAdmin :pagination="pagination" :viewMode="viewMode"/> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, ref } from 'vue'; -import XHeader from './_header_.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue'; diff --git a/packages/frontend/src/pages/admin/invites.vue b/packages/frontend/src/pages/admin/invites.vue index 6e6476b027..c990b85d93 100644 --- a/packages/frontend/src/pages/admin/invites.vue +++ b/packages/frontend/src/pages/admin/invites.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="800"> <div class="_gaps_m"> <MkFolder :expanded="false"> @@ -51,12 +50,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkPagination> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, ref, useTemplateRef } from 'vue'; -import XHeader from './_header_.vue'; import type { Paging } from '@/components/MkPagination.vue'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index 3507758b6e..983dd1b2ee 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -4,131 +4,127 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> - <FormSuspense :p="init"> - <div class="_gaps_m"> - <MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration"> - <template #label>{{ i18n.ts._serverSettings.openRegistration }}</template> - <template #caption> - <div>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</div> - <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._serverSettings.openRegistrationWarning }}</div> - </template> - </MkSwitch> +<PageWithHeader :tabs="headerTabs"> + <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <FormSuspense :p="init"> + <div class="_gaps_m"> + <MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration"> + <template #label>{{ i18n.ts._serverSettings.openRegistration }}</template> + <template #caption> + <div>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</div> + <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._serverSettings.openRegistrationWarning }}</div> + </template> + </MkSwitch> - <MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup"> - <template #label>{{ i18n.ts.emailRequiredForSignup }}</template> - </MkSwitch> + <MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup"> + <template #label>{{ i18n.ts.emailRequiredForSignup }}</template> + </MkSwitch> - <FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink> + <FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink> - <MkFolder> - <template #icon><i class="ti ti-lock-star"></i></template> - <template #label>{{ i18n.ts.preservedUsernames }}</template> + <MkFolder> + <template #icon><i class="ti ti-lock-star"></i></template> + <template #label>{{ i18n.ts.preservedUsernames }}</template> - <div class="_gaps"> - <MkTextarea v-model="preservedUsernames"> - <template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_preservedUsernames">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="preservedUsernames"> + <template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_preservedUsernames">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-message-exclamation"></i></template> - <template #label>{{ i18n.ts.sensitiveWords }}</template> + <MkFolder> + <template #icon><i class="ti ti-message-exclamation"></i></template> + <template #label>{{ i18n.ts.sensitiveWords }}</template> - <div class="_gaps"> - <MkTextarea v-model="sensitiveWords"> - <template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template> - </MkTextarea> - <MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="sensitiveWords"> + <template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template> + </MkTextarea> + <MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-message-x"></i></template> - <template #label>{{ i18n.ts.prohibitedWords }}</template> + <MkFolder> + <template #icon><i class="ti ti-message-x"></i></template> + <template #label>{{ i18n.ts.prohibitedWords }}</template> - <div class="_gaps"> - <MkTextarea v-model="prohibitedWords"> - <template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> - </MkTextarea> - <MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="prohibitedWords"> + <template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> + </MkTextarea> + <MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-user-x"></i></template> - <template #label>{{ i18n.ts.prohibitedWordsForNameOfUser }}</template> + <MkFolder> + <template #icon><i class="ti ti-user-x"></i></template> + <template #label>{{ i18n.ts.prohibitedWordsForNameOfUser }}</template> - <div class="_gaps"> - <MkTextarea v-model="prohibitedWordsForNameOfUser"> - <template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> - </MkTextarea> - <MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="prohibitedWordsForNameOfUser"> + <template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> + </MkTextarea> + <MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.hiddenTags }}</template> + <MkFolder> + <template #icon><i class="ti ti-eye-off"></i></template> + <template #label>{{ i18n.ts.hiddenTags }}</template> - <div class="_gaps"> - <MkTextarea v-model="hiddenTags"> - <template #caption>{{ i18n.ts.hiddenTagsDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_hiddenTags">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="hiddenTags"> + <template #caption>{{ i18n.ts.hiddenTagsDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_hiddenTags">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.silencedInstances }}</template> + <MkFolder> + <template #icon><i class="ti ti-eye-off"></i></template> + <template #label>{{ i18n.ts.silencedInstances }}</template> - <div class="_gaps"> - <MkTextarea v-model="silencedHosts"> - <template #caption>{{ i18n.ts.silencedInstancesDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_silencedHosts">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="silencedHosts"> + <template #caption>{{ i18n.ts.silencedInstancesDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_silencedHosts">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.mediaSilencedInstances }}</template> + <MkFolder> + <template #icon><i class="ti ti-eye-off"></i></template> + <template #label>{{ i18n.ts.mediaSilencedInstances }}</template> - <div class="_gaps"> - <MkTextarea v-model="mediaSilencedHosts"> - <template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_mediaSilencedHosts">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="mediaSilencedHosts"> + <template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_mediaSilencedHosts">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-ban"></i></template> - <template #label>{{ i18n.ts.blockedInstances }}</template> + <MkFolder> + <template #icon><i class="ti ti-ban"></i></template> + <template #label>{{ i18n.ts.blockedInstances }}</template> - <div class="_gaps"> - <MkTextarea v-model="blockedHosts"> - <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_blockedHosts">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> - </div> - </FormSuspense> - </MkSpacer> - </MkStickyContainer> -</div> + <div class="_gaps"> + <MkTextarea v-model="blockedHosts"> + <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_blockedHosts">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> + </div> + </FormSuspense> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> 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'; diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue index 89f59bcaab..4623e540f2 100644 --- a/packages/frontend/src/pages/admin/modlog.vue +++ b/packages/frontend/src/pages/admin/modlog.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="900"> <div class="_gaps"> <div style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;"> @@ -35,13 +34,12 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary rounded style="margin: 0 auto;" @click="fetchMore">{{ i18n.ts.loadMore }}</MkButton> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, useTemplateRef, ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; -import XHeader from './_header_.vue'; import XModLog from './modlog.ModLog.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkInput from '@/components/MkInput.vue'; diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue index da96eb4881..36f4392142 100644 --- a/packages/frontend/src/pages/admin/object-storage.vue +++ b/packages/frontend/src/pages/admin/object-storage.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> +<PageWithHeader :tabs="headerTabs"> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <FormSuspense :p="init"> <div class="_gaps_m"> @@ -79,12 +78,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSpacer> </div> </template> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; -import XHeader from './_header_.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; import FormSuspense from '@/components/form/suspense.vue'; diff --git a/packages/frontend/src/pages/admin/performance.vue b/packages/frontend/src/pages/admin/performance.vue index 6bb0918fea..075db4ebef 100644 --- a/packages/frontend/src/pages/admin/performance.vue +++ b/packages/frontend/src/pages/admin/performance.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <div class="_gaps"> <div class="_panel" style="padding: 16px;"> @@ -104,12 +103,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; -import XHeader from './_header_.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { fetchInstance } from '@/instance.js'; diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue index a6280e7075..7803edc360 100644 --- a/packages/frontend/src/pages/admin/relays.vue +++ b/packages/frontend/src/pages/admin/relays.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="800"> <div class="_gaps"> <div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;"> @@ -20,13 +19,12 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; -import XHeader from './_header_.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue index 7741064685..e762e3c5b1 100644 --- a/packages/frontend/src/pages/admin/roles.edit.vue +++ b/packages/frontend/src/pages/admin/roles.edit.vue @@ -4,28 +4,25 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> - <MkSpacer :contentMax="600" :marginMin="16" :marginMax="32"> - <XEditor v-if="data" v-model="data"/> - </MkSpacer> - <template #footer> - <div :class="$style.footer"> - <MkSpacer :contentMax="600" :marginMin="16" :marginMax="16"> - <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> - </MkSpacer> - </div> - </template> +<PageWithHeader :tabs="headerTabs"> + <MkSpacer :contentMax="600" :marginMin="16" :marginMax="32"> + <XEditor v-if="data" v-model="data"/> + </MkSpacer> + <template #footer> + <div :class="$style.footer"> + <MkSpacer :contentMax="600" :marginMin="16" :marginMax="16"> + <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> + </MkSpacer> + </div> + </template> </MkStickyContainer> -</div> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { v4 as uuid } from 'uuid'; -import XHeader from './_header_.vue'; import XEditor from './roles.editor.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue index a978927471..0e825ddc86 100644 --- a/packages/frontend/src/pages/admin/roles.role.vue +++ b/packages/frontend/src/pages/admin/roles.role.vue @@ -4,66 +4,62 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700"> - <div class="_gaps"> - <div class="_buttons"> - <MkButton primary rounded @click="edit"><i class="ti ti-pencil"></i> {{ i18n.ts.edit }}</MkButton> - <MkButton danger rounded @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> - </div> - <MkFolder> - <template #icon><i class="ti ti-info-circle"></i></template> - <template #label>{{ i18n.ts.info }}</template> - <XEditor :modelValue="role" readonly/> - </MkFolder> - <MkFolder v-if="role.target === 'manual'" defaultOpen> - <template #icon><i class="ti ti-users"></i></template> - <template #label>{{ i18n.ts.users }}</template> - <template #suffix>{{ role.usersCount }}</template> - <div class="_gaps"> - <MkButton primary rounded @click="assign"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> + <MkSpacer :contentMax="700"> + <div class="_gaps"> + <div class="_buttons"> + <MkButton primary rounded @click="edit"><i class="ti ti-pencil"></i> {{ i18n.ts.edit }}</MkButton> + <MkButton danger rounded @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + </div> + <MkFolder> + <template #icon><i class="ti ti-info-circle"></i></template> + <template #label>{{ i18n.ts.info }}</template> + <XEditor :modelValue="role" readonly/> + </MkFolder> + <MkFolder v-if="role.target === 'manual'" defaultOpen> + <template #icon><i class="ti ti-users"></i></template> + <template #label>{{ i18n.ts.users }}</template> + <template #suffix>{{ role.usersCount }}</template> + <div class="_gaps"> + <MkButton primary rounded @click="assign"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton> - <MkPagination :pagination="usersPagination"> - <template #empty> - <div class="_fullinfo"> - <img :src="infoImageUrl" draggable="false"/> - <div>{{ i18n.ts.noUsers }}</div> - </div> - </template> + <MkPagination :pagination="usersPagination"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" draggable="false"/> + <div>{{ i18n.ts.noUsers }}</div> + </div> + </template> - <template #default="{ items }"> - <div class="_gaps_s"> - <div v-for="item in items" :key="item.user.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedItems.includes(item.id) }]"> - <div :class="$style.userItemMain"> - <MkA :class="$style.userItemMainBody" :to="`/admin/user/${item.user.id}`"> - <MkUserCardMini :user="item.user"/> - </MkA> - <button class="_button" :class="$style.userToggle" @click="toggleItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> - <button class="_button" :class="$style.unassign" @click="unassign(item.user, $event)"><i class="ti ti-x"></i></button> - </div> - <div v-if="expandedItems.includes(item.id)" :class="$style.userItemSub"> - <div>Assigned: <MkTime :time="item.createdAt" mode="detail"/></div> - <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> - <div v-else>Period: {{ i18n.ts.indefinitely }}</div> - </div> + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.user.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedItems.includes(item.id) }]"> + <div :class="$style.userItemMain"> + <MkA :class="$style.userItemMainBody" :to="`/admin/user/${item.user.id}`"> + <MkUserCardMini :user="item.user"/> + </MkA> + <button class="_button" :class="$style.userToggle" @click="toggleItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> + <button class="_button" :class="$style.unassign" @click="unassign(item.user, $event)"><i class="ti ti-x"></i></button> + </div> + <div v-if="expandedItems.includes(item.id)" :class="$style.userItemSub"> + <div>Assigned: <MkTime :time="item.createdAt" mode="detail"/></div> + <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> + <div v-else>Period: {{ i18n.ts.indefinitely }}</div> </div> </div> - </template> - </MkPagination> - </div> - </MkFolder> - <MkInfo v-else>{{ i18n.ts._role.isConditionalRole }}</MkInfo> - </div> - </MkSpacer> - </MkStickyContainer> -</div> + </div> + </template> + </MkPagination> + </div> + </MkFolder> + <MkInfo v-else>{{ i18n.ts._role.isConditionalRole }}</MkInfo> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, reactive, ref } from 'vue'; -import XHeader from './_header_.vue'; import XEditor from './roles.editor.vue'; import MkFolder from '@/components/MkFolder.vue'; import * as os from '@/os.js'; diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 7c950957cf..7b5d63df4c 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -4,296 +4,292 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700"> - <div class="_gaps"> - <MkFolder> - <template #label>{{ i18n.ts._role.baseRole }}</template> - <template #footer> - <MkButton primary rounded @click="updateBaseRole">{{ i18n.ts.save }}</MkButton> - </template> - <div class="_gaps_s"> - <MkInput v-model="baseRoleQ" type="search"> - <template #prefix><i class="ti ti-search"></i></template> - </MkInput> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> + <MkSpacer :contentMax="700"> + <div class="_gaps"> + <MkFolder> + <template #label>{{ i18n.ts._role.baseRole }}</template> + <template #footer> + <MkButton primary rounded @click="updateBaseRole">{{ i18n.ts.save }}</MkButton> + </template> + <div class="_gaps_s"> + <MkInput v-model="baseRoleQ" type="search"> + <template #prefix><i class="ti ti-search"></i></template> + </MkInput> - <MkFolder v-if="matchQuery([i18n.ts._role._options.rateLimitFactor, 'rateLimitFactor'])"> - <template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template> - <template #suffix>{{ Math.floor(policies.rateLimitFactor * 100) }}%</template> - <MkRange :modelValue="policies.rateLimitFactor * 100" :min="30" :max="300" :step="10" :textConverter="(v) => `${v}%`" @update:modelValue="v => policies.rateLimitFactor = (v / 100)"> - <template #caption>{{ i18n.ts._role._options.descriptionOfRateLimitFactor }}</template> - </MkRange> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.rateLimitFactor, 'rateLimitFactor'])"> + <template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template> + <template #suffix>{{ Math.floor(policies.rateLimitFactor * 100) }}%</template> + <MkRange :modelValue="policies.rateLimitFactor * 100" :min="30" :max="300" :step="10" :textConverter="(v) => `${v}%`" @update:modelValue="v => policies.rateLimitFactor = (v / 100)"> + <template #caption>{{ i18n.ts._role._options.descriptionOfRateLimitFactor }}</template> + </MkRange> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.gtlAvailable, 'gtlAvailable'])"> - <template #label>{{ i18n.ts._role._options.gtlAvailable }}</template> - <template #suffix>{{ policies.gtlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.gtlAvailable"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.gtlAvailable, 'gtlAvailable'])"> + <template #label>{{ i18n.ts._role._options.gtlAvailable }}</template> + <template #suffix>{{ policies.gtlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.gtlAvailable"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.ltlAvailable, 'ltlAvailable'])"> - <template #label>{{ i18n.ts._role._options.ltlAvailable }}</template> - <template #suffix>{{ policies.ltlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.ltlAvailable"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.ltlAvailable, 'ltlAvailable'])"> + <template #label>{{ i18n.ts._role._options.ltlAvailable }}</template> + <template #suffix>{{ policies.ltlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.ltlAvailable"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canPublicNote, 'canPublicNote'])"> - <template #label>{{ i18n.ts._role._options.canPublicNote }}</template> - <template #suffix>{{ policies.canPublicNote ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canPublicNote"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canPublicNote, 'canPublicNote'])"> + <template #label>{{ i18n.ts._role._options.canPublicNote }}</template> + <template #suffix>{{ policies.canPublicNote ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canPublicNote"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.chatAvailability, 'chatAvailability'])"> - <template #label>{{ i18n.ts._role._options.chatAvailability }}</template> - <template #suffix>{{ policies.chatAvailability === 'available' ? i18n.ts.yes : policies.chatAvailability === 'readonly' ? i18n.ts.readonly : i18n.ts.no }}</template> - <MkSelect v-model="policies.chatAvailability"> - <template #label>{{ i18n.ts.enable }}</template> - <option value="available">{{ i18n.ts.enabled }}</option> - <option value="readonly">{{ i18n.ts.readonly }}</option> - <option value="unavailable">{{ i18n.ts.disabled }}</option> - </MkSelect> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.chatAvailability, 'chatAvailability'])"> + <template #label>{{ i18n.ts._role._options.chatAvailability }}</template> + <template #suffix>{{ policies.chatAvailability === 'available' ? i18n.ts.yes : policies.chatAvailability === 'readonly' ? i18n.ts.readonly : i18n.ts.no }}</template> + <MkSelect v-model="policies.chatAvailability"> + <template #label>{{ i18n.ts.enable }}</template> + <option value="available">{{ i18n.ts.enabled }}</option> + <option value="readonly">{{ i18n.ts.readonly }}</option> + <option value="unavailable">{{ i18n.ts.disabled }}</option> + </MkSelect> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> - <template #label>{{ i18n.ts._role._options.mentionMax }}</template> - <template #suffix>{{ policies.mentionLimit }}</template> - <MkInput v-model="policies.mentionLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> + <template #label>{{ i18n.ts._role._options.mentionMax }}</template> + <template #suffix>{{ policies.mentionLimit }}</template> + <MkInput v-model="policies.mentionLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> - <template #label>{{ i18n.ts._role._options.canInvite }}</template> - <template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canInvite"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> + <template #label>{{ i18n.ts._role._options.canInvite }}</template> + <template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canInvite"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimit, 'inviteLimit'])"> - <template #label>{{ i18n.ts._role._options.inviteLimit }}</template> - <template #suffix>{{ policies.inviteLimit }}</template> - <MkInput v-model="policies.inviteLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimit, 'inviteLimit'])"> + <template #label>{{ i18n.ts._role._options.inviteLimit }}</template> + <template #suffix>{{ policies.inviteLimit }}</template> + <MkInput v-model="policies.inviteLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimitCycle, 'inviteLimitCycle'])"> - <template #label>{{ i18n.ts._role._options.inviteLimitCycle }}</template> - <template #suffix>{{ policies.inviteLimitCycle + i18n.ts._time.minute }}</template> - <MkInput v-model="policies.inviteLimitCycle" type="number"> - <template #suffix>{{ i18n.ts._time.minute }}</template> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimitCycle, 'inviteLimitCycle'])"> + <template #label>{{ i18n.ts._role._options.inviteLimitCycle }}</template> + <template #suffix>{{ policies.inviteLimitCycle + i18n.ts._time.minute }}</template> + <MkInput v-model="policies.inviteLimitCycle" type="number"> + <template #suffix>{{ i18n.ts._time.minute }}</template> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteExpirationTime, 'inviteExpirationTime'])"> - <template #label>{{ i18n.ts._role._options.inviteExpirationTime }}</template> - <template #suffix>{{ policies.inviteExpirationTime + i18n.ts._time.minute }}</template> - <MkInput v-model="policies.inviteExpirationTime" type="number"> - <template #suffix>{{ i18n.ts._time.minute }}</template> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteExpirationTime, 'inviteExpirationTime'])"> + <template #label>{{ i18n.ts._role._options.inviteExpirationTime }}</template> + <template #suffix>{{ policies.inviteExpirationTime + i18n.ts._time.minute }}</template> + <MkInput v-model="policies.inviteExpirationTime" type="number"> + <template #suffix>{{ i18n.ts._time.minute }}</template> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])"> - <template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template> - <template #suffix>{{ policies.canManageAvatarDecorations ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canManageAvatarDecorations"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])"> + <template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template> + <template #suffix>{{ policies.canManageAvatarDecorations ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canManageAvatarDecorations"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageCustomEmojis, 'canManageCustomEmojis'])"> - <template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template> - <template #suffix>{{ policies.canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canManageCustomEmojis"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageCustomEmojis, 'canManageCustomEmojis'])"> + <template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template> + <template #suffix>{{ policies.canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canManageCustomEmojis"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])"> - <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> - <template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canSearchNotes"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])"> + <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> + <template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canSearchNotes"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canSearchNotes'])"> - <template #label>{{ i18n.ts._role._options.canUseTranslator }}</template> - <template #suffix>{{ policies.canUseTranslator ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canUseTranslator"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canSearchNotes'])"> + <template #label>{{ i18n.ts._role._options.canUseTranslator }}</template> + <template #suffix>{{ policies.canUseTranslator ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canUseTranslator"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])"> - <template #label>{{ i18n.ts._role._options.driveCapacity }}</template> - <template #suffix>{{ policies.driveCapacityMb }}MB</template> - <MkInput v-model="policies.driveCapacityMb" type="number"> - <template #suffix>MB</template> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])"> + <template #label>{{ i18n.ts._role._options.driveCapacity }}</template> + <template #suffix>{{ policies.driveCapacityMb }}MB</template> + <MkInput v-model="policies.driveCapacityMb" type="number"> + <template #suffix>MB</template> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])"> - <template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template> - <template #suffix>{{ policies.alwaysMarkNsfw ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.alwaysMarkNsfw"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])"> + <template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template> + <template #suffix>{{ policies.alwaysMarkNsfw ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.alwaysMarkNsfw"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canUpdateBioMedia, 'canUpdateBioMedia'])"> - <template #label>{{ i18n.ts._role._options.canUpdateBioMedia }}</template> - <template #suffix>{{ policies.canUpdateBioMedia ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canUpdateBioMedia"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canUpdateBioMedia, 'canUpdateBioMedia'])"> + <template #label>{{ i18n.ts._role._options.canUpdateBioMedia }}</template> + <template #suffix>{{ policies.canUpdateBioMedia ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canUpdateBioMedia"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])"> - <template #label>{{ i18n.ts._role._options.pinMax }}</template> - <template #suffix>{{ policies.pinLimit }}</template> - <MkInput v-model="policies.pinLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])"> + <template #label>{{ i18n.ts._role._options.pinMax }}</template> + <template #suffix>{{ policies.pinLimit }}</template> + <MkInput v-model="policies.pinLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])"> - <template #label>{{ i18n.ts._role._options.antennaMax }}</template> - <template #suffix>{{ policies.antennaLimit }}</template> - <MkInput v-model="policies.antennaLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])"> + <template #label>{{ i18n.ts._role._options.antennaMax }}</template> + <template #suffix>{{ policies.antennaLimit }}</template> + <MkInput v-model="policies.antennaLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.wordMuteMax, 'wordMuteLimit'])"> - <template #label>{{ i18n.ts._role._options.wordMuteMax }}</template> - <template #suffix>{{ policies.wordMuteLimit }}</template> - <MkInput v-model="policies.wordMuteLimit" type="number"> - <template #suffix>chars</template> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.wordMuteMax, 'wordMuteLimit'])"> + <template #label>{{ i18n.ts._role._options.wordMuteMax }}</template> + <template #suffix>{{ policies.wordMuteLimit }}</template> + <MkInput v-model="policies.wordMuteLimit" type="number"> + <template #suffix>chars</template> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.webhookMax, 'webhookLimit'])"> - <template #label>{{ i18n.ts._role._options.webhookMax }}</template> - <template #suffix>{{ policies.webhookLimit }}</template> - <MkInput v-model="policies.webhookLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.webhookMax, 'webhookLimit'])"> + <template #label>{{ i18n.ts._role._options.webhookMax }}</template> + <template #suffix>{{ policies.webhookLimit }}</template> + <MkInput v-model="policies.webhookLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.clipMax, 'clipLimit'])"> - <template #label>{{ i18n.ts._role._options.clipMax }}</template> - <template #suffix>{{ policies.clipLimit }}</template> - <MkInput v-model="policies.clipLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.clipMax, 'clipLimit'])"> + <template #label>{{ i18n.ts._role._options.clipMax }}</template> + <template #suffix>{{ policies.clipLimit }}</template> + <MkInput v-model="policies.clipLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.noteEachClipsMax, 'noteEachClipsLimit'])"> - <template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template> - <template #suffix>{{ policies.noteEachClipsLimit }}</template> - <MkInput v-model="policies.noteEachClipsLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.noteEachClipsMax, 'noteEachClipsLimit'])"> + <template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template> + <template #suffix>{{ policies.noteEachClipsLimit }}</template> + <MkInput v-model="policies.noteEachClipsLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.userListMax, 'userListLimit'])"> - <template #label>{{ i18n.ts._role._options.userListMax }}</template> - <template #suffix>{{ policies.userListLimit }}</template> - <MkInput v-model="policies.userListLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.userListMax, 'userListLimit'])"> + <template #label>{{ i18n.ts._role._options.userListMax }}</template> + <template #suffix>{{ policies.userListLimit }}</template> + <MkInput v-model="policies.userListLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.userEachUserListsMax, 'userEachUserListsLimit'])"> - <template #label>{{ i18n.ts._role._options.userEachUserListsMax }}</template> - <template #suffix>{{ policies.userEachUserListsLimit }}</template> - <MkInput v-model="policies.userEachUserListsLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.userEachUserListsMax, 'userEachUserListsLimit'])"> + <template #label>{{ i18n.ts._role._options.userEachUserListsMax }}</template> + <template #suffix>{{ policies.userEachUserListsLimit }}</template> + <MkInput v-model="policies.userEachUserListsLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canHideAds, 'canHideAds'])"> - <template #label>{{ i18n.ts._role._options.canHideAds }}</template> - <template #suffix>{{ policies.canHideAds ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canHideAds"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canHideAds, 'canHideAds'])"> + <template #label>{{ i18n.ts._role._options.canHideAds }}</template> + <template #suffix>{{ policies.canHideAds ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canHideAds"> + <template #label>{{ i18n.ts.enable }}</template> + </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="avatarDecorationLimit" type="number" :min="0" :max="16" @update:modelValue="updateAvatarDecorationLimit"> - </MkInput> - </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="avatarDecorationLimit" type="number" :min="0" :max="16" @update:modelValue="updateAvatarDecorationLimit"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportAntennas, 'canImportAntennas'])"> - <template #label>{{ i18n.ts._role._options.canImportAntennas }}</template> - <template #suffix>{{ policies.canImportAntennas ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canImportAntennas"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportAntennas, 'canImportAntennas'])"> + <template #label>{{ i18n.ts._role._options.canImportAntennas }}</template> + <template #suffix>{{ policies.canImportAntennas ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canImportAntennas"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportBlocking, 'canImportBlocking'])"> - <template #label>{{ i18n.ts._role._options.canImportBlocking }}</template> - <template #suffix>{{ policies.canImportBlocking ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canImportBlocking"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportBlocking, 'canImportBlocking'])"> + <template #label>{{ i18n.ts._role._options.canImportBlocking }}</template> + <template #suffix>{{ policies.canImportBlocking ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canImportBlocking"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportFollowing, 'canImportFollowing'])"> - <template #label>{{ i18n.ts._role._options.canImportFollowing }}</template> - <template #suffix>{{ policies.canImportFollowing ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canImportFollowing"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportFollowing, 'canImportFollowing'])"> + <template #label>{{ i18n.ts._role._options.canImportFollowing }}</template> + <template #suffix>{{ policies.canImportFollowing ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canImportFollowing"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportMuting, 'canImportMuting'])"> - <template #label>{{ i18n.ts._role._options.canImportMuting }}</template> - <template #suffix>{{ policies.canImportMuting ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canImportMuting"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportMuting, 'canImportMuting'])"> + <template #label>{{ i18n.ts._role._options.canImportMuting }}</template> + <template #suffix>{{ policies.canImportMuting ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canImportMuting"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportUserLists, 'canImportUserList'])"> - <template #label>{{ i18n.ts._role._options.canImportUserLists }}</template> - <template #suffix>{{ policies.canImportUserLists ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canImportUserLists"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> - </div> - </MkFolder> - <MkButton primary rounded @click="create"><i class="ti ti-plus"></i> {{ i18n.ts._role.new }}</MkButton> - <div class="_gaps_s"> - <MkFoldableSection> - <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>{{ 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> - </MkFoldableSection> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canImportUserLists, 'canImportUserList'])"> + <template #label>{{ i18n.ts._role._options.canImportUserLists }}</template> + <template #suffix>{{ policies.canImportUserLists ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canImportUserLists"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> </div> + </MkFolder> + <MkButton primary rounded @click="create"><i class="ti ti-plus"></i> {{ i18n.ts._role.new }}</MkButton> + <div class="_gaps_s"> + <MkFoldableSection> + <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>{{ 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> + </MkFoldableSection> </div> - </MkSpacer> - </MkStickyContainer> -</div> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, reactive, ref } from 'vue'; import { ROLE_POLICIES } from '@@/js/const.js'; -import XHeader from './_header_.vue'; import MkInput from '@/components/MkInput.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkSwitch from '@/components/MkSwitch.vue'; diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index 13f57b8549..28bf88623b 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <div class="_gaps_m"> <XBotProtection/> @@ -117,13 +116,12 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; import XBotProtection from './bot-protection.vue'; -import XHeader from './_header_.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkRadios from '@/components/MkRadios.vue'; import MkSwitch from '@/components/MkSwitch.vue'; diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue index b8722d4112..766be762fb 100644 --- a/packages/frontend/src/pages/admin/server-rules.vue +++ b/packages/frontend/src/pages/admin/server-rules.vue @@ -4,45 +4,42 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> - <div class="_gaps_m"> - <div>{{ i18n.ts._serverRules.description }}</div> - <Sortable - v-model="serverRules" - class="_gaps_m" - :itemKey="(_, i) => i" - :animation="150" - :handle="'.' + $style.itemHandle" - @start="e => e.item.classList.add('active')" - @end="e => e.item.classList.remove('active')" - > - <template #item="{element,index}"> - <div :class="$style.item"> - <div :class="$style.itemHeader"> - <div :class="$style.itemNumber" v-text="String(index + 1)"/> - <span :class="$style.itemHandle"><i class="ti ti-menu"/></span> - <button class="_button" :class="$style.itemRemove" @click="remove(index)"><i class="ti ti-x"></i></button> - </div> - <MkInput v-model="serverRules[index]"/> +<PageWithHeader :tabs="headerTabs"> + <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_gaps_m"> + <div>{{ i18n.ts._serverRules.description }}</div> + <Sortable + v-model="serverRules" + class="_gaps_m" + :itemKey="(_, i) => i" + :animation="150" + :handle="'.' + $style.itemHandle" + @start="e => e.item.classList.add('active')" + @end="e => e.item.classList.remove('active')" + > + <template #item="{element,index}"> + <div :class="$style.item"> + <div :class="$style.itemHeader"> + <div :class="$style.itemNumber" v-text="String(index + 1)"/> + <span :class="$style.itemHandle"><i class="ti ti-menu"/></span> + <button class="_button" :class="$style.itemRemove" @click="remove(index)"><i class="ti ti-x"></i></button> </div> - </template> - </Sortable> - <div :class="$style.commands"> - <MkButton rounded @click="serverRules.push('')"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> - <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> - </div> + <MkInput v-model="serverRules[index]"/> + </div> + </template> + </Sortable> + <div :class="$style.commands"> + <MkButton rounded @click="serverRules.push('')"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> + <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> </div> - </MkSpacer> + </div> + </MkSpacer> </MkStickyContainer> -</div> +</PageWithHeader> </template> <script lang="ts" setup> import { defineAsyncComponent, ref, computed } from 'vue'; -import XHeader from './_header_.vue'; import * as os from '@/os.js'; import { fetchInstance, instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index 6362ebd446..efa98da048 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -4,262 +4,258 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> - <div class="_gaps_m"> - <MkFolder :defaultOpen="true"> - <template #icon><i class="ti ti-info-circle"></i></template> - <template #label>{{ i18n.ts.info }}</template> - <template v-if="infoForm.modified.value" #footer> - <MkFormFooter :form="infoForm"/> - </template> - - <div class="_gaps"> - <MkInput v-model="infoForm.state.name"> - <template #label>{{ i18n.ts.instanceName }}<span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkInput> +<PageWithHeader :tabs="headerTabs"> + <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_gaps_m"> + <MkFolder :defaultOpen="true"> + <template #icon><i class="ti ti-info-circle"></i></template> + <template #label>{{ i18n.ts.info }}</template> + <template v-if="infoForm.modified.value" #footer> + <MkFormFooter :form="infoForm"/> + </template> - <MkInput v-model="infoForm.state.shortName"> - <template #label>{{ i18n.ts._serverSettings.shortName }} ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._serverSettings.shortNameDescription }}</template> - </MkInput> - - <MkTextarea v-model="infoForm.state.description"> - <template #label>{{ i18n.ts.instanceDescription }}<span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkTextarea> + <div class="_gaps"> + <MkInput v-model="infoForm.state.name"> + <template #label>{{ i18n.ts.instanceName }}<span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkInput> - <FormSplit :minWidth="300"> - <MkInput v-model="infoForm.state.maintainerName"> - <template #label>{{ i18n.ts.maintainerName }}<span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkInput> + <MkInput v-model="infoForm.state.shortName"> + <template #label>{{ i18n.ts._serverSettings.shortName }} ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._serverSettings.shortNameDescription }}</template> + </MkInput> - <MkInput v-model="infoForm.state.maintainerEmail" type="email"> - <template #label>{{ i18n.ts.maintainerEmail }}<span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-mail"></i></template> - </MkInput> - </FormSplit> + <MkTextarea v-model="infoForm.state.description"> + <template #label>{{ i18n.ts.instanceDescription }}<span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkTextarea> - <MkInput v-model="infoForm.state.tosUrl" type="url"> - <template #label>{{ i18n.ts.tosUrl }}<span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-link"></i></template> + <FormSplit :minWidth="300"> + <MkInput v-model="infoForm.state.maintainerName"> + <template #label>{{ i18n.ts.maintainerName }}<span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template> </MkInput> - <MkInput v-model="infoForm.state.privacyPolicyUrl" type="url"> - <template #label>{{ i18n.ts.privacyPolicyUrl }}<span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-link"></i></template> + <MkInput v-model="infoForm.state.maintainerEmail" type="email"> + <template #label>{{ i18n.ts.maintainerEmail }}<span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-mail"></i></template> </MkInput> + </FormSplit> - <MkInput v-model="infoForm.state.inquiryUrl" type="url"> - <template #label>{{ i18n.ts._serverSettings.inquiryUrl }}<span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template> - <template #prefix><i class="ti ti-link"></i></template> - </MkInput> + <MkInput v-model="infoForm.state.tosUrl" type="url"> + <template #label>{{ i18n.ts.tosUrl }}<span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> - <MkInput v-model="infoForm.state.repositoryUrl" type="url"> - <template #label>{{ i18n.ts.repositoryUrl }}<span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.repositoryUrlDescription }}</template> - <template #prefix><i class="ti ti-link"></i></template> - </MkInput> + <MkInput v-model="infoForm.state.privacyPolicyUrl" type="url"> + <template #label>{{ i18n.ts.privacyPolicyUrl }}<span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> - <MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn> - {{ i18n.ts.repositoryUrlOrTarballRequired }} - </MkInfo> + <MkInput v-model="infoForm.state.inquiryUrl" type="url"> + <template #label>{{ i18n.ts._serverSettings.inquiryUrl }}<span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> - <MkInput v-model="infoForm.state.impressumUrl" type="url"> - <template #label>{{ i18n.ts.impressumUrl }}<span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.impressumDescription }}</template> - <template #prefix><i class="ti ti-link"></i></template> - </MkInput> - </div> - </MkFolder> + <MkInput v-model="infoForm.state.repositoryUrl" type="url"> + <template #label>{{ i18n.ts.repositoryUrl }}<span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.repositoryUrlDescription }}</template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> - <MkFolder> - <template #icon><i class="ti ti-user-star"></i></template> - <template #label>{{ i18n.ts.pinnedUsers }}</template> - <template v-if="pinnedUsersForm.modified.value" #footer> - <MkFormFooter :form="pinnedUsersForm"/> - </template> + <MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn> + {{ i18n.ts.repositoryUrlOrTarballRequired }} + </MkInfo> - <MkTextarea v-model="pinnedUsersForm.state.pinnedUsers"> - <template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.pinnedUsersDescription }}</template> - </MkTextarea> - </MkFolder> + <MkInput v-model="infoForm.state.impressumUrl" type="url"> + <template #label>{{ i18n.ts.impressumUrl }}<span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.impressumDescription }}</template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-cloud"></i></template> - <template #label>{{ i18n.ts.files }}</template> - <template v-if="filesForm.modified.value" #footer> - <MkFormFooter :form="filesForm"/> - </template> + <MkFolder> + <template #icon><i class="ti ti-user-star"></i></template> + <template #label>{{ i18n.ts.pinnedUsers }}</template> + <template v-if="pinnedUsersForm.modified.value" #footer> + <MkFormFooter :form="pinnedUsersForm"/> + </template> - <div class="_gaps"> - <MkSwitch v-model="filesForm.state.cacheRemoteFiles"> - <template #label>{{ i18n.ts.cacheRemoteFiles }}<span v-if="filesForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}{{ i18n.ts.youCanCleanRemoteFilesCache }}</template> - </MkSwitch> + <MkTextarea v-model="pinnedUsersForm.state.pinnedUsers"> + <template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.pinnedUsersDescription }}</template> + </MkTextarea> + </MkFolder> - <template v-if="filesForm.state.cacheRemoteFiles"> - <MkSwitch v-model="filesForm.state.cacheRemoteSensitiveFiles"> - <template #label>{{ i18n.ts.cacheRemoteSensitiveFiles }}<span v-if="filesForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</template> - </MkSwitch> - </template> - </div> - </MkFolder> + <MkFolder> + <template #icon><i class="ti ti-cloud"></i></template> + <template #label>{{ i18n.ts.files }}</template> + <template v-if="filesForm.modified.value" #footer> + <MkFormFooter :form="filesForm"/> + </template> - <MkFolder> - <template #icon><i class="ti ti-world-cog"></i></template> - <template #label>ServiceWorker</template> - <template v-if="serviceWorkerForm.modified.value" #footer> - <MkFormFooter :form="serviceWorkerForm"/> - </template> + <div class="_gaps"> + <MkSwitch v-model="filesForm.state.cacheRemoteFiles"> + <template #label>{{ i18n.ts.cacheRemoteFiles }}<span v-if="filesForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}{{ i18n.ts.youCanCleanRemoteFilesCache }}</template> + </MkSwitch> - <div class="_gaps"> - <MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker"> - <template #label>{{ i18n.ts.enableServiceworker }}<span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.serviceworkerInfo }}</template> + <template v-if="filesForm.state.cacheRemoteFiles"> + <MkSwitch v-model="filesForm.state.cacheRemoteSensitiveFiles"> + <template #label>{{ i18n.ts.cacheRemoteSensitiveFiles }}<span v-if="filesForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</template> </MkSwitch> + </template> + </div> + </MkFolder> - <template v-if="serviceWorkerForm.state.enableServiceWorker"> - <MkInput v-model="serviceWorkerForm.state.swPublicKey"> - <template #label>Public key<span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-key"></i></template> - </MkInput> + <MkFolder> + <template #icon><i class="ti ti-world-cog"></i></template> + <template #label>ServiceWorker</template> + <template v-if="serviceWorkerForm.modified.value" #footer> + <MkFormFooter :form="serviceWorkerForm"/> + </template> - <MkInput v-model="serviceWorkerForm.state.swPrivateKey"> - <template #label>Private key<span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-key"></i></template> - </MkInput> - </template> - </div> - </MkFolder> + <div class="_gaps"> + <MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker"> + <template #label>{{ i18n.ts.enableServiceworker }}<span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.serviceworkerInfo }}</template> + </MkSwitch> + + <template v-if="serviceWorkerForm.state.enableServiceWorker"> + <MkInput v-model="serviceWorkerForm.state.swPublicKey"> + <template #label>Public key<span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-key"></i></template> + </MkInput> - <MkFolder> - <template #icon><i class="ti ti-ad"></i></template> - <template #label>{{ i18n.ts._ad.adsSettings }}</template> - <template v-if="adForm.modified.value" #footer> - <MkFormFooter :form="adForm"/> + <MkInput v-model="serviceWorkerForm.state.swPrivateKey"> + <template #label>Private key<span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-key"></i></template> + </MkInput> </template> + </div> + </MkFolder> - <div class="_gaps"> - <div class="_gaps_s"> - <MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number"> - <template #label>{{ i18n.ts._ad.notesPerOneAd }}<span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template> - </MkInput> - <MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true"> - {{ i18n.ts._ad.adsTooClose }} - </MkInfo> - </div> + <MkFolder> + <template #icon><i class="ti ti-ad"></i></template> + <template #label>{{ i18n.ts._ad.adsSettings }}</template> + <template v-if="adForm.modified.value" #footer> + <MkFormFooter :form="adForm"/> + </template> + + <div class="_gaps"> + <div class="_gaps_s"> + <MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number"> + <template #label>{{ i18n.ts._ad.notesPerOneAd }}<span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template> + </MkInput> + <MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true"> + {{ i18n.ts._ad.adsTooClose }} + </MkInfo> </div> - </MkFolder> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-world-search"></i></template> - <template #label>{{ i18n.ts._urlPreviewSetting.title }}</template> - <template v-if="urlPreviewForm.modified.value" #footer> - <MkFormFooter :form="urlPreviewForm"/> - </template> + <MkFolder> + <template #icon><i class="ti ti-world-search"></i></template> + <template #label>{{ i18n.ts._urlPreviewSetting.title }}</template> + <template v-if="urlPreviewForm.modified.value" #footer> + <MkFormFooter :form="urlPreviewForm"/> + </template> + + <div class="_gaps"> + <MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled"> + <template #label>{{ i18n.ts._urlPreviewSetting.enable }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkSwitch> - <div class="_gaps"> - <MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled"> - <template #label>{{ i18n.ts._urlPreviewSetting.enable }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template> + <template v-if="urlPreviewForm.state.urlPreviewEnabled"> + <MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength"> + <template #label>{{ i18n.ts._urlPreviewSetting.requireContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template> </MkSwitch> - <template v-if="urlPreviewForm.state.urlPreviewEnabled"> - <MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength"> - <template #label>{{ i18n.ts._urlPreviewSetting.requireContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template> - </MkSwitch> + <MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number"> + <template #label>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template> + </MkInput> - <MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number"> - <template #label>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template> - </MkInput> + <MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number"> + <template #label>{{ i18n.ts._urlPreviewSetting.timeout }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template> + </MkInput> - <MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number"> - <template #label>{{ i18n.ts._urlPreviewSetting.timeout }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template> - </MkInput> + <MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text"> + <template #label>{{ i18n.ts._urlPreviewSetting.userAgent }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template> + </MkInput> - <MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text"> - <template #label>{{ i18n.ts._urlPreviewSetting.userAgent }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template> + <div> + <MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text"> + <template #label>{{ i18n.ts._urlPreviewSetting.summaryProxy }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template> </MkInput> - <div> - <MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text"> - <template #label>{{ i18n.ts._urlPreviewSetting.summaryProxy }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template> - </MkInput> - - <div :class="$style.subCaption"> - {{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }} - <ul style="padding-left: 20px; margin: 4px 0"> - <li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li> - <li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li> - <li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li> - <li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li> - </ul> - </div> + <div :class="$style.subCaption"> + {{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }} + <ul style="padding-left: 20px; margin: 4px 0"> + <li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li> + <li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li> + <li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li> + <li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li> + </ul> </div> - </template> - </div> - </MkFolder> - - <MkFolder> - <template #icon><i class="ti ti-planet"></i></template> - <template #label>{{ i18n.ts.federation }}</template> - <template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template> - <template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template> - <template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template> - <template v-if="federationForm.modified.value" #footer> - <MkFormFooter :form="federationForm"/> + </div> </template> + </div> + </MkFolder> - <div class="_gaps"> - <MkRadios v-model="federationForm.state.federation"> - <template #label>{{ i18n.ts.behavior }}<span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template> - <option value="all">{{ i18n.ts.all }}</option> - <option value="specified">{{ i18n.ts.specifyHost }}</option> - <option value="none">{{ i18n.ts.none }}</option> - </MkRadios> + <MkFolder> + <template #icon><i class="ti ti-planet"></i></template> + <template #label>{{ i18n.ts.federation }}</template> + <template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template> + <template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template> + <template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template> + <template v-if="federationForm.modified.value" #footer> + <MkFormFooter :form="federationForm"/> + </template> - <MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts"> - <template #label>{{ i18n.ts.federationAllowedHosts }}<span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template> - </MkTextarea> - </div> - </MkFolder> + <div class="_gaps"> + <MkRadios v-model="federationForm.state.federation"> + <template #label>{{ i18n.ts.behavior }}<span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template> + <option value="all">{{ i18n.ts.all }}</option> + <option value="specified">{{ i18n.ts.specifyHost }}</option> + <option value="none">{{ i18n.ts.none }}</option> + </MkRadios> - <MkFolder> - <template #icon><i class="ti ti-ghost"></i></template> - <template #label>{{ i18n.ts.proxyAccount }}</template> - <template v-if="proxyAccountForm.modified.value" #footer> - <MkFormFooter :form="proxyAccountForm"/> - </template> + <MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts"> + <template #label>{{ i18n.ts.federationAllowedHosts }}<span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template> + </MkTextarea> + </div> + </MkFolder> - <div class="_gaps"> - <MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo> + <MkFolder> + <template #icon><i class="ti ti-ghost"></i></template> + <template #label>{{ i18n.ts.proxyAccount }}</template> + <template v-if="proxyAccountForm.modified.value" #footer> + <MkFormFooter :form="proxyAccountForm"/> + </template> - <MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true"> - <template #label>{{ i18n.ts._profile.description }}</template> - <template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> - </MkTextarea> - </div> - </MkFolder> - </div> - </MkSpacer> - </MkStickyContainer> -</div> + <div class="_gaps"> + <MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo> + + <MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true"> + <template #label>{{ i18n.ts._profile.description }}</template> + <template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> + </MkTextarea> + </div> + </MkFolder> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import { ref, computed, reactive } 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'; diff --git a/packages/frontend/src/pages/admin/system-webhook.vue b/packages/frontend/src/pages/admin/system-webhook.vue index d8eb9b92ee..a3214028e6 100644 --- a/packages/frontend/src/pages/admin/system-webhook.vue +++ b/packages/frontend/src/pages/admin/system-webhook.vue @@ -4,11 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header> - <XHeader :actions="headerActions" :tabs="headerTabs"/> - </template> - +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> <MkSpacer :contentMax="900"> <div class="_gaps_m"> <MkButton primary @click="onCreateWebhookClicked"> @@ -22,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only </FormSection> </div> </MkSpacer> -</MkStickyContainer> +</PageWithHeader> </template> <script lang="ts" setup> @@ -32,7 +28,6 @@ import XItem from './system-webhook.item.vue'; import FormSection from '@/components/form/section.vue'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; -import XHeader from '@/pages/admin/_header_.vue'; import MkButton from '@/components/MkButton.vue'; import { misskeyApi } from '@/utility/misskey-api.js'; import { showSystemWebhookEditorDialog } from '@/components/MkSystemWebhookEditor.impl.js'; diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue index a44951a947..cef0e94753 100644 --- a/packages/frontend/src/pages/admin/users.vue +++ b/packages/frontend/src/pages/admin/users.vue @@ -4,64 +4,60 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :contentMax="900"> - <div class="_gaps"> - <div :class="$style.inputs"> - <MkButton style="margin-left: auto" @click="resetQuery">{{ i18n.ts.reset }}</MkButton> - </div> - <div :class="$style.inputs"> - <MkSelect v-model="sort" style="flex: 1;"> - <template #label>{{ i18n.ts.sort }}</template> - <option value="-createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.descendingOrder }})</option> - <option value="-updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.ascendingOrder }})</option> - <option value="+updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.descendingOrder }})</option> - </MkSelect> - <MkSelect v-model="state" style="flex: 1;"> - <template #label>{{ i18n.ts.state }}</template> - <option value="all">{{ i18n.ts.all }}</option> - <option value="available">{{ i18n.ts.normal }}</option> - <option value="admin">{{ i18n.ts.administrator }}</option> - <option value="moderator">{{ i18n.ts.moderator }}</option> - <option value="suspended">{{ i18n.ts.suspend }}</option> - </MkSelect> - <MkSelect v-model="origin" style="flex: 1;"> - <template #label>{{ i18n.ts.instance }}</template> - <option value="combined">{{ i18n.ts.all }}</option> - <option value="local">{{ i18n.ts.local }}</option> - <option value="remote">{{ i18n.ts.remote }}</option> - </MkSelect> - </div> - <div :class="$style.inputs"> - <MkInput v-model="searchUsername" style="flex: 1;" type="text" :spellcheck="false"> - <template #prefix>@</template> - <template #label>{{ i18n.ts.username }}</template> - </MkInput> - <MkInput v-model="searchHost" style="flex: 1;" type="text" :spellcheck="false" :disabled="pagination.params.origin === 'local'"> - <template #prefix>@</template> - <template #label>{{ i18n.ts.host }}</template> - </MkInput> - </div> - - <MkPagination v-slot="{items}" ref="paginationComponent" :pagination="pagination"> - <div :class="$style.users"> - <MkA v-for="user in items" :key="user.id" v-tooltip.mfm="`Last posted: ${dateString(user.updatedAt)}`" :class="$style.user" :to="`/admin/user/${user.id}`"> - <MkUserCardMini :user="user"/> - </MkA> - </div> - </MkPagination> +<PageWithHeader :actions="headerActions" :tabs="headerTabs"> + <MkSpacer :contentMax="900"> + <div class="_gaps"> + <div :class="$style.inputs"> + <MkButton style="margin-left: auto" @click="resetQuery">{{ i18n.ts.reset }}</MkButton> </div> - </MkSpacer> - </MkStickyContainer> -</div> + <div :class="$style.inputs"> + <MkSelect v-model="sort" style="flex: 1;"> + <template #label>{{ i18n.ts.sort }}</template> + <option value="-createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.descendingOrder }})</option> + <option value="-updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.ascendingOrder }})</option> + <option value="+updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.descendingOrder }})</option> + </MkSelect> + <MkSelect v-model="state" style="flex: 1;"> + <template #label>{{ i18n.ts.state }}</template> + <option value="all">{{ i18n.ts.all }}</option> + <option value="available">{{ i18n.ts.normal }}</option> + <option value="admin">{{ i18n.ts.administrator }}</option> + <option value="moderator">{{ i18n.ts.moderator }}</option> + <option value="suspended">{{ i18n.ts.suspend }}</option> + </MkSelect> + <MkSelect v-model="origin" style="flex: 1;"> + <template #label>{{ i18n.ts.instance }}</template> + <option value="combined">{{ i18n.ts.all }}</option> + <option value="local">{{ i18n.ts.local }}</option> + <option value="remote">{{ i18n.ts.remote }}</option> + </MkSelect> + </div> + <div :class="$style.inputs"> + <MkInput v-model="searchUsername" style="flex: 1;" type="text" :spellcheck="false"> + <template #prefix>@</template> + <template #label>{{ i18n.ts.username }}</template> + </MkInput> + <MkInput v-model="searchHost" style="flex: 1;" type="text" :spellcheck="false" :disabled="pagination.params.origin === 'local'"> + <template #prefix>@</template> + <template #label>{{ i18n.ts.host }}</template> + </MkInput> + </div> + + <MkPagination v-slot="{items}" ref="paginationComponent" :pagination="pagination"> + <div :class="$style.users"> + <MkA v-for="user in items" :key="user.id" v-tooltip.mfm="`Last posted: ${dateString(user.updatedAt)}`" :class="$style.user" :to="`/admin/user/${user.id}`"> + <MkUserCardMini :user="user"/> + </MkA> + </div> + </MkPagination> + </div> + </MkSpacer> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, useTemplateRef, ref, watchEffect } from 'vue'; -import XHeader from './_header_.vue'; import { defaultMemoryStorage } from '@/memory-storage'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; |