enhance(frontend): tweak control panel

This commit is contained in:
syuilo 2024-09-22 18:26:21 +09:00
parent 01ec708020
commit d435d04eaf
2 changed files with 151 additions and 77 deletions

View File

@ -41,6 +41,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer :marginMin="14" :marginMax="22"> <MkSpacer :marginMin="14" :marginMax="22">
<slot></slot> <slot></slot>
</MkSpacer> </MkSpacer>
<div :class="$style.footer" v-if="withFooter">
<slot name="footer"></slot>
</div>
</div> </div>
</KeepAlive> </KeepAlive>
</Transition> </Transition>
@ -56,9 +59,11 @@ import { defaultStore } from '@/store.js';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
defaultOpen?: boolean; defaultOpen?: boolean;
maxHeight?: number | null; maxHeight?: number | null;
withFooter?: boolean;
}>(), { }>(), {
defaultOpen: false, defaultOpen: false,
maxHeight: null, maxHeight: null,
withFooter: false
}); });
const getBgColor = (el: HTMLElement) => { const getBgColor = (el: HTMLElement) => {
@ -224,4 +229,17 @@ onMounted(() => {
background: var(--bg); background: var(--bg);
} }
} }
.footer {
position: sticky !important;
z-index: 1;
bottom: var(--stickyBottom, 0px);
left: 0;
padding: 9px 12px;
border-top: solid 0.5px var(--divider);
background: var(--acrylicBg);
-webkit-backdrop-filter: var(--blur, blur(15px));
backdrop-filter: var(--blur, blur(15px));
border-radius: 0 0 6px 6px;
}
</style> </style>

View File

@ -10,71 +10,93 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
<FormSuspense :p="init"> <FormSuspense :p="init">
<div class="_gaps_m"> <div class="_gaps_m">
<MkInput v-model="name"> <MkFolder :defaultOpen="true" :withFooter="true">
<template #label>{{ i18n.ts.instanceName }}</template> <template #icon><i class="ti ti-info-circle"></i></template>
</MkInput> <template #label>{{ i18n.ts.info }}</template>
<template #footer>
<MkButton primary rounded @click="saveInfo">{{ i18n.ts.save }}</MkButton>
</template>
<MkInput v-model="shortName"> <div class="_gaps">
<template #label>{{ i18n.ts._serverSettings.shortName }} ({{ i18n.ts.optional }})</template> <MkInput v-model="name">
<template #caption>{{ i18n.ts._serverSettings.shortNameDescription }}</template> <template #label>{{ i18n.ts.instanceName }}</template>
</MkInput> </MkInput>
<MkTextarea v-model="description"> <MkInput v-model="shortName">
<template #label>{{ i18n.ts.instanceDescription }}</template> <template #label>{{ i18n.ts._serverSettings.shortName }} ({{ i18n.ts.optional }})</template>
</MkTextarea> <template #caption>{{ i18n.ts._serverSettings.shortNameDescription }}</template>
</MkInput>
<FormSplit :minWidth="300"> <MkTextarea v-model="description">
<MkInput v-model="maintainerName"> <template #label>{{ i18n.ts.instanceDescription }}</template>
<template #label>{{ i18n.ts.maintainerName }}</template> </MkTextarea>
</MkInput>
<MkInput v-model="maintainerEmail" type="email"> <FormSplit :minWidth="300">
<template #prefix><i class="ti ti-mail"></i></template> <MkInput v-model="maintainerName">
<template #label>{{ i18n.ts.maintainerEmail }}</template> <template #label>{{ i18n.ts.maintainerName }}</template>
</MkInput> </MkInput>
</FormSplit>
<MkInput v-model="tosUrl" type="url"> <MkInput v-model="maintainerEmail" type="email">
<template #prefix><i class="ti ti-link"></i></template> <template #prefix><i class="ti ti-mail"></i></template>
<template #label>{{ i18n.ts.tosUrl }}</template> <template #label>{{ i18n.ts.maintainerEmail }}</template>
</MkInput> </MkInput>
</FormSplit>
<MkInput v-model="privacyPolicyUrl" type="url"> <MkInput v-model="tosUrl" type="url">
<template #prefix><i class="ti ti-link"></i></template> <template #prefix><i class="ti ti-link"></i></template>
<template #label>{{ i18n.ts.privacyPolicyUrl }}</template> <template #label>{{ i18n.ts.tosUrl }}</template>
</MkInput> </MkInput>
<MkInput v-model="inquiryUrl" type="url"> <MkInput v-model="privacyPolicyUrl" type="url">
<template #prefix><i class="ti ti-link"></i></template> <template #prefix><i class="ti ti-link"></i></template>
<template #label>{{ i18n.ts._serverSettings.inquiryUrl }}</template> <template #label>{{ i18n.ts.privacyPolicyUrl }}</template>
<template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template> </MkInput>
</MkInput>
<MkInput v-model="repositoryUrl" type="url"> <MkInput v-model="inquiryUrl" type="url">
<template #label>{{ i18n.ts.repositoryUrl }}</template> <template #prefix><i class="ti ti-link"></i></template>
<template #prefix><i class="ti ti-link"></i></template> <template #label>{{ i18n.ts._serverSettings.inquiryUrl }}</template>
<template #caption>{{ i18n.ts.repositoryUrlDescription }}</template> <template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template>
</MkInput> </MkInput>
<MkInfo v-if="!instance.providesTarball && !repositoryUrl" warn> <MkInput v-model="repositoryUrl" type="url">
{{ i18n.ts.repositoryUrlOrTarballRequired }} <template #label>{{ i18n.ts.repositoryUrl }}</template>
</MkInfo> <template #prefix><i class="ti ti-link"></i></template>
<template #caption>{{ i18n.ts.repositoryUrlDescription }}</template>
</MkInput>
<MkInput v-model="impressumUrl" type="url"> <MkInfo v-if="!instance.providesTarball && !repositoryUrl" warn>
<template #label>{{ i18n.ts.impressumUrl }}</template> {{ i18n.ts.repositoryUrlOrTarballRequired }}
<template #prefix><i class="ti ti-link"></i></template> </MkInfo>
<template #caption>{{ i18n.ts.impressumDescription }}</template>
</MkInput>
<MkTextarea v-model="pinnedUsers"> <MkInput v-model="impressumUrl" type="url">
<template #label>{{ i18n.ts.impressumUrl }}</template>
<template #prefix><i class="ti ti-link"></i></template>
<template #caption>{{ i18n.ts.impressumDescription }}</template>
</MkInput>
</div>
</MkFolder>
<MkFolder :withFooter="true">
<template #icon><i class="ti ti-user-star"></i></template>
<template #label>{{ i18n.ts.pinnedUsers }}</template> <template #label>{{ i18n.ts.pinnedUsers }}</template>
<template #caption>{{ i18n.ts.pinnedUsersDescription }}</template> <template #footer>
</MkTextarea> <MkButton primary rounded @click="save_pinnedUsers">{{ i18n.ts.save }}</MkButton>
</template>
<FormSection> <MkTextarea v-model="pinnedUsers">
<template #label>{{ i18n.ts.pinnedUsers }}</template>
<template #caption>{{ i18n.ts.pinnedUsersDescription }}</template>
</MkTextarea>
</MkFolder>
<MkFolder :withFooter="true">
<template #icon><i class="ti ti-cloud"></i></template>
<template #label>{{ i18n.ts.files }}</template> <template #label>{{ i18n.ts.files }}</template>
<template #footer>
<MkButton primary rounded @click="saveFiles">{{ i18n.ts.save }}</MkButton>
</template>
<div class="_gaps_m"> <div class="_gaps">
<MkSwitch v-model="cacheRemoteFiles"> <MkSwitch v-model="cacheRemoteFiles">
<template #label>{{ i18n.ts.cacheRemoteFiles }}</template> <template #label>{{ i18n.ts.cacheRemoteFiles }}</template>
<template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}{{ i18n.ts.youCanCleanRemoteFilesCache }}</template> <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}{{ i18n.ts.youCanCleanRemoteFilesCache }}</template>
@ -87,12 +109,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch> </MkSwitch>
</template> </template>
</div> </div>
</FormSection> </MkFolder>
<FormSection> <MkFolder :withFooter="true">
<template #icon><i class="ti ti-world-cog"></i></template>
<template #label>ServiceWorker</template> <template #label>ServiceWorker</template>
<template #footer>
<MkButton primary rounded @click="saveServiceWorker">{{ i18n.ts.save }}</MkButton>
</template>
<div class="_gaps_m"> <div class="_gaps">
<MkSwitch v-model="enableServiceWorker"> <MkSwitch v-model="enableServiceWorker">
<template #label>{{ i18n.ts.enableServiceworker }}</template> <template #label>{{ i18n.ts.enableServiceworker }}</template>
<template #caption>{{ i18n.ts.serviceworkerInfo }}</template> <template #caption>{{ i18n.ts.serviceworkerInfo }}</template>
@ -110,12 +136,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkInput> </MkInput>
</template> </template>
</div> </div>
</FormSection> </MkFolder>
<FormSection> <MkFolder :withFooter="true">
<template #icon><i class="ti ti-ad"></i></template>
<template #label>{{ i18n.ts._ad.adsSettings }}</template> <template #label>{{ i18n.ts._ad.adsSettings }}</template>
<template #footer>
<MkButton primary rounded @click="saveAd">{{ i18n.ts.save }}</MkButton>
</template>
<div class="_gaps_m"> <div class="_gaps">
<div class="_gaps_s"> <div class="_gaps_s">
<MkInput v-model="notesPerOneAd" :min="0" type="number"> <MkInput v-model="notesPerOneAd" :min="0" type="number">
<template #label>{{ i18n.ts._ad.notesPerOneAd }}</template> <template #label>{{ i18n.ts._ad.notesPerOneAd }}</template>
@ -126,12 +156,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkInfo> </MkInfo>
</div> </div>
</div> </div>
</FormSection> </MkFolder>
<FormSection> <MkFolder :withFooter="true">
<template #icon><i class="ti ti-world-search"></i></template>
<template #label>{{ i18n.ts._urlPreviewSetting.title }}</template> <template #label>{{ i18n.ts._urlPreviewSetting.title }}</template>
<template #footer>
<MkButton primary rounded @click="saveUrlPreview">{{ i18n.ts.save }}</MkButton>
</template>
<div class="_gaps_m"> <div class="_gaps">
<MkSwitch v-model="urlPreviewEnabled"> <MkSwitch v-model="urlPreviewEnabled">
<template #label>{{ i18n.ts._urlPreviewSetting.enable }}</template> <template #label>{{ i18n.ts._urlPreviewSetting.enable }}</template>
</MkSwitch> </MkSwitch>
@ -173,17 +207,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</div> </div>
</div> </div>
</FormSection> </MkFolder>
</div> </div>
</FormSuspense> </FormSuspense>
</MkSpacer> </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>
</MkStickyContainer> </MkStickyContainer>
</div> </div>
</template> </template>
@ -195,7 +222,6 @@ import MkSwitch from '@/components/MkSwitch.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue'; import MkTextarea from '@/components/MkTextarea.vue';
import MkInfo from '@/components/MkInfo.vue'; import MkInfo from '@/components/MkInfo.vue';
import FormSection from '@/components/form/section.vue';
import FormSplit from '@/components/form/split.vue'; import FormSplit from '@/components/form/split.vue';
import FormSuspense from '@/components/form/suspense.vue'; import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
@ -258,8 +284,8 @@ async function init(): Promise<void> {
urlPreviewSummaryProxyUrl.value = meta.urlPreviewSummaryProxyUrl; urlPreviewSummaryProxyUrl.value = meta.urlPreviewSummaryProxyUrl;
} }
async function save() { function saveInfo() {
await os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
name: name.value, name: name.value,
shortName: shortName.value === '' ? null : shortName.value, shortName: shortName.value === '' ? null : shortName.value,
description: description.value, description: description.value,
@ -270,22 +296,57 @@ async function save() {
inquiryUrl: inquiryUrl.value, inquiryUrl: inquiryUrl.value,
repositoryUrl: repositoryUrl.value, repositoryUrl: repositoryUrl.value,
impressumUrl: impressumUrl.value, impressumUrl: impressumUrl.value,
}).then(() => {
fetchInstance(true);
});
}
function save_pinnedUsers() {
os.apiWithDialog('admin/update-meta', {
pinnedUsers: pinnedUsers.value.split('\n'), pinnedUsers: pinnedUsers.value.split('\n'),
}).then(() => {
fetchInstance(true);
});
}
function saveFiles() {
os.apiWithDialog('admin/update-meta', {
cacheRemoteFiles: cacheRemoteFiles.value, cacheRemoteFiles: cacheRemoteFiles.value,
cacheRemoteSensitiveFiles: cacheRemoteSensitiveFiles.value, cacheRemoteSensitiveFiles: cacheRemoteSensitiveFiles.value,
}).then(() => {
fetchInstance(true);
});
}
function saveServiceWorker() {
os.apiWithDialog('admin/update-meta', {
enableServiceWorker: enableServiceWorker.value, enableServiceWorker: enableServiceWorker.value,
swPublicKey: swPublicKey.value, swPublicKey: swPublicKey.value,
swPrivateKey: swPrivateKey.value, swPrivateKey: swPrivateKey.value,
}).then(() => {
fetchInstance(true);
});
}
function saveAd() {
os.apiWithDialog('admin/update-meta', {
notesPerOneAd: notesPerOneAd.value, notesPerOneAd: notesPerOneAd.value,
}).then(() => {
fetchInstance(true);
});
}
function saveUrlPreview() {
os.apiWithDialog('admin/update-meta', {
urlPreviewEnabled: urlPreviewEnabled.value, urlPreviewEnabled: urlPreviewEnabled.value,
urlPreviewTimeout: urlPreviewTimeout.value, urlPreviewTimeout: urlPreviewTimeout.value,
urlPreviewMaximumContentLength: urlPreviewMaximumContentLength.value, urlPreviewMaximumContentLength: urlPreviewMaximumContentLength.value,
urlPreviewRequireContentLength: urlPreviewRequireContentLength.value, urlPreviewRequireContentLength: urlPreviewRequireContentLength.value,
urlPreviewUserAgent: urlPreviewUserAgent.value, urlPreviewUserAgent: urlPreviewUserAgent.value,
urlPreviewSummaryProxyUrl: urlPreviewSummaryProxyUrl.value, urlPreviewSummaryProxyUrl: urlPreviewSummaryProxyUrl.value,
}).then(() => {
fetchInstance(true);
}); });
fetchInstance(true);
} }
const headerTabs = computed(() => []); const headerTabs = computed(() => []);
@ -297,11 +358,6 @@ definePageMetadata(() => ({
</script> </script>
<style lang="scss" module> <style lang="scss" module>
.footer {
-webkit-backdrop-filter: var(--blur, blur(15px));
backdrop-filter: var(--blur, blur(15px));
}
.subCaption { .subCaption {
font-size: 0.85em; font-size: 0.85em;
color: var(--fgTransparentWeak); color: var(--fgTransparentWeak);