mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 08:28:20 +01:00
refactor: 🔥 Remove old files
This commit is contained in:
parent
348b1ba2b0
commit
42e0b38fd8
|
|
@ -27,9 +27,9 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1 justify-center items-end" v-if="!smallLayout">
|
<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" />
|
<component :is="visibilities[visibility].icon" class="size-5" />
|
||||||
</span>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -44,7 +44,7 @@ import type {
|
||||||
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";
|
||||||
|
|
||||||
const { acct, createdAt, url } = defineProps<{
|
const { acct, createdAt, url, noteUrl } = defineProps<{
|
||||||
avatar: string;
|
avatar: string;
|
||||||
cornerAvatar?: string;
|
cornerAvatar?: string;
|
||||||
acct: string;
|
acct: string;
|
||||||
|
|
@ -52,6 +52,7 @@ const { acct, createdAt, url } = defineProps<{
|
||||||
emojis: Emoji[];
|
emojis: Emoji[];
|
||||||
visibility: StatusVisibility;
|
visibility: StatusVisibility;
|
||||||
url: string;
|
url: string;
|
||||||
|
noteUrl: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
smallLayout?: boolean;
|
smallLayout?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
@ -59,6 +60,7 @@ const { acct, createdAt, url } = defineProps<{
|
||||||
const [username, instance] = acct.split("@");
|
const [username, instance] = acct.split("@");
|
||||||
const digitRegex = /\d/;
|
const digitRegex = /\d/;
|
||||||
const urlAsPath = new URL(url).pathname;
|
const urlAsPath = new URL(url).pathname;
|
||||||
|
const noteUrlAsPath = new URL(noteUrl).pathname;
|
||||||
const timeAgo = useTimeAgo(createdAt, {
|
const timeAgo = useTimeAgo(createdAt, {
|
||||||
messages: {
|
messages: {
|
||||||
justNow: "now",
|
justNow: "now",
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,23 @@
|
||||||
<ReblogHeader v-if="note.reblog" :avatar="note.account.avatar" :display-name="note.account.display_name"
|
<ReblogHeader v-if="note.reblog" :avatar="note.account.avatar" :display-name="note.account.display_name"
|
||||||
:url="reblogAccountUrl" :emojis="note.account.emojis" />
|
:url="reblogAccountUrl" :emojis="note.account.emojis" />
|
||||||
<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"
|
: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)"
|
:visibility="noteToUse.visibility" :url="accountUrl" :created-at="new Date(noteToUse.created_at)"
|
||||||
:small-layout="smallLayout" :emojis="noteToUse.account.emojis" />
|
:small-layout="smallLayout" :emojis="noteToUse.account.emojis" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<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>
|
</CardContent>
|
||||||
<CardFooter v-if="!hideActions" class="p-4 pt-0">
|
<CardFooter v-if="!hideActions" class="p-4 pt-0">
|
||||||
<Actions :reply-count="noteToUse.replies_count" :like-count="noteToUse.favourites_count" :url="url"
|
<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>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</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>
|
<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" />
|
<Note v-for="note of context?.ancestors" :note="note" />
|
||||||
<div ref="element" class="first:rounded-t last:rounded-b overflow-hidden">
|
<Note v-if="note" :note="note" />
|
||||||
<Note class="!rounded-none border-2 -m-[2px] border-primary-500" v-if="note" :note="note" />
|
|
||||||
</div>
|
|
||||||
<Note v-for="note of context?.descendants" :note="note" />
|
<Note v-for="note of context?.descendants" :note="note" />
|
||||||
</div>
|
</div>
|
||||||
<!-- <div v-else class="mx-auto max-w-2xl w-full overflow-y-auto">
|
|
||||||
<Note v-for="_ of 5" :skeleton="true" />
|
<div v-else class="p-4 flex items-center justify-center h-48">
|
||||||
</div> -->
|
<Loader class="size-8 animate-spin" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { Loader } from "lucide-vue-next";
|
||||||
import Note from "~/components/notes/note.vue";
|
import Note from "~/components/notes/note.vue";
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue