mirror of
https://github.com/versia-pub/frontend.git
synced 2026-01-26 04:16:02 +01:00
refactor: ♻️ Make auth store require less null checks
This commit is contained in:
parent
68e23a818a
commit
b23ed66401
|
|
@ -56,13 +56,13 @@ useSeoMeta({
|
||||||
titleTemplate: (titleChunk) => {
|
titleTemplate: (titleChunk) => {
|
||||||
return titleChunk ? `${titleChunk} · Versia` : "Versia";
|
return titleChunk ? `${titleChunk} · Versia` : "Versia";
|
||||||
},
|
},
|
||||||
title: computed(() => authStore.instance?.title ?? ""),
|
title: computed(() => authStore.instanceOptional?.title ?? ""),
|
||||||
ogImage: computed(() => authStore.instance?.banner?.url),
|
ogImage: computed(() => authStore.instanceOptional?.banner?.url),
|
||||||
twitterTitle: computed(() => authStore.instance?.title ?? ""),
|
twitterTitle: computed(() => authStore.instanceOptional?.title ?? ""),
|
||||||
twitterDescription: computed(() =>
|
twitterDescription: computed(() =>
|
||||||
convert(description.value?.content ?? ""),
|
convert(description.value?.content ?? ""),
|
||||||
),
|
),
|
||||||
twitterImage: computed(() => authStore.instance?.banner?.url),
|
twitterImage: computed(() => authStore.instanceOptional?.banner?.url),
|
||||||
description: computed(() => convert(description.value?.content ?? "")),
|
description: computed(() => convert(description.value?.content ?? "")),
|
||||||
ogDescription: computed(() => convert(description.value?.content ?? "")),
|
ogDescription: computed(() => convert(description.value?.content ?? "")),
|
||||||
ogSiteName: "Versia",
|
ogSiteName: "Versia",
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ const composerKey = props.relation
|
||||||
const store = useComposerStore(composerKey)();
|
const store = useComposerStore(composerKey)();
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const charactersLeft = computed(() => {
|
const charactersLeft = computed(() => {
|
||||||
const max = authStore.instance?.configuration.statuses.max_characters ?? 0;
|
const max = authStore.instance.configuration.statuses.max_characters;
|
||||||
|
|
||||||
return max - store.rawContent.length;
|
return max - store.rawContent.length;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<Command
|
<Command
|
||||||
class="rounded border shadow-md min-w-[200px] h-fit not-prose"
|
class="rounded border shadow-md min-w-50 h-fit not-prose"
|
||||||
:selected-value="emojis[selectedIndex]?.id"
|
:selected-value="emojis[selectedIndex]?.id"
|
||||||
>
|
>
|
||||||
<CommandList>
|
<CommandList>
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
class="scroll-m-10"
|
class="scroll-m-10"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="h-[1lh] align-middle inline hover:scale-110 transition-transform duration-75 ease-in-out"
|
class="h-lh align-middle inline hover:scale-110 transition-transform duration-75 ease-in-out"
|
||||||
:src="emoji.url"
|
:src="emoji.url"
|
||||||
:title="emoji.shortcode"
|
:title="emoji.shortcode"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -126,11 +126,9 @@ export const emojiSuggestion = {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const emojis = authStore.emojis;
|
|
||||||
|
|
||||||
return go(
|
return go(
|
||||||
query,
|
query,
|
||||||
emojis
|
authStore.emojis
|
||||||
.filter((emoji) => emoji.shortcode.includes(query))
|
.filter((emoji) => emoji.shortcode.includes(query))
|
||||||
.map((emoji) => ({
|
.map((emoji) => ({
|
||||||
key: emoji.shortcode,
|
key: emoji.shortcode,
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ const isValid = ref(false);
|
||||||
:open="isOpen"
|
:open="isOpen"
|
||||||
@update:open="isOpen = false"
|
@update:open="isOpen = false"
|
||||||
>
|
>
|
||||||
<AlertDialogContent class="sm:max-w-[425px] flex flex-col">
|
<AlertDialogContent class="sm:max-w-106.25 flex flex-col">
|
||||||
<AlertDialogHeader>
|
<AlertDialogHeader>
|
||||||
<AlertDialogTitle>{{ modalOptions.title }}</AlertDialogTitle>
|
<AlertDialogTitle>{{ modalOptions.title }}</AlertDialogTitle>
|
||||||
<AlertDialogDescription v-if="modalOptions.message">
|
<AlertDialogDescription v-if="modalOptions.message">
|
||||||
|
|
|
||||||
|
|
@ -44,14 +44,13 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { CustomEmoji, Status } from "@versia/client/schemas";
|
import type { CustomEmoji, Status } from "@versia/client/schemas";
|
||||||
import { Ellipsis, Heart, Quote, Repeat, Reply, Smile } from "lucide-vue-next";
|
import { Heart, Quote, Repeat, Reply, Smile } from "lucide-vue-next";
|
||||||
import { toast } from "vue-sonner";
|
import { toast } from "vue-sonner";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
import * as m from "~~/paraglide/messages.js";
|
import * as m from "~~/paraglide/messages.js";
|
||||||
import { getLocale } from "~~/paraglide/runtime";
|
import { getLocale } from "~~/paraglide/runtime";
|
||||||
import { confirmModalService } from "../modals/composable";
|
import { confirmModalService } from "../modals/composable";
|
||||||
import ActionButton from "./action-button.vue";
|
import ActionButton from "./action-button.vue";
|
||||||
import Menu from "./menu.vue";
|
|
||||||
import type { UnicodeEmoji } from "./reactions/picker/emoji";
|
import type { UnicodeEmoji } from "./reactions/picker/emoji";
|
||||||
import Picker from "./reactions/picker/index.vue";
|
import Picker from "./reactions/picker/index.vue";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -136,8 +136,8 @@ const _delete = async () => {
|
||||||
{{ m.tense_quick_cod_favor() }}
|
{{ m.tense_quick_cod_favor() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator v-if="authStore.isSignedIn && !authorIsMe" />
|
<DropdownMenuSeparator v-if="authorIsMe" />
|
||||||
<DropdownMenuGroup v-if="authStore.isSignedIn && !authorIsMe">
|
<DropdownMenuGroup v-if="authorIsMe">
|
||||||
<DropdownMenuItem as="button" :disabled="true">
|
<DropdownMenuItem as="button" :disabled="true">
|
||||||
<Flag />
|
<Flag />
|
||||||
{{ m.great_few_jaguar_rise() }}
|
{{ m.great_few_jaguar_rise() }}
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,9 @@ const copy = (data: string) => {
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
const data: [string, string | VNode][] = [
|
const data: [string, string | VNode][] = [
|
||||||
["User ID", authStore.account?.id ?? ""],
|
["User ID", authStore.account.id],
|
||||||
["Instance domain", authStore.instance?.domain ?? ""],
|
["Instance domain", authStore.instance.domain],
|
||||||
["Instance version", authStore.instance?.versia_version ?? ""],
|
["Instance version", authStore.instance.versia_version ?? ""],
|
||||||
["Client ID", authStore.application?.client_id ?? ""],
|
["Client ID", authStore.application?.client_id ?? ""],
|
||||||
[
|
[
|
||||||
"Client secret",
|
"Client secret",
|
||||||
|
|
@ -55,7 +55,7 @@ const data: [string, string | VNode][] = [
|
||||||
class="font-sans"
|
class="font-sans"
|
||||||
size="sm"
|
size="sm"
|
||||||
// @ts-expect-error missing onClick types
|
// @ts-expect-error missing onClick types
|
||||||
onClick={() => copy(authStore.token?.access_token ?? "")}
|
onClick={() => copy(authStore.token.access_token)}
|
||||||
>
|
>
|
||||||
Click to copy
|
Click to copy
|
||||||
</Button>,
|
</Button>,
|
||||||
|
|
|
||||||
|
|
@ -76,12 +76,12 @@ useListen("preferences:open", () => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dialog v-model:open="open" v-if="authStore.isSignedIn">
|
<Dialog v-model:open="open">
|
||||||
<DialogContent
|
<DialogContent
|
||||||
class="md:max-w-5xl w-full h-full p-0 md:max-h-[70dvh] overflow-hidden"
|
class="md:max-w-5xl w-full h-full p-0 md:max-h-[70dvh] overflow-hidden"
|
||||||
>
|
>
|
||||||
<Tabs
|
<Tabs
|
||||||
class="md:grid-cols-[auto_minmax(0,1fr)] !grid gap-2 *:p-4 overflow-hidden *:overflow-y-auto *:h-full"
|
class="md:grid-cols-[auto_minmax(0,1fr)] grid! gap-2 *:p-4 overflow-hidden *:overflow-y-auto *:h-full"
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
:default-value="pages[0]"
|
:default-value="pages[0]"
|
||||||
>
|
>
|
||||||
|
|
@ -92,8 +92,8 @@ useListen("preferences:open", () => {
|
||||||
class="grid gap-3 items-center grid-cols-[auto_minmax(0,1fr)]"
|
class="grid gap-3 items-center grid-cols-[auto_minmax(0,1fr)]"
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
:name="authStore.account!.display_name || authStore.account!.username"
|
:name="authStore.account.display_name || authStore.account.username"
|
||||||
:src="authStore.account!.avatar"
|
:src="authStore.account.avatar"
|
||||||
/>
|
/>
|
||||||
<DialogTitle>Preferences</DialogTitle>
|
<DialogTitle>Preferences</DialogTitle>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -101,7 +101,7 @@ useListen("preferences:open", () => {
|
||||||
Make changes to your preferences here.
|
Make changes to your preferences here.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
<TabsList
|
<TabsList
|
||||||
class="md:grid md:grid-cols-1 w-full h-fit *:justify-start !justify-start"
|
class="md:grid md:grid-cols-1 w-full h-fit *:justify-start justify-start!"
|
||||||
>
|
>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
v-for="page in pages"
|
v-for="page in pages"
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,6 @@ const canEdit =
|
||||||
authStore.permissions.includes(RolePermission.ManageEmojis);
|
authStore.permissions.includes(RolePermission.ManageEmojis);
|
||||||
|
|
||||||
const deleteAll = async () => {
|
const deleteAll = async () => {
|
||||||
if (!authStore.isSignedIn) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { confirmed } = await confirmModalService.confirm({
|
const { confirmed } = await confirmModalService.confirm({
|
||||||
title: m.tense_quick_cod_favor(),
|
title: m.tense_quick_cod_favor(),
|
||||||
message: m.next_hour_jurgen_sprout({
|
message: m.next_hour_jurgen_sprout({
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,6 @@ const { emoji } = defineProps<{
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
const editName = async () => {
|
const editName = async () => {
|
||||||
if (!authStore.isSignedIn) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await confirmModalService.confirm({
|
const result = await confirmModalService.confirm({
|
||||||
title: m.slimy_awful_florian_sail(),
|
title: m.slimy_awful_florian_sail(),
|
||||||
defaultValue: emoji.shortcode,
|
defaultValue: emoji.shortcode,
|
||||||
|
|
@ -83,10 +79,6 @@ const editName = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const _delete = async () => {
|
const _delete = async () => {
|
||||||
if (!authStore.isSignedIn) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { confirmed } = await confirmModalService.confirm({
|
const { confirmed } = await confirmModalService.confirm({
|
||||||
title: m.tense_quick_cod_favor(),
|
title: m.tense_quick_cod_favor(),
|
||||||
message: m.honest_factual_carp_aspire(),
|
message: m.honest_factual_carp_aspire(),
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ const columns: ColumnDef<z.infer<typeof CustomEmoji>>[] = [
|
||||||
src={row.getValue("url")}
|
src={row.getValue("url")}
|
||||||
alt={`:${row.getValue("shortcode")}:`}
|
alt={`:${row.getValue("shortcode")}:`}
|
||||||
title={row.getValue("shortcode")}
|
title={row.getValue("shortcode")}
|
||||||
class="h-[1lh] align-middle inline not-prose hover:scale-110 transition-transform duration-75 ease-in-out"
|
class="h-lh align-middle inline not-prose hover:scale-110 transition-transform duration-75 ease-in-out"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
@ -108,7 +108,7 @@ const columns: ColumnDef<z.infer<typeof CustomEmoji>>[] = [
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
class="!p-0 !h-auto"
|
class="p-0! h-auto!"
|
||||||
// @ts-expect-error types don't include onClick
|
// @ts-expect-error types don't include onClick
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
column.toggleSorting(column.getIsSorted() === "asc")
|
column.toggleSorting(column.getIsSorted() === "asc")
|
||||||
|
|
@ -135,7 +135,7 @@ const columns: ColumnDef<z.infer<typeof CustomEmoji>>[] = [
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
class="!p-0 !h-auto"
|
class="p-0! h-auto!"
|
||||||
// @ts-expect-error types don't include onClick
|
// @ts-expect-error types don't include onClick
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
column.toggleSorting(column.getIsSorted() === "asc")
|
column.toggleSorting(column.getIsSorted() === "asc")
|
||||||
|
|
@ -164,7 +164,7 @@ const columns: ColumnDef<z.infer<typeof CustomEmoji>>[] = [
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
class="!p-0 !h-auto"
|
class="p-0! h-auto!"
|
||||||
// @ts-expect-error types don't include onClick
|
// @ts-expect-error types don't include onClick
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const filter = column.getFilterValue();
|
const filter = column.getFilterValue();
|
||||||
|
|
|
||||||
|
|
@ -203,25 +203,21 @@ const formSchema = toTypedSchema(
|
||||||
.refine(
|
.refine(
|
||||||
(v) =>
|
(v) =>
|
||||||
v.size <=
|
v.size <=
|
||||||
(authStore.instance?.configuration.emojis
|
authStore.instance.configuration.emojis.emoji_size_limit,
|
||||||
.emoji_size_limit ?? Number.POSITIVE_INFINITY),
|
|
||||||
m.orange_weird_parakeet_hug({
|
m.orange_weird_parakeet_hug({
|
||||||
count:
|
count: authStore.instance.configuration.emojis
|
||||||
authStore.instance?.configuration.emojis
|
.emoji_size_limit,
|
||||||
.emoji_size_limit ?? Number.POSITIVE_INFINITY,
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
shortcode: z
|
shortcode: z
|
||||||
.string()
|
.string()
|
||||||
.min(1)
|
.min(1)
|
||||||
.max(
|
.max(
|
||||||
authStore.instance?.configuration.emojis
|
authStore.instance.configuration.emojis
|
||||||
.max_shortcode_characters ?? Number.POSITIVE_INFINITY,
|
.max_shortcode_characters,
|
||||||
m.solid_inclusive_owl_hug({
|
m.solid_inclusive_owl_hug({
|
||||||
count:
|
count: authStore.instance.configuration.emojis
|
||||||
authStore.instance?.configuration.emojis
|
.max_shortcode_characters,
|
||||||
.max_shortcode_characters ??
|
|
||||||
Number.POSITIVE_INFINITY,
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.regex(emojiValidator),
|
.regex(emojiValidator),
|
||||||
|
|
@ -238,13 +234,11 @@ const formSchema = toTypedSchema(
|
||||||
alt: z
|
alt: z
|
||||||
.string()
|
.string()
|
||||||
.max(
|
.max(
|
||||||
authStore.instance?.configuration.emojis
|
authStore.instance.configuration.emojis
|
||||||
.max_description_characters ?? Number.POSITIVE_INFINITY,
|
.max_description_characters,
|
||||||
m.key_ago_hound_emerge({
|
m.key_ago_hound_emerge({
|
||||||
count:
|
count: authStore.instance.configuration.emojis
|
||||||
authStore.instance?.configuration.emojis
|
.max_description_characters,
|
||||||
.max_description_characters ??
|
|
||||||
Number.POSITIVE_INFINITY,
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
|
|
@ -255,10 +249,6 @@ const { isSubmitting, handleSubmit, values, setFieldValue } = useForm({
|
||||||
});
|
});
|
||||||
|
|
||||||
const submit = handleSubmit(async (values) => {
|
const submit = handleSubmit(async (values) => {
|
||||||
if (!authStore.isSignedIn) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = toast.loading(m.factual_gray_mouse_believe());
|
const id = toast.loading(m.factual_gray_mouse_believe());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
<FormField v-slot="{ setValue }" name="avatar">
|
<FormField v-slot="{ setValue }" name="avatar">
|
||||||
<TextInput :title="m.safe_icy_bulldog_quell()">
|
<TextInput :title="m.safe_icy_bulldog_quell()">
|
||||||
<ImageUploader
|
<ImageUploader
|
||||||
v-model:image="authStore.account!.avatar"
|
v-model:image="authStore.account.avatar"
|
||||||
@submit-file="(file) => setValue(file)"
|
@submit-file="(file) => setValue(file)"
|
||||||
@submit-url="(url) => setValue(url)"
|
@submit-url="(url) => setValue(url)"
|
||||||
/>
|
/>
|
||||||
|
|
@ -143,10 +143,6 @@ const dirty = computed(() => form.meta.value.dirty);
|
||||||
const submitting = ref(false);
|
const submitting = ref(false);
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
if (!(authStore.instance && authStore.account)) {
|
|
||||||
throw new Error("Not signed in.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const schema = formSchema(authStore.instance);
|
const schema = formSchema(authStore.instance);
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
|
|
@ -167,7 +163,7 @@ const form = useForm({
|
||||||
});
|
});
|
||||||
|
|
||||||
const save = form.handleSubmit(async (values) => {
|
const save = form.handleSubmit(async (values) => {
|
||||||
if (submitting.value || !authStore.account) {
|
if (submitting.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,7 +194,7 @@ const save = form.handleSubmit(async (values) => {
|
||||||
: values.discoverable,
|
: values.discoverable,
|
||||||
// Can't compare two arrays directly in JS, so we need to check if all fields are the same
|
// Can't compare two arrays directly in JS, so we need to check if all fields are the same
|
||||||
fields_attributes: values.fields.every((field) =>
|
fields_attributes: values.fields.every((field) =>
|
||||||
authStore.account?.source?.fields?.some(
|
authStore.account.source?.fields?.some(
|
||||||
(f) => f.name === field.name && f.value === field.value,
|
(f) => f.name === field.name && f.value === field.value,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@
|
||||||
{{ m.active_trite_lark_inspire() }}
|
{{ m.active_trite_lark_inspire() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator v-if="authStore.isSignedIn && !isMe" />
|
<DropdownMenuSeparator v-if="!isMe" />
|
||||||
<DropdownMenuGroup v-if="authStore.isSignedIn && !isMe">
|
<DropdownMenuGroup v-if="!isMe">
|
||||||
<DropdownMenuItem as="button" @click="muteUser(account.id)">
|
<DropdownMenuItem as="button" @click="muteUser(account.id)">
|
||||||
<VolumeX />
|
<VolumeX />
|
||||||
{{ m.spare_wild_mole_intend() }}
|
{{ m.spare_wild_mole_intend() }}
|
||||||
|
|
@ -63,8 +63,8 @@
|
||||||
{{ m.slow_chunky_chipmunk_hush() }}
|
{{ m.slow_chunky_chipmunk_hush() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator v-if="authStore.isSignedIn && !isMe" />
|
<DropdownMenuSeparator v-if="!isMe" />
|
||||||
<DropdownMenuGroup v-if="authStore.isSignedIn && !isMe">
|
<DropdownMenuGroup v-if="!isMe">
|
||||||
<DropdownMenuItem as="button" :disabled="true">
|
<DropdownMenuItem as="button" :disabled="true">
|
||||||
<Flag />
|
<Flag />
|
||||||
{{ m.great_few_jaguar_rise() }}
|
{{ m.great_few_jaguar_rise() }}
|
||||||
|
|
@ -104,7 +104,7 @@ const { account } = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const isMe = authStore.account?.id === account.id;
|
const isMe = authStore.accountOptional?.id === account.id;
|
||||||
|
|
||||||
const { copy } = useClipboard();
|
const { copy } = useClipboard();
|
||||||
const copyText = (text: string) => {
|
const copyText = (text: string) => {
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ const roles = account.roles.filter((r) => r.visible);
|
||||||
// Get user handle in username@instance format
|
// Get user handle in username@instance format
|
||||||
const handle = account.acct.includes("@")
|
const handle = account.acct.includes("@")
|
||||||
? account.acct
|
? account.acct
|
||||||
: `${account.acct}@${authStore.instance?.domain ?? window.location.host}`;
|
: `${account.acct}@${authStore.instance.domain}`;
|
||||||
const isDeveloper = config.DEVELOPER_HANDLES.includes(handle);
|
const isDeveloper = config.DEVELOPER_HANDLES.includes(handle);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
:disabled="isLoading || relationship?.requested"
|
:disabled="isLoading || relationship?.requested"
|
||||||
v-if="!isMe && authStore.isSignedIn"
|
v-if="!isMe"
|
||||||
@click="relationship?.following ? unfollow() : follow()"
|
@click="relationship?.following ? unfollow() : follow()"
|
||||||
>
|
>
|
||||||
<Loader v-if="isLoading" class="animate-spin" />
|
<Loader v-if="isLoading" class="animate-spin" />
|
||||||
|
|
@ -31,7 +31,7 @@ const { account } = defineProps<{
|
||||||
|
|
||||||
const { relationship, isLoading } = useRelationship(account.id);
|
const { relationship, isLoading } = useRelationship(account.id);
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const isMe = authStore.account?.id === account.id;
|
const isMe = authStore.accountOptional?.id === account.id;
|
||||||
|
|
||||||
const follow = async () => {
|
const follow = async () => {
|
||||||
if (preferences.confirm_actions.value.includes("follow")) {
|
if (preferences.confirm_actions.value.includes("follow")) {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
data-switch
|
data-switch
|
||||||
v-if="authStore.identity?.id !== identity.id"
|
v-if="authStore.identityOptional?.id !== identity.id"
|
||||||
@click="authStore.setActiveIdentity(identity.id)"
|
@click="authStore.setActiveIdentity(identity.id)"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,7 @@ const authStore = useAuthStore();
|
||||||
<SidebarMenu class="gap-3">
|
<SidebarMenu class="gap-3">
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<AccountManager>
|
<AccountManager>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton v-if="authStore.isSignedIn" size="lg">
|
||||||
v-if="authStore.account && authStore.instance"
|
|
||||||
size="lg"
|
|
||||||
>
|
|
||||||
<TinyCard
|
<TinyCard
|
||||||
:account="authStore.account"
|
:account="authStore.account"
|
||||||
:domain="authStore.instance.domain"
|
:domain="authStore.instance.domain"
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,10 @@ const authStore = useAuthStore();
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<SidebarHeader>
|
<SidebarHeader>
|
||||||
<SidebarMenu>
|
<SidebarMenu v-if="authStore.isSignedIn">
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<NuxtLink href="/">
|
<NuxtLink href="/">
|
||||||
<InstanceSmallCard
|
<InstanceSmallCard :instance="authStore.instance" />
|
||||||
v-if="authStore.instance"
|
|
||||||
:instance="authStore.instance"
|
|
||||||
/>
|
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||||
data-slot="dropdown-menu-checkbox-item"
|
data-slot="dropdown-menu-checkbox-item"
|
||||||
v-bind="forwarded"
|
v-bind="forwarded"
|
||||||
:class=" cn(
|
:class=" cn(
|
||||||
`focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,
|
`focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,
|
||||||
props.class,
|
props.class,
|
||||||
)"
|
)"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
data-slot="dropdown-menu-content"
|
data-slot="dropdown-menu-content"
|
||||||
v-bind="forwarded"
|
v-bind="forwarded"
|
||||||
:class="cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--reka-dropdown-menu-content-available-height) min-w-[8rem] origin-(--reka-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md', props.class)"
|
:class="cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--reka-dropdown-menu-content-available-height) min-w-32 origin-(--reka-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md', props.class)"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
||||||
data-slot="dropdown-menu-sub-trigger"
|
data-slot="dropdown-menu-sub-trigger"
|
||||||
v-bind="forwardedProps"
|
v-bind="forwardedProps"
|
||||||
:class="cn(
|
:class="cn(
|
||||||
'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8',
|
'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-inset:pl-8',
|
||||||
props.class,
|
props.class,
|
||||||
)"
|
)"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ const props = defineProps<{
|
||||||
data-slot="table-cell"
|
data-slot="table-cell"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
'p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-0.5',
|
||||||
props.class,
|
props.class,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ const props = defineProps<{
|
||||||
<template>
|
<template>
|
||||||
<th
|
<th
|
||||||
data-slot="table-head"
|
data-slot="table-head"
|
||||||
:class="cn('text-muted-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', props.class)"
|
:class="cn('text-muted-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-0.5', props.class)"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</th>
|
</th>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import * as m from "~~/paraglide/messages.js";
|
||||||
|
|
||||||
export const useCacheRefresh = () => {
|
export const useCacheRefresh = () => {
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const { identity } = storeToRefs(authStore);
|
const { identityOptional } = storeToRefs(authStore);
|
||||||
|
|
||||||
authStore.client.getInstance().then((res) => {
|
authStore.client.getInstance().then((res) => {
|
||||||
authStore.updateActiveIdentity({
|
authStore.updateActiveIdentity({
|
||||||
|
|
@ -13,7 +13,7 @@ export const useCacheRefresh = () => {
|
||||||
|
|
||||||
// Refresh custom emojis and instance data and me on every reload
|
// Refresh custom emojis and instance data and me on every reload
|
||||||
watch(
|
watch(
|
||||||
identity,
|
identityOptional,
|
||||||
async (oldIdentity, newIdentity) => {
|
async (oldIdentity, newIdentity) => {
|
||||||
if (newIdentity && newIdentity.id !== oldIdentity?.id) {
|
if (newIdentity && newIdentity.id !== oldIdentity?.id) {
|
||||||
console.info("Refreshing emoji, instance and account cache");
|
console.info("Refreshing emoji, instance and account cache");
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type { Client, Output } from "@versia/client";
|
import type { Output } from "@versia/client";
|
||||||
import type { Notification, Status } from "@versia/client/schemas";
|
import type { Notification, Status } from "@versia/client/schemas";
|
||||||
import { useIntervalFn } from "@vueuse/core";
|
import { useIntervalFn } from "@vueuse/core";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
|
|
@ -17,7 +17,7 @@ export function useTimeline<
|
||||||
const hasReachedEnd = ref(false);
|
const hasReachedEnd = ref(false);
|
||||||
const error = ref<Error | null>(null);
|
const error = ref<Error | null>(null);
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const { identity } = storeToRefs(authStore);
|
const { identityOptional } = storeToRefs(authStore);
|
||||||
|
|
||||||
const nextMaxId = ref<string | undefined>(undefined);
|
const nextMaxId = ref<string | undefined>(undefined);
|
||||||
const prevMinId = ref<string | undefined>(undefined);
|
const prevMinId = ref<string | undefined>(undefined);
|
||||||
|
|
@ -101,7 +101,7 @@ export function useTimeline<
|
||||||
pause();
|
pause();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(identity, (newIdentity, oldIdentity) => {
|
watch(identityOptional, (newIdentity, oldIdentity) => {
|
||||||
if (newIdentity?.id !== oldIdentity?.id) {
|
if (newIdentity?.id !== oldIdentity?.id) {
|
||||||
// Reload timeline when identity changes
|
// Reload timeline when identity changes
|
||||||
items.value = [];
|
items.value = [];
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ const route = useRoute();
|
||||||
watch(
|
watch(
|
||||||
() => route.path,
|
() => route.path,
|
||||||
async () => {
|
async () => {
|
||||||
console.log(route.meta.requiresAuth && !authStore.isSignedIn);
|
|
||||||
if (route.meta.requiresAuth && !authStore.isSignedIn) {
|
if (route.meta.requiresAuth && !authStore.isSignedIn) {
|
||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,34 +38,55 @@ export const useAuthStore = defineStore("auth", {
|
||||||
applications: {},
|
applications: {},
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
identity(state): Identity | null {
|
identity(state): Identity {
|
||||||
return state.activeIdentityId
|
if (state.activeIdentityId === null) {
|
||||||
? state.identities.find(
|
throw new Error("This functionality requires authentication.");
|
||||||
(id) => id.id === state.activeIdentityId,
|
}
|
||||||
) || null
|
|
||||||
: null;
|
const identity = state.identities.find(
|
||||||
|
(id) => id.id === state.activeIdentityId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!identity) {
|
||||||
|
throw new Error("This functionality requires authentication.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return identity;
|
||||||
},
|
},
|
||||||
emojis(): z.infer<typeof CustomEmoji>[] {
|
identityOptional(state): Identity | null {
|
||||||
return this.identity?.emojis || [];
|
if (state.activeIdentityId === null) {
|
||||||
},
|
|
||||||
instance(): z.infer<typeof Instance> | null {
|
|
||||||
return this.identity?.instance || null;
|
|
||||||
},
|
|
||||||
account(): z.infer<typeof Account> | null {
|
|
||||||
return this.identity?.account || null;
|
|
||||||
},
|
|
||||||
application(): z.infer<typeof CredentialApplication> | null {
|
|
||||||
if (!this.identity) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
state.identities.find(
|
||||||
|
(id) => id.id === state.activeIdentityId,
|
||||||
|
) || null
|
||||||
|
);
|
||||||
|
},
|
||||||
|
emojis(): z.infer<typeof CustomEmoji>[] {
|
||||||
|
return this.identity.emojis;
|
||||||
|
},
|
||||||
|
instance(): z.infer<typeof Instance> {
|
||||||
|
return this.identity.instance;
|
||||||
|
},
|
||||||
|
instanceOptional(): z.infer<typeof Instance> | null {
|
||||||
|
return this.identityOptional?.instance ?? null;
|
||||||
|
},
|
||||||
|
account(): z.infer<typeof Account> {
|
||||||
|
return this.identity.account;
|
||||||
|
},
|
||||||
|
accountOptional(): z.infer<typeof Account> | null {
|
||||||
|
return this.identityOptional?.account ?? null;
|
||||||
|
},
|
||||||
|
application(): z.infer<typeof CredentialApplication> | null {
|
||||||
return this.applications[this.identity.instance.domain] || null;
|
return this.applications[this.identity.instance.domain] || null;
|
||||||
},
|
},
|
||||||
token(): z.infer<typeof Token> | null {
|
token(): z.infer<typeof Token> {
|
||||||
return this.identity?.token || null;
|
return this.identity.token;
|
||||||
},
|
},
|
||||||
permissions(): RolePermission[] {
|
permissions(): RolePermission[] {
|
||||||
const roles = this.account?.roles ?? [];
|
const roles = this.account.roles;
|
||||||
|
|
||||||
return roles
|
return roles
|
||||||
.flatMap((r) => r.permissions)
|
.flatMap((r) => r.permissions)
|
||||||
|
|
@ -76,11 +97,11 @@ export const useAuthStore = defineStore("auth", {
|
||||||
},
|
},
|
||||||
client(): Client {
|
client(): Client {
|
||||||
const apiHost = window.location.origin;
|
const apiHost = window.location.origin;
|
||||||
const domain = this.identity?.instance.domain;
|
const domain = this.identityOptional?.instance.domain;
|
||||||
|
|
||||||
return new Client(
|
return new Client(
|
||||||
domain ? new URL(`https://${domain}`) : new URL(apiHost),
|
domain ? new URL(`https://${domain}`) : new URL(apiHost),
|
||||||
this.identity?.token.access_token ?? undefined,
|
this.identityOptional?.token.access_token ?? undefined,
|
||||||
{
|
{
|
||||||
globalCatch: (error) => {
|
globalCatch: (error) => {
|
||||||
toast.error(
|
toast.error(
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export const calculateMentionsFromReply = (
|
||||||
// Deduplicate mentions
|
// Deduplicate mentions
|
||||||
.filter((men, i, a) => a.indexOf(men) === i)
|
.filter((men, i, a) => a.indexOf(men) === i)
|
||||||
// Remove self
|
// Remove self
|
||||||
.filter((men) => men.id !== authStore.identity?.account.id);
|
.filter((men) => men.id !== authStore.identity.account.id);
|
||||||
|
|
||||||
if (peopleToMention.length === 0) {
|
if (peopleToMention.length === 0) {
|
||||||
return "";
|
return "";
|
||||||
|
|
@ -72,8 +72,8 @@ export const useComposerStore = (key: ComposerStateKey) =>
|
||||||
isOverCharacterLimit(): boolean {
|
isOverCharacterLimit(): boolean {
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const characterLimit =
|
const characterLimit =
|
||||||
authStore.identity?.instance.configuration.statuses
|
authStore.identity.instance.configuration.statuses
|
||||||
.max_characters ?? 0;
|
.max_characters;
|
||||||
|
|
||||||
return this.characterCount > characterLimit;
|
return this.characterCount > characterLimit;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ export const wrapUrl = (path: string) => {
|
||||||
|
|
||||||
return new URL(
|
return new URL(
|
||||||
path,
|
path,
|
||||||
authStore.instance
|
authStore.instanceOptional
|
||||||
? `https://${authStore.instance.domain}`
|
? `https://${authStore.instance.domain}`
|
||||||
: window.location.origin,
|
: window.location.origin,
|
||||||
).toString();
|
).toString();
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"noUselessElse": "error",
|
"noUselessElse": "error",
|
||||||
"noCommonJs": "error"
|
"noCommonJs": "error"
|
||||||
},
|
},
|
||||||
"nursery": {}
|
"nursery": {
|
||||||
|
"noUnnecessaryConditions": "error"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"formatter": {
|
"formatter": {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue