mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 16:38:20 +01:00
refactor: ♻️ Replace megalodon with @lysand-org/client
This commit is contained in:
parent
5c528e8d03
commit
0bd3237965
8
app.vue
8
app.vue
|
|
@ -19,7 +19,7 @@ provideHeadlessUseId(() => useId());
|
||||||
const code = useRequestURL().searchParams.get("code");
|
const code = useRequestURL().searchParams.get("code");
|
||||||
const appData = useAppData();
|
const appData = useAppData();
|
||||||
const tokenData = useTokenData();
|
const tokenData = useTokenData();
|
||||||
const client = useMegalodon(tokenData);
|
const client = useClient(tokenData);
|
||||||
const instance = useInstance();
|
const instance = useInstance();
|
||||||
const description = useExtendedDescription(client);
|
const description = useExtendedDescription(client);
|
||||||
|
|
||||||
|
|
@ -28,12 +28,12 @@ useSeoMeta({
|
||||||
return titleChunk ? `${titleChunk} · Lysand` : "Lysand";
|
return titleChunk ? `${titleChunk} · Lysand` : "Lysand";
|
||||||
},
|
},
|
||||||
title: computed(() => instance.value?.title ?? ""),
|
title: computed(() => instance.value?.title ?? ""),
|
||||||
ogImage: computed(() => instance.value?.banner),
|
ogImage: computed(() => instance.value?.banner.url),
|
||||||
twitterTitle: computed(() => instance.value?.title ?? ""),
|
twitterTitle: computed(() => instance.value?.title ?? ""),
|
||||||
twitterDescription: computed(() =>
|
twitterDescription: computed(() =>
|
||||||
convert(description.value?.content ?? ""),
|
convert(description.value?.content ?? ""),
|
||||||
),
|
),
|
||||||
twitterImage: computed(() => instance.value?.banner),
|
twitterImage: computed(() => instance.value?.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: "Lysand",
|
ogSiteName: "Lysand",
|
||||||
|
|
@ -57,7 +57,7 @@ if (code) {
|
||||||
new URL("/", useRequestURL().origin).toString(),
|
new URL("/", useRequestURL().origin).toString(),
|
||||||
)
|
)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
tokenData.value = res;
|
tokenData.value = res.data;
|
||||||
|
|
||||||
// Remove code from URL
|
// Remove code from URL
|
||||||
window.history.replaceState(
|
window.history.replaceState(
|
||||||
|
|
|
||||||
|
|
@ -206,87 +206,64 @@ const canSubmit = computed(
|
||||||
content.value?.trim().length <= characterLimit.value,
|
content.value?.trim().length <= characterLimit.value,
|
||||||
);
|
);
|
||||||
const tokenData = useTokenData();
|
const tokenData = useTokenData();
|
||||||
const client = useMegalodon(tokenData);
|
const client = useClient(tokenData);
|
||||||
|
|
||||||
const send = async () => {
|
const send = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
if (!tokenData.value || !client.value) {
|
||||||
if (respondingType.value === "edit") {
|
throw new Error("Not authenticated");
|
||||||
fetch(
|
|
||||||
new URL(
|
|
||||||
`/api/v1/statuses/${respondingTo.value?.id}`,
|
|
||||||
client.value?.baseUrl ?? "",
|
|
||||||
).toString(),
|
|
||||||
{
|
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: `Bearer ${tokenData.value?.access_token}`,
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
status: content.value?.trim() ?? "",
|
|
||||||
content_type: markdown.value
|
|
||||||
? "text/markdown"
|
|
||||||
: "text/plain",
|
|
||||||
spoiler_text: cw.value ? cwContent.value.trim() : undefined,
|
|
||||||
sensitive: cw.value,
|
|
||||||
media_ids: files.value
|
|
||||||
.filter((file) => !!file.api_id)
|
|
||||||
.map((file) => file.api_id),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.then(async (res) => {
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error("Failed to edit status");
|
|
||||||
}
|
|
||||||
|
|
||||||
content.value = "";
|
|
||||||
loading.value = false;
|
|
||||||
useEvent("composer:send-edit", await res.json());
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
useEvent("composer:close");
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(new URL("/api/v1/statuses", client.value?.baseUrl ?? "").toString(), {
|
if (respondingType.value === "edit" && respondingTo.value) {
|
||||||
method: "POST",
|
const response = await client.value.editStatus(respondingTo.value.id, {
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: `Bearer ${tokenData.value?.access_token}`,
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
status: content.value?.trim() ?? "",
|
status: content.value?.trim() ?? "",
|
||||||
content_type: markdown.value ? "text/markdown" : "text/plain",
|
content_type: markdown.value ? "text/markdown" : "text/plain",
|
||||||
in_reply_to_id:
|
|
||||||
respondingType.value === "reply"
|
|
||||||
? respondingTo.value?.id
|
|
||||||
: null,
|
|
||||||
quote_id:
|
|
||||||
respondingType.value === "quote"
|
|
||||||
? respondingTo.value?.id
|
|
||||||
: null,
|
|
||||||
spoiler_text: cw.value ? cwContent.value.trim() : undefined,
|
spoiler_text: cw.value ? cwContent.value.trim() : undefined,
|
||||||
sensitive: cw.value,
|
sensitive: cw.value,
|
||||||
media_ids: files.value
|
media_ids: files.value
|
||||||
.filter((file) => !!file.api_id)
|
.filter((file) => !!file.api_id)
|
||||||
.map((file) => file.api_id),
|
.map((file) => file.api_id) as string[],
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then(async (res) => {
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error("Failed to send status");
|
|
||||||
}
|
|
||||||
|
|
||||||
content.value = "";
|
|
||||||
loading.value = false;
|
|
||||||
useEvent("composer:send", await res.json());
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
useEvent("composer:close");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!response.data) {
|
||||||
|
throw new Error("Failed to edit status");
|
||||||
|
}
|
||||||
|
|
||||||
|
content.value = "";
|
||||||
|
loading.value = false;
|
||||||
|
useEvent("composer:send-edit", response.data);
|
||||||
|
useEvent("composer:close");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await client.value.postStatus(
|
||||||
|
content.value?.trim() ?? "",
|
||||||
|
{
|
||||||
|
content_type: markdown.value ? "text/markdown" : "text/plain",
|
||||||
|
in_reply_to_id:
|
||||||
|
respondingType.value === "reply"
|
||||||
|
? respondingTo.value?.id
|
||||||
|
: undefined,
|
||||||
|
quote_id:
|
||||||
|
respondingType.value === "quote"
|
||||||
|
? respondingTo.value?.id
|
||||||
|
: undefined,
|
||||||
|
spoiler_text: cw.value ? cwContent.value.trim() : undefined,
|
||||||
|
sensitive: cw.value,
|
||||||
|
media_ids: files.value
|
||||||
|
.filter((file) => !!file.api_id)
|
||||||
|
.map((file) => file.api_id) as string[],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.data) {
|
||||||
|
throw new Error("Failed to send status");
|
||||||
|
}
|
||||||
|
|
||||||
|
content.value = "";
|
||||||
|
loading.value = false;
|
||||||
|
useEvent("composer:send", response.data as Status);
|
||||||
|
useEvent("composer:close");
|
||||||
};
|
};
|
||||||
|
|
||||||
const characterLimit = computed(
|
const characterLimit = computed(
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ const files = defineModel<
|
||||||
});
|
});
|
||||||
|
|
||||||
const tokenData = useTokenData();
|
const tokenData = useTokenData();
|
||||||
const client = useMegalodon(tokenData);
|
const client = useClient(tokenData);
|
||||||
const fileInput = ref<HTMLInputElement | null>(null);
|
const fileInput = ref<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
const openFilePicker = () => {
|
const openFilePicker = () => {
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ const loadingAuth = ref(false);
|
||||||
|
|
||||||
const appData = useAppData();
|
const appData = useAppData();
|
||||||
const tokenData = useTokenData();
|
const tokenData = useTokenData();
|
||||||
const client = useMegalodon();
|
const client = useClient();
|
||||||
const me = useMe();
|
const me = useMe();
|
||||||
|
|
||||||
const compose = () => {
|
const compose = () => {
|
||||||
|
|
@ -191,18 +191,18 @@ const signIn = async () => {
|
||||||
website: useBaseUrl().value,
|
website: useBaseUrl().value,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!output) {
|
if (!output?.data) {
|
||||||
alert("Failed to create app");
|
alert("Failed to create app");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
appData.value = output;
|
appData.value = output.data;
|
||||||
|
|
||||||
const url = await client.value?.generateAuthUrl(
|
const url = await client.value?.generateAuthUrl(
|
||||||
output.client_id,
|
output.data.client_id,
|
||||||
output.client_secret,
|
output.data.client_secret,
|
||||||
{
|
{
|
||||||
scope: ["read", "write", "follow", "push"],
|
scopes: ["read", "write", "follow", "push"],
|
||||||
redirect_uri: new URL("/", useRequestURL().origin).toString(),
|
redirect_uri: new URL("/", useRequestURL().origin).toString(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
<div class="flex flex-col p-10 gap-4 h-full">
|
<div class="flex flex-col p-10 gap-4 h-full">
|
||||||
<div
|
<div
|
||||||
class="aspect-video shrink-0 w-full rounded ring-white/5 bg-dark-800 shadow overflow-hidden ring-1 hover:ring-2 duration-100">
|
class="aspect-video shrink-0 w-full rounded ring-white/5 bg-dark-800 shadow overflow-hidden ring-1 hover:ring-2 duration-100">
|
||||||
<img class="object-cover w-full h-full duration-150 hover:scale-[102%] ease-in-out" v-if="instance?.banner"
|
<img class="object-cover w-full h-full duration-150 hover:scale-[102%] ease-in-out"
|
||||||
alt="Instance banner" :src="instance.banner" />
|
v-if="instance?.banner.url" alt="Instance banner" :src="instance.banner.url" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="prose prose-invert prose-sm">
|
<div class="prose prose-invert prose-sm">
|
||||||
|
|
@ -11,15 +11,15 @@
|
||||||
<div v-html="description?.content"></div>
|
<div v-html="description?.content"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="instance?.contact_account" class="flex flex-col gap-2 mt-auto">
|
<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>
|
<h2 class="text-gray-200 font-semibold uppercase text-xs">Administrator</h2>
|
||||||
<SocialElementsUsersSmallCard :account="instance.contact_account" />
|
<SocialElementsUsersSmallCard :account="instance.contact.account" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const client = useMegalodon();
|
const client = useClient();
|
||||||
const instance = useInstance();
|
const instance = useInstance();
|
||||||
const description = useExtendedDescription(client);
|
const description = useExtendedDescription(client);
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -112,7 +112,7 @@ useListen("composer:send-edit", (note) => {
|
||||||
const tokenData = useTokenData();
|
const tokenData = useTokenData();
|
||||||
const isSignedIn = useSignedIn();
|
const isSignedIn = useSignedIn();
|
||||||
const me = useMe();
|
const me = useMe();
|
||||||
const client = useMegalodon(tokenData);
|
const client = useClient(tokenData);
|
||||||
const {
|
const {
|
||||||
loaded,
|
loaded,
|
||||||
note: outputtedNote,
|
note: outputtedNote,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,6 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const tokenData = useTokenData();
|
const tokenData = useTokenData();
|
||||||
const client = useMegalodon(tokenData);
|
const client = useClient(tokenData);
|
||||||
const account = useAccount(client, props.account_id);
|
const account = useAccount(client, props.account_id);
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -105,7 +105,7 @@ const props = defineProps<{
|
||||||
const skeleton = computed(() => !props.account);
|
const skeleton = computed(() => !props.account);
|
||||||
const tokenData = useTokenData();
|
const tokenData = useTokenData();
|
||||||
const me = useMe();
|
const me = useMe();
|
||||||
const client = useMegalodon(tokenData);
|
const client = useClient(tokenData);
|
||||||
const accountId = computed(() => props.account?.id ?? null);
|
const accountId = computed(() => props.account?.id ?? null);
|
||||||
const { relationship, isLoading } = useRelationship(client, accountId);
|
const { relationship, isLoading } = useRelationship(client, accountId);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ const props = defineProps<{
|
||||||
id?: string;
|
id?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const client = useMegalodon();
|
const client = useClient();
|
||||||
const timelineParameters = ref({});
|
const timelineParameters = ref({});
|
||||||
const { timeline, loadNext, loadPrev } = useAccountTimeline(
|
const { timeline, loadNext, loadPrev } = useAccountTimeline(
|
||||||
client.value,
|
client.value,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const tokenData = useTokenData();
|
const tokenData = useTokenData();
|
||||||
const client = useMegalodon(tokenData);
|
const client = useClient(tokenData);
|
||||||
const timelineParameters = ref({});
|
const timelineParameters = ref({});
|
||||||
const { timeline, loadNext, loadPrev } = useHomeTimeline(
|
const { timeline, loadNext, loadPrev } = useHomeTimeline(
|
||||||
client.value,
|
client.value,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const client = useMegalodon();
|
const client = useClient();
|
||||||
const timelineParameters = ref({});
|
const timelineParameters = ref({});
|
||||||
const { timeline, loadNext, loadPrev } = useLocalTimeline(
|
const { timeline, loadNext, loadPrev } = useLocalTimeline(
|
||||||
client.value,
|
client.value,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const tokenData = useTokenData();
|
const tokenData = useTokenData();
|
||||||
const client = useMegalodon(tokenData);
|
const client = useClient(tokenData);
|
||||||
|
|
||||||
const isLoading = ref(true);
|
const isLoading = ref(true);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const client = useMegalodon();
|
const client = useClient();
|
||||||
const timelineParameters = ref({});
|
const timelineParameters = ref({});
|
||||||
const { timeline, loadNext, loadPrev } = usePublicTimeline(
|
const { timeline, loadNext, loadPrev } = usePublicTimeline(
|
||||||
client.value,
|
client.value,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { Account } from "~/types/mastodon/account";
|
import type { Account } from "~/types/mastodon/account";
|
||||||
|
|
||||||
export const useAccount = (
|
export const useAccount = (
|
||||||
client: MaybeRef<Mastodon | null>,
|
client: MaybeRef<LysandClient | null>,
|
||||||
accountId: MaybeRef<string | null>,
|
accountId: MaybeRef<string | null>,
|
||||||
) => {
|
) => {
|
||||||
if (!client) {
|
if (!client) {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { Account } from "~/types/mastodon/account";
|
import type { Account } from "~/types/mastodon/account";
|
||||||
|
|
||||||
export const useAccountSearch = (
|
export const useAccountSearch = (
|
||||||
client: MaybeRef<Mastodon | null>,
|
client: MaybeRef<LysandClient | null>,
|
||||||
q: string,
|
q: string,
|
||||||
): Ref<Account[] | null> => {
|
): Ref<Account[] | null> => {
|
||||||
const output = ref(null as Account[] | null);
|
const output = ref(null as Account[] | null);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { Status } from "~/types/mastodon/status";
|
import type { Status } from "~/types/mastodon/status";
|
||||||
|
|
||||||
export const useAccountTimeline = (
|
export const useAccountTimeline = (
|
||||||
client: Mastodon | null,
|
client: LysandClient | null,
|
||||||
id: MaybeRef<string | null>,
|
id: MaybeRef<string | null>,
|
||||||
options: MaybeRef<{
|
options: MaybeRef<{
|
||||||
limit?: number | undefined;
|
limit?: number | undefined;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import { StorageSerializers } from "@vueuse/core";
|
import { StorageSerializers } from "@vueuse/core";
|
||||||
import type { OAuth } from "megalodon";
|
|
||||||
|
export type ApplicationData = Awaited<
|
||||||
|
ReturnType<LysandClient["createApp"]>
|
||||||
|
>["data"];
|
||||||
|
|
||||||
export const useAppData = () => {
|
export const useAppData = () => {
|
||||||
return useLocalStorage<OAuth.AppData | null>("lysand:app_data", null, {
|
return useLocalStorage<ApplicationData | null>("lysand:app_data", null, {
|
||||||
serializer: StorageSerializers.object,
|
serializer: StorageSerializers.object,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { InstanceWithExtra } from "./Instance";
|
|
||||||
|
|
||||||
export const useCacheRefresh = (client: MaybeRef<Mastodon | null>) => {
|
export const useCacheRefresh = (client: MaybeRef<LysandClient | null>) => {
|
||||||
if (process.server) return;
|
if (process.server) return;
|
||||||
|
|
||||||
const tokenData = useTokenData();
|
const tokenData = useTokenData();
|
||||||
|
|
@ -43,7 +42,7 @@ export const useCacheRefresh = (client: MaybeRef<Mastodon | null>) => {
|
||||||
toValue(client)
|
toValue(client)
|
||||||
?.getInstance()
|
?.getInstance()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
instance.value = res.data as InstanceWithExtra;
|
instance.value = res.data;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
|
|
||||||
type ExtendedDescription = {
|
type ExtendedDescription = {
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
content: string;
|
content: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useExtendedDescription = (client: MaybeRef<Mastodon | null>) => {
|
export const useExtendedDescription = (
|
||||||
|
client: MaybeRef<LysandClient | null>,
|
||||||
|
) => {
|
||||||
if (!ref(client).value) {
|
if (!ref(client).value) {
|
||||||
return ref(null as ExtendedDescription | null);
|
return ref(null as ExtendedDescription | null);
|
||||||
}
|
}
|
||||||
|
|
@ -13,7 +15,7 @@ export const useExtendedDescription = (client: MaybeRef<Mastodon | null>) => {
|
||||||
const output = ref(null as ExtendedDescription | null);
|
const output = ref(null as ExtendedDescription | null);
|
||||||
|
|
||||||
ref(client)
|
ref(client)
|
||||||
.value?.client.get("/api/v1/instance/extended_description")
|
.value?.getInstanceExtendedDescription()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
output.value = res.data;
|
output.value = res.data;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { Status } from "~/types/mastodon/status";
|
import type { Status } from "~/types/mastodon/status";
|
||||||
|
|
||||||
export const useHomeTimeline = (
|
export const useHomeTimeline = (
|
||||||
client: Mastodon | null,
|
client: LysandClient | null,
|
||||||
options: MaybeRef<{
|
options: MaybeRef<{
|
||||||
local?: boolean;
|
local?: boolean;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,15 @@
|
||||||
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import { StorageSerializers } from "@vueuse/core";
|
import { StorageSerializers } from "@vueuse/core";
|
||||||
import type { Mastodon } from "megalodon";
|
|
||||||
import type { Instance } from "~/types/mastodon/instance";
|
|
||||||
|
|
||||||
export type InstanceWithExtra = Instance & {
|
// Return type of LysandClient.getInstance
|
||||||
banner: string | null;
|
export type Instance = Awaited<ReturnType<LysandClient["getInstance"]>>["data"];
|
||||||
lysand_version: string;
|
|
||||||
sso: {
|
|
||||||
forced: boolean;
|
|
||||||
providers: {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
icon?: string;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useInstance = () => {
|
export const useInstance = () => {
|
||||||
if (process.server) {
|
if (process.server) {
|
||||||
return ref(null);
|
return ref(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return useLocalStorage<InstanceWithExtra | null>("lysand:instance", null, {
|
return useLocalStorage<Instance | null>("lysand:instance", null, {
|
||||||
serializer: StorageSerializers.object,
|
serializer: StorageSerializers.object,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { Status } from "~/types/mastodon/status";
|
import type { Status } from "~/types/mastodon/status";
|
||||||
|
|
||||||
export const useLocalTimeline = (
|
export const useLocalTimeline = (
|
||||||
client: Mastodon | null,
|
client: LysandClient | null,
|
||||||
options: MaybeRef<
|
options: MaybeRef<
|
||||||
Partial<{
|
Partial<{
|
||||||
only_media: boolean;
|
only_media: boolean;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
import { Mastodon, type OAuth } from "megalodon";
|
import { LysandClient, type Token } from "@lysand-org/client";
|
||||||
|
|
||||||
export const useMegalodon = (
|
export const useClient = (
|
||||||
tokenData?: MaybeRef<OAuth.TokenData | null>,
|
tokenData?: MaybeRef<Token | null>,
|
||||||
disableOnServer = false,
|
disableOnServer = false,
|
||||||
): Ref<Mastodon | null> => {
|
): Ref<LysandClient | null> => {
|
||||||
if (disableOnServer && process.server) {
|
if (disableOnServer && process.server) {
|
||||||
return ref(null);
|
return ref(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return computed(
|
return computed(
|
||||||
() =>
|
() =>
|
||||||
new Mastodon(useBaseUrl().value, toValue(tokenData)?.access_token),
|
new LysandClient(
|
||||||
|
new URL(useBaseUrl().value),
|
||||||
|
toValue(tokenData)?.access_token,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { Status } from "~/types/mastodon/status";
|
import type { Status } from "~/types/mastodon/status";
|
||||||
|
|
||||||
export const useNote = (client: MaybeRef<Mastodon | null>, noteId: string) => {
|
export const useNote = (
|
||||||
|
client: MaybeRef<LysandClient | null>,
|
||||||
|
noteId: string,
|
||||||
|
) => {
|
||||||
if (!ref(client).value) {
|
if (!ref(client).value) {
|
||||||
return ref(null as Status | null);
|
return ref(null as Status | null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { Context } from "~/types/mastodon/context";
|
import type { Context } from "~/types/mastodon/context";
|
||||||
|
|
||||||
export const useNoteContext = (
|
export const useNoteContext = (
|
||||||
client: MaybeRef<Mastodon | null>,
|
client: MaybeRef<LysandClient | null>,
|
||||||
noteId: MaybeRef<string | null>,
|
noteId: MaybeRef<string | null>,
|
||||||
) => {
|
) => {
|
||||||
if (!ref(client).value) {
|
if (!ref(client).value) {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { Status } from "~/types/mastodon/status";
|
import type { Status } from "~/types/mastodon/status";
|
||||||
|
|
||||||
export const useNoteData = (
|
export const useNoteData = (
|
||||||
noteProp: MaybeRef<Status | undefined>,
|
noteProp: MaybeRef<Status | undefined>,
|
||||||
client: Ref<Mastodon | null>,
|
client: Ref<LysandClient | null>,
|
||||||
) => {
|
) => {
|
||||||
const isReply = computed(() => !!toValue(noteProp)?.in_reply_to_id);
|
const isReply = computed(() => !!toValue(noteProp)?.in_reply_to_id);
|
||||||
const isQuote = computed(() => !!toValue(noteProp)?.quote);
|
const isQuote = computed(() => !!toValue(noteProp)?.quote);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { Notification } from "~/types/mastodon/notification";
|
import type { Notification } from "~/types/mastodon/notification";
|
||||||
|
|
||||||
export const useNotificationTimeline = (
|
export const useNotificationTimeline = (
|
||||||
client: Mastodon | null,
|
client: LysandClient | null,
|
||||||
options: MaybeRef<{
|
options: MaybeRef<{
|
||||||
limit?: number | undefined;
|
limit?: number | undefined;
|
||||||
max_id?: string | undefined;
|
max_id?: string | undefined;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { Status } from "~/types/mastodon/status";
|
import type { Status } from "~/types/mastodon/status";
|
||||||
|
|
||||||
export const usePublicTimeline = (
|
export const usePublicTimeline = (
|
||||||
client: Mastodon | null,
|
client: LysandClient | null,
|
||||||
options: MaybeRef<{
|
options: MaybeRef<{
|
||||||
only_media?: boolean;
|
only_media?: boolean;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { Relationship } from "~/types/mastodon/relationship";
|
import type { Relationship } from "~/types/mastodon/relationship";
|
||||||
|
|
||||||
export const useRelationship = (
|
export const useRelationship = (
|
||||||
client: MaybeRef<Mastodon | null>,
|
client: MaybeRef<LysandClient | null>,
|
||||||
accountId: MaybeRef<string | null>,
|
accountId: MaybeRef<string | null>,
|
||||||
) => {
|
) => {
|
||||||
const relationship = ref(null as Relationship | null);
|
const relationship = ref(null as Relationship | null);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import type { Mastodon } from "megalodon";
|
import type { LysandClient } from "@lysand-org/client";
|
||||||
import type { Account } from "~/types/mastodon/account";
|
import type { Account } from "~/types/mastodon/account";
|
||||||
import type { Mention } from "~/types/mastodon/mention";
|
import type { Mention } from "~/types/mastodon/mention";
|
||||||
|
|
||||||
export const useResolveMentions = (
|
export const useResolveMentions = (
|
||||||
mentions: Ref<Mention[]>,
|
mentions: Ref<Mention[]>,
|
||||||
client: Mastodon | null,
|
client: LysandClient | null,
|
||||||
): Ref<Account[]> => {
|
): Ref<Account[]> => {
|
||||||
if (!client) {
|
if (!client) {
|
||||||
return ref([]);
|
return ref([]);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { InstanceWithExtra } from "./Instance";
|
import type { Instance } from "./Instance";
|
||||||
|
|
||||||
export const useSSOConfig = (): Ref<InstanceWithExtra["sso"] | null> => {
|
export const useSSOConfig = (): Ref<Instance["sso"] | null> => {
|
||||||
const instance = useInstance();
|
const instance = useInstance();
|
||||||
|
|
||||||
return computed(() => instance.value?.sso || null);
|
return computed(() => instance.value?.sso || null);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type { Mastodon, Response } from "megalodon";
|
import type { LysandClient, Output } from "@lysand-org/client";
|
||||||
|
|
||||||
interface BaseOptions {
|
interface BaseOptions {
|
||||||
max_id?: string;
|
max_id?: string;
|
||||||
|
|
@ -6,9 +6,9 @@ interface BaseOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
type FetchTimelineFunction<Element, Options> = (
|
type FetchTimelineFunction<Element, Options> = (
|
||||||
client: Mastodon,
|
client: LysandClient,
|
||||||
options: Options & BaseOptions,
|
options: Options & BaseOptions,
|
||||||
) => Promise<Response<Element[]>>;
|
) => Promise<Output<Element[]>>;
|
||||||
|
|
||||||
export const useTimeline = <
|
export const useTimeline = <
|
||||||
Element extends {
|
Element extends {
|
||||||
|
|
@ -16,7 +16,7 @@ export const useTimeline = <
|
||||||
},
|
},
|
||||||
Options,
|
Options,
|
||||||
>(
|
>(
|
||||||
client: Mastodon | null,
|
client: LysandClient | null,
|
||||||
fetchTimeline: FetchTimelineFunction<Element, Options> | null | undefined,
|
fetchTimeline: FetchTimelineFunction<Element, Options> | null | undefined,
|
||||||
options: MaybeRef<Options & BaseOptions>,
|
options: MaybeRef<Options & BaseOptions>,
|
||||||
): {
|
): {
|
||||||
|
|
@ -98,7 +98,7 @@ export const useIdTimeline = <
|
||||||
},
|
},
|
||||||
Options,
|
Options,
|
||||||
>(
|
>(
|
||||||
client: Mastodon | null,
|
client: LysandClient | null,
|
||||||
id: MaybeRef<string | null>,
|
id: MaybeRef<string | null>,
|
||||||
fetchTimeline: FetchTimelineFunction<Element, Options> | null | undefined,
|
fetchTimeline: FetchTimelineFunction<Element, Options> | null | undefined,
|
||||||
options: MaybeRef<Options & BaseOptions>,
|
options: MaybeRef<Options & BaseOptions>,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
import type { Token } from "@lysand-org/client";
|
||||||
import { StorageSerializers } from "@vueuse/core";
|
import { StorageSerializers } from "@vueuse/core";
|
||||||
import type { OAuth } from "megalodon";
|
|
||||||
|
|
||||||
export const useTokenData = () => {
|
export const useTokenData = () => {
|
||||||
return useLocalStorage<OAuth.TokenData | null>("lysand:token_data", null, {
|
return useLocalStorage<Token | null>("lysand:token_data", null, {
|
||||||
serializer: StorageSerializers.object,
|
serializer: StorageSerializers.object,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ const { width } = useWindowSize();
|
||||||
|
|
||||||
const { n, o_i_d_c } = useMagicKeys();
|
const { n, o_i_d_c } = useMagicKeys();
|
||||||
const tokenData = useTokenData();
|
const tokenData = useTokenData();
|
||||||
const client = useMegalodon(tokenData);
|
const client = useClient(tokenData);
|
||||||
const providers = useSSOConfig();
|
const providers = useSSOConfig();
|
||||||
|
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
|
|
@ -71,7 +71,7 @@ watchEffect(async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
new URL("/api/v1/sso", client.value?.baseUrl),
|
new URL("/api/v1/sso", client.value?.url),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
||||||
12
package.json
12
package.json
|
|
@ -25,10 +25,12 @@
|
||||||
"generate": "nuxt generate",
|
"generate": "nuxt generate",
|
||||||
"preview": "nuxt preview",
|
"preview": "nuxt preview",
|
||||||
"postinstall": "nuxt prepare",
|
"postinstall": "nuxt prepare",
|
||||||
"lint": "bunx @biomejs/biome check ."
|
"lint": "bunx @biomejs/biome check .",
|
||||||
|
"check": "bunx tsc -p ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ark-ui/vue": "^3.3.0",
|
"@ark-ui/vue": "^3.3.1",
|
||||||
|
"@lysand-org": "github:lysand-org/api#main",
|
||||||
"@nuxt/fonts": "^0.7.0",
|
"@nuxt/fonts": "^0.7.0",
|
||||||
"@tailwindcss/typography": "^0.5.13",
|
"@tailwindcss/typography": "^0.5.13",
|
||||||
"@vee-validate/nuxt": "^4.13.1",
|
"@vee-validate/nuxt": "^4.13.1",
|
||||||
|
|
@ -39,7 +41,6 @@
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
"iconify-icon": "^2.1.0",
|
"iconify-icon": "^2.1.0",
|
||||||
"magic-regexp": "^0.8.0",
|
"magic-regexp": "^0.8.0",
|
||||||
"megalodon": "^10.0.1",
|
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
"nuxt": "^3.11.2",
|
"nuxt": "^3.11.2",
|
||||||
|
|
@ -48,7 +49,7 @@
|
||||||
"nuxt-shiki": "^0.3.0",
|
"nuxt-shiki": "^0.3.0",
|
||||||
"overlayscrollbars": "^2.8.3",
|
"overlayscrollbars": "^2.8.3",
|
||||||
"overlayscrollbars-vue": "^0.5.9",
|
"overlayscrollbars-vue": "^0.5.9",
|
||||||
"shiki": "^1.6.2",
|
"shiki": "^1.6.3",
|
||||||
"vue": "^3.4.27",
|
"vue": "^3.4.27",
|
||||||
"vue-router": "^4.3.2",
|
"vue-router": "^4.3.2",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
|
|
@ -59,7 +60,8 @@
|
||||||
"@nuxtjs/tailwindcss": "^6.12.0",
|
"@nuxtjs/tailwindcss": "^6.12.0",
|
||||||
"@tailwindcss/forms": "^0.5.7",
|
"@tailwindcss/forms": "^0.5.7",
|
||||||
"@types/html-to-text": "^9.0.4",
|
"@types/html-to-text": "^9.0.4",
|
||||||
"@vue-email/nuxt": "^0.8.19"
|
"@vue-email/nuxt": "^0.8.19",
|
||||||
|
"typescript": "^5.4.5"
|
||||||
},
|
},
|
||||||
"trustedDependencies": [
|
"trustedDependencies": [
|
||||||
"@biomejs/biome",
|
"@biomejs/biome",
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ definePageMeta({
|
||||||
|
|
||||||
const element = ref<HTMLElement | null>(null);
|
const element = ref<HTMLElement | null>(null);
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const client = useMegalodon();
|
const client = useClient();
|
||||||
const uuid = route.params.uuid as string;
|
const uuid = route.params.uuid as string;
|
||||||
|
|
||||||
const note = useNote(client, uuid);
|
const note = useNote(client, uuid);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ definePageMeta({
|
||||||
});
|
});
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const client = useMegalodon(undefined, true);
|
const client = useClient(undefined, true);
|
||||||
const username = (route.params.username as string).replace("@", "");
|
const username = (route.params.username as string).replace("@", "");
|
||||||
|
|
||||||
const accounts = useAccountSearch(client, username);
|
const accounts = useAccountSearch(client, username);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,4 @@
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: "app",
|
layout: "app",
|
||||||
});
|
});
|
||||||
|
|
||||||
const me = useMe();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -27,7 +27,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
const url = useRequestURL();
|
|
||||||
const query = useRoute().query;
|
const query = useRoute().query;
|
||||||
|
|
||||||
const code = query.code;
|
const code = query.code;
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
</VeeErrorMessage>
|
</VeeErrorMessage>
|
||||||
</VeeField>
|
</VeeField>
|
||||||
|
|
||||||
<VeeField name="reason" as="div" v-slot="{ errors, field }" validate-on-change>
|
<VeeField name="reason" as="div" v-slot="{ errors }" validate-on-change>
|
||||||
<label for="reason" class="block text-sm font-medium leading-6 text-gray-50">Why do you want to
|
<label for="reason" class="block text-sm font-medium leading-6 text-gray-50">Why do you want to
|
||||||
join?</label>
|
join?</label>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
|
|
@ -62,7 +62,7 @@
|
||||||
class="rounded disabled:hover:cursor-wait mr-1 align-middle mb-0.5 text-pink-700 !ring-0 !outline-none"
|
class="rounded disabled:hover:cursor-wait mr-1 align-middle mb-0.5 text-pink-700 !ring-0 !outline-none"
|
||||||
required />
|
required />
|
||||||
<span class="text-sm text-gray-100">I agree to the terms and conditions of this server <a
|
<span class="text-sm text-gray-100">I agree to the terms and conditions of this server <a
|
||||||
class="underline font-bold" target="_blank" :href="instance.uri">available here</a></span>
|
class="underline font-bold" target="_blank" :href="'#'">available here</a></span>
|
||||||
<VeeErrorMessage name="tos" as="p" class="mt-2 text-sm text-red-600" v-slot="{ message }">
|
<VeeErrorMessage name="tos" as="p" class="mt-2 text-sm text-red-600" v-slot="{ message }">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</VeeErrorMessage>
|
</VeeErrorMessage>
|
||||||
|
|
@ -88,6 +88,7 @@ import { toTypedSchema } from "@vee-validate/zod";
|
||||||
import type { AxiosError } from "axios";
|
import type { AxiosError } from "axios";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import LoginInput from "../../components/LoginInput.vue";
|
import LoginInput from "../../components/LoginInput.vue";
|
||||||
|
// TODO: Add instance TOS link
|
||||||
|
|
||||||
const schema = toTypedSchema(
|
const schema = toTypedSchema(
|
||||||
z
|
z
|
||||||
|
|
@ -114,7 +115,7 @@ const schema = toTypedSchema(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const client = useMegalodon();
|
const client = useClient();
|
||||||
const instance = useInstance();
|
const instance = useInstance();
|
||||||
|
|
||||||
const errors = ref<{
|
const errors = ref<{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue