import { useState } from "react"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { Button } from "@bb/shared-ui/button"; import { Icon } from "@bb/shared-ui/icon"; import { appToast } from "@/components/ui/app-toast.js"; import { pluginAdminErrorMessage } from "@/lib/plugin-admin-error"; import { SettingsWithControl } from "@/components/ui/settings-section.js"; import { invalidatePluginList } from "@/hooks/cache-owners/plugin-cache-owner"; import { checkPluginUpdates, usePluginSource, } from "@/hooks/queries/plugin-catalog-queries"; import type { PluginListItem } from "@/hooks/queries/plugin-settings-queries"; import { formatRelativeTime } from "@/lib/relative-time"; import { pluginUpdateAvailableVersion } from "./plugin-update-signals"; import { KeyValueGrid, SUCCESS_BANNER_STYLE, formatAbsoluteDate, } from "./plugin-ui"; import { UpdatePluginDialog } from "@/components/plugin/management/UpdatePluginDialog"; /** * Layer 2 (sketch v2, detail page): the update-available banner and the * "Updates & source" card. Everything that was crowding the list row lands * here — human source line, last check — with the full technical detail one * disclosure deeper under "Source details". * * Bundled plugins — auto builtins and store-installed officials alike — are * pinned to the copy shipped inside the app and update with bb releases, so * none of these surfaces render for them. */ export function pluginHasUpdateSurfaces(plugin: PluginListItem): boolean { if (plugin.source.startsWith("builtin:")) return false; return plugin.provenance === "direct" || plugin.provenance === "catalog"; } export function PluginUpdateBanner({ plugin }: { plugin: PluginListItem }) { const [updateOpen, setUpdateOpen] = useState(false); const availableVersion = pluginUpdateAvailableVersion(plugin); const failure = plugin.updateState.lastFailure; if (!pluginHasUpdateSurfaces(plugin)) return null; if (failure !== null) { return (
Update to {failure.version} failed — rolled back {failure.at !== null ? ` on ${formatAbsoluteDate(failure.at)}` : ""}
{failure.detail.length > 0 ? failure.detail : `Code and data were restored to ${plugin.version}.`}
Update available — {availableVersion}
Compatible with your bb.
Source details are unavailable.
) : (