mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-22 05:45:32 +02:00
Merge branch 'develop' into lowpowermode
This commit is contained in:
@@ -145,7 +145,7 @@ import { claimAchievement } from '@/utility/achievements.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { chooseFileFromPcAndUpload, selectDriveFolder } from '@/utility/drive.js';
|
||||
import { store } from '@/store.js';
|
||||
import { isSeparatorNeeded, getSeparatorInfo, makeDateGroupedTimelineComputedRef } from '@/utility/timeline-date-separate.js';
|
||||
import { makeDateGroupedTimelineComputedRef } from '@/utility/timeline-date-separate.js';
|
||||
import { globalEvents, useGlobalEvent } from '@/events.js';
|
||||
import { checkDragDataType, getDragData, setDragData } from '@/drag-and-drop.js';
|
||||
import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js';
|
||||
|
||||
@@ -52,15 +52,20 @@ import TestWebGL2 from '@/workers/test-webgl2?worker';
|
||||
import { WorkerMultiDispatch } from '@@/js/worker-multi-dispatch.js';
|
||||
import { extractAvgColorFromBlurhash } from '@@/js/extract-avg-color-from-blurhash.js';
|
||||
|
||||
// テスト環境で Web Worker インスタンスは作成できない
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const isTest = (import.meta.env.MODE === 'test' || window.Cypress != null);
|
||||
|
||||
const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resolve => {
|
||||
// テスト環境で Web Worker インスタンスは作成できない
|
||||
if (import.meta.env.MODE === 'test') {
|
||||
if (isTest) {
|
||||
const canvas = window.document.createElement('canvas');
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
resolve(canvas);
|
||||
return;
|
||||
}
|
||||
|
||||
const testWorker = new TestWebGL2();
|
||||
testWorker.addEventListener('message', event => {
|
||||
if (event.data.result) {
|
||||
@@ -189,7 +194,7 @@ function drawAvg() {
|
||||
}
|
||||
|
||||
async function draw() {
|
||||
if (import.meta.env.MODE === 'test' && props.hash == null) return;
|
||||
if (isTest && props.hash == null) return;
|
||||
|
||||
drawAvg();
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
:class="[$style.root, { [$style.showActionsOnlyHover]: prefer.s.showNoteActionsOnlyHover, [$style.skipRender]: prefer.s.skipNoteRender }]"
|
||||
tabindex="0"
|
||||
>
|
||||
<MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
|
||||
<MkNoteSub v-if="appearNote.replyId && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
|
||||
<div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div>
|
||||
<div v-if="isRenote" :class="$style.renote">
|
||||
<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
|
||||
@@ -99,7 +99,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div v-if="isEnabledUrlPreview">
|
||||
<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview"/>
|
||||
</div>
|
||||
<div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
|
||||
<div v-if="appearNote.renoteId" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
|
||||
<button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false">
|
||||
<span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span>
|
||||
</button>
|
||||
@@ -282,7 +282,7 @@ let note = deepClone(props.note);
|
||||
//}
|
||||
|
||||
const isRenote = Misskey.note.isPureRenote(note);
|
||||
const appearNote = getAppearNote(note);
|
||||
const appearNote = getAppearNote(note) ?? note;
|
||||
const { $note: $appearNote, subscribe: subscribeManuallyToNoteCapture } = useNoteCapture({
|
||||
note: appearNote,
|
||||
parentNote: note,
|
||||
|
||||
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
<MkNoteSub v-for="note in conversation" :key="note.id" :class="$style.replyToMore" :note="note"/>
|
||||
</div>
|
||||
<MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" :class="$style.replyTo"/>
|
||||
<MkNoteSub v-if="appearNote.replyId" :note="appearNote.reply" :class="$style.replyTo"/>
|
||||
<div v-if="isRenote" :class="$style.renote">
|
||||
<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
|
||||
<i class="ti ti-repeat" style="margin-right: 4px;"></i>
|
||||
|
||||
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div :class="$style.root">
|
||||
<div v-if="note" :class="$style.root">
|
||||
<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
|
||||
<div :class="$style.main">
|
||||
<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
|
||||
@@ -19,6 +19,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else :class="$style.deleted">
|
||||
{{ i18n.ts.deletedNote }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -27,9 +30,10 @@ import * as Misskey from 'misskey-js';
|
||||
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
||||
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
||||
import MkCwButton from '@/components/MkCwButton.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
||||
const props = defineProps<{
|
||||
note: Misskey.entities.Note;
|
||||
note: Misskey.entities.Note | null;
|
||||
}>();
|
||||
|
||||
const showContent = ref(false);
|
||||
@@ -101,4 +105,14 @@ const showContent = ref(false);
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.deleted {
|
||||
text-align: center;
|
||||
padding: 8px !important;
|
||||
margin: 8px 8px 0 8px;
|
||||
--color: light-dark(rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.15));
|
||||
background-size: auto auto;
|
||||
background-image: repeating-linear-gradient(135deg, transparent, transparent 10px, var(--color) 4px, var(--color) 14px);
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,7 +4,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div v-if="!muted" :class="[$style.root, { [$style.children]: depth > 1 }]">
|
||||
<div v-if="note == null" :class="$style.deleted">
|
||||
{{ i18n.ts.deletedNote }}
|
||||
</div>
|
||||
<div v-else-if="!muted" :class="[$style.root, { [$style.children]: depth > 1 }]">
|
||||
<div :class="$style.main">
|
||||
<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
|
||||
<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
|
||||
@@ -53,7 +56,7 @@ import { userPage } from '@/filters/user.js';
|
||||
import { checkWordMute } from '@/utility/check-word-mute.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
note: Misskey.entities.Note;
|
||||
note: Misskey.entities.Note | null;
|
||||
detail?: boolean;
|
||||
|
||||
// how many notes are in between this one and the note being viewed in detail
|
||||
@@ -62,12 +65,12 @@ const props = withDefaults(defineProps<{
|
||||
depth: 1,
|
||||
});
|
||||
|
||||
const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false);
|
||||
const muted = ref(props.note && $i ? checkWordMute(props.note, $i, $i.mutedWords) : false);
|
||||
|
||||
const showContent = ref(false);
|
||||
const replies = ref<Misskey.entities.Note[]>([]);
|
||||
|
||||
if (props.detail) {
|
||||
if (props.detail && props.note) {
|
||||
misskeyApi('notes/children', {
|
||||
noteId: props.note.id,
|
||||
limit: 5,
|
||||
@@ -160,4 +163,14 @@ if (props.detail) {
|
||||
margin: 8px 8px 0 8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.deleted {
|
||||
text-align: center;
|
||||
padding: 8px !important;
|
||||
margin: 8px 8px 0 8px;
|
||||
--color: light-dark(rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.15));
|
||||
background-size: auto auto;
|
||||
background-image: repeating-linear-gradient(135deg, transparent, transparent 10px, var(--color) 4px, var(--color) 14px);
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,15 +10,22 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #default="{ items: notes }">
|
||||
<div :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap }]">
|
||||
<template v-for="(note, i) in notes" :key="note.id">
|
||||
<div v-if="i > 0 && isSeparatorNeeded(paginator.items.value[i -1].createdAt, note.createdAt)" :data-scroll-anchor="note.id">
|
||||
<div :class="$style.date">
|
||||
<span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(paginator.items.value[i -1].createdAt, note.createdAt).prevText }}</span>
|
||||
<div
|
||||
v-if="i > 0 && isSeparatorNeeded(paginator.items.value[i - 1].createdAt, note.createdAt)"
|
||||
:data-scroll-anchor="note.id"
|
||||
:class="{ '_gaps': !noGap }"
|
||||
>
|
||||
<div :class="[$style.date, { [$style.noGap]: noGap }]">
|
||||
<span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(paginator.items.value[i - 1].createdAt, note.createdAt)?.prevText }}</span>
|
||||
<span style="height: 1em; width: 1px; background: var(--MI_THEME-divider);"></span>
|
||||
<span>{{ getSeparatorInfo(paginator.items.value[i -1].createdAt, note.createdAt).nextText }} <i class="ti ti-chevron-down"></i></span>
|
||||
<span>{{ getSeparatorInfo(paginator.items.value[i - 1].createdAt, note.createdAt)?.nextText }} <i class="ti ti-chevron-down"></i></span>
|
||||
</div>
|
||||
<MkNote :class="$style.note" :note="note" :withHardMute="true"/>
|
||||
<div v-if="note._shouldInsertAd_" :class="$style.ad">
|
||||
<MkAd :preferForms="['horizontal', 'horizontal-big']"/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="note._shouldInsertAd_" :class="[$style.noteWithAd, { '_gaps': !noGap }]" :data-scroll-anchor="note.id">
|
||||
<div v-else-if="note._shouldInsertAd_" :class="{ '_gaps': !noGap }" :data-scroll-anchor="note.id">
|
||||
<MkNote :class="$style.note" :note="note" :withHardMute="true"/>
|
||||
<div :class="$style.ad">
|
||||
<MkAd :preferForms="['horizontal', 'horizontal-big']"/>
|
||||
@@ -103,7 +110,10 @@ defineExpose({
|
||||
opacity: 0.75;
|
||||
padding: 8px 8px;
|
||||
margin: 0 auto;
|
||||
border-bottom: solid 0.5px var(--MI_THEME-divider);
|
||||
|
||||
&.noGap {
|
||||
border-bottom: solid 0.5px var(--MI_THEME-divider);
|
||||
}
|
||||
}
|
||||
|
||||
.ad:empty {
|
||||
|
||||
@@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #icon><i class="ti ti-planet"></i></template>
|
||||
|
||||
<div class="_gaps_s">
|
||||
<div>{{ i18n.ts._serverSetupWizard.doYouConnectToFediverse_description1 }}<br>{{ i18n.ts._serverSetupWizard.doYouConnectToFediverse_description2 }}</div>
|
||||
<div>{{ i18n.ts._serverSetupWizard.doYouConnectToFediverse_description1 }}<br>{{ i18n.ts._serverSetupWizard.doYouConnectToFediverse_description2 }}<br><MkLink target="_blank" url="https://wikipedia.org/wiki/Fediverse">{{ i18n.ts.learnMore }}</MkLink></div>
|
||||
|
||||
<MkRadios v-model="q_federation" :vertical="true">
|
||||
<option value="yes">{{ i18n.ts.yes }}</option>
|
||||
@@ -63,6 +63,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkRadios>
|
||||
|
||||
<MkInfo v-if="q_federation === 'yes'">{{ i18n.ts._serverSetupWizard.youCanConfigureMoreFederationSettingsLater }}</MkInfo>
|
||||
|
||||
<MkSwitch v-if="q_federation === 'yes'" v-model="q_remoteContentsCleaning">
|
||||
<template #label>{{ i18n.ts._serverSetupWizard.remoteContentsCleaning }}</template>
|
||||
<template #caption>{{ i18n.ts._serverSetupWizard.remoteContentsCleaning_description }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
@@ -110,6 +115,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div><b>{{ i18n.ts.federation }}:</b></div>
|
||||
<div>{{ serverSettings.federation === 'none' ? i18n.ts.no : i18n.ts.all }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div><b>{{ i18n.ts._serverSettings.remoteNotesCleaning }}:</b></div>
|
||||
<div>{{ serverSettings.enableRemoteNotesCleaning ? i18n.ts.yes : i18n.ts.no }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div><b>FTT:</b></div>
|
||||
<div>{{ serverSettings.enableFanoutTimeline ? i18n.ts.yes : i18n.ts.no }}</div>
|
||||
@@ -185,7 +194,9 @@ import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkRadios from '@/components/MkRadios.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import MkLink from '@/components/MkLink.vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'finished'): void;
|
||||
@@ -200,6 +211,7 @@ const q_name = ref('');
|
||||
const q_use = ref('single');
|
||||
const q_scale = ref('small');
|
||||
const q_federation = ref('yes');
|
||||
const q_remoteContentsCleaning = ref(true);
|
||||
const q_adminName = ref('');
|
||||
const q_adminEmail = ref('');
|
||||
|
||||
@@ -217,6 +229,7 @@ const serverSettings = computed<Misskey.entities.AdminUpdateMetaRequest>(() => {
|
||||
emailRequiredForSignup: q_use.value === 'open',
|
||||
enableIpLogging: q_use.value === 'open',
|
||||
federation: q_federation.value === 'yes' ? 'all' : 'none',
|
||||
enableRemoteNotesCleaning: q_remoteContentsCleaning.value,
|
||||
enableFanoutTimeline: true,
|
||||
enableFanoutTimelineDbFallback: q_use.value === 'single',
|
||||
enableReactionsBuffering,
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkModalWindow
|
||||
ref="windowEl"
|
||||
:withOkButton="false"
|
||||
:okButtonDisabled="false"
|
||||
:width="500"
|
||||
:height="600"
|
||||
@close="onCloseModalWindow"
|
||||
@closed="emit('closed')"
|
||||
>
|
||||
<template #header>Server setup wizard</template>
|
||||
<div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;">
|
||||
<Suspense>
|
||||
<template #default>
|
||||
<MkServerSetupWizard @finished="onWizardFinished"/>
|
||||
</template>
|
||||
<template #fallback>
|
||||
<MkLoading/>
|
||||
</template>
|
||||
</Suspense>
|
||||
</div>
|
||||
</MkModalWindow>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useTemplateRef } from 'vue';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkServerSetupWizard from '@/components/MkServerSetupWizard.vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'closed'),
|
||||
}>();
|
||||
|
||||
const windowEl = useTemplateRef('windowEl');
|
||||
|
||||
function onWizardFinished() {
|
||||
windowEl.value?.close();
|
||||
}
|
||||
|
||||
function onCloseModalWindow() {
|
||||
windowEl.value?.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style module lang="scss">
|
||||
.root {
|
||||
max-height: 410px;
|
||||
height: 410px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
@@ -32,9 +32,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template v-for="(note, i) in paginator.items.value" :key="note.id">
|
||||
<div v-if="i > 0 && isSeparatorNeeded(paginator.items.value[i -1].createdAt, note.createdAt)" :data-scroll-anchor="note.id">
|
||||
<div :class="$style.date">
|
||||
<span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(paginator.items.value[i -1].createdAt, note.createdAt).prevText }}</span>
|
||||
<span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(paginator.items.value[i -1].createdAt, note.createdAt)?.prevText }}</span>
|
||||
<span style="height: 1em; width: 1px; background: var(--MI_THEME-divider);"></span>
|
||||
<span>{{ getSeparatorInfo(paginator.items.value[i -1].createdAt, note.createdAt).nextText }} <i class="ti ti-chevron-down"></i></span>
|
||||
<span>{{ getSeparatorInfo(paginator.items.value[i -1].createdAt, note.createdAt)?.nextText }} <i class="ti ti-chevron-down"></i></span>
|
||||
</div>
|
||||
<MkNote :class="$style.note" :note="note" :withHardMute="true"/>
|
||||
</div>
|
||||
|
||||
@@ -25,11 +25,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
>
|
||||
<div v-for="(notification, i) in paginator.items.value" :key="notification.id" :data-scroll-anchor="notification.id" :class="$style.item">
|
||||
<div v-if="i > 0 && isSeparatorNeeded(paginator.items.value[i -1].createdAt, notification.createdAt)" :class="$style.date">
|
||||
<span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(paginator.items.value[i -1].createdAt, notification.createdAt).prevText }}</span>
|
||||
<span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(paginator.items.value[i -1].createdAt, notification.createdAt)?.prevText }}</span>
|
||||
<span style="height: 1em; width: 1px; background: var(--MI_THEME-divider);"></span>
|
||||
<span>{{ getSeparatorInfo(paginator.items.value[i -1].createdAt, notification.createdAt).nextText }} <i class="ti ti-chevron-down"></i></span>
|
||||
<span>{{ getSeparatorInfo(paginator.items.value[i -1].createdAt, notification.createdAt)?.nextText }} <i class="ti ti-chevron-down"></i></span>
|
||||
</div>
|
||||
<MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :class="$style.content" :note="notification.note" :withHardMute="true"/>
|
||||
<MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type) && 'note' in notification" :class="$style.content" :note="notification.note" :withHardMute="true"/>
|
||||
<XNotification v-else :class="$style.content" :notification="notification" :withTime="true" :full="true"/>
|
||||
</div>
|
||||
</component>
|
||||
|
||||
@@ -98,7 +98,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkKeyValue>
|
||||
<MkKeyValue v-if="job.progress != null && typeof job.progress === 'number' && job.progress > 0">
|
||||
<template #key>Progress</template>
|
||||
<template #value>{{ Math.floor(job.progress * 100) }}%</template>
|
||||
<template #value>{{ Math.floor(job.progress) }}%</template>
|
||||
</MkKeyValue>
|
||||
</div>
|
||||
<MkFolder :withSpacer="false">
|
||||
@@ -150,11 +150,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkButton><i class="ti ti-device-floppy"></i> Update</MkButton>
|
||||
</div>
|
||||
<div v-else-if="tab === 'result'">
|
||||
<MkCode :code="String(job.returnValue)"/>
|
||||
<MkCode :code="JSON5.stringify(job.returnValue, null, '\t')" lang="json5"/>
|
||||
</div>
|
||||
<div v-else-if="tab === 'error'" class="_gaps_s">
|
||||
<MkCode v-for="log in job.stacktrace" :code="log" lang="stacktrace"/>
|
||||
</div>
|
||||
<div v-else-if="tab === 'logs'">
|
||||
<MkButton primary rounded @click="loadLogs()"><i class="ti ti-refresh"></i> Load logs</MkButton>
|
||||
<div v-for="log in logs">{{ log }}</div>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</template>
|
||||
|
||||
@@ -198,6 +202,7 @@ const emit = defineEmits<{
|
||||
const tab = ref('info');
|
||||
const editData = ref(JSON5.stringify(props.job.data, null, '\t'));
|
||||
const canEdit = true;
|
||||
const logs = ref<string[]>([]);
|
||||
|
||||
type TlType = TlEvent<{
|
||||
type: 'created' | 'processed' | 'finished';
|
||||
@@ -268,6 +273,10 @@ async function removeJob() {
|
||||
os.apiWithDialog('admin/queue/remove-job', { queue: props.queueType, jobId: props.job.id });
|
||||
}
|
||||
|
||||
async function loadLogs() {
|
||||
logs.value = await os.apiWithDialog('admin/queue/show-job-logs', { queue: props.queueType, jobId: props.job.id });
|
||||
}
|
||||
|
||||
// TODO
|
||||
// function moveJob() {
|
||||
//
|
||||
|
||||
@@ -101,6 +101,35 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><i class="ti ti-recycle"></i></template>
|
||||
<template #label>Remote Notes Cleaning (仮)</template>
|
||||
<template v-if="remoteNotesCleaningForm.savedState.enableRemoteNotesCleaning" #suffix>Enabled</template>
|
||||
<template v-else #suffix>Disabled</template>
|
||||
<template v-if="remoteNotesCleaningForm.modified.value" #footer>
|
||||
<MkFormFooter :form="remoteNotesCleaningForm"/>
|
||||
</template>
|
||||
|
||||
<div class="_gaps_m">
|
||||
<MkSwitch v-model="remoteNotesCleaningForm.state.enableRemoteNotesCleaning">
|
||||
<template #label>{{ i18n.ts.enable }}<span v-if="remoteNotesCleaningForm.modifiedStates.enableRemoteNotesCleaning" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #caption>{{ i18n.ts._serverSettings.remoteNotesCleaning_description }}</template>
|
||||
</MkSwitch>
|
||||
|
||||
<template v-if="remoteNotesCleaningForm.state.enableRemoteNotesCleaning">
|
||||
<MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningExpiryDaysForEachNotes" type="number">
|
||||
<template #label>{{ i18n.ts._serverSettings.remoteNotesCleaningExpiryDaysForEachNotes }} ({{ i18n.ts.inDays }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningExpiryDaysForEachNotes" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #suffix>{{ i18n.ts._time.day }}</template>
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningMaxProcessingDurationInMinutes" type="number">
|
||||
<template #label>{{ i18n.ts._serverSettings.remoteNotesCleaningMaxProcessingDuration }} ({{ i18n.ts.inMinutes }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningMaxProcessingDurationInMinutes" class="_modified">{{ i18n.ts.modified }}</span></template>
|
||||
<template #suffix>{{ i18n.ts._time.minute }}</template>
|
||||
</MkInput>
|
||||
</template>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</div>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
@@ -196,6 +225,19 @@ const rbtForm = useForm({
|
||||
fetchInstance(true);
|
||||
});
|
||||
|
||||
const remoteNotesCleaningForm = useForm({
|
||||
enableRemoteNotesCleaning: meta.enableRemoteNotesCleaning,
|
||||
remoteNotesCleaningExpiryDaysForEachNotes: meta.remoteNotesCleaningExpiryDaysForEachNotes,
|
||||
remoteNotesCleaningMaxProcessingDurationInMinutes: meta.remoteNotesCleaningMaxProcessingDurationInMinutes,
|
||||
}, async (state) => {
|
||||
await os.apiWithDialog('admin/update-meta', {
|
||||
enableRemoteNotesCleaning: state.enableRemoteNotesCleaning,
|
||||
remoteNotesCleaningExpiryDaysForEachNotes: state.remoteNotesCleaningExpiryDaysForEachNotes,
|
||||
remoteNotesCleaningMaxProcessingDurationInMinutes: state.remoteNotesCleaningMaxProcessingDurationInMinutes,
|
||||
});
|
||||
fetchInstance(true);
|
||||
});
|
||||
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
@@ -287,6 +287,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkTextarea>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkButton primary @click="openSetupWizard">
|
||||
Open setup wizard
|
||||
</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</PageWithHeader>
|
||||
@@ -425,6 +429,20 @@ const proxyAccountForm = useForm({
|
||||
fetchInstance(true);
|
||||
});
|
||||
|
||||
async function openSetupWizard() {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
title: i18n.ts._serverSettings.restartServerSetupWizardConfirm_title,
|
||||
text: i18n.ts._serverSettings.restartServerSetupWizardConfirm_text,
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkServerSetupWizardDialog.vue').then(x => x.default), {
|
||||
}, {
|
||||
closed: () => dispose(),
|
||||
});
|
||||
}
|
||||
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePage(() => ({
|
||||
|
||||
@@ -366,6 +366,7 @@ definePage(() => ({
|
||||
|
||||
> .items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
|
||||
@@ -87,7 +87,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div>{{ i18n.ts._serverSetupWizard.settingsYouMakeHereCanBeChangedLater }}</div>
|
||||
</div>
|
||||
|
||||
<MkServerSetupWizard :token="token" @finished="onWizardFinished"/>
|
||||
<Suspense>
|
||||
<template #default>
|
||||
<MkServerSetupWizard :token="token" @finished="onWizardFinished"/>
|
||||
</template>
|
||||
<template #fallback>
|
||||
<MkLoading/>
|
||||
</template>
|
||||
</Suspense>
|
||||
|
||||
<MkButton rounded style="margin: 0 auto;" @click="skipSettings">
|
||||
{{ i18n.ts._serverSetupWizard.skipSettings }}
|
||||
|
||||
@@ -64,12 +64,6 @@ html {
|
||||
}
|
||||
}
|
||||
|
||||
html._themeChangingFallback_ {
|
||||
&, * {
|
||||
transition: background 0.5s ease, border 0.5s ease !important;
|
||||
}
|
||||
}
|
||||
|
||||
html._themeChanging_ {
|
||||
view-transition-name: theme-changing;
|
||||
}
|
||||
|
||||
@@ -137,9 +137,10 @@ export function applyTheme(theme: Theme, persist = true) {
|
||||
}
|
||||
|
||||
if (deepEqual(currentTheme, theme)) return;
|
||||
currentTheme = theme;
|
||||
// リアクティビティ解除
|
||||
currentTheme = deepClone(theme);
|
||||
|
||||
if (window.document.startViewTransition != null && prefer.s.animation) {
|
||||
if (window.document.startViewTransition != null) {
|
||||
window.document.documentElement.classList.add('_themeChanging_');
|
||||
window.document.startViewTransition(async () => {
|
||||
applyThemeInternal(theme, persist);
|
||||
@@ -150,15 +151,9 @@ export function applyTheme(theme: Theme, persist = true) {
|
||||
globalEvents.emit('themeChanged');
|
||||
});
|
||||
} else {
|
||||
// TODO: ViewTransition API が主要ブラウザで対応したら消す
|
||||
window.document.documentElement.classList.add('_themeChangingFallback_');
|
||||
timeout = window.setTimeout(() => {
|
||||
window.document.documentElement.classList.remove('_themeChangingFallback_');
|
||||
// 色計算など再度行えるようにクライアント全体に通知
|
||||
globalEvents.emit('themeChanged');
|
||||
}, 500);
|
||||
|
||||
applyThemeInternal(theme, persist);
|
||||
// 色計算など再度行えるようにクライアント全体に通知
|
||||
globalEvents.emit('themeChanged');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ for (const item of generated) {
|
||||
const inline = rootMods.get(id);
|
||||
if (inline) {
|
||||
inline.parentId = item.id;
|
||||
inline.path = item.path;
|
||||
} else {
|
||||
console.log('[Settings Search Index] Failed to inline', id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user