Files
kandev/apps/web/hooks/use-nav-availability.ts
T

74 lines
3.7 KiB
TypeScript

"use client";
import { useAppStore } from "@/components/state-provider";
import { useAzureDevOpsAvailable } from "@/hooks/domains/azure-devops/use-azure-devops-availability";
import { useGitHubStatus } from "@/hooks/domains/github/use-github-status";
import { useGitLabAvailable } from "@/hooks/domains/gitlab/use-task-mr";
import { useJiraAvailable } from "@/hooks/domains/jira/use-jira-availability";
import { useLinearAvailable } from "@/hooks/domains/linear/use-linear-availability";
import type { AvailabilityMap } from "@/lib/navigation/types";
import type { GitHubStatus } from "@/lib/types/github";
function getStatusLabel(loading: boolean | undefined): string {
return loading ? "Checking" : "Setup";
}
export function getGitHubIntegrationStatus(status: GitHubStatus | null, loading: boolean) {
if (status?.authenticated) return { ready: true, label: "Connected" };
if (status?.token_configured) return { ready: true, label: "Configured" };
return { ready: false, label: getStatusLabel(loading) };
}
/**
* Availability map the navigation manifest filters on
* (`lib/navigation/`) — currently which integrations are
* configured. Extend here when a destination gains a gate.
*
* Extracted from `useConfiguredIntegrationLinks` so the sidebar link list and the
* mobile menu gate on one implementation instead of each calling the five domain
* hooks themselves.
*
* Availability is **not** deduplicated across consumers. GitHub and GitLab are
* store-backed, but Jira, Linear and Azure DevOps go through
* `useIntegrationAuthed`, which owns a fetch plus a 90s `setInterval` per mounted
* consumer (`hooks/domains/integrations/use-integration-availability.ts`). So each
* mounted caller of this hook may add its own polling subscription: keep the
* callers to the surfaces that render gated sections, and use
* `useStaticDestinations` everywhere else. Deduplicating this properly means
* hoisting the probes into the store, which is a change to those domain hooks
* rather than to the navigation manifest.
*
* Not to be confused with `hooks/domains/integrations/use-integration-availability`,
* which is the generic per-integration primitive these hooks are built on.
*/
export function useNavAvailability(): AvailabilityMap {
// Jira and Linear are per-workspace integrations, so their availability must
// be checked against the active workspace. Omitting the id makes the backend
// fall back to a legacy default-workspace resolver that can point at the
// wrong workspace, hiding a configured integration from the sidebar. GitHub,
// Jira, and Linear all follow the active workspace; GitLab remains install-wide.
const activeWorkspaceId = useAppStore((s) => s.workspaces.activeId);
const activeWorkspaceExists = useAppStore((s) =>
s.workspaces.items.some((item) => item.id === s.workspaces.activeId),
);
// Guard against a stale active id: if the active workspace was removed but
// activeId was not reconciled (e.g. setWorkspaces keeps a non-null id),
// scoping to the deleted id would return no config and hide the links even
// when another workspace is configured. Fall back to null so the backend's
// default-workspace resolution applies instead.
const scopedWorkspaceId = activeWorkspaceExists ? activeWorkspaceId : null;
const { status, loading } = useGitHubStatus();
const gitlabAvailable = useGitLabAvailable();
const jiraAvailable = useJiraAvailable(scopedWorkspaceId);
const linearAvailable = useLinearAvailable(scopedWorkspaceId);
const azureDevOpsAvailable = useAzureDevOpsAvailable(scopedWorkspaceId);
return {
"azure-devops": !!azureDevOpsAvailable,
github: getGitHubIntegrationStatus(status, loading).ready,
gitlab: gitlabAvailable,
jira: jiraAvailable,
linear: linearAvailable,
};
}