diff --git a/apps/web/components/content/documentation-proposals.tsx b/apps/web/components/content/documentation-proposals.tsx index f163d8a..c84a3c5 100644 --- a/apps/web/components/content/documentation-proposals.tsx +++ b/apps/web/components/content/documentation-proposals.tsx @@ -607,7 +607,8 @@ export function DocumentationProposals({ {proposal.title} - {proposal.captured.length} files ·{" "} + {proposal.captured.length}{" "} + {proposal.captured.length === 1 ? "file" : "files"} ·{" "} {new Date(proposal.createdAt).toLocaleString()} diff --git a/apps/web/components/content/documentation-source-manage.tsx b/apps/web/components/content/documentation-source-manage.tsx index 652f190..9fdc373 100644 --- a/apps/web/components/content/documentation-source-manage.tsx +++ b/apps/web/components/content/documentation-source-manage.tsx @@ -21,7 +21,10 @@ import { SummaryRow, } from "@/components/content/documentation-connect-parts"; import type { DocumentationGitSource } from "@/lib/api"; -import { PagesSourceSection } from "@/components/content/pages-source-section"; +import { + DiagnosticList, + PagesSourceSection, +} from "@/components/content/pages-source-section"; import { ApiReferenceSourceSection } from "@/components/content/api-reference-source-section"; import { cn } from "@/lib/utils"; @@ -84,6 +87,11 @@ type Health = { body: string; }; +/** A sync is underway ("Syncing"); the dialog polls until it settles. */ +export function syncInProgress(source: DocumentationGitSource) { + return Boolean(source.binding) && health(source, source.binding!).tone === "info"; +} + function health(source: DocumentationGitSource, binding: Binding): Health { if (source.status === "access_lost") return { @@ -103,22 +111,21 @@ function health(source: DocumentationGitSource, binding: Binding): Health { title: "Sync paused", body: "New commits aren't being picked up. Your site keeps showing the last good version.", }; + const syncing: Health = { + tone: "info", + title: "Syncing", + body: `Checking the newest commit on ${binding.branch}. Your site still shows the previous version.`, + }; + // A new run goes first: retrying after a failure must show (and poll) as syncing. + if (source.sync?.status === "queued" || source.sync?.status === "running") + return syncing; if (source.sync?.status === "failed" || binding.lastDiagnostic) return { tone: "warning", title: "Latest sync didn't go live", body: "Your site keeps showing the last good version. Fix the problem in GitHub and sync again.", }; - if ( - source.sync?.status === "queued" || - source.sync?.status === "running" || - binding.observedSha !== binding.liveSha - ) - return { - tone: "info", - title: "Syncing", - body: `Checking the newest commit on ${binding.branch}. Your site still shows the previous version.`, - }; + if (binding.observedSha !== binding.liveSha) return syncing; return { tone: "success", title: "In sync", @@ -324,9 +331,13 @@ export function ManageSource({ onRefresh={onRefresh} busy={busy} /> - {(binding.lastDiagnostic || source.sync?.diagnostic) && ( + {state.tone !== "info" && (binding.lastDiagnostic || source.sync?.diagnostic) && ( - {binding.lastDiagnostic ?? source.sync?.diagnostic} + {source.sync?.diagnostics.length ? ( + + ) : ( + (binding.lastDiagnostic ?? source.sync?.diagnostic) + )} )} {binding.lastPrReconcileError && ( diff --git a/apps/web/components/content/documentation-source.tsx b/apps/web/components/content/documentation-source.tsx index 3374e8d..8faf2b4 100644 --- a/apps/web/components/content/documentation-source.tsx +++ b/apps/web/components/content/documentation-source.tsx @@ -6,6 +6,7 @@ import { Check, ExternalLink, Github, GitPullRequest, Loader2 } from "lucide-rea import { ManageShell, ManageSource, + syncInProgress, } from "@/components/content/documentation-source-manage"; import { Badge } from "@/components/ui/codelit/badge"; import { Button } from "@/components/ui/codelit/button"; @@ -555,8 +556,24 @@ export function DocumentationSource({ verificationAttempt.current = attempt; verifyMergedSetup(); }, [migration?.planId, migration?.status, migration?.pullRequest?.status, setupId]); + // While the connected dialog is open, poll: quickly during a sync, and + // slowly otherwise so a sync started by a push is noticed too. + const watching = open && !wizardMode && source?.binding != null; + const pollMs = source && syncInProgress(source) ? 5000 : 30000; + useEffect(() => { + if (!watching) return; + const timer = setInterval( + () => + void getDocumentationGitSource() + .then(setSource) + .catch(() => {}), + pollMs, + ); + return () => clearInterval(timer); + }, [watching, pollMs]); const repoLabel = migration?.repositoryName ?? repositoryName ?? "your repository"; const fileCount = migration?.total ?? 0; + const filesLabel = `${fileCount} ${fileCount === 1 ? "file" : "files"}`; const isEmptyRepo = Boolean(migration?.initializeEmptyRepositoryRequired); const draftCount = migration?.unpublishedDraftCount ?? 0; const noGitHub = !setupId; @@ -601,7 +618,7 @@ export function DocumentationSource({ title: "FrontLit checks the files and connects", note: "Usually takes a few seconds.", state: at[2], - waiting: `Checking ${fileCount} files`, + waiting: `Checking ${filesLabel}`, }, ]; }; @@ -1124,7 +1141,7 @@ export function DocumentationSource({ disabled={busy} > {isEmptyRepo - ? `I approve adding ${migration.selection.directory}/.gitkeep to ${migration.selection.branch} and opening a pull request with these ${fileCount} files.` + ? `I approve adding ${migration.selection.directory}/.gitkeep to ${migration.selection.branch} and opening a pull request with ${fileCount === 1 ? "this file" : `these ${filesLabel}`}.` : fileCount ? `I approve opening a pull request with ${fileCount === 1 ? "this file" : `these ${fileCount} files`}.` : `I approve opening a pull request that creates the empty ${migration.selection.directory}/ folder.`} @@ -1195,7 +1212,7 @@ export function DocumentationSource({ Pull request opened
@@ -1208,7 +1225,7 @@ export function DocumentationSource({ {migration.pullRequest?.pullRequestNumber ? ` · #${migration.pullRequest.pullRequestNumber}` : ""}{" "} - · {fileCount} files + · {filesLabel} {migration.pullRequest?.pullRequestUrl && ( @@ -1417,7 +1434,8 @@ export function DocumentationSource({ {preflight.directory}/ - {preflight.publishedCount} public of {preflight.fileCount} files + {preflight.publishedCount} public of {preflight.fileCount}{" "} + {preflight.fileCount === 1 ? "file" : "files"} ) : (