diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 32bf47c209..b187fe2473 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -473,6 +473,9 @@ createAccount: "アカウントを作成" existingAccount: "既存のアカウント" regenerate: "再生成" fontSize: "フォントサイズ" +mediaListWithOneImageAppearance: "画像が1枚のみのメディアリストの高さ" +expandAsImage: "画像に応じて拡張" +fixTo: "{x}に固定" noFollowRequests: "フォロー申請はありません" openImageInNewTab: "画像を新しいタブで開く" dashboard: "ダッシュボード" diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue index 7cfe5d5f8f..9178a3dfe4 100644 --- a/packages/frontend/src/components/MkMediaList.vue +++ b/packages/frontend/src/components/MkMediaList.vue @@ -2,7 +2,10 @@ <div> <XBanner v-for="media in mediaList.filter(media => !previewable(media))" :key="media.id" :media="media"/> <div v-if="mediaList.filter(media => previewable(media)).length > 0" :class="$style.container"> - <div ref="gallery" :class="[$style.medias, count <= 4 ? $style['n' + count] : $style.nMany]"> + <div ref="gallery" :class="[ + $style.medias, + count <= 4 ? $style['n' + count] : $style.nMany, + $style[`n1${defaultStore.reactiveState.mediaListWithOneImageAppearance.value}`]]"> <template v-for="media in mediaList.filter(media => previewable(media))"> <XVideo v-if="media.type.startsWith('video')" :key="`video:${media.id}`" :class="$style.media" :video="media"/> <XImage v-else-if="media.type.startsWith('image')" :key="`image:${media.id}`" :class="$style.media" class="image" :data-id="media.id" :image="media" :raw="raw"/> @@ -23,6 +26,7 @@ import XImage from '@/components/MkMediaImage.vue'; import XVideo from '@/components/MkMediaVideo.vue'; import * as os from '@/os'; import { FILE_TYPE_BROWSERSAFE } from '@/const'; +import { defaultStore } from '@/store'; const props = defineProps<{ mediaList: misskey.entities.DriveFile[]; @@ -160,7 +164,15 @@ const previewable = (file: misskey.entities.DriveFile): boolean => { &.n1 { grid-template-rows: 1fr; min-height: 64px; - max-height: max(min(calc(var(--containerHeight, 100svh) * 0.5), 50svh, 334px), 64px); + max-height: 128px; + + &.n1expand { + max-height: max(min(calc(var(--containerHeight, 100svh) * 0.5), 50svh, 334px), 64px); + } + + &.n116_9 { + aspect-ratio: 16/9; + } } &.n2 { diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue index fcf454c77a..5db2f5ee6d 100644 --- a/packages/frontend/src/components/MkRadio.vue +++ b/packages/frontend/src/components/MkRadio.vue @@ -24,7 +24,7 @@ import { } from 'vue'; const props = defineProps<{ modelValue: any; value: any; - disabled: boolean; + disabled?: boolean; }>(); const emit = defineEmits<{ diff --git a/packages/frontend/src/components/MkRadios.vue b/packages/frontend/src/components/MkRadios.vue index 8590ccf9ae..5ef686c4dd 100644 --- a/packages/frontend/src/components/MkRadios.vue +++ b/packages/frontend/src/components/MkRadios.vue @@ -1,5 +1,5 @@ <script lang="ts"> -import { defineComponent, h } from 'vue'; +import { VNode, defineComponent, h } from 'vue'; import MkRadio from './MkRadio.vue'; export default defineComponent({ @@ -22,31 +22,32 @@ export default defineComponent({ }, }, render() { + if (!this.$slots.default) return null; let options = this.$slots.default(); const label = this.$slots.label && this.$slots.label(); const caption = this.$slots.caption && this.$slots.caption(); // なぜかFragmentになることがあるため - if (options.length === 1 && options[0].props == null) options = options[0].children; + if (options.length === 1 && options[0].props == null) options = options[0].children as VNode[]; return h('div', { class: 'novjtcto', }, [ ...(label ? [h('div', { class: 'label', - }, [label])] : []), + }, () => [label])] : []), h('div', { class: 'body', }, options.map(option => h(MkRadio, { key: option.key, - value: option.props.value, + value: option.props?.value, modelValue: this.value, 'onUpdate:modelValue': value => this.value = value, - }, option.children)), + }, () => option.children)), ), ...(caption ? [h('div', { class: 'caption', - }, [caption])] : []), + }, () => [caption])] : []), ]); }, }); diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index b960927642..4ca4454203 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -82,10 +82,8 @@ <MkRadios v-model="mediaListWithOneImageAppearance"> <template #label>{{ i18n.ts.mediaListWithOneImageAppearance }}</template> - <option :value="null"><span style="font-size: 14px;">Aa</span></option> - <option value="1"><span style="font-size: 15px;">Aa</span></option> - <option value="2"><span style="font-size: 16px;">Aa</span></option> - <option value="3"><span style="font-size: 17px;">Aa</span></option> + <option value="expand">{{ i18n.ts.expandAsImage }}</option> + <option value="16_9">{{ i18n.t('fixTo', { x: '16:9' }) }}</option> </MkRadios> </div> </FormSection> @@ -176,6 +174,7 @@ const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfin const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu')); const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars')); const aiChanMode = computed(defaultStore.makeGetterSetter('aiChanMode')); +const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance')); watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index 28e39236f7..d43bc2c51b 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -88,6 +88,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ 'squareAvatars', 'numberOfPageCache', 'aiChanMode', + 'mediaListWithOneImageAppearance', ]; const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [ 'lightTheme', diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index f50812ca04..65d5bab3f6 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -304,7 +304,7 @@ export const defaultStore = markRaw(new Storage('base', { }, mediaListWithOneImageAppearance: { where: 'device', - default: 'expand' as 'expand' | '16:9', + default: 'expand' as 'expand' | '16_9', }, }));