diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 9de5b99d1..17de04ebc 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -858,6 +858,8 @@ isSystemAccount: "システムにより自動で作成・管理されている typeToConfirm: "この操作を行うには {x} と入力してください" deleteAccount: "アカウント削除" document: "ドキュメント" +numberOfPageCache: "ページキャッシュ数" +numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。" _emailUnavailable: used: "既に使用されています" diff --git a/packages/client/src/components/form/group.vue b/packages/client/src/components/form/group.vue deleted file mode 100644 index 1e8376ca4..000000000 --- a/packages/client/src/components/form/group.vue +++ /dev/null @@ -1,36 +0,0 @@ -<template> -<div v-sticky-container class="adfeebaf _formBlock"> - <div class="label"><slot name="label"></slot></div> - <div class="main _formRoot"> - <slot></slot> - </div> -</div> -</template> - -<script lang="ts"> -import { defineComponent } from 'vue'; - -export default defineComponent({ -}); -</script> - -<style lang="scss" scoped> -.adfeebaf { - padding: 24px 24px; - border: solid 1px var(--divider); - border-radius: var(--radius); - - > .label { - font-weight: bold; - padding: 0 0 16px 0; - - &:empty { - display: none; - } - } - - > .main { - - } -} -</style> diff --git a/packages/client/src/components/form/range.vue b/packages/client/src/components/form/range.vue index ac4a781e3..9bb0164a2 100644 --- a/packages/client/src/components/form/range.vue +++ b/packages/client/src/components/form/range.vue @@ -4,7 +4,7 @@ <div v-adaptive-border class="body"> <div ref="containerEl" class="container"> <div class="track"> - <div class="highlight" :style="{ width: (steppedValue * 100) + '%' }"></div> + <div class="highlight" :style="{ width: (steppedRawValue * 100) + '%' }"></div> </div> <div v-if="steps" class="ticks"> <div v-for="i in (steps + 1)" class="tick" :style="{ left: (((i - 1) / steps) * 100) + '%' }"></div> @@ -12,6 +12,7 @@ <div ref="thumbEl" v-tooltip="textConverter(finalValue)" class="thumb" :style="{ left: thumbPosition + 'px' }" @mousedown="onMousedown" @touchstart="onMousedown"></div> </div> </div> + <div class="caption"><slot name="caption"></slot></div> </div> </template> @@ -62,7 +63,7 @@ export default defineComponent({ const thumbEl = ref<HTMLElement>(); const rawValue = ref((props.modelValue - props.min) / (props.max - props.min)); - const steppedValue = computed(() => { + const steppedRawValue = computed(() => { if (props.step) { const step = props.step / (props.max - props.min); return (step * Math.round(rawValue.value / step)); @@ -71,7 +72,11 @@ export default defineComponent({ } }); const finalValue = computed(() => { - return (steppedValue.value * (props.max - props.min)) + props.min; + if (Number.isInteger(props.step)) { + return Math.round((steppedRawValue.value * (props.max - props.min)) + props.min); + } else { + return (steppedRawValue.value * (props.max - props.min)) + props.min; + } }); watch(finalValue, () => { context.emit('update:modelValue', finalValue.value); @@ -86,10 +91,10 @@ export default defineComponent({ if (containerEl.value == null) { thumbPosition.value = 0; } else { - thumbPosition.value = (containerEl.value.offsetWidth - thumbWidth.value) * steppedValue.value; + thumbPosition.value = (containerEl.value.offsetWidth - thumbWidth.value) * steppedRawValue.value; } }; - watch([steppedValue, containerEl], calcThumbPosition); + watch([steppedRawValue, containerEl], calcThumbPosition); let ro: ResizeObserver | undefined; @@ -154,7 +159,7 @@ export default defineComponent({ return { rawValue, finalValue, - steppedValue, + steppedRawValue, onMousedown, containerEl, thumbEl, diff --git a/packages/client/src/components/global/router-view.vue b/packages/client/src/components/global/router-view.vue index 56b53e012..7138faaa9 100644 --- a/packages/client/src/components/global/router-view.vue +++ b/packages/client/src/components/global/router-view.vue @@ -1,5 +1,5 @@ <template> -<KeepAlive max="5"> +<KeepAlive :max="defaultStore.state.numberOfPageCache"> <component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/> </KeepAlive> </template> @@ -7,6 +7,7 @@ <script lang="ts" setup> import { inject, nextTick, onMounted, onUnmounted, watch } from 'vue'; import { Router } from '@/nirax'; +import { defaultStore } from '@/store'; const props = defineProps<{ router?: Router; diff --git a/packages/client/src/menu.ts b/packages/client/src/menu.ts index 2c0126eb8..72e395160 100644 --- a/packages/client/src/menu.ts +++ b/packages/client/src/menu.ts @@ -81,12 +81,14 @@ export const menuDef = reactive({ os.popupMenu(items, ev.currentTarget ?? ev.target); }, }, + /* groups: { title: 'groups', icon: 'fas fa-users', show: computed(() => $i != null), to: '/my/groups', }, + */ antennas: { title: 'antennas', icon: 'fas fa-satellite', diff --git a/packages/client/src/pages/admin/object-storage.vue b/packages/client/src/pages/admin/object-storage.vue index bae5277f4..450fd134e 100644 --- a/packages/client/src/pages/admin/object-storage.vue +++ b/packages/client/src/pages/admin/object-storage.vue @@ -73,7 +73,6 @@ import { } from 'vue'; import XHeader from './_header_.vue'; import FormSwitch from '@/components/form/switch.vue'; import FormInput from '@/components/form/input.vue'; -import FormGroup from '@/components/form/group.vue'; import FormSuspense from '@/components/form/suspense.vue'; import FormSplit from '@/components/form/split.vue'; import FormSection from '@/components/form/section.vue'; diff --git a/packages/client/src/pages/gallery/edit.vue b/packages/client/src/pages/gallery/edit.vue index 6d1140ba3..fa3063bde 100644 --- a/packages/client/src/pages/gallery/edit.vue +++ b/packages/client/src/pages/gallery/edit.vue @@ -9,13 +9,13 @@ <template #label>{{ $ts.description }}</template> </FormTextarea> - <FormGroup> - <div v-for="file in files" :key="file.id" class="_formGroup wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }"> + <div class=""> + <div v-for="file in files" :key="file.id" class="wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }"> <div class="name">{{ file.name }}</div> <button v-tooltip="$ts.remove" class="remove _button" @click="remove(file)"><i class="fas fa-times"></i></button> </div> <FormButton primary @click="selectFile"><i class="fas fa-plus"></i> {{ $ts.attachFile }}</FormButton> - </FormGroup> + </div> <FormSwitch v-model="isSensitive">{{ $ts.markAsSensitive }}</FormSwitch> @@ -33,7 +33,6 @@ import FormButton from '@/components/ui/button.vue'; import FormInput from '@/components/form/input.vue'; import FormTextarea from '@/components/form/textarea.vue'; import FormSwitch from '@/components/form/switch.vue'; -import FormGroup from '@/components/form/group.vue'; import FormSuspense from '@/components/form/suspense.vue'; import { selectFiles } from '@/scripts/select-file'; import * as os from '@/os'; diff --git a/packages/client/src/pages/settings/deck.vue b/packages/client/src/pages/settings/deck.vue index edada683a..295357377 100644 --- a/packages/client/src/pages/settings/deck.vue +++ b/packages/client/src/pages/settings/deck.vue @@ -1,9 +1,6 @@ <template> <div class="_formRoot"> - <FormGroup> - <template #label>{{ i18n.ts.defaultNavigationBehaviour }}</template> - <FormSwitch v-model="navWindow">{{ i18n.ts.openInWindow }}</FormSwitch> - </FormGroup> + <FormSwitch v-model="navWindow">{{ i18n.ts.defaultNavigationBehaviour }}: {{ i18n.ts.openInWindow }}</FormSwitch> <FormSwitch v-model="alwaysShowMainColumn" class="_formBlock">{{ i18n.ts._deck.alwaysShowMainColumn }}</FormSwitch> @@ -35,7 +32,6 @@ import FormSwitch from '@/components/form/switch.vue'; import FormLink from '@/components/form/link.vue'; import FormRadios from '@/components/form/radios.vue'; import FormInput from '@/components/form/input.vue'; -import FormGroup from '@/components/form/group.vue'; import { deckStore } from '@/ui/deck/deck-store'; import * as os from '@/os'; import { unisonReload } from '@/scripts/unison-reload'; diff --git a/packages/client/src/pages/settings/general.vue b/packages/client/src/pages/settings/general.vue index ac2e3a496..e7339af14 100644 --- a/packages/client/src/pages/settings/general.vue +++ b/packages/client/src/pages/settings/general.vue @@ -81,10 +81,10 @@ <option value="force">{{ i18n.ts._nsfw.force }}</option> </FormSelect> - <FormGroup> - <template #label>{{ i18n.ts.defaultNavigationBehaviour }}</template> - <FormSwitch v-model="defaultSideView">{{ i18n.ts.openInSideView }}</FormSwitch> - </FormGroup> + <FormRange v-model="numberOfPageCache" :min="1" :max="10" :step="1" class="_formBlock"> + <template #label>{{ i18n.ts.numberOfPageCache }}</template> + <template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template> + </FormRange> <FormLink to="/settings/deck" class="_formBlock">{{ i18n.ts.deck }}</FormLink> @@ -97,7 +97,7 @@ import { computed, ref, watch } from 'vue'; import FormSwitch from '@/components/form/switch.vue'; import FormSelect from '@/components/form/select.vue'; import FormRadios from '@/components/form/radios.vue'; -import FormGroup from '@/components/form/group.vue'; +import FormRange from '@/components/form/range.vue'; import FormSection from '@/components/form/section.vue'; import FormLink from '@/components/form/link.vue'; import MkLink from '@/components/link.vue'; @@ -137,7 +137,7 @@ const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); const disablePagesScript = computed(defaultStore.makeGetterSetter('disablePagesScript')); const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm')); -const defaultSideView = computed(defaultStore.makeGetterSetter('defaultSideView')); +const numberOfPageCache = computed(defaultStore.makeGetterSetter('numberOfPageCache')); const instanceTicker = computed(defaultStore.makeGetterSetter('instanceTicker')); const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfiniteScroll')); const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu')); diff --git a/packages/client/src/scripts/get-note-menu.ts b/packages/client/src/scripts/get-note-menu.ts index 283c90362..632143f51 100644 --- a/packages/client/src/scripts/get-note-menu.ts +++ b/packages/client/src/scripts/get-note-menu.ts @@ -1,5 +1,6 @@ import { defineAsyncComponent, Ref, inject } from 'vue'; import * as misskey from 'misskey-js'; +import { pleaseLogin } from './please-login'; import { $i } from '@/account'; import { i18n } from '@/i18n'; import { instance } from '@/instance'; @@ -7,7 +8,6 @@ import * as os from '@/os'; import copyToClipboard from '@/scripts/copy-to-clipboard'; import { url } from '@/config'; import { noteActions } from '@/store'; -import { pleaseLogin } from './please-login'; export function getNoteMenu(props: { note: misskey.entities.Note; @@ -34,7 +34,7 @@ export function getNoteMenu(props: { if (canceled) return; os.api('notes/delete', { - noteId: appearNote.id + noteId: appearNote.id, }); }); } @@ -47,7 +47,7 @@ export function getNoteMenu(props: { if (canceled) return; os.api('notes/delete', { - noteId: appearNote.id + noteId: appearNote.id, }); os.post({ initialNote: appearNote, renote: appearNote.renote, reply: appearNote.reply, channel: appearNote.channel }); @@ -56,19 +56,19 @@ export function getNoteMenu(props: { function toggleFavorite(favorite: boolean): void { os.apiWithDialog(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', { - noteId: appearNote.id + noteId: appearNote.id, }); } function toggleWatch(watch: boolean): void { os.apiWithDialog(watch ? 'notes/watching/create' : 'notes/watching/delete', { - noteId: appearNote.id + noteId: appearNote.id, }); } function toggleThreadMute(mute: boolean): void { os.apiWithDialog(mute ? 'notes/thread-muting/create' : 'notes/thread-muting/delete', { - noteId: appearNote.id + noteId: appearNote.id, }); } @@ -84,12 +84,12 @@ export function getNoteMenu(props: { function togglePin(pin: boolean): void { os.apiWithDialog(pin ? 'i/pin' : 'i/unpin', { - noteId: appearNote.id + noteId: appearNote.id, }, undefined, null, res => { if (res.id === '72dab508-c64d-498f-8740-a8eec1ba385a') { os.alert({ type: 'error', - text: i18n.ts.pinLimitExceeded + text: i18n.ts.pinLimitExceeded, }); } }); @@ -104,26 +104,26 @@ export function getNoteMenu(props: { const { canceled, result } = await os.form(i18n.ts.createNewClip, { name: { type: 'string', - label: i18n.ts.name + label: i18n.ts.name, }, description: { type: 'string', required: false, multiline: true, - label: i18n.ts.description + label: i18n.ts.description, }, isPublic: { type: 'boolean', label: i18n.ts.public, - default: false - } + default: false, + }, }); if (canceled) return; const clip = await os.apiWithDialog('clips/create', result); os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id }); - } + }, }, null, ...clips.map(clip => ({ text: clip.name, action: () => { @@ -146,9 +146,9 @@ export function getNoteMenu(props: { text: err.message + '\n' + err.id, }); } - } + }, ); - } + }, }))], props.menuButton.value, { }).then(focus); } @@ -193,86 +193,86 @@ export function getNoteMenu(props: { let menu; if ($i) { const statePromise = os.api('notes/state', { - noteId: appearNote.id + noteId: appearNote.id, }); menu = [ - ...( - props.currentClipPage?.value.userId === $i.id ? [{ - icon: 'fas fa-circle-minus', - text: i18n.ts.unclip, - danger: true, - action: unclip, - }, null] : [] - ), - { - icon: 'fas fa-copy', - text: i18n.ts.copyContent, - action: copyContent - }, { - icon: 'fas fa-link', - text: i18n.ts.copyLink, - action: copyLink - }, (appearNote.url || appearNote.uri) ? { - icon: 'fas fa-external-link-square-alt', - text: i18n.ts.showOnRemote, - action: () => { - window.open(appearNote.url || appearNote.uri, '_blank'); - } - } : undefined, - { - icon: 'fas fa-share-alt', - text: i18n.ts.share, - action: share - }, - instance.translatorAvailable ? { - icon: 'fas fa-language', - text: i18n.ts.translate, - action: translate - } : undefined, - null, - statePromise.then(state => state.isFavorited ? { - icon: 'fas fa-star', - text: i18n.ts.unfavorite, - action: () => toggleFavorite(false) - } : { - icon: 'fas fa-star', - text: i18n.ts.favorite, - action: () => toggleFavorite(true) - }), - { - icon: 'fas fa-paperclip', - text: i18n.ts.clip, - action: () => clip() - }, - (appearNote.userId !== $i.id) ? statePromise.then(state => state.isWatching ? { - icon: 'fas fa-eye-slash', - text: i18n.ts.unwatch, - action: () => toggleWatch(false) - } : { - icon: 'fas fa-eye', - text: i18n.ts.watch, - action: () => toggleWatch(true) - }) : undefined, - statePromise.then(state => state.isMutedThread ? { - icon: 'fas fa-comment-slash', - text: i18n.ts.unmuteThread, - action: () => toggleThreadMute(false) - } : { - icon: 'fas fa-comment-slash', - text: i18n.ts.muteThread, - action: () => toggleThreadMute(true) - }), - appearNote.userId === $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? { - icon: 'fas fa-thumbtack', - text: i18n.ts.unpin, - action: () => togglePin(false) - } : { - icon: 'fas fa-thumbtack', - text: i18n.ts.pin, - action: () => togglePin(true) - } : undefined, - /* + ...( + props.currentClipPage?.value.userId === $i.id ? [{ + icon: 'fas fa-circle-minus', + text: i18n.ts.unclip, + danger: true, + action: unclip, + }, null] : [] + ), + { + icon: 'fas fa-copy', + text: i18n.ts.copyContent, + action: copyContent, + }, { + icon: 'fas fa-link', + text: i18n.ts.copyLink, + action: copyLink, + }, (appearNote.url || appearNote.uri) ? { + icon: 'fas fa-external-link-square-alt', + text: i18n.ts.showOnRemote, + action: () => { + window.open(appearNote.url || appearNote.uri, '_blank'); + }, + } : undefined, + { + icon: 'fas fa-share-alt', + text: i18n.ts.share, + action: share, + }, + instance.translatorAvailable ? { + icon: 'fas fa-language', + text: i18n.ts.translate, + action: translate, + } : undefined, + null, + statePromise.then(state => state.isFavorited ? { + icon: 'fas fa-star', + text: i18n.ts.unfavorite, + action: () => toggleFavorite(false), + } : { + icon: 'fas fa-star', + text: i18n.ts.favorite, + action: () => toggleFavorite(true), + }), + { + icon: 'fas fa-paperclip', + text: i18n.ts.clip, + action: () => clip(), + }, + (appearNote.userId !== $i.id) ? statePromise.then(state => state.isWatching ? { + icon: 'fas fa-eye-slash', + text: i18n.ts.unwatch, + action: () => toggleWatch(false), + } : { + icon: 'fas fa-eye', + text: i18n.ts.watch, + action: () => toggleWatch(true), + }) : undefined, + statePromise.then(state => state.isMutedThread ? { + icon: 'fas fa-comment-slash', + text: i18n.ts.unmuteThread, + action: () => toggleThreadMute(false), + } : { + icon: 'fas fa-comment-slash', + text: i18n.ts.muteThread, + action: () => toggleThreadMute(true), + }), + appearNote.userId === $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? { + icon: 'fas fa-thumbtack', + text: i18n.ts.unpin, + action: () => togglePin(false), + } : { + icon: 'fas fa-thumbtack', + text: i18n.ts.pin, + action: () => togglePin(true), + } : undefined, + /* ...($i.isModerator || $i.isAdmin ? [ null, { @@ -282,52 +282,52 @@ export function getNoteMenu(props: { }] : [] ),*/ - ...(appearNote.userId !== $i.id ? [ - null, - { - icon: 'fas fa-exclamation-circle', - text: i18n.ts.reportAbuse, - action: () => { - const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`; - os.popup(defineAsyncComponent(() => import('@/components/abuse-report-window.vue')), { - user: appearNote.user, - initialComment: `Note: ${u}\n-----\n` - }, {}, 'closed'); - } - }] + ...(appearNote.userId !== $i.id ? [ + null, + { + icon: 'fas fa-exclamation-circle', + text: i18n.ts.reportAbuse, + action: () => { + const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`; + os.popup(defineAsyncComponent(() => import('@/components/abuse-report-window.vue')), { + user: appearNote.user, + initialComment: `Note: ${u}\n-----\n`, + }, {}, 'closed'); + }, + }] : [] - ), - ...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [ - null, - appearNote.userId === $i.id ? { - icon: 'fas fa-edit', - text: i18n.ts.deleteAndEdit, - action: delEdit - } : undefined, - { - icon: 'fas fa-trash-alt', - text: i18n.ts.delete, - danger: true, - action: del - }] + ), + ...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [ + null, + appearNote.userId === $i.id ? { + icon: 'fas fa-edit', + text: i18n.ts.deleteAndEdit, + action: delEdit, + } : undefined, + { + icon: 'fas fa-trash-alt', + text: i18n.ts.delete, + danger: true, + action: del, + }] : [] - )] + )] .filter(x => x !== undefined); } else { menu = [{ icon: 'fas fa-copy', text: i18n.ts.copyContent, - action: copyContent + action: copyContent, }, { icon: 'fas fa-link', text: i18n.ts.copyLink, - action: copyLink + action: copyLink, }, (appearNote.url || appearNote.uri) ? { icon: 'fas fa-external-link-square-alt', text: i18n.ts.showOnRemote, action: () => { window.open(appearNote.url || appearNote.uri, '_blank'); - } + }, } : undefined] .filter(x => x !== undefined); } @@ -338,7 +338,7 @@ export function getNoteMenu(props: { text: action.title, action: () => { action.handler(appearNote); - } + }, }))]); } diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts index a273a46c7..94d9d9138 100644 --- a/packages/client/src/store.ts +++ b/packages/client/src/store.ts @@ -233,6 +233,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: true, }, + numberOfPageCache: { + where: 'device', + default: 5, + }, aiChanMode: { where: 'device', default: false, diff --git a/packages/client/src/widgets/instance-cloud.vue b/packages/client/src/widgets/instance-cloud.vue index cb66c5fa3..7dc4b4ce9 100644 --- a/packages/client/src/widgets/instance-cloud.vue +++ b/packages/client/src/widgets/instance-cloud.vue @@ -1,5 +1,5 @@ <template> -<MkContainer :naked="widgetProps.transparent" class="mkw-instance-cloud"> +<MkContainer :naked="widgetProps.transparent" :show-header="false" class="mkw-instance-cloud"> <div class=""> <MkTagCloud v-if="activeInstances"> <li v-for="instance in activeInstances">