summaryrefslogtreecommitdiff
path: root/apps/web/lib/queries/use-timeline.ts
blob: cb1f0992cdf548c09c99393b178188a87c7d596a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
"use client"

import { useInfiniteQuery } from "@tanstack/react-query"
import { createSupabaseBrowserClient } from "@/lib/supabase/client"
import { queryKeys } from "./query-keys"
import type { TimelineEntry } from "@/lib/types/timeline"

const TIMELINE_PAGE_SIZE = 50

interface TimelineRow {
  entry_id: string
  feed_id: string
  feed_title: string
  custom_title: string | null
  entry_title: string
  entry_url: string
  author: string | null
  summary: string | null
  image_url: string | null
  published_at: string
  is_read: boolean
  is_saved: boolean
  enclosure_url: string | null
  enclosure_type: string | null
}

function mapRowToTimelineEntry(row: TimelineRow): TimelineEntry {
  return {
    entryIdentifier: row.entry_id,
    feedIdentifier: row.feed_id,
    feedTitle: row.feed_title,
    customTitle: row.custom_title,
    entryTitle: row.entry_title,
    entryUrl: row.entry_url,
    author: row.author,
    summary: row.summary,
    imageUrl: row.image_url,
    publishedAt: row.published_at,
    isRead: row.is_read,
    isSaved: row.is_saved,
    enclosureUrl: row.enclosure_url,
    enclosureType: row.enclosure_type,
  }
}

interface TimelineCursor {
  publishedAt: string
  isRead: boolean
}

export function useTimeline(
  folderIdentifier?: string | null,
  feedIdentifier?: string | null,
  unreadOnly?: boolean,
  prioritiseUnread?: boolean
) {
  const supabaseClient = createSupabaseBrowserClient()

  return useInfiniteQuery({
    queryKey: queryKeys.timeline.list(folderIdentifier, feedIdentifier, unreadOnly, prioritiseUnread),
    queryFn: async ({
      pageParam,
    }: {
      pageParam: TimelineCursor | undefined
    }) => {
      const { data, error } = await supabaseClient.rpc("get_timeline", {
        target_folder_id: folderIdentifier ?? undefined,
        target_feed_id: feedIdentifier ?? undefined,
        result_limit: TIMELINE_PAGE_SIZE,
        pagination_cursor: pageParam?.publishedAt ?? undefined,
        unread_only: unreadOnly ?? false,
        prioritise_unread: prioritiseUnread ?? false,
        cursor_is_read: pageParam?.isRead ?? undefined,
      })

      if (error) throw error

      return ((data as TimelineRow[]) ?? []).map(mapRowToTimelineEntry)
    },
    initialPageParam: undefined as TimelineCursor | undefined,
    getNextPageParam: (lastPage: TimelineEntry[]) => {
      if (lastPage.length < TIMELINE_PAGE_SIZE) return undefined
      const lastEntry = lastPage[lastPage.length - 1]
      return {
        publishedAt: lastEntry.publishedAt,
        isRead: lastEntry.isRead,
      }
    },
  })
}