diff --git a/app.vue b/app.vue
index 44f11df..fcdd354 100644
--- a/app.vue
+++ b/app.vue
@@ -1,6 +1,5 @@
-
@@ -13,15 +12,12 @@
import "~/styles/theme.css";
import { convert } from "html-to-text";
import "iconify-icon";
-import Loading from "./components/loading.vue";
import NotificationsRenderer from "./components/notifications/notifications-renderer.vue";
// Use SSR-safe IDs for Headless UI
provideHeadlessUseId(() => useId());
const code = useRequestURL().searchParams.get("code");
const appData = useAppData();
-const identity = useCurrentIdentity();
-const client = useClient();
const instance = useInstance();
const description = useExtendedDescription(client);
diff --git a/components/composer/composer.vue b/components/composer/composer.vue
index 5596d71..378d9fb 100644
--- a/components/composer/composer.vue
+++ b/components/composer/composer.vue
@@ -60,7 +60,6 @@ const { Control_Enter, Command_Enter, Control_Alt } = useMagicKeys();
const content = ref("");
const respondingTo = ref(null);
const respondingType = ref<"reply" | "quote" | "edit" | null>(null);
-const identity = useCurrentIdentity();
const cw = ref(false);
const cwContent = ref("");
const markdown = ref(true);
@@ -178,7 +177,6 @@ const canSubmit = computed(
(content.value?.trim().length > 0 || files.value.length > 0) &&
content.value?.trim().length <= characterLimit.value,
);
-const client = useClient();
const send = async () => {
loading.value = true;
diff --git a/components/composer/emoji-suggestbox.vue b/components/composer/emoji-suggestbox.vue
index 6ef9f3a..4d0cc5c 100644
--- a/components/composer/emoji-suggestbox.vue
+++ b/components/composer/emoji-suggestbox.vue
@@ -19,7 +19,6 @@ defineProps<{
textarea: HTMLTextAreaElement | undefined;
}>();
-const identity = useCurrentIdentity();
const emojis = computed(
() =>
identity.value?.emojis.map((emoji) => ({
diff --git a/components/composer/file-uploader.vue b/components/composer/file-uploader.vue
index 8bea360..1f427b9 100644
--- a/components/composer/file-uploader.vue
+++ b/components/composer/file-uploader.vue
@@ -74,7 +74,6 @@ const files = defineModel<
required: true,
});
-const client = useClient();
const fileInput = ref(null);
const openFilePicker = () => {
diff --git a/components/composer/mention-suggestbox.vue b/components/composer/mention-suggestbox.vue
index 8dc254f..35ce2da 100644
--- a/components/composer/mention-suggestbox.vue
+++ b/components/composer/mention-suggestbox.vue
@@ -18,7 +18,6 @@ const props = defineProps<{
textarea: HTMLTextAreaElement | undefined;
}>();
-const client = useClient();
const mentions = ref<{ key: string; value: Account }[]>([]);
watch(
diff --git a/components/composer/modal.client.vue b/components/composer/modal.client.vue
index 7c3de21..e0c528b 100644
--- a/components/composer/modal.client.vue
+++ b/components/composer/modal.client.vue
@@ -34,7 +34,6 @@ import { Dialog } from "@ark-ui/vue";
import Composer from "./composer.vue";
const open = ref(false);
-const identity = useCurrentIdentity();
useListen("note:reply", async (note) => {
open.value = true;
await nextTick();
diff --git a/components/headers/greeting.vue b/components/headers/greeting.vue
index 2c54faa..f8c159a 100644
--- a/components/headers/greeting.vue
+++ b/components/headers/greeting.vue
@@ -18,8 +18,6 @@
\ No newline at end of file
diff --git a/components/social-elements/notes/mention.vue b/components/social-elements/notes/mention.vue
index f141872..7c07717 100644
--- a/components/social-elements/notes/mention.vue
+++ b/components/social-elements/notes/mention.vue
@@ -1,9 +1,9 @@
+ class="shrink break-all rounded bg-dark-200 ring-1 ring-white/5 ring-inset text-primary-200 px-2 py-1 not-prose font-semibold cursor-pointer [&:not(:last-child)]:mr-1 duration-200 hover:bg-primary-600/30">
- {{ account.display_name }}
+ {{ account.display_name || account.acct }}
diff --git a/components/social-elements/notes/note.vue b/components/social-elements/notes/note.vue
index e34e496..d156bee 100644
--- a/components/social-elements/notes/note.vue
+++ b/components/social-elements/notes/note.vue
@@ -176,9 +176,6 @@ useListen("composer:send-edit", (note) => {
}
});
-const client = useClient();
-const identity = useCurrentIdentity();
-const settings = useSettings();
const {
loaded,
note: outputtedNote,
diff --git a/components/social-elements/notes/reply-header.vue b/components/social-elements/notes/reply-header.vue
index c721d5d..8a3b0ce 100644
--- a/components/social-elements/notes/reply-header.vue
+++ b/components/social-elements/notes/reply-header.vue
@@ -18,6 +18,5 @@ const props = defineProps<{
account_id: string | null;
}>();
-const client = useClient();
const account = useAccount(client, props.account_id);
\ No newline at end of file
diff --git a/components/social-elements/notifications/notif.vue b/components/social-elements/notifications/notif.vue
index 6ab2c1a..f28f282 100644
--- a/components/social-elements/notifications/notif.vue
+++ b/components/social-elements/notifications/notif.vue
@@ -39,7 +39,6 @@ const props = defineProps<{
element?: Notification;
}>();
-const client = useClient();
const isWorkingOnFollowRequest = ref(false);
const { relationship } = useRelationship(
client,
@@ -70,7 +69,6 @@ const rejectFollowRequest = async () => {
isWorkingOnFollowRequest.value = false;
};
-const settings = useSettings();
const { display_name } = useParsedAccount(props.element?.account, settings);
const text = computed(() => {
diff --git a/components/social-elements/users/Account.vue b/components/social-elements/users/Account.vue
index 5fb7859..6acd2d4 100644
--- a/components/social-elements/users/Account.vue
+++ b/components/social-elements/users/Account.vue
@@ -109,8 +109,6 @@ const props = defineProps<{
}>();
const skeleton = computed(() => !props.account);
-const identity = useCurrentIdentity();
-const client = useClient();
const config = useConfig();
const accountId = computed(() => props.account?.id ?? null);
const { relationship, isLoading } = useRelationship(client, accountId);
@@ -155,7 +153,6 @@ const visibleRoles = computed(
() => props.account?.roles.filter((r) => r.visible) ?? [],
);
-const settings = useSettings();
const { display_name, fields, note } = useParsedAccount(
computed(() => props.account),
settings,
diff --git a/components/timelines/account.vue b/components/timelines/account.vue
index f59a45b..9885f00 100644
--- a/components/timelines/account.vue
+++ b/components/timelines/account.vue
@@ -8,8 +8,6 @@
import type { Status } from "@lysand-org/client/types";
import Timeline from "./timeline.vue";
-const client = useClient();
-
const props = defineProps<{
id: string;
}>();
diff --git a/components/timelines/home.vue b/components/timelines/home.vue
index 38ed467..a6aca74 100644
--- a/components/timelines/home.vue
+++ b/components/timelines/home.vue
@@ -8,7 +8,6 @@
import type { Status } from "@lysand-org/client/types";
import { useHomeTimeline } from "~/composables/HomeTimeline";
import Timeline from "./timeline.vue";
-const client = useClient();
const {
error,
diff --git a/components/timelines/local.vue b/components/timelines/local.vue
index 35249e7..69ce555 100644
--- a/components/timelines/local.vue
+++ b/components/timelines/local.vue
@@ -8,7 +8,6 @@
import type { Status } from "@lysand-org/client/types";
import { useLocalTimeline } from "~/composables/LocalTimeline";
import Timeline from "./timeline.vue";
-const client = useClient();
const {
error,
diff --git a/components/timelines/notifications.vue b/components/timelines/notifications.vue
index 8692d64..91f723c 100644
--- a/components/timelines/notifications.vue
+++ b/components/timelines/notifications.vue
@@ -9,8 +9,6 @@ import type { Notification } from "@lysand-org/client/types";
import { useNotificationTimeline } from "~/composables/NotificationTimeline";
import Timeline from "./timeline.vue";
-const client = useClient();
-
const {
error,
hasReachedEnd,
diff --git a/components/timelines/public.vue b/components/timelines/public.vue
index 1a3992a..ef5a3c3 100644
--- a/components/timelines/public.vue
+++ b/components/timelines/public.vue
@@ -8,7 +8,6 @@
import type { Status } from "@lysand-org/client/types";
import { usePublicTimeline } from "~/composables/PublicTimeline";
import Timeline from "./timeline.vue";
-const client = useClient();
const {
error,
diff --git a/composables/CacheRefresh.ts b/composables/CacheRefresh.ts
index 2f3dcb5..fe93a7a 100644
--- a/composables/CacheRefresh.ts
+++ b/composables/CacheRefresh.ts
@@ -1,14 +1,11 @@
import type { LysandClient } from "@lysand-org/client";
import type { RolePermission } from "@lysand-org/client/types";
-import { useCurrentIdentity } from "./Identities";
export const useCacheRefresh = (client: MaybeRef) => {
if (import.meta.server) {
return;
}
- const identity = useCurrentIdentity();
-
// Refresh custom emojis and instance data and me on every reload
watchEffect(async () => {
console.info("Refreshing emoji, instance and account cache");
diff --git a/composables/Client.ts b/composables/Client.ts
index d1a68d4..d2e0910 100644
--- a/composables/Client.ts
+++ b/composables/Client.ts
@@ -1,11 +1,8 @@
import { LysandClient, type Token } from "@lysand-org/client";
-import { useCurrentIdentity } from "./Identities";
export const useClient = (
customToken: MaybeRef = null,
): Ref => {
- const identity = useCurrentIdentity();
-
return computed(
() =>
new LysandClient(
@@ -25,3 +22,5 @@ export const useClient = (
),
);
};
+
+export const client = useClient();
diff --git a/composables/Identities.ts b/composables/Identities.ts
index f8acd3a..cc273ce 100644
--- a/composables/Identities.ts
+++ b/composables/Identities.ts
@@ -5,72 +5,99 @@ import type {
Instance,
RolePermission,
} from "@lysand-org/client/types";
-import { StorageSerializers } from "@vueuse/core";
+import { StorageSerializers, useLocalStorage } from "@vueuse/core";
+import { ref, watch } from "vue";
-export type Identity = {
+/**
+ * Represents an identity with associated tokens, account, instance, permissions, and emojis.
+ */
+export interface Identity {
id: string;
tokens: Token;
account: Account;
instance: Instance;
permissions: RolePermission[];
emojis: Emoji[];
-};
+}
-export const useIdentities = (): Ref => {
+/**
+ * Composable to manage multiple identities.
+ * @returns A reactive reference to an array of identities.
+ */
+function useIdentities(): Ref {
return useLocalStorage("lysand:identities", [], {
serializer: StorageSerializers.object,
});
-};
+}
-export const useCurrentIdentity = (): Ref => {
- const currentId = useLocalStorage(
- "lysand:identities:current",
- null,
- );
+export const identities = useIdentities();
- const identities = useIdentities();
- const current = ref(
- identities.value.find((i) => i.id === currentId.value) ?? null,
- );
+const currentId = useLocalStorage(
+ "lysand:identities:current",
+ null,
+);
- watch(identities, (ids) => {
- if (ids.length === 0) {
- current.value = null;
- }
- });
+const current = ref(null);
+/**
+ * Composable to manage the current identity.
+ * @returns A reactive reference to the current identity or null if not set.
+ */
+function useCurrentIdentity(): Ref {
+ // Initialize current identity
+ function updateCurrentIdentity() {
+ current.value =
+ identities.value.find((i) => i.id === currentId.value) ?? null;
+ }
+
+ // Watch for changes in identities
watch(
- current,
- (newCurrent) => {
- if (newCurrent) {
- currentId.value = newCurrent.id;
- // If the identity is updated, update the identity in the list
- if (identities.value.find((i) => i.id === newCurrent.id)) {
- identities.value = identities.value.map((i) =>
- i.id === newCurrent.id ? newCurrent : i,
- );
- }
- // If the identity is not in the list, add it
- else {
- identities.value.push(newCurrent);
- }
-
- // Force update the identities
- identities.value = [...identities.value];
+ identities,
+ (ids) => {
+ if (ids.length === 0) {
+ current.value = null;
+ currentId.value = null;
} else {
- identities.value = identities.value.filter(
- (i) => i.id !== currentId.value,
- );
-
- if (identities.value.length > 0) {
- currentId.value = identities.value[0]?.id;
- } else {
- currentId.value = null;
- }
+ updateCurrentIdentity();
}
},
{ deep: true },
);
+ // Watch for changes in currentId
+ watch(currentId, updateCurrentIdentity);
+
+ // Watch for changes in current identity
+ watch(
+ current,
+ (newCurrent) => {
+ if (newCurrent) {
+ currentId.value = newCurrent.id;
+ const index = identities.value.findIndex(
+ (i) => i.id === newCurrent.id,
+ );
+ if (index !== -1) {
+ // Update existing identity
+ identities.value[index] = newCurrent;
+ } else {
+ // Add new identity
+ identities.value.push(newCurrent);
+ }
+ } else {
+ // Remove current identity
+ identities.value = identities.value.filter(
+ (i) => i.id !== currentId.value,
+ );
+ currentId.value = identities.value[0]?.id ?? null;
+ }
+ },
+ { deep: true },
+ );
+
+ // Initial setup
+ updateCurrentIdentity();
+
return current;
-};
+}
+
+export const identity = useCurrentIdentity();
diff --git a/composables/Instance.ts b/composables/Instance.ts
index e640d45..9b36191 100644
--- a/composables/Instance.ts
+++ b/composables/Instance.ts
@@ -2,8 +2,6 @@ import type { LysandClient } from "@lysand-org/client";
import type { ExtendedDescription, Instance } from "@lysand-org/client/types";
export const useInstance = () => {
- const identity = useCurrentIdentity();
-
return computed(() => identity.value?.instance);
};
diff --git a/composables/Permissions.ts b/composables/Permissions.ts
index e996ef5..af212f0 100644
--- a/composables/Permissions.ts
+++ b/composables/Permissions.ts
@@ -1,5 +1,3 @@
export const usePermissions = () => {
- const identity = useCurrentIdentity();
-
return computed(() => identity.value?.permissions ?? []);
};
diff --git a/composables/Relationship.ts b/composables/Relationship.ts
index 429dfc9..623f8ff 100644
--- a/composables/Relationship.ts
+++ b/composables/Relationship.ts
@@ -1,6 +1,5 @@
import type { LysandClient } from "@lysand-org/client";
import type { Relationship } from "@lysand-org/client/types";
-import { useCurrentIdentity } from "./Identities";
export const useRelationship = (
client: MaybeRef,
@@ -9,7 +8,7 @@ export const useRelationship = (
const relationship = ref(null as Relationship | null);
const isLoading = ref(false);
- if (!useCurrentIdentity().value) {
+ if (!identity.value) {
return { relationship, isLoading };
}
diff --git a/composables/ResolveMentions.ts b/composables/ResolveMentions.ts
index c026610..2a80e07 100644
--- a/composables/ResolveMentions.ts
+++ b/composables/ResolveMentions.ts
@@ -11,14 +11,18 @@ export const useResolveMentions = (
const output = ref([]);
- watch(mentions, async () => {
- output.value = await Promise.all(
- toValue(mentions).map(async (mention) => {
- const response = await client.getAccount(mention.id);
- return response.data;
- }),
- );
- });
+ watch(
+ mentions,
+ async () => {
+ output.value = await Promise.all(
+ toValue(mentions).map(async (mention) => {
+ const response = await client.getAccount(mention.id);
+ return response.data;
+ }),
+ );
+ },
+ { immediate: true },
+ );
return output;
};
diff --git a/composables/Settings.ts b/composables/Settings.ts
index 171db1c..2a5d743 100644
--- a/composables/Settings.ts
+++ b/composables/Settings.ts
@@ -4,11 +4,11 @@ import {
type SettingIds,
type Settings,
parseFromJson,
- settings,
+ settings as settingsJson,
} from "~/settings";
-export const useSettings = () => {
- return useLocalStorage("lysand:settings", settings, {
+const useSettings = () => {
+ return useLocalStorage("lysand:settings", settingsJson, {
serializer: {
read(raw) {
const json = StorageSerializers.object.read(raw);
@@ -31,9 +31,9 @@ export const useSettings = () => {
});
};
-export const useSetting = (id: SettingIds) => {
- const settings = useSettings();
+export const settings = useSettings();
+export const useSetting = (id: SettingIds) => {
const setting: Ref = ref(
settings.value.find((s) => s.id === id) as T,
) as unknown as Ref;
@@ -52,5 +52,5 @@ export const useSetting = (id: SettingIds) => {
};
export const getSetting = (id: SettingIds) => {
- return settings.find((s) => s.id === id) as T;
+ return settingsJson.find((s) => s.id === id) as T;
};
diff --git a/layouts/app.vue b/layouts/app.vue
index e9a7fc8..1e8def5 100644
--- a/layouts/app.vue
+++ b/layouts/app.vue
@@ -37,7 +37,6 @@ const notUsingInput = computed(
activeElement.value?.tagName !== "INPUT" &&
activeElement.value?.tagName !== "TEXTAREA",
);
-const identity = useCurrentIdentity();
watchEffect(async () => {
if (n?.value && notUsingInput.value) {
diff --git a/pages/[username]/[uuid].vue b/pages/[username]/[uuid].vue
index a67696d..60a5ccc 100644
--- a/pages/[username]/[uuid].vue
+++ b/pages/[username]/[uuid].vue
@@ -20,7 +20,6 @@ definePageMeta({
const element = ref(null);
const route = useRoute();
-const client = useClient();
const uuid = route.params.uuid as string;
const note = useNote(client, uuid);
diff --git a/pages/[username]/index.vue b/pages/[username]/index.vue
index 0cc50c8..c35e896 100644
--- a/pages/[username]/index.vue
+++ b/pages/[username]/index.vue
@@ -21,7 +21,6 @@ definePageMeta({
});
const route = useRoute();
-const client = useClient();
const username = (route.params.username as string).startsWith("@")
? (route.params.username as string).substring(1)
: (route.params.username as string);
diff --git a/pages/notifications.vue b/pages/notifications.vue
index ff0691e..3a46a95 100644
--- a/pages/notifications.vue
+++ b/pages/notifications.vue
@@ -28,5 +28,4 @@ import TimelineScroller from "~/components/timelines/timeline-scroller.vue";
definePageMeta({
layout: "app",
});
-const identity = useCurrentIdentity();
\ No newline at end of file
diff --git a/pages/oauth/reset.vue b/pages/oauth/reset.vue
index 4e2eef6..432ea89 100644
--- a/pages/oauth/reset.vue
+++ b/pages/oauth/reset.vue
@@ -76,7 +76,6 @@ import Label from "~/components/inputs/label.vue";
import PasswordInput from "~/components/inputs/password-input.vue";
import Button from "~/packages/ui/components/buttons/button.vue";
-const identity = useCurrentIdentity();
identity.value = null;
const schema = toTypedSchema(
diff --git a/pages/register/index.vue b/pages/register/index.vue
index 96e3718..e3871e3 100644
--- a/pages/register/index.vue
+++ b/pages/register/index.vue
@@ -84,7 +84,8 @@
cannot see your password.
-