Files
remanso/src/hooks/useNoteFreshness.hook.ts
Julien Calixte aad07184fd fix(freshness): surface silent failures when pulling latest
queryFileContent threw on octokit errors (stale SHA 404, expired token,
network blip) and the rejection bubbled up unhandled through pullLatest
and onBadgeClick, leaving the badge stuck on "Outdated" with no log or
toast. Wrap the octokit call, log on failure, clear the cached SHA so
the next click re-resolves it, and show an error toast.

Also fix a dead `if (!user || !repo) { null }` that did nothing.
2026-05-06 22:02:50 +02:00

94 lines
2.6 KiB
TypeScript

import { Ref, ref } from "vue"
import { useGitHubContent } from "@/hooks/useGitHubContent.hook"
import { markdownBuilder } from "@/hooks/useMarkdown.hook"
import { prepareNoteCache } from "@/modules/note/cache/prepareNoteCache"
import { queryFileContent } from "@/modules/repo/services/repo"
import { useUserRepoStore } from "@/modules/repo/store/userRepo.store"
export type FreshnessStatus =
| "unknown"
| "checking"
| "verified"
| "outdated"
| "offline"
export const useNoteFreshness = ({
user,
repo,
sha,
path,
getEditedSha
}: {
user: string
repo: string
sha: Ref<string>
path: Ref<string | undefined>
getEditedSha: () => Promise<string | null>
}) => {
const store = useUserRepoStore()
const { fetchLatestSha } = useGitHubContent({ user, repo })
const status = ref<FreshnessStatus>("unknown")
const lastCheckedAt = ref<Date | null>(null)
const latestSha = ref<string | null>(null)
const expectedSha = async () => (await getEditedSha()) ?? sha.value
const check = async () => {
if (!path.value) return
status.value = "checking"
const remoteSha = await fetchLatestSha(path.value)
if (remoteSha === null) {
status.value = "offline"
return
}
latestSha.value = remoteSha
lastCheckedAt.value = new Date()
const local = await expectedSha()
status.value = remoteSha === local ? "verified" : "outdated"
}
const pullLatest = async (): Promise<string | null> => {
if (!path.value) return null
const usedCachedSha = latestSha.value !== null
const remoteSha = latestSha.value ?? (await fetchLatestSha(path.value))
if (!remoteSha) {
console.warn("pullLatest: could not resolve remote sha", { path: path.value })
status.value = "offline"
return null
}
const fileContent = await queryFileContent(user, repo, remoteSha)
if (!fileContent) {
console.warn("pullLatest: failed to fetch blob content", {
path: path.value,
remoteSha,
usedCachedSha
})
// Cached SHA may be stale — clear so the next click re-resolves it.
if (usedCachedSha) latestSha.value = null
status.value = "offline"
return null
}
const { saveCacheNote } = prepareNoteCache(sha.value, path.value)
await saveCacheNote(fileContent, {
editedSha: remoteSha,
path: path.value
})
store.addFile({ path: path.value, sha: remoteSha })
latestSha.value = remoteSha
lastCheckedAt.value = new Date()
status.value = "verified"
const { getRawContent } = markdownBuilder(sha.value)
return getRawContent(fileContent)
}
return {
status,
lastCheckedAt,
latestSha,
check,
pullLatest
}
}