mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 08:28:20 +01:00
feat: ✨ Add bottom sidebar on mobile
This commit is contained in:
parent
951a806477
commit
d9173b4ce2
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<HeadlessMenu v-slot="{ close }">
|
||||
<HeadlessMenu v-slot="{ close }" v-bind="$props">
|
||||
<slot name="button"></slot>
|
||||
|
||||
<HeadlessMenuItems @click="close" class="fixed z-20 inset-0 z-5 bg-black/50">
|
||||
|
|
@ -25,5 +25,5 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
const { width } = useWindowSize();
|
||||
const isSmallScreen = computed(() => width.value < 640);
|
||||
const isSmallScreen = computed(() => width.value < 768);
|
||||
</script>
|
||||
|
|
@ -8,13 +8,15 @@
|
|||
<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>
|
||||
<NuxtLink v-for="timeline in timelines" :key="timeline.href" :to="timeline.href">
|
||||
<ButtonsBase v-if="!timeline.requiresAuth || (timeline.requiresAuth && tokenData)"
|
||||
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">
|
||||
<Icon :name="timeline.icon" class="shrink-0 text-2xl" />
|
||||
<span class="pr-28 line-clamp-1">{{ timeline.name }}</span>
|
||||
</ButtonsBase>
|
||||
</NuxtLink>
|
||||
<ClientOnly>
|
||||
<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">
|
||||
<Icon :name="timeline.icon" class="shrink-0 text-2xl" />
|
||||
<span class="pr-28 line-clamp-1">{{ timeline.name }}</span>
|
||||
</ButtonsBase>
|
||||
</NuxtLink>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-3 mt-auto">
|
||||
|
|
@ -50,6 +52,67 @@
|
|||
</ClientOnly>
|
||||
</div>
|
||||
</aside>
|
||||
<!-- Mobile bottom navbar -->
|
||||
<nav
|
||||
class="fixed bottom-0 left-0 right-0 z-20 md:hidden flex justify-around p-2 *:shadow-xl bg-dark-900 ring-1 ring-white/10 text-gray-200">
|
||||
<DropdownsAdaptiveDropdown>
|
||||
<template #button>
|
||||
<HeadlessMenuButton class="flex flex-col items-center justify-center p-2 rounded">
|
||||
<Icon name="tabler:home" class="text-2xl" />
|
||||
<span class="text-xs">Timelines</span>
|
||||
</HeadlessMenuButton>
|
||||
</template>
|
||||
|
||||
<template #items>
|
||||
<HeadlessMenuItem v-for="timeline in visibleTimelines" :key="timeline.href" :href="timeline.href">
|
||||
<NuxtLink>
|
||||
<ButtonsDropdownElement :icon="timeline.icon" class="w-full">
|
||||
{{ timeline.name }}
|
||||
</ButtonsDropdownElement>
|
||||
</NuxtLink>
|
||||
</HeadlessMenuItem>
|
||||
</template>
|
||||
</DropdownsAdaptiveDropdown>
|
||||
<NuxtLink href="/notifications" class="flex flex-col items-center justify-center p-2 rounded">
|
||||
<Icon name="tabler:bell" class="text-2xl" />
|
||||
<span class="text-xs">Notifications</span>
|
||||
</NuxtLink>
|
||||
<DropdownsAdaptiveDropdown>
|
||||
<template #button>
|
||||
<HeadlessMenuButton class="flex flex-col items-center justify-center p-2 rounded">
|
||||
<Icon name="tabler:user" class="text-2xl" />
|
||||
<span class="text-xs">Account</span>
|
||||
</HeadlessMenuButton>
|
||||
</template>
|
||||
|
||||
<template #items>
|
||||
<HeadlessMenuItem v-if="tokenData">
|
||||
<ButtonsDropdownElement icon="tabler:logout" class="w-full"
|
||||
@click="signOut().finally(() => loadingAuth = false)" :loading="loadingAuth">
|
||||
Sign Out
|
||||
</ButtonsDropdownElement>
|
||||
</HeadlessMenuItem>
|
||||
<HeadlessMenuItem v-if="!tokenData">
|
||||
<ButtonsDropdownElement icon="tabler:login" class="w-full"
|
||||
@click="signIn().finally(() => loadingAuth = false)" :loading="loadingAuth">
|
||||
Sign In
|
||||
</ButtonsDropdownElement>
|
||||
</HeadlessMenuItem>
|
||||
<HeadlessMenuItem v-if="!tokenData">
|
||||
<NuxtLink href="/register">
|
||||
<ButtonsDropdownElement icon="tabler:certificate" class="w-full">
|
||||
Register
|
||||
</ButtonsDropdownElement>
|
||||
</NuxtLink>
|
||||
</HeadlessMenuItem>
|
||||
</template>
|
||||
</DropdownsAdaptiveDropdown>
|
||||
<button @click="compose" v-if="tokenData"
|
||||
class="flex flex-col items-center justify-center p-2 rounded bg-gradient-to-tr from-pink-300/70 via-purple-300/70 to-indigo-400/70">
|
||||
<Icon name="tabler:writing" class="text-2xl" />
|
||||
<span class="text-xs">Compose</span>
|
||||
</button>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
@ -72,6 +135,12 @@ const timelines = ref([
|
|||
},
|
||||
]);
|
||||
|
||||
const visibleTimelines = computed(() =>
|
||||
timelines.value.filter(
|
||||
(timeline) => !timeline.requiresAuth || tokenData.value,
|
||||
),
|
||||
);
|
||||
|
||||
const loadingAuth = ref(false);
|
||||
|
||||
const appData = useAppData();
|
||||
|
|
@ -131,7 +200,7 @@ const signOut = async () => {
|
|||
tokenData.value.access_token,
|
||||
tokenData.value.access_token,
|
||||
)
|
||||
.catch(() => { });
|
||||
.catch(() => {});
|
||||
|
||||
tokenData.value = null;
|
||||
me.value = null;
|
||||
|
|
|
|||
23
components/timelines/TimelineScroller.vue
Normal file
23
components/timelines/TimelineScroller.vue
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<slot />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const root = useParentElement();
|
||||
// Store and keep y to restore it on page change
|
||||
const route = useRoute();
|
||||
const yStored = useLocalStorage(`lysand:scroll-${route.fullPath}`, 0);
|
||||
const { y } = useScroll(root);
|
||||
|
||||
useEventListener("popstate", async (event) => {
|
||||
if (yStored.value !== null) {
|
||||
// Wait for the Vue component to load
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
y.value = yStored.value;
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeRouteLeave(() => {
|
||||
yStored.value = y.value;
|
||||
});
|
||||
</script>
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
<div class="from-dark-600 to-dark-900 bg-gradient-to-tl min-h-dvh">
|
||||
<SidebarsNavigation />
|
||||
<div class="relative md:pl-20 min-h-dvh flex flex-row justify-center lg:justify-between">
|
||||
<aside
|
||||
class="max-w-md max-h-dvh overflow-y-auto w-full bg-dark-900 ring-1 ring-white/10 hidden lg:flex flex-col gap-10">
|
||||
<aside v-if="width > 1024"
|
||||
class="max-w-md max-h-dvh overflow-y-auto w-full bg-dark-900 ring-1 ring-white/10 hidden lg:flex flex-col">
|
||||
<ClientOnly>
|
||||
<div class="grow p-10" v-if="!tokenData">
|
||||
<div class="grow p-10 mb-10" v-if="!tokenData">
|
||||
<button type="button"
|
||||
class="relative block h-full w-full rounded-lg border-2 border-dashed border-dark-300 p-12 text-center">
|
||||
<Icon name="tabler:notification" class="mx-auto h-12 w-12 text-gray-400" />
|
||||
|
|
@ -16,37 +16,38 @@
|
|||
sign in</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grow" v-else>
|
||||
<TimelinesTimelineScroller v-else>
|
||||
<TimelinesNotifications />
|
||||
</TimelinesTimelineScroller>
|
||||
<div class="mt-auto prose prose-invert prose-sm flex flex-col gap-4 px-10 pb-10" v-if="!tokenData">
|
||||
<div class="text-center">
|
||||
<strong
|
||||
class="bg-gradient-to-tr from-pink-300 via-purple-300 to-indigo-400 text-transparent bg-clip-text">Lysand
|
||||
{{ instance?.lysand_version ?? instance?.version }}</strong> • <a
|
||||
href="https://github.com/lysand-org/lysand" target="_blank">Source Code</a>• <a
|
||||
href="https://github.com/lysand-org/lysand/issues" target="_blank">Report an Issue</a>
|
||||
</div>
|
||||
|
||||
|
||||
<NuxtLink href="https://github.com/lysand-org/lysand" target="_blank">
|
||||
<ButtonsSecondary class="w-full">
|
||||
Create your own instance
|
||||
</ButtonsSecondary>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink href="/about/apps">
|
||||
<ButtonsSecondary class="w-full">
|
||||
Mobile Apps
|
||||
</ButtonsSecondary>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</ClientOnly>
|
||||
<div class="mt-auto prose prose-invert prose-sm flex flex-col gap-4 px-10 pb-10">
|
||||
<div class="text-center">
|
||||
<strong
|
||||
class="bg-gradient-to-tr from-pink-300 via-purple-300 to-indigo-400 text-transparent bg-clip-text">Lysand
|
||||
{{ instance?.lysand_version ?? instance?.version }}</strong> • <a
|
||||
href="https://github.com/lysand-org/lysand" target="_blank">Source Code</a>• <a
|
||||
href="https://github.com/lysand-org/lysand/issues" target="_blank">Report an Issue</a>
|
||||
</div>
|
||||
|
||||
|
||||
<NuxtLink href="https://github.com/lysand-org/lysand" target="_blank">
|
||||
<ButtonsSecondary class="w-full">
|
||||
Create your own instance
|
||||
</ButtonsSecondary>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink href="/about/apps">
|
||||
<ButtonsSecondary class="w-full">
|
||||
Mobile Apps
|
||||
</ButtonsSecondary>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="w-full max-h-dvh overflow-y-auto">
|
||||
<div class="w-full max-h-dvh">
|
||||
<slot />
|
||||
</div>
|
||||
<aside class="max-w-md max-h-dvh overflow-y-auto w-full bg-dark-900 ring-1 ring-white/10 lg:block hidden">
|
||||
<aside v-if="width > 1024"
|
||||
class="max-w-md max-h-dvh overflow-y-auto w-full bg-dark-900 ring-1 ring-white/10 lg:block hidden">
|
||||
<slot name="right">
|
||||
<SocialElementsInstancePresentation />
|
||||
</slot>
|
||||
|
|
@ -63,6 +64,7 @@ const tokenData = useTokenData();
|
|||
const client = useMegalodon(tokenData);
|
||||
const instance = useInstance(client);
|
||||
const description = useExtendedDescription(client);
|
||||
const { width } = useWindowSize();
|
||||
|
||||
useServerSeoMeta({
|
||||
title: instance.value?.title,
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ export default defineNuxtConfig({
|
|||
],
|
||||
htmlAttrs: { lang: "en-us" },
|
||||
},
|
||||
keepalive: true,
|
||||
},
|
||||
|
||||
/* shiki: {
|
||||
defaultTheme: "rose-pine",
|
||||
bundledLangs: [
|
||||
|
|
@ -42,7 +42,6 @@ export default defineNuxtConfig({
|
|||
"yaml",
|
||||
],
|
||||
}, */
|
||||
|
||||
nitro: {
|
||||
preset: "bun",
|
||||
minify: true,
|
||||
|
|
@ -54,28 +53,12 @@ export default defineNuxtConfig({
|
|||
gzip: false,
|
||||
},
|
||||
},
|
||||
|
||||
schemaOrg: {
|
||||
enabled: false,
|
||||
},
|
||||
|
||||
ogImage: {
|
||||
enabled: false,
|
||||
},
|
||||
|
||||
vite: {
|
||||
define: {
|
||||
__VERSION__: JSON.stringify("0.4"),
|
||||
},
|
||||
server: {
|
||||
hmr: {
|
||||
clientPort: 3000,
|
||||
host: "localhost",
|
||||
protocol: "ws",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
veeValidate: {
|
||||
autoImports: true,
|
||||
componentNames: {
|
||||
|
|
@ -85,7 +68,6 @@ export default defineNuxtConfig({
|
|||
ErrorMessage: "VeeErrorMessage",
|
||||
},
|
||||
},
|
||||
|
||||
runtimeConfig: {
|
||||
public: {
|
||||
language: "en-US",
|
||||
|
|
@ -95,11 +77,9 @@ export default defineNuxtConfig({
|
|||
apiHost: "https://social.lysand.org",
|
||||
},
|
||||
},
|
||||
|
||||
site: {
|
||||
url: "https://social.lysand.org",
|
||||
},
|
||||
|
||||
devtools: {
|
||||
enabled: true,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
<template>
|
||||
<NuxtLayout name="app">
|
||||
<SocialElementsUsersAccount v-if="isMobile" :account="account ?? undefined" />
|
||||
<TimelinesAccount :id="accountId ?? undefined" :key="accountId ?? undefined" />
|
||||
<div class="max-h-dvh overflow-y-scroll">
|
||||
|
||||
<TimelinesTimelineScroller>
|
||||
<TimelinesAccount :id="accountId ?? undefined" :key="accountId ?? undefined" />
|
||||
</TimelinesTimelineScroller>
|
||||
</div>
|
||||
<template #right>
|
||||
<SocialElementsUsersAccount v-if="!isMobile" :account="account ?? undefined" />
|
||||
<div v-else>
|
||||
|
|
@ -17,6 +20,7 @@ import type { Account } from "~/types/mastodon/account";
|
|||
|
||||
definePageMeta({
|
||||
layout: false,
|
||||
keepalive: true,
|
||||
});
|
||||
|
||||
const { width } = useWindowSize();
|
||||
|
|
@ -32,7 +36,7 @@ const account = computed<Account | null>(
|
|||
);
|
||||
const accountId = computed(() => account.value?.id ?? null);
|
||||
|
||||
useServerSeoMeta({
|
||||
useSeoMeta({
|
||||
title: account.value?.display_name,
|
||||
description: account.value?.note,
|
||||
ogImage: account.value?.avatar,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
<template>
|
||||
<TimelinesHome />
|
||||
<div class="max-h-dvh overflow-y-auto">
|
||||
<TimelinesTimelineScroller>
|
||||
<TimelinesHome />
|
||||
</TimelinesTimelineScroller>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
<template>
|
||||
<TimelinesPublic />
|
||||
<div class="max-h-dvh overflow-y-scroll">
|
||||
<TimelinesTimelineScroller>
|
||||
<TimelinesPublic />
|
||||
</TimelinesTimelineScroller>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
<template>
|
||||
<TimelinesLocal />
|
||||
<div class="max-h-dvh overflow-y-auto">
|
||||
<TimelinesTimelineScroller>
|
||||
<TimelinesLocal />
|
||||
</TimelinesTimelineScroller>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
|||
27
pages/notifications.vue
Normal file
27
pages/notifications.vue
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<template>
|
||||
<ClientOnly>
|
||||
<div class="max-h-dvh overflow-y-auto">
|
||||
<div class="shrink-0 p-10 h-dvh" v-if="!tokenData">
|
||||
<button type="button"
|
||||
class="relative block h-full w-full rounded-lg border-2 border-dashed border-dark-300 p-12 text-center">
|
||||
<Icon name="tabler:notification" class="mx-auto h-12 w-12 text-gray-400" />
|
||||
<span class="mt-3 block text-sm font-semibold text-gray-200 max-w-56 mx-auto">Notifications
|
||||
will
|
||||
appear here
|
||||
when you
|
||||
sign in</span>
|
||||
</button>
|
||||
</div>
|
||||
<TimelinesTimelineScroller v-else>
|
||||
<TimelinesNotifications />
|
||||
</TimelinesTimelineScroller>
|
||||
</div>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
definePageMeta({
|
||||
layout: "app",
|
||||
});
|
||||
const tokenData = useTokenData();
|
||||
</script>
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
<template>
|
||||
<TimelinesPublic />
|
||||
<div class="max-h-dvh overflow-y-auto">
|
||||
<TimelinesTimelineScroller>
|
||||
<TimelinesPublic />
|
||||
</TimelinesTimelineScroller>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
|
|||
Loading…
Reference in a new issue