Merge pull request 'Add reactions tab to detailed notes view' (#10161) from Freeplay/calckey:notes into develop
Reviewed-on: https://codeberg.org/calckey/calckey/pulls/10161
This commit is contained in:
commit
7a75ba477d
6 changed files with 138 additions and 152 deletions
|
@ -55,6 +55,13 @@
|
|||
</template>
|
||||
{{ i18n.ts._notification._types.quote }}
|
||||
</option>
|
||||
<option value="reactions">
|
||||
<i class="ph-smiley ph-bold ph-lg"></i>
|
||||
<template v-if="reactionsCount > 0">
|
||||
<span class="count">{{ reactionsCount }}</span>
|
||||
</template>
|
||||
{{ i18n.ts.reaction }}
|
||||
</option>
|
||||
<option value="clips">
|
||||
<i class="ph-paperclip ph-bold ph-lg"></i>
|
||||
<template v-if="clips?.length > 0">
|
||||
|
@ -128,6 +135,11 @@
|
|||
</MkA>
|
||||
</div>
|
||||
<MkLoading v-else-if="tab === 'clips' && clips.length > 0" />
|
||||
|
||||
<MkReactedUsers
|
||||
v-if="tab === 'reactions' && reactionsCount > 0"
|
||||
:note-id="appearNote.id"
|
||||
></MkReactedUsers>
|
||||
</div>
|
||||
<div v-else class="_panel muted" @click="muted.muted = false">
|
||||
<I18n :src="softMuteReasonI18nSrc(muted.what)" tag="small">
|
||||
|
@ -165,6 +177,7 @@ import XStarButton from "@/components/MkStarButton.vue";
|
|||
import XRenoteButton from "@/components/MkRenoteButton.vue";
|
||||
import MkPagination from "@/components/MkPagination.vue";
|
||||
import MkUserCardMini from "@/components/MkUserCardMini.vue";
|
||||
import MkReactedUsers from "@/components/MkReactedUsers.vue";
|
||||
import { pleaseLogin } from "@/scripts/please-login";
|
||||
import { getWordSoftMute } from "@/scripts/check-word-mute";
|
||||
import { userPage } from "@/filters/user";
|
||||
|
@ -240,6 +253,8 @@ let clips = $ref();
|
|||
let renotes = $ref();
|
||||
let isScrolling;
|
||||
|
||||
const reactionsCount = Object.values(props.note.reactions).reduce((x,y) => x + y, 0);
|
||||
|
||||
const keymap = {
|
||||
r: () => reply(true),
|
||||
"e|a|plus": () => react(true),
|
||||
|
@ -509,14 +524,22 @@ onUnmounted(() => {
|
|||
}
|
||||
}
|
||||
|
||||
> :deep(.chips) {
|
||||
padding: 6px 32px 12px;
|
||||
> :deep(.chips), {
|
||||
padding-block: 6px 12px;
|
||||
padding-left: 32px;
|
||||
&:last-child {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
> :deep(.user-card-mini) {
|
||||
> :deep(.user-card-mini),
|
||||
> :deep(.reacted-users > *) {
|
||||
padding-inline: 32px;
|
||||
border-top: 1px solid var(--divider);
|
||||
border-radius: 0;
|
||||
}
|
||||
> :deep(.reacted-users > div) {
|
||||
padding-block: 12px;
|
||||
}
|
||||
|
||||
> .reply {
|
||||
border-top: solid 0.5px var(--divider);
|
||||
|
@ -612,10 +635,13 @@ onUnmounted(() => {
|
|||
}
|
||||
}
|
||||
> .clips,
|
||||
> .chips,
|
||||
> :deep(.user-card-mini) {
|
||||
> :deep(.user-card-mini),
|
||||
> :deep(.reacted-users > *) {
|
||||
padding-inline: 16px !important;
|
||||
}
|
||||
> .chips {
|
||||
padding-left: 16px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.max-width_300px {
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
class="reply"
|
||||
:class="{ single: replies.length == 1 }"
|
||||
:conversation="conversation"
|
||||
:depth="replies.lenght == 1 ? depth : depth + 1"
|
||||
:depth="replies.length == 1 ? depth : depth + 1"
|
||||
:replyLevel="replyLevel + 1"
|
||||
:parentId="appearNote.replyId"
|
||||
:detailedView="detailedView"
|
||||
|
|
97
packages/client/src/components/MkReactedUsers.vue
Normal file
97
packages/client/src/components/MkReactedUsers.vue
Normal file
|
@ -0,0 +1,97 @@
|
|||
<template>
|
||||
<div v-if="note" class="_gaps reacted-users">
|
||||
<div :class="$style.tabs">
|
||||
<button
|
||||
v-for="reaction in reactions"
|
||||
:key="reaction"
|
||||
:class="[
|
||||
$style.tab,
|
||||
{ [$style.tabActive]: tab === reaction },
|
||||
]"
|
||||
class="_button"
|
||||
@click="tab = reaction"
|
||||
>
|
||||
<MkReactionIcon
|
||||
ref="reactionRef"
|
||||
:reaction="
|
||||
reaction
|
||||
? reaction.replace(
|
||||
/^:(\w+):$/,
|
||||
':$1@.:'
|
||||
)
|
||||
: reaction
|
||||
"
|
||||
:custom-emojis="note.emojis"
|
||||
/>
|
||||
<span style="margin-left: 4px">{{
|
||||
note.reactions[reaction]
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<MkUserCardMini
|
||||
v-for="user in users"
|
||||
:key="user.id"
|
||||
:user="user"
|
||||
:with-chart="false"
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<MkLoading />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, watch } from "vue";
|
||||
import * as misskey from "calckey-js";
|
||||
import MkReactionIcon from "@/components/MkReactionIcon.vue";
|
||||
import MkUserCardMini from "@/components/MkUserCardMini.vue";
|
||||
import { i18n } from "@/i18n";
|
||||
import * as os from "@/os";
|
||||
|
||||
const props = defineProps<{
|
||||
noteId: misskey.entities.Note["id"];
|
||||
}>();
|
||||
|
||||
let note = $ref<misskey.entities.Note>();
|
||||
let tab = $ref<string>();
|
||||
let reactions = $ref<string[]>();
|
||||
let users = $ref();
|
||||
|
||||
watch($$(tab), async () => {
|
||||
const res = await os.api("notes/reactions", {
|
||||
noteId: props.noteId,
|
||||
type: tab,
|
||||
limit: 30,
|
||||
});
|
||||
|
||||
users = res.map((x) => x.user);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
os.api("notes/show", {
|
||||
noteId: props.noteId,
|
||||
}).then((res) => {
|
||||
reactions = Object.keys(res.reactions);
|
||||
tab = reactions[0];
|
||||
note = res;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 4px 6px;
|
||||
border: solid 1px var(--divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.tabActive {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
</style>
|
|
@ -1,128 +0,0 @@
|
|||
<template>
|
||||
<MkModalWindow
|
||||
ref="dialog"
|
||||
:width="400"
|
||||
:height="450"
|
||||
@close="dialog.close()"
|
||||
@closed="emit('closed')"
|
||||
>
|
||||
<template #header>{{ i18n.ts.reaction }}</template>
|
||||
|
||||
<MkSpacer :margin-min="20" :margin-max="28">
|
||||
<div v-if="note" class="_gaps">
|
||||
<div v-if="reactions.length === 0" class="_fullinfo">
|
||||
<img
|
||||
src="/static-assets/badges/info.png"
|
||||
class="_ghost"
|
||||
alt="Info"
|
||||
/>
|
||||
<div>{{ i18n.ts.nothing }}</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div :class="$style.tabs">
|
||||
<button
|
||||
v-for="reaction in reactions"
|
||||
:key="reaction"
|
||||
:class="[
|
||||
$style.tab,
|
||||
{ [$style.tabActive]: tab === reaction },
|
||||
]"
|
||||
class="_button"
|
||||
@click="tab = reaction"
|
||||
>
|
||||
<MkReactionIcon
|
||||
ref="reactionRef"
|
||||
:reaction="
|
||||
reaction
|
||||
? reaction.replace(
|
||||
/^:(\w+):$/,
|
||||
':$1@.:'
|
||||
)
|
||||
: reaction
|
||||
"
|
||||
:custom-emojis="note.emojis"
|
||||
/>
|
||||
<span style="margin-left: 4px">{{
|
||||
note.reactions[reaction]
|
||||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<MkA
|
||||
v-for="user in users"
|
||||
:key="user.id"
|
||||
:to="userPage(user)"
|
||||
>
|
||||
<MkUserCardMini :user="user" :with-chart="false" />
|
||||
</MkA>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else>
|
||||
<MkLoading />
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</MkModalWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, watch } from "vue";
|
||||
import * as misskey from "calckey-js";
|
||||
import MkModalWindow from "@/components/MkModalWindow.vue";
|
||||
import MkReactionIcon from "@/components/MkReactionIcon.vue";
|
||||
import MkUserCardMini from "@/components/MkUserCardMini.vue";
|
||||
import { userPage } from "@/filters/user";
|
||||
import { i18n } from "@/i18n";
|
||||
import * as os from "@/os";
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: "closed"): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
noteId: misskey.entities.Note["id"];
|
||||
}>();
|
||||
|
||||
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
|
||||
let note = $ref<misskey.entities.Note>();
|
||||
let tab = $ref<string>();
|
||||
let reactions = $ref<string[]>();
|
||||
let users = $ref();
|
||||
|
||||
watch($$(tab), async () => {
|
||||
const res = await os.api("notes/reactions", {
|
||||
noteId: props.noteId,
|
||||
type: tab,
|
||||
limit: 30,
|
||||
});
|
||||
|
||||
users = res.map((x) => x.user);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
os.api("notes/show", {
|
||||
noteId: props.noteId,
|
||||
}).then((res) => {
|
||||
reactions = Object.keys(res.reactions);
|
||||
tab = reactions[0];
|
||||
note = res;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 4px 6px;
|
||||
border: solid 1px var(--divider);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.tabActive {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
</style>
|
|
@ -89,6 +89,12 @@ export default defineComponent({
|
|||
padding: 12px 32px;
|
||||
font-size: 0.85em;
|
||||
overflow-x: auto;
|
||||
mask: linear-gradient(to right, black calc(100% - 90px), transparent);
|
||||
-webkit-mask: linear-gradient(to right, black calc(100% - 90px), transparent);
|
||||
padding-right: 90px !important;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
> button {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
@ -102,6 +108,9 @@ export default defineComponent({
|
|||
> i {
|
||||
margin-top: -0.1em;
|
||||
}
|
||||
> .count {
|
||||
margin-right: -.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -230,19 +230,6 @@ export function getNoteMenu(props: {
|
|||
});
|
||||
}
|
||||
|
||||
function showReactions(): void {
|
||||
os.popup(
|
||||
defineAsyncComponent(
|
||||
() => import("@/components/MkReactedUsersDialog.vue"),
|
||||
),
|
||||
{
|
||||
noteId: appearNote.id,
|
||||
},
|
||||
{},
|
||||
"closed",
|
||||
);
|
||||
}
|
||||
|
||||
async function translate(): Promise<void> {
|
||||
if (props.translation.value != null) return;
|
||||
props.translating.value = true;
|
||||
|
@ -282,11 +269,6 @@ export function getNoteMenu(props: {
|
|||
action: edit,
|
||||
}
|
||||
: undefined,
|
||||
{
|
||||
icon: "ph-smiley ph-bold ph-lg",
|
||||
text: i18n.ts.reaction,
|
||||
action: showReactions,
|
||||
},
|
||||
{
|
||||
icon: "ph-clipboard-text ph-bold ph-lg",
|
||||
text: i18n.ts.copyContent,
|
||||
|
|
Loading…
Reference in a new issue