enhance(client): MkNoteのリアクションの表示数は16に制限 (#9841)
* enhance(client): MkNoteのリアクションの表示数は16に制限・リアクションの横の?ボタンでリアクション詳細 * info-circleにする * - Layout Shiftが起こらないように - 自分のリアクションは必ずつける * https://github.com/misskey-dev/misskey/pull/9841#issuecomment-1423786235 * remove logger * refactor * refactor Co-authored-by: acid-chicken <root@acid-chicken.com> * Revert "https://github.com/misskey-dev/misskey/pull/9841#issuecomment-1423786235" This reverts commit ec1315b1fb207e0c5b2a5f2f4a00de7379c7a29b. * wip * wip * 🎨 * fix * fix * fix * 🎨 * wip * remove extras from MkNoteDetailed * もっと! * no v-if * dashed --------- Co-authored-by: acid-chicken <root@acid-chicken.com>
This commit is contained in:
parent
3004fe573d
commit
6f33be6c75
3 changed files with 76 additions and 12 deletions
|
@ -5,7 +5,7 @@
|
||||||
ref="el"
|
ref="el"
|
||||||
v-hotkey="keymap"
|
v-hotkey="keymap"
|
||||||
:class="$style.root"
|
:class="$style.root"
|
||||||
:tabindex="!isDeleted ? '-1' : null"
|
:tabindex="!isDeleted ? '-1' : undefined"
|
||||||
>
|
>
|
||||||
<MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
|
<MkNoteSub v-if="appearNote.reply && !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="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div>
|
||||||
|
@ -77,7 +77,13 @@
|
||||||
<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>
|
||||||
<footer :class="$style.footer">
|
<footer :class="$style.footer">
|
||||||
<MkReactionsViewer :note="appearNote"/>
|
<MkReactionsViewer :note="appearNote" :max-number="16">
|
||||||
|
<template v-slot:more>
|
||||||
|
<button class="_button" :class="$style.reactionDetailsButton" @click="showReactions">
|
||||||
|
{{ i18n.ts.more }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</MkReactionsViewer>
|
||||||
<button :class="$style.footerButton" class="_button" @click="reply()">
|
<button :class="$style.footerButton" class="_button" @click="reply()">
|
||||||
<i class="ti ti-arrow-back-up"></i>
|
<i class="ti ti-arrow-back-up"></i>
|
||||||
<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p>
|
<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p>
|
||||||
|
@ -120,7 +126,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, inject, onMounted, onUnmounted, reactive, ref, shallowRef, Ref } from 'vue';
|
import { computed, inject, onMounted, onUnmounted, reactive, ref, shallowRef, Ref, defineAsyncComponent } from 'vue';
|
||||||
import * as mfm from 'mfm-js';
|
import * as mfm from 'mfm-js';
|
||||||
import * as misskey from 'misskey-js';
|
import * as misskey from 'misskey-js';
|
||||||
import MkNoteSub from '@/components/MkNoteSub.vue';
|
import MkNoteSub from '@/components/MkNoteSub.vue';
|
||||||
|
@ -196,7 +202,7 @@ const isLong = (appearNote.cw == null && appearNote.text != null && (
|
||||||
const collapsed = ref(appearNote.cw == null && isLong);
|
const collapsed = ref(appearNote.cw == null && isLong);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref(checkWordMute(appearNote, $i, defaultStore.state.mutedWords));
|
const muted = ref(checkWordMute(appearNote, $i, defaultStore.state.mutedWords));
|
||||||
const translation = ref(null);
|
const translation = ref<any>(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
|
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
|
||||||
const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || appearNote.userId === $i.id);
|
const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || appearNote.userId === $i.id);
|
||||||
|
@ -361,6 +367,12 @@ function readPromo() {
|
||||||
});
|
});
|
||||||
isDeleted.value = true;
|
isDeleted.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showReactions(): void {
|
||||||
|
os.popup(defineAsyncComponent(() => import('@/components/MkReactedUsersDialog.vue')), {
|
||||||
|
noteId: appearNote.id,
|
||||||
|
}, {}, 'closed');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
@ -697,4 +709,19 @@ function readPromo() {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reactionDetailsButton {
|
||||||
|
display: inline-block;
|
||||||
|
height: 32px;
|
||||||
|
margin: 2px;
|
||||||
|
padding: 0 6px;
|
||||||
|
border: dashed 1px var(--divider);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: transparent;
|
||||||
|
opacity: .8;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--X5);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -107,7 +107,7 @@ useTooltip(buttonEl, async (showing) => {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
&.canToggle {
|
&.canToggle {
|
||||||
background: rgba(0, 0, 0, 0.05);
|
background: var(--buttonBg);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(0, 0, 0, 0.1);
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
|
|
@ -7,23 +7,60 @@
|
||||||
:move-class="$store.state.animation ? $style.transition_x_move : ''"
|
:move-class="$store.state.animation ? $style.transition_x_move : ''"
|
||||||
tag="div" :class="$style.root"
|
tag="div" :class="$style.root"
|
||||||
>
|
>
|
||||||
<XReaction v-for="(count, reaction) in note.reactions" :key="reaction" :reaction="reaction" :count="count" :is-initial="initialReactions.has(reaction)" :note="note"/>
|
<XReaction v-for="[reaction, count] in reactions" :key="reaction" :reaction="reaction" :count="count" :is-initial="initialReactions.has(reaction)" :note="note"/>
|
||||||
|
<slot v-if="hasMoreReactions" name="more" />
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
|
||||||
import * as misskey from 'misskey-js';
|
import * as misskey from 'misskey-js';
|
||||||
import { $i } from '@/account';
|
|
||||||
import XReaction from '@/components/MkReactionsViewer.reaction.vue';
|
import XReaction from '@/components/MkReactionsViewer.reaction.vue';
|
||||||
|
import { watch } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
note: misskey.entities.Note;
|
note: misskey.entities.Note;
|
||||||
}>();
|
maxNumber?: number;
|
||||||
|
}>(), {
|
||||||
|
maxNumber: Infinity,
|
||||||
|
});
|
||||||
|
|
||||||
const initialReactions = new Set(Object.keys(props.note.reactions));
|
const initialReactions = new Set(Object.keys(props.note.reactions));
|
||||||
|
|
||||||
const isMe = computed(() => $i && $i.id === props.note.userId);
|
let reactions = $ref<[string, number][]>([]);
|
||||||
|
let hasMoreReactions = $ref(false);
|
||||||
|
|
||||||
|
if (props.note.myReaction && !Object.keys(reactions).includes(props.note.myReaction)) {
|
||||||
|
reactions[props.note.myReaction] = props.note.reactions[props.note.myReaction];
|
||||||
|
}
|
||||||
|
|
||||||
|
watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumber]) => {
|
||||||
|
let newReactions: [string, number][] = [];
|
||||||
|
hasMoreReactions = Object.keys(newSource).length > maxNumber;
|
||||||
|
|
||||||
|
for (let i = 0; i < reactions.length; i++) {
|
||||||
|
const reaction = reactions[i][0];
|
||||||
|
if (reaction in newSource && newSource[reaction] !== 0) {
|
||||||
|
reactions[i][1] = newSource[reaction];
|
||||||
|
newReactions.push(reactions[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newReactionsNames = newReactions.map(([x]) => x);
|
||||||
|
newReactions = [
|
||||||
|
...newReactions,
|
||||||
|
...Object.entries(newSource)
|
||||||
|
.sort(([, a], [, b]) => b - a)
|
||||||
|
.filter(([y], i) => i < maxNumber && !newReactionsNames.includes(y)),
|
||||||
|
]
|
||||||
|
|
||||||
|
newReactions = newReactions.slice(0, props.maxNumber);
|
||||||
|
|
||||||
|
if (props.note.myReaction && !newReactions.map(([x]) => x).includes(props.note.myReaction)) {
|
||||||
|
newReactions.push([props.note.myReaction, newSource[props.note.myReaction]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
reactions = newReactions;
|
||||||
|
}, { immediate: true, deep: true });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
Loading…
Reference in a new issue