note collapsing methods
This commit is contained in:
parent
1fabab6274
commit
e1a6ab9114
|
@ -515,6 +515,11 @@ regenerate: "再生成"
|
||||||
fontSize: "フォントサイズ"
|
fontSize: "フォントサイズ"
|
||||||
mediaListWithOneImageAppearance: "画像が1枚のみのメディアリストの高さ"
|
mediaListWithOneImageAppearance: "画像が1枚のみのメディアリストの高さ"
|
||||||
limitTo: "{x}を上限に"
|
limitTo: "{x}を上限に"
|
||||||
|
collapsingNoteSize: "ノートを畳む大きさ"
|
||||||
|
collapsingNoteCondition: "ノートを畳む条件"
|
||||||
|
detailedCalculation: "高精度の算出方法"
|
||||||
|
legacyCalculation: "旧式の算出方法"
|
||||||
|
seeRenderedSize: "実際の表示サイズに基づく"
|
||||||
noFollowRequests: "フォロー申請はありません"
|
noFollowRequests: "フォロー申請はありません"
|
||||||
openImageInNewTab: "画像を新しいタブで開く"
|
openImageInNewTab: "画像を新しいタブで開く"
|
||||||
dashboard: "ダッシュボード"
|
dashboard: "ダッシュボード"
|
||||||
|
|
|
@ -56,42 +56,44 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
|
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
|
||||||
<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;"/>
|
<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;"/>
|
||||||
</p>
|
</p>
|
||||||
<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]">
|
<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]" :style="`max-height: ${collapseSize}`">
|
||||||
<div :class="$style.text">
|
<div ref="collapsibleInner">
|
||||||
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
|
<div :class="$style.text">
|
||||||
<MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA>
|
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
|
||||||
<Mfm
|
<MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA>
|
||||||
v-if="appearNote.text"
|
<Mfm
|
||||||
:parsedNodes="parsed"
|
v-if="appearNote.text"
|
||||||
:text="appearNote.text"
|
:parsedNodes="parsed"
|
||||||
:author="appearNote.user"
|
:text="appearNote.text"
|
||||||
:nyaize="'respect'"
|
:author="appearNote.user"
|
||||||
:emojiUrls="appearNote.emojis"
|
:nyaize="'respect'"
|
||||||
:enableEmojiMenu="true"
|
:emojiUrls="appearNote.emojis"
|
||||||
:enableEmojiMenuReaction="true"
|
:enableEmojiMenu="true"
|
||||||
/>
|
:enableEmojiMenuReaction="true"
|
||||||
<div v-if="translating || translation" :class="$style.translation">
|
/>
|
||||||
<MkLoading v-if="translating" mini/>
|
<div v-if="translating || translation" :class="$style.translation">
|
||||||
<div v-else-if="translation">
|
<MkLoading v-if="translating" mini/>
|
||||||
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
<div v-else-if="translation">
|
||||||
<Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
|
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
||||||
|
<Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="appearNote.files && appearNote.files.length > 0">
|
||||||
|
<MkMediaList :mediaList="appearNote.files"/>
|
||||||
|
</div>
|
||||||
|
<MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/>
|
||||||
|
<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>
|
||||||
|
<button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false">
|
||||||
|
<span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span>
|
||||||
|
</button>
|
||||||
|
<button v-else-if="isLong && !collapsed" :class="$style.showLess" class="_button" @click="collapsed = true">
|
||||||
|
<span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="appearNote.files && appearNote.files.length > 0">
|
|
||||||
<MkMediaList :mediaList="appearNote.files"/>
|
|
||||||
</div>
|
|
||||||
<MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/>
|
|
||||||
<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>
|
|
||||||
<button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false">
|
|
||||||
<span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span>
|
|
||||||
</button>
|
|
||||||
<button v-else-if="isLong && !collapsed" :class="$style.showLess" class="_button" @click="collapsed = true">
|
|
||||||
<span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA>
|
<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA>
|
||||||
</div>
|
</div>
|
||||||
|
@ -196,7 +198,7 @@ import { getNoteSummary } from '@/scripts/get-note-summary.js';
|
||||||
import { MenuItem } from '@/types/menu.js';
|
import { MenuItem } from '@/types/menu.js';
|
||||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||||
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
|
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
|
||||||
import { shouldCollapsed } from '@/scripts/collapsed.js';
|
import { shouldCollapsedLegacy, shouldCollapsed } from '@/scripts/collapsed.js';
|
||||||
import { isEnabledUrlPreview } from '@/instance.js';
|
import { isEnabledUrlPreview } from '@/instance.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
|
@ -260,8 +262,22 @@ const isMyRenote = $i && ($i.id === note.value.userId);
|
||||||
const showContent = ref(false);
|
const showContent = ref(false);
|
||||||
const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
|
const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
|
||||||
const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null);
|
const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null);
|
||||||
const isLong = shouldCollapsed(appearNote.value, parsed.value, urls.value ?? []);
|
const collapsibleArea = ref(null);
|
||||||
const collapsed = ref(appearNote.value.cw == null && isLong);
|
const collapseSize = computed(() => ({
|
||||||
|
small: 9,
|
||||||
|
medium: 13.5,
|
||||||
|
large: 18,
|
||||||
|
})[defaultStore.state.collapsingNoteSize]);
|
||||||
|
const isLong = computed(() => {
|
||||||
|
switch (defaultStore.state.collapsingNoteCondition) {
|
||||||
|
case 'detailedCalculation': return shouldCollapsed(appearNote.value, collapseSize.value, parsed.value, urls.value ?? []);
|
||||||
|
case 'seeRenderedSize': return collapsibleArea.value == null ? false : collapsibleInner.value.clientHeight > collapseSize.value * parseFloat(getComputedStyle(collapsibleInner.value).fontSize);
|
||||||
|
// fail safe
|
||||||
|
case 'legacyCalculation':
|
||||||
|
default: return shouldCollapsedLegacy(appearNote.value, urls.value ?? []);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const collapsed = ref(appearNote.value.cw == null && isLong.value);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref(checkMute(appearNote.value, $i?.mutedWords));
|
const muted = ref(checkMute(appearNote.value, $i?.mutedWords));
|
||||||
const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords, true));
|
const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords, true));
|
||||||
|
@ -808,7 +824,6 @@ function emitUpdReaction(emoji: string, delta: number) {
|
||||||
|
|
||||||
.contentCollapsed {
|
.contentCollapsed {
|
||||||
position: relative;
|
position: relative;
|
||||||
max-height: 9em;
|
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,18 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<option value="1_1">{{ i18n.tsx.limitTo({ x: '1:1' }) }}</option>
|
<option value="1_1">{{ i18n.tsx.limitTo({ x: '1:1' }) }}</option>
|
||||||
<option value="2_3">{{ i18n.tsx.limitTo({ x: '2:3' }) }}</option>
|
<option value="2_3">{{ i18n.tsx.limitTo({ x: '2:3' }) }}</option>
|
||||||
</MkRadios>
|
</MkRadios>
|
||||||
|
<MkRadios v-model="collapsingNoteCondition">
|
||||||
|
<template #label>{{ i18n.ts.collapsingNoteCondition }}</template>
|
||||||
|
<option value="detailedCalculation">{{ i18n.ts.detailedCalculation }}</option>
|
||||||
|
<option value="legacyCalculation">{{ i18n.ts.legacyCalculation }}</option>
|
||||||
|
<option value="seeRenderedSize">{{ i18n.ts.seeRenderedSize }}</option>
|
||||||
|
</MkRadios>
|
||||||
|
<MkRadios v-model="collapsingNoteSize" v-if="collapsingNoteCondition !== 'legacyCalculation'">
|
||||||
|
<template #label>{{ i18n.ts.collapsingNoteSize }}</template>
|
||||||
|
<option value="large">{{ i18n.ts.large }}</option>
|
||||||
|
<option value="medium">{{ i18n.ts.medium }}</option>
|
||||||
|
<option value="small">{{ i18n.tsx.small }}</option>
|
||||||
|
</MkRadios>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
|
@ -306,6 +318,8 @@ const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('
|
||||||
const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars'));
|
const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars'));
|
||||||
const showAvatarDecorations = computed(defaultStore.makeGetterSetter('showAvatarDecorations'));
|
const showAvatarDecorations = computed(defaultStore.makeGetterSetter('showAvatarDecorations'));
|
||||||
const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance'));
|
const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance'));
|
||||||
|
const collapsingNoteSize = computed(defaultStore.makeGetterSetter('collapsingNoteSize'));
|
||||||
|
const collapsingNoteCondition = computed(defaultStore.makeGetterSetter('collapsingNoteCondition'));
|
||||||
const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition'));
|
const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition'));
|
||||||
const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis'));
|
const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis'));
|
||||||
const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn'));
|
const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn'));
|
||||||
|
|
|
@ -102,6 +102,8 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
|
||||||
'aiChanMode',
|
'aiChanMode',
|
||||||
'devMode',
|
'devMode',
|
||||||
'mediaListWithOneImageAppearance',
|
'mediaListWithOneImageAppearance',
|
||||||
|
'collapsingNoteSize',
|
||||||
|
'collapsingNoteCondition',
|
||||||
'notificationPosition',
|
'notificationPosition',
|
||||||
'notificationStackAxis',
|
'notificationStackAxis',
|
||||||
'enableCondensedLineForAcct',
|
'enableCondensedLineForAcct',
|
||||||
|
|
|
@ -23,7 +23,7 @@ export function shouldCollapsedLegacy(note: Misskey.entities.Note, urls: string[
|
||||||
return collapsed;
|
return collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shouldCollapsed(note: Misskey.entities.Note, ast?: mfm.MfmNode[] | null, urls?: string[]): boolean {
|
export function shouldCollapsed(note: Misskey.entities.Note, limitY: number, ast?: mfm.MfmNode[] | null, urls?: string[]): boolean {
|
||||||
if (note.cw != null) return false;
|
if (note.cw != null) return false;
|
||||||
if (note.text == null) return false;
|
if (note.text == null) return false;
|
||||||
if (note.files && note.files.length >= 5) return true;
|
if (note.files && note.files.length >= 5) return true;
|
||||||
|
@ -34,7 +34,7 @@ export function shouldCollapsed(note: Misskey.entities.Note, ast?: mfm.MfmNode[]
|
||||||
|
|
||||||
// しきい値(X方向の文字数は半角換算)
|
// しきい値(X方向の文字数は半角換算)
|
||||||
const limitX = 55;
|
const limitX = 55;
|
||||||
const limitY = 13.5;
|
// const limitY = 13.5;
|
||||||
|
|
||||||
let forceCollapsed = false;
|
let forceCollapsed = false;
|
||||||
|
|
||||||
|
@ -171,6 +171,12 @@ export function shouldCollapsed(note: Misskey.entities.Note, ast?: mfm.MfmNode[]
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'border': {
|
||||||
|
const width = safeParseFloat(node.props.args.width) ?? 1;
|
||||||
|
addHeightsInline(getHeightForEachLine(node.children, depth + 1).map(l => l + (width * 2)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
addHeightsInline(getHeightForEachLine(node.children, depth + 1));
|
addHeightsInline(getHeightForEachLine(node.children, depth + 1));
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -382,6 +382,14 @@ export const defaultStore = markRaw(new Storage('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: 'expand' as 'expand' | '16_9' | '1_1' | '2_3',
|
default: 'expand' as 'expand' | '16_9' | '1_1' | '2_3',
|
||||||
},
|
},
|
||||||
|
collapsingNoteSize: {
|
||||||
|
where: 'device',
|
||||||
|
default: 'medium' as 'small' | 'medium' | 'large',
|
||||||
|
},
|
||||||
|
collapsingNoteCondition: {
|
||||||
|
where: 'device',
|
||||||
|
default: 'detailedCalculation' as 'detailedCalculation' | 'legacyCalculation' | 'seeRenderedSize',
|
||||||
|
},
|
||||||
notificationPosition: {
|
notificationPosition: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: 'rightBottom' as 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom',
|
default: 'rightBottom' as 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom',
|
||||||
|
|
Loading…
Reference in New Issue