mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 16:38:20 +01:00
refactor: 🔥 Remove old files
This commit is contained in:
parent
348b1ba2b0
commit
42e0b38fd8
|
|
@ -27,9 +27,9 @@
|
|||
</span>
|
||||
</div>
|
||||
<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">
|
||||
<NuxtLink :href="noteUrlAsPath" class="text-xs text-muted-foreground" :title="visibilities[visibility].text">
|
||||
<component :is="visibilities[visibility].icon" class="size-5" />
|
||||
</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -44,7 +44,7 @@ import type {
|
|||
import { AtSign, Globe, Lock, LockOpen } from "lucide-vue-next";
|
||||
import CopyableText from "./copyable-text.vue";
|
||||
|
||||
const { acct, createdAt, url } = defineProps<{
|
||||
const { acct, createdAt, url, noteUrl } = defineProps<{
|
||||
avatar: string;
|
||||
cornerAvatar?: string;
|
||||
acct: string;
|
||||
|
|
@ -52,6 +52,7 @@ const { acct, createdAt, url } = defineProps<{
|
|||
emojis: Emoji[];
|
||||
visibility: StatusVisibility;
|
||||
url: string;
|
||||
noteUrl: string;
|
||||
createdAt: Date;
|
||||
smallLayout?: boolean;
|
||||
}>();
|
||||
|
|
@ -59,6 +60,7 @@ const { acct, createdAt, url } = defineProps<{
|
|||
const [username, instance] = acct.split("@");
|
||||
const digitRegex = /\d/;
|
||||
const urlAsPath = new URL(url).pathname;
|
||||
const noteUrlAsPath = new URL(noteUrl).pathname;
|
||||
const timeAgo = useTimeAgo(createdAt, {
|
||||
messages: {
|
||||
justNow: "now",
|
||||
|
|
|
|||
|
|
@ -4,16 +4,23 @@
|
|||
<ReblogHeader v-if="note.reblog" :avatar="note.account.avatar" :display-name="note.account.display_name"
|
||||
:url="reblogAccountUrl" :emojis="note.account.emojis" />
|
||||
<Header :avatar="noteToUse.account.avatar" :corner-avatar="note.reblog ? note.account.avatar : undefined"
|
||||
:acct="noteToUse.account.acct" :display-name="noteToUse.account.display_name"
|
||||
:note-url="url" :acct="noteToUse.account.acct" :display-name="noteToUse.account.display_name"
|
||||
:visibility="noteToUse.visibility" :url="accountUrl" :created-at="new Date(noteToUse.created_at)"
|
||||
:small-layout="smallLayout" :emojis="noteToUse.account.emojis" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Content :content="noteToUse.content" :quote="note.quote ?? undefined" :attachments="noteToUse.media_attachments" :plain-content="noteToUse.plain_content ?? undefined" :emojis="noteToUse.emojis" />
|
||||
<Content :content="noteToUse.content" :quote="note.quote ?? undefined"
|
||||
:attachments="noteToUse.media_attachments" :plain-content="noteToUse.plain_content ?? undefined"
|
||||
:emojis="noteToUse.emojis" />
|
||||
</CardContent>
|
||||
<CardFooter v-if="!hideActions" class="p-4 pt-0">
|
||||
<Actions :reply-count="noteToUse.replies_count" :like-count="noteToUse.favourites_count" :url="url"
|
||||
:api-note-string="JSON.stringify(note, null, 4)" :reblog-count="noteToUse.reblogs_count" :remote-url="noteToUse.url" :is-remote="isRemote" :author-id="noteToUse.account.id" @edit="useEvent('composer:edit', note)" @reply="useEvent('composer:reply', note)" @quote="useEvent('composer:quote', note)" @delete="useEvent('note:delete', note)" :note-id="noteToUse.id" :liked="noteToUse.favourited ?? false" :reblogged="noteToUse.reblogged ?? false" />
|
||||
:api-note-string="JSON.stringify(note, null, 4)" :reblog-count="noteToUse.reblogs_count"
|
||||
:remote-url="noteToUse.url" :is-remote="isRemote" :author-id="noteToUse.account.id"
|
||||
@edit="useEvent('composer:edit', note)" @reply="useEvent('composer:reply', note)"
|
||||
@quote="useEvent('composer:quote', note)" @delete="useEvent('note:delete', note)"
|
||||
:note-id="noteToUse.id" :liked="noteToUse.favourited ?? false"
|
||||
:reblogged="noteToUse.reblogged ?? false" />
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
<template>
|
||||
<component :is="disableLink ? 'div' : NuxtLink" :href="accountUrl" class="flex flex-row">
|
||||
<Skeleton :enabled="!account" shape="rect" class="!h-12 w-12">
|
||||
<div class="shrink-0">
|
||||
<Avatar class="h-12 w-12 rounded ring-1 ring-white/5" :src="account?.avatar"
|
||||
:alt="`${account?.acct}'s avatar'`" />
|
||||
</div>
|
||||
</Skeleton>
|
||||
<div class="flex flex-col items-start justify-around ml-4 grow overflow-hidden">
|
||||
<div class="flex flex-row items-center justify-between w-full">
|
||||
<div class="font-semibold text-gray-200 line-clamp-1 break-all">
|
||||
<Skeleton :enabled="!account" :min-width="90" :max-width="170" shape="rect">
|
||||
{{
|
||||
account?.display_name }}
|
||||
</Skeleton>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-gray-400 text-sm line-clamp-1 break-all w-full">
|
||||
<Skeleton :enabled="!account" :min-width="130" :max-width="250" shape="rect">
|
||||
@{{
|
||||
account?.acct
|
||||
}}
|
||||
</Skeleton>
|
||||
</span>
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Account } from "@versia/client/types";
|
||||
import Avatar from "~/components/avatars/avatar.vue";
|
||||
import Skeleton from "~/components/skeleton/Skeleton.vue";
|
||||
import { NuxtLink } from "#components";
|
||||
|
||||
const props = defineProps<{
|
||||
account?: Account;
|
||||
disableLink?: boolean;
|
||||
}>();
|
||||
|
||||
const accountUrl = props.account && `/@${props.account.acct}`;
|
||||
</script>
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
import type { Client } from "@versia/client";
|
||||
import type { Status } from "@versia/client/types";
|
||||
import { SettingIds, type Settings } from "~/settings";
|
||||
|
||||
export const useNoteData = (
|
||||
noteProp: MaybeRef<Status | undefined>,
|
||||
client: Ref<Client>,
|
||||
settings: MaybeRef<Settings>,
|
||||
) => {
|
||||
const isReply = computed(() => !!toValue(noteProp)?.in_reply_to_id);
|
||||
const isQuote = computed(() => !!toValue(noteProp)?.quote);
|
||||
const isReblog = computed(
|
||||
() => !isQuote.value && !!toValue(noteProp)?.reblog,
|
||||
);
|
||||
const renderedNote = computed(() =>
|
||||
isReblog.value
|
||||
? (toValue(noteProp)?.reblog ?? toValue(noteProp))
|
||||
: toValue(noteProp),
|
||||
);
|
||||
const showContentWarning = useSetting(SettingIds.ShowContentWarning);
|
||||
const shouldHide = computed(
|
||||
() =>
|
||||
(renderedNote.value?.sensitive ||
|
||||
!!renderedNote.value?.spoiler_text) &&
|
||||
(showContentWarning.value.value as boolean),
|
||||
);
|
||||
const mentions = useResolveMentions(
|
||||
computed(() => renderedNote.value?.mentions ?? []),
|
||||
client.value,
|
||||
);
|
||||
const content = useParsedContent(
|
||||
computed(() => renderedNote.value?.content ?? ""),
|
||||
computed(() => renderedNote.value?.emojis ?? []),
|
||||
mentions,
|
||||
settings,
|
||||
);
|
||||
const loaded = computed(() => content.value !== null);
|
||||
|
||||
const reblogDisplayName = useParsedContent(
|
||||
toValue(noteProp)?.account.display_name ?? "",
|
||||
toValue(noteProp)?.account.emojis ?? [],
|
||||
undefined,
|
||||
settings,
|
||||
);
|
||||
const reblog = computed(() =>
|
||||
isReblog.value && toValue(noteProp) && !isQuote.value
|
||||
? {
|
||||
avatar: toValue(noteProp)?.account.avatar,
|
||||
acct: toValue(noteProp)?.account.acct,
|
||||
}
|
||||
: null,
|
||||
);
|
||||
|
||||
const url = computed(() =>
|
||||
new URL(
|
||||
`/@${renderedNote.value?.account.acct}/${renderedNote.value?.id}`,
|
||||
window.location.origin,
|
||||
).toString(),
|
||||
);
|
||||
|
||||
const remove = async () => {
|
||||
const result = await client.value.deleteStatus(
|
||||
renderedNote.value?.id ?? "",
|
||||
);
|
||||
|
||||
if (result?.data) {
|
||||
useEvent("note:delete", result.data);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
loaded,
|
||||
note: renderedNote,
|
||||
content,
|
||||
isQuote,
|
||||
reblog,
|
||||
reblogDisplayName,
|
||||
shouldHide,
|
||||
isReply,
|
||||
url,
|
||||
remove,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
import type { Account, Emoji } from "@versia/client/types";
|
||||
import { SettingIds, type Settings } from "~/settings";
|
||||
|
||||
const emojisRegex =
|
||||
/\p{RI}\p{RI}|\p{Emoji}(\p{EMod}|\uFE0F\u20E3?|[\u{E0020}-\u{E007E}]+\u{E007F})?(\u200D(\p{RI}\p{RI}|\p{Emoji}(\p{EMod}|\uFE0F\u20E3?|[\u{E0020}-\u{E007E}]+\u{E007F})?))*/gu;
|
||||
const incorrectEmojisRegex = /^[#*0-9©®]$/;
|
||||
|
||||
/**
|
||||
* Takes in an HTML string, parses emojis and returns a reactive object with the parsed content.
|
||||
* @param content String of HTML content to parse
|
||||
* @param emojis Array of emojis to parse
|
||||
* @returns Reactive object with the parsed content
|
||||
*/
|
||||
export const useParsedContent = (
|
||||
content: MaybeRef<string>,
|
||||
emojis: MaybeRef<Emoji[]>,
|
||||
mentions: MaybeRef<Account[]> = ref([]),
|
||||
settings: MaybeRef<Settings | null> = ref(null),
|
||||
): Ref<string | null> => {
|
||||
const result = ref(null as string | null);
|
||||
|
||||
watch(
|
||||
isRef(content)
|
||||
? isRef(emojis)
|
||||
? [content, mentions, emojis]
|
||||
: [content, mentions]
|
||||
: mentions,
|
||||
async () => {
|
||||
const contentHtml = document.createElement("div");
|
||||
contentHtml.innerHTML = toValue(content);
|
||||
|
||||
const shouldRenderEmoji =
|
||||
toValue(settings)?.[SettingIds.CustomEmojis].value;
|
||||
const emojiFont = toValue(settings)?.[SettingIds.EmojiTheme].value;
|
||||
|
||||
// Replace emoji shortcodes with images
|
||||
if (shouldRenderEmoji) {
|
||||
contentHtml.innerHTML = contentHtml.innerHTML.replace(
|
||||
/:([a-zA-Z0-9_-]+):/g,
|
||||
(match, emoji) => {
|
||||
const emojiData = toValue(emojis).find(
|
||||
(e) => e.shortcode === emoji,
|
||||
);
|
||||
if (!emojiData) {
|
||||
return match;
|
||||
}
|
||||
const image = document.createElement("img");
|
||||
image.src = emojiData.url;
|
||||
image.alt = `:${emoji}:`;
|
||||
image.title = emojiData.shortcode;
|
||||
image.className =
|
||||
"h-[1.6em] mt-[-0.2ex] mx-1 mb-[0.2ex] align-middle inline not-prose hover:scale-110 transition-transform duration-75 ease-in-out";
|
||||
return image.outerHTML;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (emojiFont !== "native") {
|
||||
contentHtml.innerHTML = contentHtml.innerHTML.replace(
|
||||
emojisRegex,
|
||||
(match) => {
|
||||
if (incorrectEmojisRegex.test(match)) {
|
||||
return match;
|
||||
}
|
||||
|
||||
return `<img src="/emojis/${emojiFont}/${match}.svg" alt="${match}" class="h-[1em] inline not-prose hover:scale-110 transi''tion-transform duration-75 ease-in-out">`;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
result.value = contentHtml.innerHTML;
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const useParsedAccount = (
|
||||
account: MaybeRef<Account | undefined | null>,
|
||||
settings: MaybeRef<Settings>,
|
||||
) => {
|
||||
const displayName = computed(
|
||||
() =>
|
||||
toValue(account)?.display_name ?? toValue(account)?.username ?? "",
|
||||
);
|
||||
const note = computed(() => toValue(account)?.note ?? "");
|
||||
const fields = computed(() => toValue(account)?.fields ?? []);
|
||||
const emojis = computed(() => toValue(account)?.emojis ?? []);
|
||||
|
||||
const parsedDisplayName = useParsedContent(
|
||||
displayName,
|
||||
emojis,
|
||||
undefined,
|
||||
settings,
|
||||
);
|
||||
|
||||
const parsedNote = useParsedContent(note, emojis, undefined, settings);
|
||||
|
||||
const parsedFields = computed(() =>
|
||||
fields.value.map((field) => ({
|
||||
...field,
|
||||
name: useParsedContent(field.name, emojis, undefined, settings)
|
||||
.value,
|
||||
value: useParsedContent(field.value, emojis, undefined, settings)
|
||||
.value,
|
||||
})),
|
||||
);
|
||||
|
||||
return {
|
||||
display_name: parsedDisplayName,
|
||||
note: parsedNote,
|
||||
fields: parsedFields,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<div v-if="loaded" :defer="true" class="mx-auto max-w-2xl w-full pb-72">
|
||||
<div v-if="loaded" class="mx-auto max-w-2xl w-full pb-72 *:rounded space-y-4 *:border *:border-border/50">
|
||||
<Note v-for="note of context?.ancestors" :note="note" />
|
||||
<div ref="element" class="first:rounded-t last:rounded-b overflow-hidden">
|
||||
<Note class="!rounded-none border-2 -m-[2px] border-primary-500" v-if="note" :note="note" />
|
||||
</div>
|
||||
<Note v-if="note" :note="note" />
|
||||
<Note v-for="note of context?.descendants" :note="note" />
|
||||
</div>
|
||||
<!-- <div v-else class="mx-auto max-w-2xl w-full overflow-y-auto">
|
||||
<Note v-for="_ of 5" :skeleton="true" />
|
||||
</div> -->
|
||||
|
||||
<div v-else class="p-4 flex items-center justify-center h-48">
|
||||
<Loader class="size-8 animate-spin" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Loader } from "lucide-vue-next";
|
||||
import Note from "~/components/notes/note.vue";
|
||||
|
||||
definePageMeta({
|
||||
|
|
|
|||
Loading…
Reference in a new issue