mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 08:28:20 +01:00
feat: ✨ Implement quotes
This commit is contained in:
parent
8cc4ff1348
commit
33cf7f5806
|
|
@ -1,11 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="['prose block relative dark:prose-invert duration-200 !max-w-full break-words prose-a:no-underline prose-a:hover:underline', $style.content]" v-html="content">
|
<div :class="['prose block relative dark:prose-invert duration-200 !max-w-full break-words prose-a:no-underline prose-a:hover:underline', $style.content]" v-html="content">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="quote" class="mt-4 rounded border">
|
||||||
|
<Note :note="quote" :hide-actions="true" :small-layout="true" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { Status } from "@versia/client/types";
|
||||||
|
import Note from "./note.vue";
|
||||||
|
|
||||||
const { content } = defineProps<{
|
const { content } = defineProps<{
|
||||||
content: string;
|
content: string;
|
||||||
|
quote?: NonNullable<Status["quote"]>;
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="rounded flex flex-row gap-3">
|
<NuxtLink :href="url" class="rounded flex flex-row items-center gap-3">
|
||||||
<div class="relative">
|
<div :class="cn('relative size-14', smallLayout && 'size-6')">
|
||||||
<Avatar class="size-14 rounded-md border border-card">
|
<Avatar :class="cn('size-14 rounded-md border border-card', smallLayout && 'size-6')">
|
||||||
<AvatarImage :src="avatar" alt="" />
|
<AvatarImage :src="avatar" alt="" />
|
||||||
<AvatarFallback class="rounded-lg"> AA </AvatarFallback>
|
<AvatarFallback class="rounded-lg"> AA </AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<AvatarFallback class="rounded-lg"> AA </AvatarFallback>
|
<AvatarFallback class="rounded-lg"> AA </AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-0.5 justify-center flex-1 text-left leading-tight">
|
<div :class="cn('flex flex-col gap-0.5 justify-center flex-1 text-left leading-tight', smallLayout && 'flex-row justify-start items-center gap-2')">
|
||||||
<span class="truncate font-semibold">{{
|
<span class="truncate font-semibold">{{
|
||||||
displayName
|
displayName
|
||||||
}}</span>
|
}}</span>
|
||||||
|
|
@ -26,15 +26,16 @@
|
||||||
<span class="text-muted-foreground ml-auto tracking-normal" :title="fullTime">{{ timeAgo }}</span>
|
<span class="text-muted-foreground ml-auto tracking-normal" :title="fullTime">{{ timeAgo }}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1 justify-center items-end">
|
<div class="flex flex-col gap-1 justify-center items-end" v-if="!smallLayout">
|
||||||
<span class="text-xs text-muted-foreground" :title="visibilities[visibility].text">
|
<span class="text-xs text-muted-foreground" :title="visibilities[visibility].text">
|
||||||
<component :is="visibilities[visibility].icon" class="size-5" />
|
<component :is="visibilities[visibility].icon" class="size-5" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</NuxtLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import type { StatusVisibility } from "@versia/client/types";
|
import type { StatusVisibility } from "@versia/client/types";
|
||||||
import { AtSign, Globe, Lock, LockOpen } from "lucide-vue-next";
|
import { AtSign, Globe, Lock, LockOpen } from "lucide-vue-next";
|
||||||
import CopyableText from "./copyable-text.vue";
|
import CopyableText from "./copyable-text.vue";
|
||||||
|
|
@ -47,6 +48,7 @@ const { acct, createdAt } = defineProps<{
|
||||||
visibility: StatusVisibility;
|
visibility: StatusVisibility;
|
||||||
url: string;
|
url: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
smallLayout?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const [username, instance] = acct.split("@");
|
const [username, instance] = acct.split("@");
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<Card as="article" class="rounded-none border-0 duration-200 shadow-none">
|
<Card as="article" class="rounded-none border-0 duration-200 shadow-none">
|
||||||
<CardHeader class="pb-4">
|
<CardHeader class="pb-4">
|
||||||
<ReblogHeader v-if="!!note.reblog" :avatar="note.account.avatar"
|
<ReblogHeader v-if="note.reblog" :avatar="note.account.avatar"
|
||||||
:display-name="note.account.display_name" />
|
:display-name="note.account.display_name" :url="reblogAccountUrl" />
|
||||||
<Header :avatar="noteToUse.account.avatar" :corner-avatar="note.reblog ? note.account.avatar : undefined"
|
<Header :avatar="noteToUse.account.avatar" :corner-avatar="note.reblog ? note.account.avatar : undefined"
|
||||||
:acct="noteToUse.account.acct" :display-name="noteToUse.account.display_name"
|
:acct="noteToUse.account.acct" :display-name="noteToUse.account.display_name"
|
||||||
:visibility="noteToUse.visibility" :url="accountUrl" :created-at="new Date(noteToUse.created_at)" />
|
:visibility="noteToUse.visibility" :url="accountUrl" :created-at="new Date(noteToUse.created_at)" :small-layout="smallLayout" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Content :content="noteToUse.content" />
|
<Content :content="noteToUse.content" :quote="note.quote ?? undefined" />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter>
|
<CardFooter v-if="!hideActions">
|
||||||
<Actions :reply-count="noteToUse.replies_count" :like-count="noteToUse.favourites_count"
|
<Actions :reply-count="noteToUse.replies_count" :like-count="noteToUse.favourites_count"
|
||||||
:reblog-count="noteToUse.reblogs_count" />
|
:reblog-count="noteToUse.reblogs_count" />
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
|
|
@ -27,12 +27,14 @@ import ReblogHeader from "./reblog-header.vue";
|
||||||
|
|
||||||
const { note } = defineProps<{
|
const { note } = defineProps<{
|
||||||
note: Status;
|
note: Status;
|
||||||
|
hideActions?: boolean;
|
||||||
|
smallLayout?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// Notes can be reblogs or quotes, in which case
|
// Notes can be reblogs, in which case the actual thing to render is inside the reblog property
|
||||||
// the actual thing to render is inside the reblog or quote
|
const noteToUse = note.reblog ? note.reblog : note;
|
||||||
const noteToUse = note.reblog ? note.reblog : note.quote ? note.quote : note;
|
|
||||||
|
|
||||||
const url = `/@${noteToUse.account.acct}/${noteToUse.id}`;
|
const url = `/@${noteToUse.account.acct}/${noteToUse.id}`;
|
||||||
const accountUrl = `/@${noteToUse.account.acct}`;
|
const accountUrl = `/@${noteToUse.account.acct}`;
|
||||||
|
const reblogAccountUrl = `/@${note.account.acct}`;
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="rounded border hover:bg-muted duration-100 text-sm flex flex-row items-center gap-2 px-2 py-1 mb-2">
|
<NuxtLink :href="url" class="rounded border hover:bg-muted duration-100 text-sm flex flex-row items-center gap-2 px-2 py-1 mb-4">
|
||||||
<Repeat class="size-4 text-primary" />
|
<Repeat class="size-4 text-primary" />
|
||||||
<Avatar class="size-6 rounded border">
|
<Avatar class="size-6 rounded border">
|
||||||
<AvatarImage :src="avatar" alt="" />
|
<AvatarImage :src="avatar" alt="" />
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<span class="font-semibold">{{ displayName }}</span>
|
<span class="font-semibold">{{ displayName }}</span>
|
||||||
reblogged
|
reblogged
|
||||||
</div>
|
</NuxtLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
@ -17,5 +17,6 @@ import { Repeat } from "lucide-vue-next";
|
||||||
defineProps<{
|
defineProps<{
|
||||||
avatar: string;
|
avatar: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
|
url: string;
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
import type { Notification, Status } from "@versia/client/types";
|
import type { Notification, Status } from "@versia/client/types";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import NewNoteItem from "../notes/note.vue";
|
import NewNoteItem from "../notes/note.vue";
|
||||||
import NoteItem from "../social-elements/notes/note.vue";
|
|
||||||
import NotificationItem from "../social-elements/notifications/notif.vue";
|
import NotificationItem from "../social-elements/notifications/notif.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<!-- Timeline.vue -->
|
<!-- Timeline.vue -->
|
||||||
<template>
|
<template>
|
||||||
<div class="timeline rounded overflow-hidden ring-1 ring-ring/10">
|
<div class="timeline rounded overflow-hidden">
|
||||||
<TransitionGroup name="timeline-item" tag="div" class="timeline-items *:!border-b-[0.5px] *:last:border-0">
|
<TransitionGroup name="timeline-item" tag="div" class="timeline-items *:rounded space-y-4 *:border *:border-border/50">
|
||||||
<TimelineItem :type="type" v-for="item in items" :key="item.id" :item="item" @update="updateItem"
|
<TimelineItem :type="type" v-for="item in items" :key="item.id" :item="item" @update="updateItem"
|
||||||
@delete="removeItem" />
|
@delete="removeItem" />
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue