refactor: ♻️ Disable Nuxt component auto-importing (obscures code flow)

This commit is contained in:
Jesse Wierzbinski 2024-06-20 16:09:09 -10:00
parent 32d1acb4c1
commit e309c56a86
No known key found for this signature in database
58 changed files with 440 additions and 292 deletions

View file

@ -8,6 +8,7 @@
<script lang="ts" setup>
import type { HTMLAttributes } from "vue";
import Skeleton from "../skeleton/Skeleton.vue";
interface Props extends /* @vue-ignore */ HTMLAttributes {
src?: string;

View file

@ -1,12 +1,13 @@
<template>
<ButtonsBase class="enabled:hover:bg-white/20 !rounded-sm !text-left flex flex-row gap-x-3 !ring-0 !p-4 sm:!p-2">
<ButtonBase class="enabled:hover:bg-white/20 !rounded-sm !text-left flex flex-row gap-x-3 !ring-0 !p-4 sm:!p-2">
<iconify-icon :icon="icon" width="none" class="text-gray-200 size-5" aria-hidden="true" />
<slot />
</ButtonsBase>
</ButtonBase>
</template>
<script lang="ts" setup>
import type { ButtonHTMLAttributes } from "vue";
import ButtonBase from "./button-base.vue";
interface Props extends /* @vue-ignore */ ButtonHTMLAttributes {}

View file

@ -1,12 +1,14 @@
<template>
<ButtonsBase
<ButtonBase
class="hover:bg-white/5 text-xs max-w-full w-full gap-1 h-full !px-0 flex flex-col items-center justify-center">
<iconify-icon :icon="icon" class="size-6" width="none" />
<span class="text-xs hidden md:inline">{{ text }}</span>
</ButtonsBase>
</ButtonBase>
</template>
<script lang="ts" setup>
import ButtonBase from "./button-base.vue";
defineProps<{
icon: string;
text: string;

View file

@ -1,13 +1,14 @@
<template>
<ButtonsBase :loading="loading"
<ButtonBase :loading="loading"
class="[--btn-border:theme(colors.primary.950/90%)] [--btn-bg:theme(colors.primary.600)] [--btn-hover-overlay:theme(colors.white/5%)] [--btn-icon:theme(colors.primary.400)] active:[--btn-icon:theme(colors.primary.300)] hover:[--btn-icon:theme(colors.primary.300)] after:shadow-[shadow:inset_0_1px_theme(colors.white/15%)] border border-white/5"
v-bind="$props">
<slot />
</ButtonsBase>
</ButtonBase>
</template>
<script lang="ts" setup>
import type { ButtonHTMLAttributes } from "vue";
import ButtonBase from "./button-base.vue";
interface Props extends /* @vue-ignore */ ButtonHTMLAttributes {}

View file

@ -1,13 +1,14 @@
<template>
<ButtonsBase
<ButtonBase
class="[--btn-border:theme(colors.zinc.950/90%)] [--btn-bg:theme(colors.zinc.800)] [--btn-hover-overlay:theme(colors.white/5%)] [--btn-icon:theme(colors.zinc.400)] active:[--btn-icon:theme(colors.zinc.300)] hover:[--btn-icon:theme(colors.zinc.300)] after:shadow-[shadow:inset_0_1px_theme(colors.white/15%)] border border-white/5"
v-bind="$props" :loading="loading">
<slot />
</ButtonsBase>
</ButtonBase>
</template>
<script lang="ts" setup>
import type { ButtonHTMLAttributes } from "vue";
import ButtonBase from "./button-base.vue";
interface Props extends /* @vue-ignore */ ButtonHTMLAttributes {}

View file

@ -1,12 +1,11 @@
<template>
<div v-if="respondingTo" class="mb-4" role="region" aria-label="Responding to">
<OverlayScrollbarsComponent :defer="true" class="max-h-72 overflow-y-auto">
<LazySocialElementsNotesNote :note="respondingTo" :small="true" :disabled="true"
class="!rounded-none !bg-primary-500/10" />
<Note :note="respondingTo" :small="true" :disabled="true" class="!rounded-none !bg-primary-500/10" />
</OverlayScrollbarsComponent>
</div>
<div class="px-6 pb-4 pt-5">
<InputsRichTextbox v-model:model-content="content" @paste="handlePaste" :disabled="loading"
<RichTextboxInput v-model:model-content="content" @paste="handlePaste" :disabled="loading"
:placeholder="chosenSplash" :max-characters="characterLimit" class="focus:!ring-0 max-h-[70dvh]" />
<!-- Content warning textbox -->
<div v-if="cw" class="mb-4">
@ -14,33 +13,33 @@
class="w-full p-2 mt-1 text-sm prose prose-invert bg-dark-900 rounded focus:!ring-0 !ring-none !border-none !outline-none placeholder:text-zinc-500 appearance-none focus:!border-none focus:!outline-none"
aria-label="Content warning" />
</div>
<ComposerFileUploader v-model:files="files" ref="uploader" />
<FileUploader v-model:files="files" ref="uploader" />
<div class="flex flex-row gap-1 border-white/20">
<ComposerButton title="Mention someone">
<Button title="Mention someone">
<iconify-icon height="1.5rem" width="1.5rem" icon="tabler:at" aria-hidden="true" />
</ComposerButton>
<ComposerButton title="Toggle Markdown" @click="markdown = !markdown" :toggled="markdown">
</Button>
<Button title="Toggle Markdown" @click="markdown = !markdown" :toggled="markdown">
<iconify-icon width="1.25rem" height="1.25rem"
:icon="markdown ? 'tabler:markdown' : 'tabler:markdown-off'" aria-hidden="true" />
</ComposerButton>
<ComposerButton title="Use a custom emoji">
</Button>
<Button title="Use a custom emoji">
<iconify-icon width="1.25rem" height="1.25rem" icon="tabler:mood-smile" aria-hidden="true" />
</ComposerButton>
<ComposerButton title="Add media" @click="openFilePicker">
</Button>
<Button title="Add media" @click="openFilePicker">
<iconify-icon width="1.25rem" height="1.25rem" icon="tabler:photo-up" aria-hidden="true" />
</ComposerButton>
<ComposerButton title="Add a file" @click="openFilePicker">
</Button>
<Button title="Add a file" @click="openFilePicker">
<iconify-icon width="1.25rem" height="1.25rem" icon="tabler:file-upload" aria-hidden="true" />
</ComposerButton>
<ComposerButton title="Add content warning" @click="cw = !cw" :toggled="cw">
</Button>
<Button title="Add content warning" @click="cw = !cw" :toggled="cw">
<iconify-icon width="1.25rem" height="1.25rem" icon="tabler:rating-18-plus" aria-hidden="true" />
</ComposerButton>
<ButtonsPrimary :loading="loading" @click="send" class="ml-auto rounded-full"
</Button>
<ButtonPrimary :loading="loading" @click="send" class="ml-auto rounded-full"
:disabled="!canSubmit || loading">
<span>{{
respondingType === "edit" ? "Edit!" : "Send!"
}}</span>
</ButtonsPrimary>
</ButtonPrimary>
</div>
</div>
</template>
@ -49,7 +48,12 @@
import type { Instance, Status } from "@lysand-org/client/types";
import { nanoid } from "nanoid";
import { OverlayScrollbarsComponent } from "#imports";
import type FileUploader from "./file-uploader.vue";
import ButtonPrimary from "../buttons/button-primary.vue";
import RichTextboxInput from "../inputs/rich-textbox-input.vue";
import Note from "../social-elements/notes/note.vue";
import Button from "./button.vue";
// biome-ignore lint/style/useImportType: Biome doesn't see the Vue code
import FileUploader from "./file-uploader.vue";
const uploader = ref<InstanceType<typeof FileUploader> | undefined>(undefined);
const { Control_Enter, Command_Enter, Control_Alt } = useMagicKeys();

View file

@ -1,17 +1,18 @@
<template>
<ComposerSuggestbox class="max-h-40 overflow-auto !w-auto !flex-row">
<Suggestbox class="max-h-40 overflow-auto !w-auto !flex-row">
<div v-for="(emoji, index) in topEmojis" :key="emoji.shortcode" @click="emit('autocomplete', emoji.shortcode)"
:ref="el => { if (el) emojiRefs[index] = el as Element }" :title="emoji.shortcode"
:class="['flex', 'justify-center', 'shrink-0', 'items-center', 'p-2', 'size-12', 'hover:bg-dark-900/70', { 'bg-primary-500': index === selectedEmojiIndex }]">
<img :src="emoji.url" class="w-full h-full object-contain"
:alt="`Emoji with shortcode ${emoji.shortcode}`" />
</div>
</ComposerSuggestbox>
</Suggestbox>
</template>
<script lang="ts" setup>
import type { Emoji } from "@lysand-org/client/types";
import { distance } from "fastest-levenshtein";
import Suggestbox from "./suggestbox.vue";
const props = defineProps<{
currentlyTypingEmoji: string | null;
textarea: HTMLTextAreaElement | undefined;

View file

@ -30,8 +30,8 @@
</button>
<!-- Alt text editor -->
<Popover.Root :positioning="{
strategy: 'fixed',
}" v-if="data.api_id" @update:open="o => !o && updateAltText(data.id, data.alt_text)">
strategy: 'fixed',
}" v-if="data.api_id" @update:open="o => !o && updateAltText(data.id, data.alt_text)">
<Popover.Trigger aria-hidden="true"
class="absolute top-1 left-1 p-1 bg-dark-800 ring-1 ring-white/5 text-white text-xs rounded size-6">
<iconify-icon icon="tabler:alt" width="none" class="size-4" />
@ -42,10 +42,10 @@
<textarea :disabled="data.progress < 1.0" @keydown.enter.stop v-model="data.alt_text"
placeholder="Add alt text"
class="w-full p-2 text-sm prose prose-invert bg-dark-900 rounded focus:!ring-0 !ring-none !border-none !outline-none placeholder:text-zinc-500 appearance-none focus:!border-none focus:!outline-none" />
<ButtonsSecondary @click="updateAltText(data.id, data.alt_text)" class="w-full"
<ButtonSecondary @click="updateAltText(data.id, data.alt_text)" class="w-full"
:loading="data.progress < 1.0">
<span>Edit</span>
</ButtonsSecondary>
</ButtonSecondary>
</Popover.Content>
</Popover.Positioner>
</Popover.Root>
@ -57,6 +57,7 @@
<script lang="ts" setup>
import { Popover } from "@ark-ui/vue";
import { nanoid } from "nanoid";
import ButtonSecondary from "../buttons/button-secondary.vue";
const files = defineModel<
{

View file

@ -1,16 +1,17 @@
<template>
<ComposerSuggestbox class="max-h-40 overflow-auto !w-auto !flex-row">
<Suggestbox class="max-h-40 overflow-auto !w-auto !flex-row">
<div v-for="(user, index) in topUsers" :key="user.username" @click="emit('autocomplete', user.acct)"
:ref="el => { if (el) userRefs[index] = el as Element }" :title="user.acct"
:class="['flex', 'justify-center', 'shrink-0', 'items-center', 'p-2', 'size-12', 'hover:bg-dark-900/70', { 'bg-primary-500': index === selectedUserIndex }]">
<img :src="user.avatar" class="w-full h-full object-contain" :alt="`User ${user.acct}`" />
</div>
</ComposerSuggestbox>
</Suggestbox>
</template>
<script lang="ts" setup>
import type { Account } from "@lysand-org/client/types";
import { distance } from "fastest-levenshtein";
import Suggestbox from "./suggestbox.vue";
const props = defineProps<{
currentlyTypingMention: string | null;
textarea: HTMLTextAreaElement | undefined;

View file

@ -31,6 +31,7 @@
<script lang="ts" setup>
import { Dialog } from "@ark-ui/vue";
import Composer from "./composer.vue";
const open = ref(false);
const identity = useCurrentIdentity();

View file

@ -7,9 +7,9 @@
</h1>
<p class="mt-6 text-base leading-7 text-gray-400" v-html="error.message"></p>
<div class="mt-10 grid grid-cols-2 gap-x-6 mx-auto max-w-md">
<ButtonsPrimary class="w-full" @click="back">Go back</ButtonsPrimary>
<ButtonPrimary class="w-full" @click="back">Go back</ButtonPrimary>
<a href="https://github.com/lysand-org/lysand-fe/issues" target="_blank">
<ButtonsSecondary class="w-full">Report an issue</ButtonsSecondary>
<ButtonSecondary class="w-full">Report an issue</ButtonSecondary>
</a>
</div>
</div>
@ -19,6 +19,9 @@
</template>
<script lang="ts" setup>
import ButtonPrimary from "../buttons/button-primary.vue";
import ButtonSecondary from "../buttons/button-secondary.vue";
const error = ref<{
title: string;
message: string;

View file

@ -1,6 +1,10 @@
<script setup lang="ts">
import SquarePattern from "../graphics/square-pattern.vue";
</script>
<template>
<div class="grid min-h-screen place-items-center px-6 py-24 sm:py-32 lg:px-8 fixed inset-0 z-[1000000] bg-dark-900">
<GraphicsSquarePattern />
<SquarePattern />
<div class="prose prose-invert max-w-lg">
<h1 class="mt-4 text-3xl font-bold tracking-tight text-gray-100 sm:text-5xl">JavaScript is disabled
</h1>

View file

@ -2,7 +2,7 @@
<div v-if="identity" class="bg-dark-800 z-0 p-6 my-5 relative overflow-hidden rounded ring-1 ring-white/5">
<div class="sm:flex sm:items-center sm:justify-between gap-3">
<div class="sm:flex sm:space-x-5 grow">
<AvatarsCentered :src="identity.account.avatar"
<Avatar :src="identity.account.avatar"
class="mx-auto shrink-0 size-20 rounded overflow-hidden ring-1 ring-white/10"
:alt="'Your avatar'" />
<div
@ -16,6 +16,8 @@
</template>
<script lang="ts" setup>
import Avatar from "../avatars/avatar.vue";
const identity = useCurrentIdentity();
const settings = useSettings();
const { display_name } = useParsedAccount(

View file

@ -1,10 +1,11 @@
<template>
<InputsText type="checkbox" v-bind="$attrs, $props"
<TextInput type="checkbox" v-bind="$attrs, $props"
class="rounded disabled:hover:cursor-wait text-primary-700 !size-5" />
</template>
<script lang="ts" setup>
import type { InputHTMLAttributes } from "vue";
import TextInput from "./text-input.vue";
interface Props extends /* @vue-ignore */ InputHTMLAttributes {}

View file

@ -1,13 +1,13 @@
<template>
<InputsText @input="e => content = (e.target as HTMLInputElement).value" v-bind="$attrs, $props" v-model="content"
<TextInput @input="e => content = (e.target as HTMLInputElement).value" v-bind="$attrs, $props" v-model="content"
:type="showPassword ? 'text' : 'password'" :spellcheck="false" />
<Progress.Root class="flex flex-row items-center gap-x-2" v-if="showStrength">
<Progress.Label class="text-xs text-gray-300 font-semibold w-12">
{{ text }}
</Progress.Label>
<Progress.Track class="rounded-sm w-full h-2 duration-300" :style="{
backgroundColor: color,
}">
backgroundColor: color,
}">
<Progress.Range />
</Progress.Track>
</Progress.Root>
@ -27,6 +27,7 @@ const showPassword = ref(false);
const content = ref("");
import type { InputHTMLAttributes } from "vue";
import TextInput from "./text-input.vue";
interface Props extends /* @vue-ignore */ InputHTMLAttributes {
isInvalid?: boolean;
@ -66,6 +67,7 @@ const color = computed(() => {
});
onMounted(() => {
// Workaround to make sure the teleport is rendered after the parent component
teleport.value = true;
});
</script>

View file

@ -8,15 +8,17 @@
aria-live="polite">
{{ remainingCharacters }}
</div>
<ComposerEmojiSuggestbox :textarea="textarea" v-if="!!currentlyBeingTypedEmoji"
<EmojiSuggestbox :textarea="textarea" v-if="!!currentlyBeingTypedEmoji"
:currently-typing-emoji="currentlyBeingTypedEmoji" @autocomplete="autocompleteEmoji" />
<ComposerMentionSuggestbox :textarea="textarea" v-if="!!currentlyBeingTypedMention"
<MentionSuggestbox :textarea="textarea" v-if="!!currentlyBeingTypedMention"
:currently-typing-mention="currentlyBeingTypedMention" @autocomplete="autocompleteMention" />
</div>
</template>
<script lang="ts" setup>
import { char, createRegExp, exactly } from "magic-regexp";
import EmojiSuggestbox from "../composer/emoji-suggestbox.vue";
import MentionSuggestbox from "../composer/mention-suggestbox.vue";
defineOptions({
inheritAttrs: false,

View file

@ -9,17 +9,17 @@
<div v-for="provider of ssoConfig?.providers" :key="provider.id"
class="flex items-center justify-between p-4 bg-dark-700 rounded">
<div class="flex items-center gap-4">
<AvatarsCentered :src="provider.icon" :alt="provider.name" class="h-8 w-8" />
<Avatar :src="provider.icon" :alt="provider.name" class="h-8 w-8" />
<span class="font-semibold text-gray-300">{{ provider.name }}</span>
</div>
<div>
<ButtonsPrimary :loading="loading" v-if="!linkedProviders?.find(p => p.id === provider.id)"
<ButtonPrimary :loading="loading" v-if="!linkedProviders?.find(p => p.id === provider.id)"
@click="link(provider.id)">
<span>Link</span>
</ButtonsPrimary>
<ButtonsSecondary :loading="loading" v-else @click="unlink(provider.id)">
</ButtonPrimary>
<ButtonSecondary :loading="loading" v-else @click="unlink(provider.id)">
<span>Unlink</span>
</ButtonsSecondary>
</ButtonSecondary>
</div>
</div>
</div>
@ -29,6 +29,9 @@
<script lang="ts" setup>
import type { ResponseError } from "@lysand-org/client";
import Avatar from "../avatars/avatar.vue";
import ButtonPrimary from "../buttons/button-primary.vue";
import ButtonSecondary from "../buttons/button-secondary.vue";
const client = useClient();
const ssoConfig = useSSOConfig();

View file

@ -3,20 +3,20 @@
class="w-full md:px-8 px-4 py-4 bg-dark-700 grid justify-center lg:grid-cols-[minmax(auto,_36rem)_1fr] grid-cols-1 gap-4">
<form class="w-full ring-1 ring-inset ring-white/5 pb-5 bg-dark-800 rounded overflow-hidden"
@submit.prevent="save">
<AvatarsCentered :src="account?.header" :alt="`${account?.acct}'s header image'`"
<Avatar :src="account?.header" :alt="`${account?.acct}'s header image'`"
class="w-full aspect-[8/3] border-b border-white/10 bg-dark-700 !rounded-none" />
<div class="flex items-start justify-between px-4 py-3">
<AvatarsCentered :src="account?.avatar" :alt="`${account?.acct}'s avatar'`"
<Avatar :src="account?.avatar" :alt="`${account?.acct}'s avatar'`"
class="h-32 w-32 -mt-[4.5rem] z-10 shrink-0 rounded ring-2 ring-dark-800" />
</div>
<div class="mt-2 px-4">
<InputsText @input="displayName = ($event.target as HTMLInputElement).value" :value="displayName"
<TextInput @input="displayName = ($event.target as HTMLInputElement).value" :value="displayName"
aria-label="Display name" :disabled="loading" />
<div class="mt-2 grid grid-cols-[auto_1fr] items-center gap-x-2">
<iconify-icon icon="tabler:at" width="none" class="size-6" aria-hidden="true" />
<InputsText @input="acct = ($event.target as HTMLInputElement).value" :value="acct"
<TextInput @input="acct = ($event.target as HTMLInputElement).value" :value="acct"
aria-label="Username" :disabled="loading" />
</div>
<p class="text-gray-300 text-xs mt-2">
@ -25,27 +25,33 @@
</div>
<div class="mt-3 px-4">
<InputsRichTextbox v-model:model-content="note" :max-characters="bio" :disabled="loading"
<RichTextboxInput v-model:model-content="note" :max-characters="bio" :disabled="loading"
class="rounded ring-white/10 ring-2 focus:ring-primary-600 px-4 py-2 max-h-[40dvh] max-w-full" />
</div>
<div class="px-4 mt-4 grid grid-cols-2 gap-2">
<ButtonsPrimary class="w-full" type="submit" :loading="loading">
<ButtonPrimary class="w-full" type="submit" :loading="loading">
<span>Save</span>
</ButtonsPrimary>
<ButtonsSecondary class="w-full" @click="revert" type="button" :loading="loading">
</ButtonPrimary>
<ButtonSecondary class="w-full" @click="revert" type="button" :loading="loading">
<span>Revert</span>
</ButtonsSecondary>
</ButtonSecondary>
</div>
</form>
<div>
<SettingsOidc />
<Oidc />
</div>
</div>
</template>
<script lang="ts" setup>
import type { ResponseError } from "@lysand-org/client";
import Avatar from "../avatars/avatar.vue";
import ButtonPrimary from "../buttons/button-primary.vue";
import ButtonSecondary from "../buttons/button-secondary.vue";
import RichTextboxInput from "../inputs/rich-textbox-input.vue";
import TextInput from "../inputs/text-input.vue";
import Oidc from "./oidc.vue";
const identity = useCurrentIdentity();
const account = computed(() => identity.value?.account);

View file

@ -1,12 +1,12 @@
<template>
<DropdownsAdaptiveDropdown>
<AdaptiveDropdown>
<template #button>
<slot>
<div class="rounded text-left flex flex-row gap-x-2 hover:scale-[95%] duration-100"
v-if="currentIdentity">
<div class="shrink-0">
<AvatarsCentered class="size-12 rounded ring-1 ring-white/5"
:src="currentIdentity.account.avatar" :alt="`${currentIdentity.account.acct}'s avatar'`" />
<Avatar class="size-12 rounded ring-1 ring-white/5" :src="currentIdentity.account.avatar"
:alt="`${currentIdentity.account.acct}'s avatar'`" />
</div>
<div class="flex flex-col items-start p-1 justify-around grow overflow-hidden">
<div class="flex flex-row items-center justify-between w-full">
@ -20,11 +20,11 @@
</span>
</div>
</div>
<ButtonsBase v-else class="flex flex-row text-left items-center justify-start gap-3 text-lg hover:ring-1 ring-white/10
<ButtonBase v-else class="flex flex-row text-left items-center justify-start gap-3 text-lg hover:ring-1 ring-white/10
overflow-hidden h-12 w-full duration-200">
<iconify-icon icon="tabler:login" class="shrink-0 text-2xl" />
<span class="pr-28 line-clamp-1">Sign In</span>
</ButtonsBase>
</ButtonBase>
</slot>
</template>
<template #items>
@ -36,8 +36,8 @@
<Menu.Item value="" v-for="identity of identities" class="hover:scale-[95%] duration-100">
<div class="flex flex-row gap-x-4">
<div class="shrink-0" data-part="item" @click="useEvent('identity:change', identity)">
<AvatarsCentered class="h-12 w-12 rounded ring-1 ring-white/5"
:src="identity.account.avatar" :alt="`${identity.account.acct}'s avatar'`" />
<Avatar class="h-12 w-12 rounded ring-1 ring-white/5" :src="identity.account.avatar"
:alt="`${identity.account.acct}'s avatar'`" />
</div>
<div data-part="item" class="flex flex-col items-start justify-around grow overflow-hidden"
@click="useEvent('identity:change', identity)">
@ -104,11 +104,14 @@
</Menu.Item>
</div>
</template>
</DropdownsAdaptiveDropdown>
</AdaptiveDropdown>
</template>
<script lang="ts" setup>
import { Menu } from "@ark-ui/vue";
import Avatar from "../avatars/avatar.vue";
import ButtonBase from "../buttons/button-base.vue";
import AdaptiveDropdown from "../dropdowns/AdaptiveDropdown.vue";
const identities = useIdentities();
const currentIdentity = useCurrentIdentity();

View file

@ -12,11 +12,11 @@
Timelines</h3>
<NuxtLink v-for="timeline in visibleTimelines" :key="timeline.href" :to="timeline.href">
<ButtonsBase
<ButtonBase
class="flex flex-row text-left items-center justify-start gap-3 text-lg hover:ring-1 ring-white/10 overflow-hidden h-12 w-full duration-200">
<iconify-icon :icon="timeline.icon" class="shrink-0 text-2xl" />
<span class="pr-28 line-clamp-1">{{ timeline.name }}</span>
</ButtonsBase>
</ButtonBase>
</NuxtLink>
</div>
@ -25,14 +25,14 @@
<h3 class="font-semibold text-gray-300 text-xs uppercase opacity-0 group-hover:opacity-100 duration-200">
Account</h3>
<SidebarsAccountPicker @sign-in="signIn().finally(() => loadingAuth = false)"
<AccountPicker @sign-in="signIn().finally(() => loadingAuth = false)"
@sign-out="id => signOut(id).finally(() => loadingAuth = false)" />
<NuxtLink href="/register" v-if="!identity">
<ButtonsBase
<ButtonBase
class="flex flex-row text-left items-center justify-start gap-3 text-lg hover:ring-1 ring-white/10 overflow-hidden h-12 w-full duration-200">
<iconify-icon icon="tabler:certificate" class="shrink-0 text-2xl" />
<span class="pr-28 line-clamp-1">Register</span>
</ButtonsBase>
</ButtonBase>
</NuxtLink>
<NuxtLink href="/settings" v-if="identity">
<button @click="$emit('signIn')" class="w-full overflow-hidden">
@ -51,7 +51,7 @@
<h3 v-if="identity"
class="font-semibold text-gray-300 text-xs uppercase opacity-0 group-hover:opacity-100 duration-200">
Posts</h3>
<ButtonsBase v-if="identity" @click="compose" title="Open composer (shortcut: n)"
<ButtonBase v-if="identity" @click="compose" title="Open composer (shortcut: n)"
class="flex flex-row text-left items-center justify-start gap-3 text-lg hover:ring-1 ring-white/10 bg-gradient-to-tr from-primary-300 via-purple-300 to-indigo-400 overflow-hidden h-12 w-full duration-200">
<iconify-icon icon="tabler:writing" class="shrink-0 text-2xl" />
<span class="pr-28 line-clamp-1">Compose</span>
@ -60,12 +60,12 @@
<iconify-icon icon="tabler:letter-n-small" height="1rem" width="1rem" class="inline -mr-1"
aria-hidden="true" />
</kbd>
</ButtonsBase>
<ButtonsBase v-if="$pwa?.needRefresh" @click="$pwa?.updateServiceWorker()" title="Update service worker"
</ButtonBase>
<ButtonBase v-if="$pwa?.needRefresh" @click="$pwa?.updateServiceWorker()" title="Update service worker"
class="flex flex-row text-left items-center justify-start gap-3 text-lg ring-2 ring-primary-600 overflow-hidden h-12 w-full duration-200">
<iconify-icon icon="tabler:refresh" class="shrink-0 text-2xl" />
<span class="pr-28 line-clamp-1">Update</span>
</ButtonsBase>
</ButtonBase>
</div>
</aside>
@ -73,30 +73,30 @@
<nav
:class="['fixed bottom-0 left-0 right-0 z-20 h-16 md:hidden grid gap-3 p-2 *:shadow-xl bg-dark-900 ring-1 ring-white/10 text-gray-200', !!identity ? 'grid-cols-4' : 'grid-cols-3']">
<DropdownsAdaptiveDropdown>
<AdaptiveDropdown>
<template #button>
<ButtonsMobileNavbarButton icon="tabler:home" text="Timelines" />
<ButtonMobileNavbar icon="tabler:home" text="Timelines" />
</template>
<template #items>
<Menu.Item value="" v-for="timeline in visibleTimelines" :key="timeline.href">
<NuxtLink :href="timeline.href">
<ButtonsDropdownElement :icon="timeline.icon" class="w-full">
<ButtonDropdown :icon="timeline.icon" class="w-full">
{{ timeline.name }}
</ButtonsDropdownElement>
</ButtonDropdown>
</NuxtLink>
</Menu.Item>
</template>
</DropdownsAdaptiveDropdown>
</AdaptiveDropdown>
<NuxtLink href="/notifications" class="w-full">
<ButtonsMobileNavbarButton icon="tabler:bell" text="Notifications" />
<ButtonMobileNavbar icon="tabler:bell" text="Notifications" />
</NuxtLink>
<ButtonsMobileNavbarButton v-if="$pwa?.needRefresh" @click="$pwa?.updateServiceWorker(true)"
icon="tabler:refresh" text="Update" />
<SidebarsAccountPicker v-else @sign-in="signIn().finally(() => loadingAuth = false)"
<ButtonMobileNavbar v-if="$pwa?.needRefresh" @click="$pwa?.updateServiceWorker(true)" icon="tabler:refresh"
text="Update" />
<AccountPicker v-else @sign-in="signIn().finally(() => loadingAuth = false)"
@sign-out="id => signOut(id).finally(() => loadingAuth = false)">
<ButtonsMobileNavbarButton icon="tabler:user" text="Account" />
</SidebarsAccountPicker>
<ButtonMobileNavbar icon="tabler:user" text="Account" />
</AccountPicker>
<button @click="compose" v-if="identity"
class="flex flex-col items-center justify-center p-2 rounded bg-gradient-to-tr from-[theme(colors.primary.300/70%)] via-purple-300/70 to-indigo-400/70">
<iconify-icon icon="tabler:writing" class="text-2xl" />
@ -107,6 +107,11 @@
<script lang="ts" setup>
import { Menu } from "@ark-ui/vue";
import ButtonBase from "../buttons/button-base.vue";
import ButtonDropdown from "../buttons/button-dropdown.vue";
import ButtonMobileNavbar from "../buttons/button-mobile-navbar.vue";
import AdaptiveDropdown from "../dropdowns/AdaptiveDropdown.vue";
import AccountPicker from "./account-picker.vue";
const { $pwa } = useNuxtApp();
const timelines = ref([
{

View file

@ -2,9 +2,9 @@
<Tabs.Root v-model="tab">
<Tabs.List class="flex flex-row p-4 gap-4 bg-dark-800 relative ring-1 ring-white/5 overflow-x-auto">
<Tabs.Trigger :value="page" v-for="page of SettingPages" :as-child="true">
<ButtonsBase class="capitalize hover:bg-white/5">
<ButtonBase class="capitalize hover:bg-white/5">
{{ page }}
</ButtonsBase>
</ButtonBase>
</Tabs.Trigger>
<Tabs.Indicator class="h-1 bg-gray-300 w-[--width] top-0 rounded-b" />
</Tabs.List>
@ -17,6 +17,7 @@
<script lang="ts" setup>
import { Tabs } from "@ark-ui/vue";
import { SettingPages } from "~/settings";
import ButtonBase from "../buttons/button-base.vue";
const tab = ref<SettingPages>(
(window.location.hash.slice(1) as SettingPages) || SettingPages.Account,

View file

@ -13,12 +13,14 @@
<div v-if="instance?.contact.account" class="flex flex-col gap-2 mt-auto">
<h2 class="text-gray-200 font-semibold uppercase text-xs">Administrator</h2>
<SocialElementsUsersSmallCard :account="instance.contact.account" />
<SmallCard :account="instance.contact.account" />
</div>
</div>
</template>
<script lang="ts" setup>
import SmallCard from "../users/SmallCard.vue";
const client = useClient();
const instance = useInstance();
const description = useExtendedDescription(client);

View file

@ -1,7 +1,7 @@
<template>
<div v-if="small" class="flex flex-row">
<NuxtLink :href="accountUrl" class="shrink-0">
<AvatarsCentered :src="note?.account.avatar" :alt="`${note?.account.acct}'s avatar`"
<Avatar :src="note?.account.avatar" :alt="`${note?.account.acct}'s avatar`"
class="size-6 rounded ring-1 ring-white/5" />
<span class="sr-only">Account profile</span>
</NuxtLink>
@ -23,7 +23,7 @@
</div>
<div v-else class="flex flex-row">
<NuxtLink :href="accountUrl" class="shrink-0">
<AvatarsCentered :src="note?.account.avatar" :alt="`${note?.account.acct}'s avatar`"
<Avatar :src="note?.account.avatar" :alt="`${note?.account.acct}'s avatar`"
class="h-12 w-12 rounded ring-1 ring-white/5" />
<span class="sr-only">Account profile</span>
</NuxtLink>
@ -67,6 +67,8 @@
<script lang="ts" setup>
import type { Status } from "@lysand-org/client/types";
import Avatar from "~/components/avatars/avatar.vue";
import Skeleton from "~/components/skeleton/Skeleton.vue";
const props = defineProps<{
note?: Status;

View file

@ -10,7 +10,7 @@
<script lang="ts" setup>
import type { Account } from "@lysand-org/client/types";
const props = defineProps<{
defineProps<{
account: Account;
}>();
</script>

View file

@ -9,11 +9,9 @@
</Skeleton>
<div v-if="note && note.media_attachments.length > 0"
class="[&:not(:first-child)]:mt-6 grid grid-cols-2 gap-4 [&>*]:aspect-square [&:has(>:last-child:nth-child(1))>*]:aspect-video [&:has(>:last-child:nth-child(1))]:block">
<SocialElementsNotesAttachment v-for="attachment of note.media_attachments" :key="attachment.id"
:attachment="attachment" />
<Attachment v-for="attachment of note.media_attachments" :key="attachment.id" :attachment="attachment" />
</div>
<LazySocialElementsNotesNote v-if="isQuote && note?.quote" :note="note?.quote" :small="true"
class="mt-4 !rounded" />
<Note v-if="isQuote && note?.quote" :note="note?.quote" :small="true" class="mt-4 !rounded" />
</div>
<div v-else
class="rounded text-center ring-1 !max-w-full ring-white/10 h-52 mt-6 prose prose-invert p-4 flex flex-col justify-center items-center">
@ -22,12 +20,16 @@
<!-- Spoiler text is it's specified -->
<span v-if="note?.spoiler_text" class="mt-2 break-all">{{ note.spoiler_text
}}</span>
<ButtonsSecondary @click="collapsed = false" class="mt-4">Show content</ButtonsSecondary>
<ButtonSecondary @click="collapsed = false" class="mt-4">Show content</ButtonSecondary>
</div>
</template>
<script lang="ts" setup>
import type { Status } from "@lysand-org/client/types";
import ButtonSecondary from "~/components/buttons/button-secondary.vue";
import Skeleton from "~/components/skeleton/Skeleton.vue";
import Attachment from "./attachment.vue";
import Note from "./note.vue";
const props = defineProps<{
content: string | null;

View file

@ -7,15 +7,15 @@
<div v-if="reblog" class="mb-4 flex flex-row gap-2 items-center text-primary-400">
<Skeleton :enabled="!loaded" shape="rect" class="!h-6" :min-width="40" :max-width="100" width-unit="%">
<iconify-icon width="1.5rem" height="1.5rem" icon="tabler:repeat" class="size-6" aria-hidden="true" />
<AvatarsCentered v-if="reblog.avatar" :src="reblog.avatar" :alt="`${reblog.acct}'s avatar'`"
<Avatar v-if="reblog.avatar" :src="reblog.avatar" :alt="`${reblog.acct}'s avatar'`"
class="size-6 rounded shrink-0 ring-1 ring-white/10" />
<span><strong v-html="reblogDisplayName"></strong> reblogged</span>
</Skeleton>
</div>
<SocialElementsNotesReplyHeader v-if="isReply" :account_id="outputtedNote?.in_reply_to_account_id ?? null" />
<SocialElementsNotesHeader :note="outputtedNote" :small="small" />
<LazySocialElementsNotesNoteContent :note="outputtedNote" :loaded="loaded" :url="url" :content="content"
:is-quote="isQuote" :should-hide="shouldHide" />
<ReplyHeader v-if="isReply" :account_id="outputtedNote?.in_reply_to_account_id ?? null" />
<Header :note="outputtedNote" :small="small" />
<NoteContent :note="outputtedNote" :loaded="loaded" :url="url" :content="content" :is-quote="isQuote"
:should-hide="shouldHide" />
<Skeleton class="!h-10 w-full mt-6" :enabled="!props.note || !loaded" v-if="!small || !showInteractions">
<div v-if="showInteractions"
class="mt-6 flex flex-row items-stretch disabled:*:opacity-70 [&>button]:max-w-28 disabled:*:cursor-not-allowed relative justify-around text-sm h-10 hover:enabled:[&>button]:bg-dark-800 [&>button]:duration-200 [&>button]:rounded [&>button]:flex [&>button]:flex-1 [&>button]:flex-row [&>button]:items-center [&>button]:justify-center">
@ -45,7 +45,7 @@
class="size-5 text-gray-200 group-hover:group-enabled:text-blue-600" aria-hidden="true" />
<span class="text-gray-400 mt-0.5 ml-2">{{ numberFormat(0) }}</span>
</button>
<DropdownsAdaptiveDropdown>
<AdaptiveDropdown>
<template #button>
<iconify-icon width="1.25rem" height="1.25rem" icon="tabler:dots" class="size-5 text-gray-200"
aria-hidden="true" />
@ -55,93 +55,91 @@
<template #items>
<Menu.ItemGroup>
<Menu.Item value="" v-if="isMyAccount">
<ButtonsDropdownElement @click="outputtedNote && useEvent('note:edit', outputtedNote)"
<ButtonDropdown @click="outputtedNote && useEvent('note:edit', outputtedNote)"
icon="tabler:pencil" class="w-full">
Edit
</ButtonsDropdownElement>
</ButtonDropdown>
</Menu.Item>
<Menu.Item value="">
<ButtonsDropdownElement @click="copy(JSON.stringify(props.note, null, 4))"
icon="tabler:code" class="w-full">
<ButtonDropdown @click="copy(JSON.stringify(props.note, null, 4))" icon="tabler:code"
class="w-full">
Copy API
Response
</ButtonsDropdownElement>
</ButtonDropdown>
</Menu.Item>
<Menu.Item value="">
<ButtonsDropdownElement @click="copy(url)" icon="tabler:link" class="w-full">
<ButtonDropdown @click="copy(url)" icon="tabler:link" class="w-full">
Copy Link
</ButtonsDropdownElement>
</ButtonDropdown>
</Menu.Item>
<Menu.Item value="" v-if="outputtedNote?.url && isRemote">
<ButtonsDropdownElement @click="copy(outputtedNote.url)" icon="tabler:link"
class="w-full">
<ButtonDropdown @click="copy(outputtedNote.url)" icon="tabler:link" class="w-full">
Copy Link (Origin)
</ButtonsDropdownElement>
</ButtonDropdown>
</Menu.Item>
<Menu.Item value="" v-if="outputtedNote?.url && isRemote">
<ButtonsDropdownElement @click="openBlank(outputtedNote.url)"
icon="tabler:external-link" class="w-full">
<ButtonDropdown @click="openBlank(outputtedNote.url)" icon="tabler:external-link"
class="w-full">
View on Remote
</ButtonsDropdownElement>
</ButtonDropdown>
</Menu.Item>
<Menu.Item value="" v-if="isMyAccount">
<ButtonsDropdownElement @click="remove" icon="tabler:backspace" :disabled="!identity"
<ButtonDropdown @click="remove" icon="tabler:backspace" :disabled="!identity"
class="w-full border-r-2 border-red-500">
Delete
</ButtonsDropdownElement>
</ButtonDropdown>
</Menu.Item>
</Menu.ItemGroup>
<hr class="border-white/10 rounded" v-if="identity" />
<Menu.ItemGroup v-if="identity">
<Menu.Item value="">
<ButtonsDropdownElement @click="outputtedNote && useEvent('note:reply', outputtedNote)"
<ButtonDropdown @click="outputtedNote && useEvent('note:reply', outputtedNote)"
icon="tabler:arrow-back-up" class="w-full">
Reply
</ButtonsDropdownElement>
</ButtonDropdown>
</Menu.Item>
<Menu.Item value="">
<ButtonsDropdownElement @click="likeFn" icon="tabler:heart" class="w-full"
<ButtonDropdown @click="likeFn" icon="tabler:heart" class="w-full"
v-if="!outputtedNote?.favourited">
Like
</ButtonsDropdownElement>
<ButtonsDropdownElement @click="likeFn" icon="tabler:heart-filled" class="w-full"
v-else>
</ButtonDropdown>
<ButtonDropdown @click="likeFn" icon="tabler:heart-filled" class="w-full" v-else>
Unlike
</ButtonsDropdownElement>
</ButtonDropdown>
</Menu.Item>
<Menu.Item value="">
<ButtonsDropdownElement @click="reblogFn" icon="tabler:repeat" class="w-full"
<ButtonDropdown @click="reblogFn" icon="tabler:repeat" class="w-full"
v-if="!outputtedNote?.reblogged">
Reblog
</ButtonsDropdownElement>
<ButtonsDropdownElement @click="reblogFn" icon="tabler:repeat" class="w-full" v-else>
</ButtonDropdown>
<ButtonDropdown @click="reblogFn" icon="tabler:repeat" class="w-full" v-else>
Unreblog
</ButtonsDropdownElement>
</ButtonDropdown>
</Menu.Item>
<Menu.Item value="">
<ButtonsDropdownElement @click="outputtedNote && useEvent('note:quote', outputtedNote)"
<ButtonDropdown @click="outputtedNote && useEvent('note:quote', outputtedNote)"
icon="tabler:quote" class="w-full">
Quote
</ButtonsDropdownElement>
</ButtonDropdown>
</Menu.Item>
</Menu.ItemGroup>
<hr class="border-white/10 rounded" v-if="identity" />
<Menu.ItemGroup v-if="identity">
<Menu.Item value="">
<ButtonsDropdownElement @click="outputtedNote && useEvent('note:report', outputtedNote)"
<ButtonDropdown @click="outputtedNote && useEvent('note:report', outputtedNote)"
icon="tabler:flag" class="w-full"
:disabled="!permissions.includes(RolePermission.ManageOwnReports)">
Report
</ButtonsDropdownElement>
</ButtonDropdown>
</Menu.Item>
<Menu.Item value="" v-if="permissions.includes(RolePermission.ManageAccounts)">
<ButtonsDropdownElement icon="tabler:shield-bolt" class="w-full">
<ButtonDropdown icon="tabler:shield-bolt" class="w-full">
Open Moderation Panel
</ButtonsDropdownElement>
</ButtonDropdown>
</Menu.Item>
</Menu.ItemGroup>
</template>
</DropdownsAdaptiveDropdown>
</AdaptiveDropdown>
</div>
</Skeleton>
</article>
@ -150,7 +148,13 @@
<script lang="ts" setup>
import { Menu } from "@ark-ui/vue";
import { RolePermission, type Status } from "@lysand-org/client/types";
import Avatar from "~/components/avatars/avatar.vue";
import ButtonDropdown from "~/components/buttons/button-dropdown.vue";
import AdaptiveDropdown from "~/components/dropdowns/AdaptiveDropdown.vue";
import Skeleton from "~/components/skeleton/Skeleton.vue";
import Header from "./header.vue";
import NoteContent from "./note-content.vue";
import ReplyHeader from "./reply-header.vue";
const props = withDefaults(
defineProps<{

View file

@ -3,7 +3,7 @@
<Skeleton :enabled="!account" shape="rect" class="!h-6" :min-width="40" :max-width="100" width-unit="%">
<iconify-icon icon="tabler:arrow-back-up" width="1.5rem" height="1.5rem" aria-hidden="true" />
<span class="shrink-0">Replying to</span>
<AvatarsCentered v-if="account?.avatar" :src="account?.avatar" :alt="`${account?.acct}'s avatar'`"
<Avatar v-if="account?.avatar" :src="account?.avatar" :alt="`${account?.acct}'s avatar'`"
class="size-5 shrink-0 rounded ring-1 ring-white/10" />
<strong class="line-clamp-1">{{ account?.display_name || account?.acct }}</strong>
</Skeleton>
@ -11,6 +11,9 @@
</template>
<script lang="ts" setup>
import Avatar from "~/components/avatars/avatar.vue";
import Skeleton from "~/components/skeleton/Skeleton.vue";
const props = defineProps<{
account_id: string | null;
}>();

View file

@ -4,7 +4,7 @@
<Skeleton :enabled="!notification" shape="rect" class="!h-6" :min-width="40" :max-width="100"
width-unit="%">
<iconify-icon :icon="icon" width="1.5rem" height="1.5rem" class="text-gray-200" aria-hidden="true" />
<AvatarsCentered v-if="notification?.account?.avatar" :src="notification?.account.avatar"
<Avatar v-if="notification?.account?.avatar" :src="notification?.account.avatar"
:alt="`${notification?.account.acct}'s avatar'`"
class="h-6 w-6 shrink-0 rounded ring-1 ring-white/10" />
<span class="text-gray-200 line-clamp-1"><strong v-html="display_name"></strong> {{ text
@ -12,24 +12,29 @@
</Skeleton>
</div>
<div>
<LazySocialElementsNotesNote v-if="notification?.status || !notification" :note="notification?.status"
:small="true" />
<Note v-if="notification?.status || !notification" :note="notification?.status" :small="true" />
<div v-else-if="notification.account" class="p-6 ring-1 ring-white/5 bg-dark-800">
<SocialElementsUsersSmallCard :account="notification.account" />
<SmallCard :account="notification.account" />
</div>
<div v-if="notification?.type === 'follow_request' && relationship?.requested_by"
class="w-full grid grid-cols-2 gap-4 p-2 ">
<ButtonsPrimary :loading="isWorkingOnFollowRequest" @click="acceptFollowRequest"><span>Accept</span>
</ButtonsPrimary>
<ButtonsSecondary :loading="isWorkingOnFollowRequest" @click="rejectFollowRequest"><span>Reject</span>
</ButtonsSecondary>
<ButtonPrimary :loading="isWorkingOnFollowRequest" @click="acceptFollowRequest"><span>Accept</span>
</ButtonPrimary>
<ButtonSecondary :loading="isWorkingOnFollowRequest" @click="rejectFollowRequest"><span>Reject</span>
</ButtonSecondary>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import type { Notification, Relationship } from "@lysand-org/client/types";
import type { Notification } from "@lysand-org/client/types";
import Avatar from "~/components/avatars/avatar.vue";
import ButtonPrimary from "~/components/buttons/button-primary.vue";
import ButtonSecondary from "~/components/buttons/button-secondary.vue";
import Skeleton from "~/components/skeleton/Skeleton.vue";
import Note from "../notes/note.vue";
import SmallCard from "../users/SmallCard.vue";
const props = defineProps<{
notification?: Notification;

View file

@ -1,26 +1,26 @@
<template>
<div class="w-full ring-1 ring-inset ring-white/5 pb-10 bg-dark-800">
<AvatarsCentered :src="account?.header" :alt="`${account?.acct}'s header image'`"
<Avatar :src="account?.header" :alt="`${account?.acct}'s header image'`"
class="w-full aspect-[8/3] border-b border-white/10 bg-dark-700 !rounded-none" />
<div class="flex items-start justify-between px-4 py-3">
<AvatarsCentered :src="account?.avatar" :alt="`${account?.acct}'s avatar'`"
<Avatar :src="account?.avatar" :alt="`${account?.acct}'s avatar'`"
class="h-32 w-32 -mt-[4.5rem] z-10 shrink-0 rounded ring-2 ring-dark-800" />
<ButtonsSecondary v-if="account && account?.id === identity?.account?.id">Edit Profile
</ButtonsSecondary>
<ButtonsSecondary :loading="isLoading" @click="follow()"
<ButtonSecondary v-if="account && account?.id === identity?.account?.id">Edit Profile
</ButtonSecondary>
<ButtonSecondary :loading="isLoading" @click="follow()"
v-if="account && account?.id !== identity?.account?.id && relationship && !relationship.following && !relationship.requested">
<span>Follow</span>
</ButtonsSecondary>
<ButtonsSecondary :loading="isLoading" @click="unfollow()"
</ButtonSecondary>
<ButtonSecondary :loading="isLoading" @click="unfollow()"
v-if="account && account?.id !== identity?.account?.id && relationship && relationship.following">
<span>Unfollow</span>
</ButtonsSecondary>
<ButtonsSecondary :loading="isLoading" :disabled="true"
</ButtonSecondary>
<ButtonSecondary :loading="isLoading" :disabled="true"
v-if="account && account?.id !== identity?.account?.id && relationship && !relationship.following && relationship.requested">
<span>Requested</span>
</ButtonsSecondary>
</ButtonSecondary>
</div>
<div class="mt-2 px-4">
@ -37,10 +37,10 @@
<Skeleton :enabled="skeleton" :min-width="130" :max-width="250">@{{ account?.acct }}</Skeleton>
</span>
<div class="flex flex-row flex-wrap gap-4 mt-4" v-if="isDeveloper || visibleRoles.length > 0">
<SocialElementsUsersBadge v-for="role of visibleRoles" :key="role.id" :name="role.name"
:description="role.description" :img="role.icon" />
<SocialElementsUsersBadge v-if="isDeveloper" name="Lysand Developer"
description="This user is a Lysand developer." :verified="true" />
<Badge v-for="role of visibleRoles" :key="role.id" :name="role.name" :description="role.description"
:img="role.icon" />
<Badge v-if="isDeveloper" name="Lysand Developer" description="This user is a Lysand developer."
:verified="true" />
</div>
</div>
@ -99,6 +99,10 @@
<script lang="ts" setup>
import type { Account } from "@lysand-org/client/types";
import Avatar from "~/components/avatars/avatar.vue";
import ButtonSecondary from "~/components/buttons/button-secondary.vue";
import Skeleton from "~/components/skeleton/Skeleton.vue";
import Badge from "./Badge.vue";
const props = defineProps<{
account?: Account;

View file

@ -2,7 +2,7 @@
<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">
<AvatarsCentered class="h-12 w-12 rounded ring-1 ring-white/5" :src="account?.avatar"
<Avatar class="h-12 w-12 rounded ring-1 ring-white/5" :src="account?.avatar"
:alt="`${account?.acct}'s avatar'`" />
</div>
</Skeleton>
@ -28,6 +28,8 @@
<script lang="ts" setup>
import type { Account } from "@lysand-org/client/types";
import Avatar from "~/components/avatars/avatar.vue";
import Skeleton from "~/components/skeleton/Skeleton.vue";
import { NuxtLink } from "#components";
const props = defineProps<{

View file

@ -1,8 +1,10 @@
<template>
<TimelinesTimeline :timeline="timeline" :load-next="loadNext" :load-prev="loadPrev" />
<Timeline :timeline="timeline" :load-next="loadNext" :load-prev="loadPrev" />
</template>
<script lang="ts" setup>
import Timeline from "./timeline.vue";
const props = defineProps<{
id?: string;
}>();

View file

@ -1,8 +1,10 @@
<template>
<TimelinesTimeline :timeline="timeline" :load-next="loadNext" :load-prev="loadPrev" />
<Timeline :timeline="timeline" :load-next="loadNext" :load-prev="loadPrev" />
</template>
<script lang="ts" setup>
import Timeline from "./timeline.vue";
const client = useClient();
const timelineParameters = ref({});
const { timeline, loadNext, loadPrev } = useHomeTimeline(

View file

@ -1,8 +1,10 @@
<template>
<TimelinesTimeline :timeline="timeline" :load-next="loadNext" :load-prev="loadPrev" />
<Timeline :timeline="timeline" :load-next="loadNext" :load-prev="loadPrev" />
</template>
<script lang="ts" setup>
import Timeline from "./timeline.vue";
const client = useClient();
const timelineParameters = ref({});
const { timeline, loadNext, loadPrev } = useLocalTimeline(

View file

@ -1,7 +1,7 @@
<template>
<SocialElementsNotificationsNotif v-for="notif of timeline" :key="notif.id" :notification="notif" />
<Notif v-for="notif of timeline" :key="notif.id" :notification="notif" />
<span ref="skeleton"></span>
<SocialElementsNotificationsNotif v-for="index of 5" v-if="!hasReachedEnd" :skeleton="true" />
<Notif v-for="index of 5" v-if="!hasReachedEnd" :skeleton="true" />
<div v-if="hasReachedEnd" class="text-center flex flex-row justify-center items-center py-10 text-gray-400 gap-3">
<iconify-icon name="tabler:message-off" width="1.5rem" height="1.5rem" />
@ -10,6 +10,8 @@
</template>
<script lang="ts" setup>
import Notif from "../social-elements/notifications/notif.vue";
const client = useClient();
const isLoading = ref(true);

View file

@ -1,8 +1,10 @@
<template>
<TimelinesTimeline :timeline="timeline" :load-next="loadNext" :load-prev="loadPrev" />
<Timeline :timeline="timeline" :load-next="loadNext" :load-prev="loadPrev" />
</template>
<script lang="ts" setup>
import Timeline from "./timeline.vue";
const client = useClient();
const timelineParameters = ref({});
const { timeline, loadNext, loadPrev } = usePublicTimeline(

View file

@ -1,10 +1,10 @@
<template>
<TransitionGroup leave-active-class="ease-in duration-200" leave-from-class="scale-100 opacity-100"
leave-to-class="opacity-0 scale-90">
<SocialElementsNotesNote v-for="note of timeline" :key="note.id" :note="note" />
<Note v-for="note of timeline" :key="note.id" :note="note" />
</TransitionGroup>
<span ref="skeleton"></span>
<LazySocialElementsNotesNote v-for="index of 5" v-if="!hasReachedEnd" :skeleton="true" />
<Note v-for="_ of 5" v-if="!hasReachedEnd" :skeleton="true" />
<div v-if="hasReachedEnd" class="text-center flex flex-row justify-center items-center py-10 text-gray-400 gap-3">
<iconify-icon icon="tabler:message-off" width="1.5rem" height="1.5rem" />
@ -14,6 +14,7 @@
<script lang="ts" setup>
import type { Status } from "@lysand-org/client/types";
import Note from "../social-elements/notes/note.vue";
const props = defineProps<{
timeline: Status[];