mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 08:28:20 +01:00
feat: ✨ Render emoji reactions
Some checks failed
Some checks failed
This commit is contained in:
parent
0aef313fc6
commit
b77700b8b8
4
bun.lock
4
bun.lock
|
|
@ -24,7 +24,7 @@
|
||||||
"@tiptap/suggestion": "^2.12.0",
|
"@tiptap/suggestion": "^2.12.0",
|
||||||
"@tiptap/vue-3": "^2.12.0",
|
"@tiptap/vue-3": "^2.12.0",
|
||||||
"@vee-validate/zod": "^4.15.0",
|
"@vee-validate/zod": "^4.15.0",
|
||||||
"@versia/client": "0.2.0-alpha.2",
|
"@versia/client": "0.2.0-alpha.3",
|
||||||
"@videojs-player/vue": "^1.0.0",
|
"@videojs-player/vue": "^1.0.0",
|
||||||
"@vite-pwa/nuxt": "^1.0.1",
|
"@vite-pwa/nuxt": "^1.0.1",
|
||||||
"@vueuse/core": "^13.2.0",
|
"@vueuse/core": "^13.2.0",
|
||||||
|
|
@ -780,7 +780,7 @@
|
||||||
|
|
||||||
"@vercel/nft": ["@vercel/nft@0.29.3", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^10.4.5", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-aVV0E6vJpuvImiMwU1/5QKkw2N96BRFE7mBYGS7FhXUoS6V7SarQ+8tuj33o7ofECz8JtHpmQ9JW+oVzOoB7MA=="],
|
"@vercel/nft": ["@vercel/nft@0.29.3", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^10.4.5", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-aVV0E6vJpuvImiMwU1/5QKkw2N96BRFE7mBYGS7FhXUoS6V7SarQ+8tuj33o7ofECz8JtHpmQ9JW+oVzOoB7MA=="],
|
||||||
|
|
||||||
"@versia/client": ["@versia/client@0.2.0-alpha.2", "", { "dependencies": { "@badgateway/oauth2-client": "^3.0.0", "iso-639-1": "^3.1.5", "magic-regexp": "^0.10.0", "zod": "^3.24.2", "zod-openapi": "^4.2.4" } }, "sha512-/x1Z2tyJsfckCOLX8K8XDVs3rd3vX0wfCz20IMTc9uaGRkDTcL7MYzz20WCgOEW/WmnwL3iVf/jVgJm0NzoF4Q=="],
|
"@versia/client": ["@versia/client@0.2.0-alpha.3", "", { "dependencies": { "@badgateway/oauth2-client": "^3.0.0", "iso-639-1": "^3.1.5", "magic-regexp": "^0.10.0", "zod": "^3.24.2", "zod-openapi": "^4.2.4" } }, "sha512-osJ1+Y1GfnhyqZ+NRg8paAUfADa4d6uGEd87viruXtDAYkHWkDBS/cb353ArI9y4d2Mjenkqk0B1Zz4cm71fDQ=="],
|
||||||
|
|
||||||
"@videojs-player/vue": ["@videojs-player/vue@1.0.0", "", { "peerDependencies": { "@types/video.js": "7.x", "video.js": "7.x", "vue": "3.x" } }, "sha512-WonTezRfKu3fYdQLt/ta+nuKH6gMZUv8l40Jke/j4Lae7IqeO/+lLAmBnh3ni88bwR+vkFXIlZ2Ci7VKInIYJg=="],
|
"@videojs-player/vue": ["@videojs-player/vue@1.0.0", "", { "peerDependencies": { "@types/video.js": "7.x", "video.js": "7.x", "vue": "3.x" } }, "sha512-WonTezRfKu3fYdQLt/ta+nuKH6gMZUv8l40Jke/j4Lae7IqeO/+lLAmBnh3ni88bwR+vkFXIlZ2Ci7VKInIYJg=="],
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
:sensitive="noteToUse.sensitive"
|
:sensitive="noteToUse.sensitive"
|
||||||
:content-warning="noteToUse.spoiler_text"
|
:content-warning="noteToUse.spoiler_text"
|
||||||
/>
|
/>
|
||||||
|
<Reactions v-if="noteToUse.reactions && noteToUse.reactions.length > 0" :reactions="noteToUse.reactions" :emojis="noteToUse.emojis" :status-id="noteToUse.id" />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter v-if="!hideActions">
|
<CardFooter v-if="!hideActions">
|
||||||
<Actions
|
<Actions
|
||||||
|
|
@ -81,6 +82,7 @@ import { Card, CardContent, CardFooter, CardHeader } from "../ui/card";
|
||||||
import Actions from "./actions.vue";
|
import Actions from "./actions.vue";
|
||||||
import Content from "./content.vue";
|
import Content from "./content.vue";
|
||||||
import Header from "./header.vue";
|
import Header from "./header.vue";
|
||||||
|
import Reactions from "./reactions/index.vue";
|
||||||
import ReblogHeader from "./reblog-header.vue";
|
import ReblogHeader from "./reblog-header.vue";
|
||||||
|
|
||||||
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||||
|
|
|
||||||
17
components/notes/reactions/index.vue
Normal file
17
components/notes/reactions/index.vue
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-row gap-2 flex-wrap">
|
||||||
|
<Reaction v-for="reaction in reactions" :key="reaction.name" :reaction="reaction" :emoji="emojis.find(e => `:${e.shortcode}:` === reaction.name)" :status-id="statusId" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { CustomEmoji, NoteReaction } from "@versia/client/schemas";
|
||||||
|
import type { z } from "zod";
|
||||||
|
import Reaction from "./reaction.vue";
|
||||||
|
|
||||||
|
const { statusId, reactions, emojis } = defineProps<{
|
||||||
|
statusId: string;
|
||||||
|
reactions: z.infer<typeof NoteReaction>[];
|
||||||
|
emojis: z.infer<typeof CustomEmoji>[];
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
73
components/notes/reactions/reaction.vue
Normal file
73
components/notes/reactions/reaction.vue
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
<template>
|
||||||
|
<HoverCard @update:open="(open) => open && accounts === null && refreshReactions()">
|
||||||
|
<HoverCardTrigger as-child>
|
||||||
|
<Button variant="secondary" size="sm" class="gap-2">
|
||||||
|
<img v-if="emoji" :src="emoji.url" :alt="emoji.shortcode"
|
||||||
|
class="h-[1lh] align-middle inline not-prose" />
|
||||||
|
<span v-else>
|
||||||
|
{{ reaction.name }}
|
||||||
|
</span>
|
||||||
|
{{ formatNumber(reaction.count) }}
|
||||||
|
</Button>
|
||||||
|
</HoverCardTrigger>
|
||||||
|
<HoverCardContent class="p-3">
|
||||||
|
<Spinner v-if="accounts === null" class="border-0" />
|
||||||
|
<ul v-else class="flex flex-col gap-4">
|
||||||
|
<li
|
||||||
|
v-for="account in accounts">
|
||||||
|
<NuxtLink :to="`/@${account.acct}`" class="flex items-center gap-2">
|
||||||
|
<Avatar class="size-6" :key="account.id" :src="account.avatar"
|
||||||
|
:name="account.display_name || account.username" />
|
||||||
|
<span class="text-sm font-semibold line-clamp-1">
|
||||||
|
{{ account.display_name || account.username }}
|
||||||
|
</span>
|
||||||
|
</NuxtLink>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</HoverCardContent>
|
||||||
|
</HoverCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type {
|
||||||
|
Account,
|
||||||
|
CustomEmoji,
|
||||||
|
NoteReaction,
|
||||||
|
} from "@versia/client/schemas";
|
||||||
|
import type { z } from "zod";
|
||||||
|
import Spinner from "~/components/graphics/spinner.vue";
|
||||||
|
import Avatar from "~/components/profiles/avatar.vue";
|
||||||
|
import { Button } from "~/components/ui/button";
|
||||||
|
import {
|
||||||
|
HoverCard,
|
||||||
|
HoverCardContent,
|
||||||
|
HoverCardTrigger,
|
||||||
|
} from "~/components/ui/hover-card";
|
||||||
|
import { getLocale } from "~/paraglide/runtime.js";
|
||||||
|
|
||||||
|
const { reaction, emoji, statusId } = defineProps<{
|
||||||
|
statusId: string;
|
||||||
|
reaction: z.infer<typeof NoteReaction>;
|
||||||
|
emoji?: z.infer<typeof CustomEmoji>;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const formatNumber = (number: number) =>
|
||||||
|
new Intl.NumberFormat(getLocale(), {
|
||||||
|
notation: "compact",
|
||||||
|
compactDisplay: "short",
|
||||||
|
maximumFractionDigits: 1,
|
||||||
|
}).format(number);
|
||||||
|
|
||||||
|
const accounts = ref<z.infer<typeof Account>[] | null>(null);
|
||||||
|
|
||||||
|
const refreshReactions = async () => {
|
||||||
|
const { data } = await client.value.getStatusReactions(statusId);
|
||||||
|
const accountIds =
|
||||||
|
data.find((r) => r.name === reaction.name)?.account_ids.slice(0, 10) ??
|
||||||
|
[];
|
||||||
|
|
||||||
|
const { data: accountsData } = await client.value.getAccounts(accountIds);
|
||||||
|
|
||||||
|
accounts.value = accountsData;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
@ -17,7 +17,7 @@ in
|
||||||
|
|
||||||
pnpmDeps = pnpm.fetchDeps {
|
pnpmDeps = pnpm.fetchDeps {
|
||||||
inherit (finalAttrs) pname version src;
|
inherit (finalAttrs) pname version src;
|
||||||
hash = "sha256-ZR+YidKF+zGeMuPGVXLz7OAehqssCp9AfU1W9yul4HQ=";
|
hash = "sha256-oYouqCoLTyVemqURLW0j5MVKqupqRDxP5rkR3reMQvk=";
|
||||||
};
|
};
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@
|
||||||
"@tiptap/suggestion": "^2.12.0",
|
"@tiptap/suggestion": "^2.12.0",
|
||||||
"@tiptap/vue-3": "^2.12.0",
|
"@tiptap/vue-3": "^2.12.0",
|
||||||
"@vee-validate/zod": "^4.15.0",
|
"@vee-validate/zod": "^4.15.0",
|
||||||
"@versia/client": "0.2.0-alpha.2",
|
"@versia/client": "0.2.0-alpha.3",
|
||||||
"@videojs-player/vue": "^1.0.0",
|
"@videojs-player/vue": "^1.0.0",
|
||||||
"@vite-pwa/nuxt": "^1.0.1",
|
"@vite-pwa/nuxt": "^1.0.1",
|
||||||
"@vueuse/core": "^13.2.0",
|
"@vueuse/core": "^13.2.0",
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,8 @@ importers:
|
||||||
specifier: ^4.15.0
|
specifier: ^4.15.0
|
||||||
version: 4.15.0(vue@3.5.14(typescript@5.8.3))(zod@3.25.23)
|
version: 4.15.0(vue@3.5.14(typescript@5.8.3))(zod@3.25.23)
|
||||||
'@versia/client':
|
'@versia/client':
|
||||||
specifier: 0.2.0-alpha.2
|
specifier: 0.2.0-alpha.3
|
||||||
version: 0.2.0-alpha.2
|
version: 0.2.0-alpha.3
|
||||||
'@videojs-player/vue':
|
'@videojs-player/vue':
|
||||||
specifier: ^1.0.0
|
specifier: ^1.0.0
|
||||||
version: 1.0.0(@types/video.js@7.3.58)(video.js@7.21.7)(vue@3.5.14(typescript@5.8.3))
|
version: 1.0.0(@types/video.js@7.3.58)(video.js@7.21.7)(vue@3.5.14(typescript@5.8.3))
|
||||||
|
|
@ -1993,8 +1993,8 @@ packages:
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@versia/client@0.2.0-alpha.2':
|
'@versia/client@0.2.0-alpha.3':
|
||||||
resolution: {integrity: sha512-/x1Z2tyJsfckCOLX8K8XDVs3rd3vX0wfCz20IMTc9uaGRkDTcL7MYzz20WCgOEW/WmnwL3iVf/jVgJm0NzoF4Q==}
|
resolution: {integrity: sha512-osJ1+Y1GfnhyqZ+NRg8paAUfADa4d6uGEd87viruXtDAYkHWkDBS/cb353ArI9y4d2Mjenkqk0B1Zz4cm71fDQ==}
|
||||||
engines: {bun: '>=1.2.5'}
|
engines: {bun: '>=1.2.5'}
|
||||||
|
|
||||||
'@videojs-player/vue@1.0.0':
|
'@videojs-player/vue@1.0.0':
|
||||||
|
|
@ -7857,7 +7857,7 @@ snapshots:
|
||||||
- rollup
|
- rollup
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@versia/client@0.2.0-alpha.2':
|
'@versia/client@0.2.0-alpha.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@badgateway/oauth2-client': 3.2.0
|
'@badgateway/oauth2-client': 3.2.0
|
||||||
iso-639-1: 3.1.5
|
iso-639-1: 3.1.5
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue