2024-04-26 01:04:45 +02:00
|
|
|
<template>
|
|
|
|
|
<aside
|
2024-06-10 05:24:55 +02:00
|
|
|
class="fixed h-dvh z-10 md:flex hidden flex-col p-4 bg-dark-800 gap-10 max-w-20 hover:max-w-72 duration-200 group ring-1 ring-dark-500"
|
2024-06-10 06:33:14 +02:00
|
|
|
aria-label="Navigation" role="complementary">
|
2024-05-12 10:37:57 +02:00
|
|
|
<NuxtLink href="/">
|
|
|
|
|
<img crossorigin="anonymous" class="size-11 rounded ring-1 ring-white/10 hover:scale-105 duration-200"
|
2024-06-15 23:18:58 +02:00
|
|
|
:src="instance?.thumbnail.url ?? '/logo.webp'" alt="Logo of your instance" />
|
2024-04-26 01:04:45 +02:00
|
|
|
</NuxtLink>
|
|
|
|
|
|
|
|
|
|
<div class="flex flex-col gap-3">
|
|
|
|
|
<h3 class="font-semibold text-gray-300 text-xs uppercase opacity-0 group-hover:opacity-100 duration-200">
|
|
|
|
|
Timelines</h3>
|
2024-06-15 23:18:58 +02:00
|
|
|
|
|
|
|
|
<NuxtLink v-for="timeline in visibleTimelines" :key="timeline.href" :to="timeline.href">
|
|
|
|
|
<ButtonsBase
|
|
|
|
|
class="flex flex-row text-left items-center justify-start gap-3 text-lg hover:ring-1 ring-white/10 overflow-hidden h-12 w-full duration-200">
|
|
|
|
|
<iconify-icon :icon="timeline.icon" class="shrink-0 text-2xl" />
|
|
|
|
|
<span class="pr-28 line-clamp-1">{{ timeline.name }}</span>
|
|
|
|
|
</ButtonsBase>
|
|
|
|
|
</NuxtLink>
|
|
|
|
|
|
2024-04-26 01:04:45 +02:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="flex flex-col gap-3 mt-auto">
|
|
|
|
|
<h3 class="font-semibold text-gray-300 text-xs uppercase opacity-0 group-hover:opacity-100 duration-200">
|
|
|
|
|
Account</h3>
|
2024-06-15 23:18:58 +02:00
|
|
|
|
|
|
|
|
<SidebarsAccountPicker @sign-in="signIn().finally(() => loadingAuth = false)"
|
|
|
|
|
@sign-out="id => signOut(id).finally(() => loadingAuth = false)" />
|
|
|
|
|
<NuxtLink href="/register" v-if="!identity">
|
|
|
|
|
<ButtonsBase
|
|
|
|
|
class="flex flex-row text-left items-center justify-start gap-3 text-lg hover:ring-1 ring-white/10 overflow-hidden h-12 w-full duration-200">
|
|
|
|
|
<iconify-icon icon="tabler:certificate" class="shrink-0 text-2xl" />
|
|
|
|
|
<span class="pr-28 line-clamp-1">Register</span>
|
2024-05-12 09:30:02 +02:00
|
|
|
</ButtonsBase>
|
2024-06-15 23:18:58 +02:00
|
|
|
</NuxtLink>
|
2024-06-19 08:16:28 +02:00
|
|
|
<NuxtLink href="/settings" v-if="identity">
|
|
|
|
|
<button @click="$emit('signIn')" class="w-full overflow-hidden">
|
|
|
|
|
<div class="rounded text-left flex flex-row items-center gap-x-2 hover:scale-[95%]">
|
|
|
|
|
<div
|
|
|
|
|
class="shrink-0 size-12 border-dashed border-white/20 border flex items-center justify-center rounded">
|
|
|
|
|
<iconify-icon icon="tabler:adjustments" class="size-6 text-gray-200" width="none" />
|
|
|
|
|
</div>
|
|
|
|
|
<span
|
|
|
|
|
class="line-clamp-1 font-semibold p-1 justify-around text-sm text-gray-300 grow overflow-hidden">
|
|
|
|
|
Settings
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</button>
|
|
|
|
|
</NuxtLink>
|
2024-06-15 23:18:58 +02:00
|
|
|
<h3 v-if="identity"
|
|
|
|
|
class="font-semibold text-gray-300 text-xs uppercase opacity-0 group-hover:opacity-100 duration-200">
|
|
|
|
|
Posts</h3>
|
|
|
|
|
<ButtonsBase v-if="identity" @click="compose" title="Open composer (shortcut: n)"
|
2024-06-16 03:42:48 +02:00
|
|
|
class="flex flex-row text-left items-center justify-start gap-3 text-lg hover:ring-1 ring-white/10 bg-gradient-to-tr from-primary-300 via-purple-300 to-indigo-400 overflow-hidden h-12 w-full duration-200">
|
2024-06-15 23:18:58 +02:00
|
|
|
<iconify-icon icon="tabler:writing" class="shrink-0 text-2xl" />
|
|
|
|
|
<span class="pr-28 line-clamp-1">Compose</span>
|
|
|
|
|
<kbd class="text-xs font-semibold rounded bg-dark-500 font-mono px-1 flex flex-row">
|
|
|
|
|
<iconify-icon icon="tabler:keyboard" height="1rem" width="1rem" class="inline" aria-hidden="true" />
|
|
|
|
|
<iconify-icon icon="tabler:letter-n-small" height="1rem" width="1rem" class="inline -mr-1"
|
|
|
|
|
aria-hidden="true" />
|
|
|
|
|
</kbd>
|
|
|
|
|
</ButtonsBase>
|
|
|
|
|
<ButtonsBase v-if="$pwa?.needRefresh" @click="$pwa?.updateServiceWorker()" title="Update service worker"
|
2024-06-16 03:42:48 +02:00
|
|
|
class="flex flex-row text-left items-center justify-start gap-3 text-lg ring-2 ring-primary-600 overflow-hidden h-12 w-full duration-200">
|
2024-06-15 23:18:58 +02:00
|
|
|
<iconify-icon icon="tabler:refresh" class="shrink-0 text-2xl" />
|
|
|
|
|
<span class="pr-28 line-clamp-1">Update</span>
|
|
|
|
|
</ButtonsBase>
|
|
|
|
|
|
2024-04-26 01:04:45 +02:00
|
|
|
</div>
|
|
|
|
|
</aside>
|
2024-05-01 10:40:33 +02:00
|
|
|
<!-- Mobile bottom navbar -->
|
|
|
|
|
<nav
|
2024-06-19 08:39:18 +02:00
|
|
|
:class="['fixed bottom-0 left-0 right-0 z-20 h-16 md:hidden grid gap-3 p-2 *:shadow-xl bg-dark-900 ring-1 ring-white/10 text-gray-200', !!identity ? 'grid-cols-4' : 'grid-cols-3']">
|
2024-06-15 23:18:58 +02:00
|
|
|
|
|
|
|
|
<DropdownsAdaptiveDropdown>
|
|
|
|
|
<template #button>
|
2024-06-19 08:39:18 +02:00
|
|
|
<ButtonsMobileNavbarButton icon="tabler:home" text="Timelines" />
|
2024-06-15 23:18:58 +02:00
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<template #items>
|
|
|
|
|
<Menu.Item value="" v-for="timeline in visibleTimelines" :key="timeline.href">
|
|
|
|
|
<NuxtLink :href="timeline.href">
|
|
|
|
|
<ButtonsDropdownElement :icon="timeline.icon" class="w-full">
|
|
|
|
|
{{ timeline.name }}
|
|
|
|
|
</ButtonsDropdownElement>
|
|
|
|
|
</NuxtLink>
|
|
|
|
|
</Menu.Item>
|
|
|
|
|
</template>
|
|
|
|
|
</DropdownsAdaptiveDropdown>
|
2024-06-19 08:39:18 +02:00
|
|
|
<NuxtLink href="/notifications" class="w-full">
|
|
|
|
|
<ButtonsMobileNavbarButton icon="tabler:bell" text="Notifications" />
|
2024-06-15 23:18:58 +02:00
|
|
|
</NuxtLink>
|
2024-06-19 08:39:18 +02:00
|
|
|
<ButtonsMobileNavbarButton v-if="$pwa?.needRefresh" @click="$pwa?.updateServiceWorker(true)"
|
|
|
|
|
icon="tabler:refresh" text="Update" />
|
2024-06-15 23:18:58 +02:00
|
|
|
<SidebarsAccountPicker v-else @sign-in="signIn().finally(() => loadingAuth = false)"
|
|
|
|
|
@sign-out="id => signOut(id).finally(() => loadingAuth = false)">
|
2024-06-19 08:39:18 +02:00
|
|
|
<ButtonsMobileNavbarButton icon="tabler:user" text="Account" />
|
2024-06-15 23:18:58 +02:00
|
|
|
</SidebarsAccountPicker>
|
|
|
|
|
<button @click="compose" v-if="identity"
|
2024-06-19 08:39:18 +02:00
|
|
|
class="flex flex-col items-center justify-center p-2 rounded bg-gradient-to-tr from-[theme(colors.primary.300/70%)] via-purple-300/70 to-indigo-400/70">
|
2024-06-15 23:18:58 +02:00
|
|
|
<iconify-icon icon="tabler:writing" class="text-2xl" />
|
2024-06-19 08:39:18 +02:00
|
|
|
<span class="text-xs hidden md:inline">Compose</span>
|
2024-06-15 23:18:58 +02:00
|
|
|
</button>
|
2024-05-01 10:40:33 +02:00
|
|
|
</nav>
|
2024-04-26 01:04:45 +02:00
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
2024-06-05 02:03:15 +02:00
|
|
|
import { Menu } from "@ark-ui/vue";
|
2024-05-12 09:30:02 +02:00
|
|
|
const { $pwa } = useNuxtApp();
|
2024-04-26 01:04:45 +02:00
|
|
|
const timelines = ref([
|
2024-04-29 04:59:28 +02:00
|
|
|
{
|
|
|
|
|
href: "/home",
|
|
|
|
|
name: "Home",
|
|
|
|
|
icon: "tabler:home",
|
|
|
|
|
requiresAuth: true,
|
|
|
|
|
},
|
2024-04-26 01:04:45 +02:00
|
|
|
{
|
|
|
|
|
href: "/public",
|
|
|
|
|
name: "Public",
|
|
|
|
|
icon: "tabler:world",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
href: "/local",
|
|
|
|
|
name: "Local",
|
|
|
|
|
icon: "tabler:home",
|
|
|
|
|
},
|
2024-05-08 14:15:21 +02:00
|
|
|
{
|
|
|
|
|
href: "/notifications",
|
|
|
|
|
name: "Notifications",
|
|
|
|
|
icon: "tabler:bell",
|
|
|
|
|
requiresAuth: true,
|
|
|
|
|
},
|
2024-04-26 01:04:45 +02:00
|
|
|
]);
|
2024-04-27 06:50:30 +02:00
|
|
|
|
2024-05-01 10:40:33 +02:00
|
|
|
const visibleTimelines = computed(() =>
|
|
|
|
|
timelines.value.filter(
|
2024-06-10 05:24:55 +02:00
|
|
|
(timeline) => !timeline.requiresAuth || identity.value,
|
2024-05-01 10:40:33 +02:00
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
2024-04-27 06:50:30 +02:00
|
|
|
const loadingAuth = ref(false);
|
|
|
|
|
|
|
|
|
|
const appData = useAppData();
|
2024-06-10 05:24:55 +02:00
|
|
|
const identity = useCurrentIdentity();
|
|
|
|
|
const identities = useIdentities();
|
2024-06-08 01:09:15 +02:00
|
|
|
const client = useClient();
|
2024-06-15 23:18:58 +02:00
|
|
|
const instance = useInstance();
|
2024-04-27 06:50:30 +02:00
|
|
|
|
2024-04-27 07:47:23 +02:00
|
|
|
const compose = () => {
|
2024-04-28 07:02:27 +02:00
|
|
|
useEvent("composer:open");
|
2024-04-27 07:47:23 +02:00
|
|
|
};
|
|
|
|
|
|
2024-04-27 06:50:30 +02:00
|
|
|
const signIn = async () => {
|
|
|
|
|
loadingAuth.value = true;
|
|
|
|
|
|
2024-06-10 05:24:55 +02:00
|
|
|
const output = await client.value.createApp("Lysand", {
|
2024-04-27 06:50:30 +02:00
|
|
|
scopes: ["read", "write", "follow", "push"],
|
|
|
|
|
redirect_uris: new URL("/", useRequestURL().origin).toString(),
|
|
|
|
|
website: useBaseUrl().value,
|
|
|
|
|
});
|
|
|
|
|
|
2024-06-08 01:09:15 +02:00
|
|
|
if (!output?.data) {
|
2024-04-27 06:50:30 +02:00
|
|
|
alert("Failed to create app");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 01:09:15 +02:00
|
|
|
appData.value = output.data;
|
2024-04-27 06:50:30 +02:00
|
|
|
|
2024-06-10 05:24:55 +02:00
|
|
|
const url = await client.value.generateAuthUrl(
|
2024-06-08 01:09:15 +02:00
|
|
|
output.data.client_id,
|
|
|
|
|
output.data.client_secret,
|
2024-04-27 06:50:30 +02:00
|
|
|
{
|
2024-06-08 01:09:15 +02:00
|
|
|
scopes: ["read", "write", "follow", "push"],
|
2024-04-27 06:50:30 +02:00
|
|
|
redirect_uri: new URL("/", useRequestURL().origin).toString(),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!url) {
|
|
|
|
|
alert("Failed to generate auth URL");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-27 07:47:23 +02:00
|
|
|
window.location.href = url;
|
2024-04-27 06:50:30 +02:00
|
|
|
};
|
|
|
|
|
|
2024-06-10 05:24:55 +02:00
|
|
|
const signOut = async (id?: string) => {
|
2024-04-27 06:50:30 +02:00
|
|
|
loadingAuth.value = true;
|
|
|
|
|
|
2024-06-10 05:24:55 +02:00
|
|
|
if (!appData.value || !identity.value) {
|
|
|
|
|
console.error("No app or identity data to sign out");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const identityToRevoke = id
|
|
|
|
|
? identities.value.find((i) => i.id === id)
|
|
|
|
|
: identity.value;
|
|
|
|
|
|
|
|
|
|
if (!identityToRevoke) {
|
|
|
|
|
console.error("No identity to revoke");
|
2024-04-27 06:50:30 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Don't do anything on error, as Lysand doesn't implement the revoke endpoint yet
|
|
|
|
|
await client.value
|
|
|
|
|
?.revokeToken(
|
|
|
|
|
appData.value.client_id,
|
2024-06-10 05:24:55 +02:00
|
|
|
identityToRevoke.tokens.access_token,
|
|
|
|
|
identityToRevoke.tokens.access_token,
|
2024-04-27 06:50:30 +02:00
|
|
|
)
|
2024-06-16 02:04:17 +02:00
|
|
|
.catch(() => {});
|
2024-04-27 06:50:30 +02:00
|
|
|
|
2024-06-10 05:24:55 +02:00
|
|
|
if (id === identity.value.id) {
|
|
|
|
|
identity.value = null;
|
|
|
|
|
await navigateTo("/");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
identities.value = identities.value.filter((i) => i.id !== id);
|
|
|
|
|
await useEvent("notification:new", {
|
|
|
|
|
type: "success",
|
|
|
|
|
title: "Signed out",
|
|
|
|
|
message: "Account signed out successfully",
|
|
|
|
|
});
|
2024-04-27 06:50:30 +02:00
|
|
|
};
|
2024-04-26 01:04:45 +02:00
|
|
|
</script>
|