From cb15eac85464335241d74b7857555bdc54fa2bec Mon Sep 17 00:00:00 2001 From: Julien Calixte Date: Tue, 10 Mar 2026 15:53:25 +0100 Subject: [PATCH] feat: use POST /notes/feed for following tab with server-side DID filtering --- src/hooks/useFollowingNoteList.hook.ts | 55 ++++++++++++++++++++++++++ src/hooks/usePublicNoteList.hook.ts | 9 +---- src/views/PublicNoteListView.vue | 3 +- 3 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 src/hooks/useFollowingNoteList.hook.ts diff --git a/src/hooks/useFollowingNoteList.hook.ts b/src/hooks/useFollowingNoteList.hook.ts new file mode 100644 index 0000000..ed54f66 --- /dev/null +++ b/src/hooks/useFollowingNoteList.hook.ts @@ -0,0 +1,55 @@ +import { Author, getAuthors } from "@/modules/atproto/getAuthor" +import { PublicNoteListItem } from "@/modules/note/models/Note" +import { computedAsync } from "@vueuse/core" +import { computed, ref, Ref, watch } from "vue" + +export function useFollowingNoteList(dids: Ref>) { + const isLoading = ref(false) + const notes = ref([]) + const cursor = ref(null) + const canLoadMore = computed(() => dids.value.size > 0 && cursor.value !== undefined) + + const onLoadMore = async () => { + if (isLoading.value) return + if (dids.value.size === 0) return + + isLoading.value = true + + const body: { dids: string[]; limit: number; cursor?: string } = { + dids: [...dids.value], + limit: 20, + } + + if (cursor.value) { + body.cursor = cursor.value + } + + const response = await fetch("https://api.litenote.li212.fr/notes/feed", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }) + + const data: { notes: PublicNoteListItem[]; cursor?: string } = await response.json() + + notes.value.push(...data.notes) + cursor.value = data.cursor + isLoading.value = false + } + + watch(dids, (newDids) => { + if (newDids.size > 0 && notes.value.length === 0) { + onLoadMore() + } + }) + + const authors = computedAsync>(async () => { + if (notes.value.length === 0) return new Map() + return getAuthors(new Set(notes.value.map((n) => n.did))) + }, new Map()) + + const getAuthor = (did: string) => + authors.value.has(did) ? authors.value.get(did)!.handle : "" + + return { notes, isLoading, canLoadMore, onLoadMore, getAuthor } +} diff --git a/src/hooks/usePublicNoteList.hook.ts b/src/hooks/usePublicNoteList.hook.ts index 7389bc3..09be6d9 100644 --- a/src/hooks/usePublicNoteList.hook.ts +++ b/src/hooks/usePublicNoteList.hook.ts @@ -5,7 +5,6 @@ import { computed, ref, Ref } from "vue" interface UsePublicNoteListOptions { did?: Ref - followsFilter?: Ref> } export function usePublicNoteList(options?: UsePublicNoteListOptions) { @@ -45,14 +44,8 @@ export function usePublicNoteList(options?: UsePublicNoteListOptions) { const getAuthor = (did: string) => authors.value.has(did) ? authors.value.get(did)!.handle : "" - const filteredNotes = computed(() => { - const filter = options?.followsFilter?.value - if (!filter || filter.size === 0) return notes.value - return notes.value.filter((n) => filter.has(n.did)) - }) - return { - notes: filteredNotes, + notes, isLoading, canLoadMore, onLoadMore, diff --git a/src/views/PublicNoteListView.vue b/src/views/PublicNoteListView.vue index 351d86c..79e30e3 100644 --- a/src/views/PublicNoteListView.vue +++ b/src/views/PublicNoteListView.vue @@ -4,6 +4,7 @@ import PublicNoteList from "@/components/PublicNoteList.vue" import SignInAtproto from "@/components/SignInAtproto.vue" import { useATProtoLogin } from "@/hooks/useATProtoLogin.hook" import { useFollows } from "@/hooks/useFollows.hook" +import { useFollowingNoteList } from "@/hooks/useFollowingNoteList.hook" import { usePublicNoteList } from "@/hooks/usePublicNoteList.hook" import { computed } from "vue" import { useRoute, useRouter } from "vue-router" @@ -23,7 +24,7 @@ const tab = computed<"all" | "following">({ }) const all = usePublicNoteList() -const following = usePublicNoteList({ followsFilter: follows }) +const following = useFollowingNoteList(follows)