diff --git a/src/hooks/useRepoCommits.ts b/src/hooks/useRepoCommits.ts index e766078..a22a3b8 100644 --- a/src/hooks/useRepoCommits.ts +++ b/src/hooks/useRepoCommits.ts @@ -1,31 +1,15 @@ -import { useEffect, useState } from 'react' +import { useCallback } from 'react' import type { Repo } from '../lib/tangled' import { getRecentCommits, type RepoCommit } from '../lib/tangled/repo' +import { useDeferredResource } from './useDeferredResource' export function useRepoCommits(repo: Repo, branch?: string) { - const [commits, setCommits] = useState(null) - const [error, setError] = useState(null) - - useEffect(() => { - let isCancelled = false - - async function loadCommits() { - setError(null) - try { - const response = await getRecentCommits(repo, { branch }) - if (!isCancelled) setCommits(response) - } catch (caught) { - if (!isCancelled) { - setError(caught instanceof Error ? caught : new Error('Unable to load repository log')) - } - } - } - - void loadCommits() - return () => { - isCancelled = true - } - }, [branch, repo]) + const loadCommits = useCallback( + (): Promise => getRecentCommits(repo, { branch }), + [branch, repo], + ) + const key = `${repo.uri}:${branch ?? 'default'}` + const { data: commits, error } = useDeferredResource(key, true, loadCommits) return { commits, error } } diff --git a/src/hooks/useRepoPage.ts b/src/hooks/useRepoPage.ts index d8b9ae5..5459901 100644 --- a/src/hooks/useRepoPage.ts +++ b/src/hooks/useRepoPage.ts @@ -1,49 +1,21 @@ import type { Handle } from '@atcute/lexicons' -import { useEffect, useState } from 'react' +import { useCallback } from 'react' import { loadRepoPage, type RepoPageData } from '../lib/repoPage' - -type RepoPageState = { - data?: RepoPageData - error?: Error - key: string -} +import { useDeferredResource } from './useDeferredResource' export function useRepoPage(handle: Handle | null, repoKey?: string) { const pageKey = handle === null || repoKey === undefined ? null : `${handle}/${repoKey}` - const [state, setState] = useState(null) - - useEffect(() => { - if (handle === null || repoKey === undefined) return - const ownerHandle = handle - const repositoryKey = repoKey - const currentPageKey = `${ownerHandle}/${repositoryKey}` - let isCancelled = false - - async function loadPage() { - try { - const data = await loadRepoPage(ownerHandle, repositoryKey) - if (isCancelled) return - - setState({ key: currentPageKey, data }) - } catch (caught) { - if (!isCancelled) { - setState({ - key: currentPageKey, - error: caught instanceof Error ? caught : new Error('Unable to load repository'), - }) - } - } + const loadPage = useCallback((): Promise => { + if (handle === null || repoKey === undefined) { + throw new Error('Repository identity is required') } - void loadPage() - return () => { - isCancelled = true - } + return loadRepoPage(handle, repoKey) }, [handle, repoKey]) + const { data, error } = useDeferredResource(pageKey, true, loadPage) - const isCurrentRoute = state?.key === pageKey return { - pageData: isCurrentRoute ? (state.data ?? null) : null, - error: isCurrentRoute ? (state.error ?? null) : null, + pageData: data, + error, } } diff --git a/src/hooks/useRepoStarCount.ts b/src/hooks/useRepoStarCount.ts index fb6f47d..6f2be33 100644 --- a/src/hooks/useRepoStarCount.ts +++ b/src/hooks/useRepoStarCount.ts @@ -1,29 +1,16 @@ import type { Did } from '@atcute/lexicons' -import { useEffect, useState } from 'react' +import { useCallback } from 'react' import { countStars } from '../lib/tangled/feed' +import { useDeferredResource } from './useDeferredResource' export function useRepoStarCount(repoDid?: Did, isEnabled = true) { - const [starCount, setStarCount] = useState(null) - const [hasFailed, setHasFailed] = useState(false) + const loadStarCount = useCallback((): Promise => { + if (repoDid === undefined) throw new Error('Repository DID is required') - useEffect(() => { - setStarCount(null) - setHasFailed(false) - if (repoDid === undefined || !isEnabled) return + return countStars(repoDid) + }, [repoDid]) + const key = isEnabled ? (repoDid ?? null) : null + const { data: starCount, error } = useDeferredResource(key, true, loadStarCount) - let isCancelled = false - countStars(repoDid) - .then((count) => { - if (!isCancelled) setStarCount(count) - }) - .catch(() => { - if (!isCancelled) setHasFailed(true) - }) - - return () => { - isCancelled = true - } - }, [isEnabled, repoDid]) - - return { starCount, hasFailed } + return { starCount, hasFailed: error !== null } } diff --git a/src/hooks/useRepoTree.ts b/src/hooks/useRepoTree.ts index 8902fcd..894ad28 100644 --- a/src/hooks/useRepoTree.ts +++ b/src/hooks/useRepoTree.ts @@ -40,7 +40,7 @@ export function useRepoTree({ initialTree, path, repo }: UseRepoTreeOptions) { setError(null) try { - const response = await getRepoTree(repo, path) + const response = await getRepoTree(repo, path, initialTree?.ref) cache.current.set(path, response) if (!isCancelled) { @@ -58,7 +58,7 @@ export function useRepoTree({ initialTree, path, repo }: UseRepoTreeOptions) { return () => { isCancelled = true } - }, [path, repo]) + }, [initialTree?.ref, path, repo]) return { error, tree } } diff --git a/src/lib/tangled/repo/getRepoTree.ts b/src/lib/tangled/repo/getRepoTree.ts index 4b4f501..e7d8eb7 100644 --- a/src/lib/tangled/repo/getRepoTree.ts +++ b/src/lib/tangled/repo/getRepoTree.ts @@ -7,11 +7,11 @@ import { rpc } from '../client' import { getDefaultBranch } from './getDefaultBranch' import type { Repo } from './types' -export async function getRepoTree(repo: Repo, path = ''): Promise { - const defaultBranch = await getDefaultBranch(repo) +export async function getRepoTree(repo: Repo, path = '', ref?: string): Promise { + const defaultBranch = ref ?? (await getDefaultBranch(repo)).name const treeResponse = await ok( rpc.get('sh.tangled.repo.tree', { - params: { repo: repo.uri, ref: defaultBranch.name, path }, + params: { repo: repo.uri, ref: defaultBranch, path }, }), )