-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ✨ Add new navigation sidebar and instance description block on /
- Loading branch information
1 parent
b105c40
commit 9467cef
Showing
11 changed files
with
251 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<template> | ||
<aside | ||
class="fixed h-dvh z-20 md:flex hidden flex-col p-4 bg-dark-800 gap-10 max-w-20 hover:max-w-72 duration-200 group"> | ||
<NuxtLink class="rounded w-11 h-11 ring-1 ring-white/10 hover:scale-105 duration-200" href="/"> | ||
<img src="https://lysand.org/logo.png" alt="Lysand logo" /> | ||
</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> | ||
<NuxtLink v-for="timeline in timelines" :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> | ||
</div> | ||
|
||
<!-- Login buttons --> | ||
<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> | ||
<NuxtLink> | ||
<ButtonsBase disabled | ||
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="tabler:login" class="shrink-0 text-2xl" /> | ||
<span class="pr-28 line-clamp-1">Sign In</span> | ||
</ButtonsBase> | ||
</NuxtLink> | ||
</div> | ||
</aside> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
const timelines = ref([ | ||
{ | ||
href: "/public", | ||
name: "Public", | ||
icon: "tabler:world", | ||
}, | ||
{ | ||
href: "/local", | ||
name: "Local", | ||
icon: "tabler:home", | ||
}, | ||
]); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import type { Mastodon } from "megalodon"; | ||
|
||
export const useExtendedDescription = async (client: Mastodon | null) => { | ||
if (!client) { | ||
return null; | ||
} | ||
|
||
return (await client.client.get("/api/v1/instance/extended_description")) | ||
.data as { | ||
updated_at: string; | ||
content: string; | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,12 @@ | ||
import type { Mastodon } from "megalodon"; | ||
import type { Instance } from "~/types/mastodon/instance"; | ||
|
||
export const useInstance = async (client: Mastodon | null) => { | ||
if (!client) { | ||
return null; | ||
} | ||
|
||
return (await client.getInstance()).data; | ||
return (await client.getInstance()).data as Instance & { | ||
banner?: string; | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import type { Mastodon } from "megalodon"; | ||
import type { Status } from "~/types/mastodon/status"; | ||
|
||
export const useLocalTimeline = ( | ||
client: Mastodon | null, | ||
options: MaybeRef< | ||
Partial<{ | ||
only_media: boolean; | ||
max_id: string; | ||
since_id: string; | ||
min_id: string; | ||
limit: number; | ||
}> | ||
>, | ||
): { | ||
timeline: Ref<Status[]>; | ||
loadNext: () => Promise<void>; | ||
loadPrev: () => Promise<void>; | ||
} => { | ||
if (!client) { | ||
return { | ||
timeline: ref([]), | ||
loadNext: async () => {}, | ||
loadPrev: async () => {}, | ||
}; | ||
} | ||
|
||
const fetchedNotes = ref<Status[]>([]); | ||
const fetchedNoteIds = new Set<string>(); | ||
let nextMaxId: string | undefined = undefined; | ||
let prevMinId: string | undefined = undefined; | ||
|
||
const loadNext = async () => { | ||
const response = await client.getLocalTimeline({ | ||
...ref(options).value, | ||
max_id: nextMaxId, | ||
limit: useConfig().NOTES_PER_PAGE, | ||
}); | ||
|
||
const newNotes = response.data.filter( | ||
(note) => !fetchedNoteIds.has(note.id), | ||
); | ||
if (newNotes.length > 0) { | ||
fetchedNotes.value = [...fetchedNotes.value, ...newNotes]; | ||
nextMaxId = newNotes[newNotes.length - 1].id; | ||
for (const note of newNotes) { | ||
fetchedNoteIds.add(note.id); | ||
} | ||
} else { | ||
nextMaxId = undefined; | ||
} | ||
}; | ||
|
||
const loadPrev = async () => { | ||
const response = await client.getLocalTimeline({ | ||
...ref(options).value, | ||
min_id: prevMinId, | ||
limit: useConfig().NOTES_PER_PAGE, | ||
}); | ||
|
||
const newNotes = response.data.filter( | ||
(note) => !fetchedNoteIds.has(note.id), | ||
); | ||
if (newNotes.length > 0) { | ||
fetchedNotes.value = [...newNotes, ...fetchedNotes.value]; | ||
prevMinId = newNotes[0].id; | ||
for (const note of newNotes) { | ||
fetchedNoteIds.add(note.id); | ||
} | ||
} else { | ||
prevMinId = undefined; | ||
} | ||
}; | ||
|
||
watch( | ||
() => ref(options).value, | ||
async ({ max_id, min_id }) => { | ||
nextMaxId = max_id; | ||
prevMinId = min_id; | ||
await loadNext(); | ||
}, | ||
{ immediate: true }, | ||
); | ||
|
||
return { timeline: fetchedNotes, loadNext, loadPrev }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<template> | ||
<ClientOnly> | ||
<div class="max-w-2xl mx-auto md:py-20 md:px-10"> | ||
<SocialElementsNotesNote v-for="note of timeline" :key="note.id" :note="note" /> | ||
<span ref="skeleton"></span> | ||
<SocialElementsNotesNote v-for="index of 5" v-if="!hasReachedEnd" :skeleton="true" /> | ||
|
||
<div v-if="hasReachedEnd" | ||
class="text-center flex flex-row justify-center items-center py-10 text-gray-400 gap-3"> | ||
<Icon name="tabler:message-off" class="h-6 w-6" /> | ||
<span>No more posts, you've seen them all</span> | ||
</div> | ||
</div> | ||
</ClientOnly> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
const client = await useMegalodon(); | ||
const isLoading = ref(true); | ||
const timelineParameters = ref({}); | ||
const hasReachedEnd = ref(false); | ||
const { timeline, loadNext, loadPrev } = useLocalTimeline( | ||
client, | ||
timelineParameters, | ||
); | ||
const skeleton = ref<HTMLSpanElement | null>(null); | ||
onMounted(() => { | ||
useIntersectionObserver(skeleton, async (entries) => { | ||
if ( | ||
entries[0].isIntersecting && | ||
!hasReachedEnd.value && | ||
!isLoading.value | ||
) { | ||
isLoading.value = true; | ||
await loadNext(); | ||
} | ||
}); | ||
}); | ||
watch(timeline, (newTimeline, oldTimeline) => { | ||
isLoading.value = false; | ||
// If less than NOTES_PER_PAGE statuses are returned, we have reached the end | ||
if (newTimeline.length - oldTimeline.length < useConfig().NOTES_PER_PAGE) { | ||
hasReachedEnd.value = true; | ||
} | ||
}); | ||
</script> |