Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions apps/web/src/components/ProviderUpdateEnvironmentRows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ function EnvironmentUpdateRow({
readonly onUpdate: () => void;
}) {
const canManageProviders = useEnvironmentScope(group.environmentId, AuthProvidersManageScope);
const canUpdateProviders = canManageProviders && group.isReady;
let trailing: ReactNode;
switch (status.kind) {
case "loading":
Expand All @@ -122,14 +123,14 @@ function EnvironmentUpdateRow({
case "failed":
case "unchanged":
trailing = (
<Button size="xs" variant="outline" disabled={!canManageProviders} onClick={onUpdate}>
<Button size="xs" variant="outline" disabled={!canUpdateProviders} onClick={onUpdate}>
Retry
</Button>
);
break;
default:
trailing = (
<Button size="xs" variant="outline" disabled={!canManageProviders} onClick={onUpdate}>
<Button size="xs" variant="outline" disabled={!canUpdateProviders} onClick={onUpdate}>
Update
</Button>
);
Expand Down Expand Up @@ -203,7 +204,7 @@ export function ProviderUpdateEnvironmentRows({
const handleUpdate = async (environmentId: EnvironmentId) => {
if (!readEnvironmentScope(environmentId, AuthProvidersManageScope)) return;
const group = groupByEnvironment.get(environmentId);
if (!group || group.candidates.length === 0) {
if (!group?.isReady || group.candidates.length === 0) {
return;
}
if (inFlightEnvironmentsRef.current.has(environmentId)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function useLocalEnvironmentUpdateGroups(): {
})
: environment.label,
isPrimary,
connectionState: isPrimary ? "ready" : normalizeConnectionState(environment.connection.phase),
connectionState: normalizeConnectionState(environment.connection.phase),
providers: providersByEnvironment.get(environment.environmentId) ?? [],
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
hasOneClickUpdateProviderCandidate,
isProviderUpdateCandidate,
isProviderSettingsUpdateCandidate,
localEnvironmentUpdateNotificationKey,
providerUpdateNotificationKey,
resolveEnvironmentUpdateRowStatus,
type LocalEnvironmentProvidersInput,
Expand Down Expand Up @@ -445,6 +446,27 @@ describe("provider update launch notification logic", () => {
?.isSettling,
).toBe(true);
});

it("keeps the notification key stable while connection readiness changes", () => {
const cachedProvider = provider({ driver: driver("codex") });
const primary = environment({
environmentId: "env-cached",
isPrimary: true,
connectionState: "connecting",
providers: [cachedProvider],
});
for (const connectionState of [
"connecting",
"ready",
"disconnected",
"error",
"ready",
] as const) {
const { groups } = buildLocalEnvironmentUpdateGroups([{ ...primary, connectionState }]);
expect(groups[0]?.isReady).toBe(connectionState === "ready");
expect(localEnvironmentUpdateNotificationKey(groups)).toBe("env-cached=codex:1.1.0");
}
});
});

describe("resolveEnvironmentUpdateRowStatus", () => {
Expand All @@ -453,6 +475,7 @@ describe("provider update launch notification logic", () => {
label: "WSL",
isPrimary: false,
isSettling: false,
isReady: true,
candidates: [updateCandidate({ driver: driver("codex"), latestVersion: "1.1.0" })],
providers: [],
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,7 @@ export interface LocalEnvironmentUpdateGroup {
readonly label: string;
readonly isPrimary: boolean;
readonly isSettling: boolean;
readonly isReady: boolean;
readonly candidates: ProviderUpdateCandidate[];
readonly providers: ReadonlyArray<ServerProvider>;
}
Expand All @@ -749,6 +750,7 @@ export function buildLocalEnvironmentUpdateGroups(
label: environment.label,
isPrimary: environment.isPrimary,
isSettling: environment.connectionState === "connecting",
isReady: environment.connectionState === "ready",
candidates: collectProviderUpdateCandidates(environment.providers).filter((candidate) =>
canOneClickUpdateProviderCandidate(candidate, environment.providers),
),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
Expand Down
19 changes: 15 additions & 4 deletions apps/web/src/components/ProviderUpdatePrimaryNotification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type ActiveProviderUpdateToast =
readonly key: string;
readonly toastId: ProviderUpdateToastId;
readonly canManageProviders: boolean;
readonly canUpdateProviders: boolean;
}
| {
readonly kind: "update";
Expand Down Expand Up @@ -109,6 +110,8 @@ export function ProviderUpdatePrimaryNotification() {
primaryEnvironment?.environmentId ?? null,
AuthProvidersManageScope,
);
const canUpdateProviders =
canManageProviders && primaryEnvironment?.connection.phase === "connected";
const updateProvider = useAtomCommand(serverEnvironment.updateProvider, {
reportFailure: false,
});
Expand Down Expand Up @@ -179,7 +182,8 @@ export function ProviderUpdatePrimaryNotification() {
const updateExistingPrompt =
activeToast?.kind === "prompt" &&
activeToast.key === notificationKey &&
activeToast.canManageProviders !== canManageProviders;
(activeToast.canManageProviders !== canManageProviders ||
activeToast.canUpdateProviders !== canUpdateProviders);
if (
!notificationKey ||
dismissedNotificationKeys.has(notificationKey) ||
Expand All @@ -204,7 +208,7 @@ export function ProviderUpdatePrimaryNotification() {
if (
updateStarted ||
oneClickProviders.length === 0 ||
!primaryEnvironment ||
primaryEnvironment?.connection.phase !== "connected" ||
!readEnvironmentScope(primaryEnvironment.environmentId, AuthProvidersManageScope)
) {
return;
Expand Down Expand Up @@ -291,7 +295,7 @@ export function ProviderUpdatePrimaryNotification() {
oneClickProviders.length > 0
? {
children: "Update",
disabled: !canManageProviders,
disabled: !canUpdateProviders,
onClick: runUpdates,
}
: {
Expand Down Expand Up @@ -323,9 +327,16 @@ export function ProviderUpdatePrimaryNotification() {
} else {
toastId = toastManager.add(toastOptions);
}
activeToastRef.current = { kind: "prompt", key: notificationKey, toastId, canManageProviders };
activeToastRef.current = {
kind: "prompt",
key: notificationKey,
toastId,
canManageProviders,
canUpdateProviders,
};
}, [
canManageProviders,
canUpdateProviders,
updateProvider,
dismissNotificationKey,
dismissedNotificationKeys,
Expand Down
Loading