| 1 | /** |
| 2 | * `useHomeLive`: Home refreshing itself as the workspace moves |
| 3 | * (lib/home-live.ts says what it shows), and `useArrived`: which rows of |
| 4 | * a list on it are new, so they come in rather than appear. |
| 5 | * |
| 6 | * Besides its projects' changes, Home refreshes when the inbox moves or a |
| 7 | * notification for this workspace arrives: Where you're needed reads them. |
| 8 | */ |
| 9 | import { useEffect, useRef } from "react"; |
| 10 | |
| 11 | import { HOME_GAP_MS, type HomeRepo, type Seen, arrivedOf, homeShows } from "./home-live"; |
| 12 | import { useNotifyState } from "./notify-client"; |
| 13 | import { useLive } from "./use-repo-live"; |
| 14 | |
| 15 | export function useHomeLive(slug: string, repos: readonly HomeRepo[]): boolean { |
| 16 | const { live, nudge } = useLive({ repos, signedIn: true, match: (change) => homeShows(change, repos), gap: HOME_GAP_MS }); |
| 17 | |
| 18 | const { inbox, recent } = useNotifyState(); |
| 19 | const newest = recent.find((n) => n.workspace.toLowerCase() === slug)?.id ?? null; |
| 20 | const before = useRef<{ inbox: number | null; newest: string | null } | null>(null); |
| 21 | useEffect(() => { |
| 22 | const was = before.current; |
| 23 | before.current = { inbox, newest }; |
| 24 | // The first the feed says is where the page started, not a change. |
| 25 | if (was && ((was.inbox != null && inbox !== was.inbox) || newest !== was.newest)) nudge(); |
| 26 | }, [inbox, newest, nudge]); |
| 27 | |
| 28 | return live; |
| 29 | } |
| 30 | |
| 31 | /** |
| 32 | * The keys of a list's rows that were not on it when it last drew: none |
| 33 | * on the first draw, nor right after `scope` (the span) changes. The same |
| 34 | * keys again give the same answer, so a row keeps its entrance for as |
| 35 | * long as it plays. |
| 36 | */ |
| 37 | export function useArrived(keys: readonly string[], scope = ""): ReadonlySet<string> { |
| 38 | const last = useRef<{ seen: Seen | null; id: string; arrived: ReadonlySet<string> }>({ seen: null, id: "", arrived: new Set() }); |
| 39 | const id = `${scope}\n${keys.join("\n")}`; |
| 40 | if (last.current.seen == null || id !== last.current.id) { |
| 41 | const next = arrivedOf(last.current.seen, scope, keys); |
| 42 | last.current = { seen: next.seen, id, arrived: next.arrived }; |
| 43 | } |
| 44 | return last.current.arrived; |
| 45 | } |