Skip to content

Commit f2be33b

Browse files
committed
fix: refresh extension versions on delete conflict
1 parent 37a6bc6 commit f2be33b

6 files changed

Lines changed: 43 additions & 21 deletions

File tree

webui/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ This change log covers only the frontend library (webui) of Open VSX.
1313
- Migrate admin dashboard to use `@tanstack/react-query` ([#1917](https://github.com/eclipse-openvsx/openvsx/pull/1917)
1414
- Replace formatting from `stylistic` with `prettier` ([#1916](https://github.com/eclipse-openvsx/openvsx/pull/1916))
1515

16+
### Fixed
17+
18+
- Refresh the extension version list in the delete views when a delete fails with a conflict
19+
1620
## [v1.0.2] (23/06/2026)
1721

1822
### Changed

webui/src/context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { ErrorResponse } from './server-request';
1717
export interface MainContext {
1818
service: ExtensionRegistryService;
1919
pageSettings: PageSettings;
20-
handleError: (err: Error | Partial<ErrorResponse>) => void;
20+
handleError: (err: Error | Partial<ErrorResponse>, options?: { onClose?: () => void }) => void;
2121
user?: UserData;
2222
updateUser: () => void;
2323
loginProviders?: Record<string, string>;

webui/src/main.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ export const Main: FunctionComponent<MainProps> = props => {
3737
const [error, setError] = useState<{ message: string; code?: number | string }>();
3838
const [isErrorDialogOpen, setIsErrorDialogOpen] = useState<boolean>(false);
3939
const abortController = useRef<AbortController>(new AbortController());
40+
// Optional callback to run when the error dialog is dismissed (e.g. re-fetch stale data).
41+
const errorDialogOnClose = useRef<(() => void) | undefined>(undefined);
4042

4143
useEffect(() => {
4244
getLoginProviders();
@@ -83,20 +85,24 @@ export const Main: FunctionComponent<MainProps> = props => {
8385
}
8486
};
8587

86-
const onError = (err: Error | Partial<ErrorResponse> | ReportedError) => {
88+
const onError = (err: Error | Partial<ErrorResponse> | ReportedError, options?: { onClose?: () => void }) => {
8789
if (err instanceof DOMException && err.message.trim() === 'The operation was aborted.') {
8890
// ignore error caused by AbortController.abort()
8991
return;
9092
}
9193

9294
const message = handleError(err);
9395
const code = (err as ReportedError).code;
96+
errorDialogOnClose.current = options?.onClose;
9497
setError({ message, code });
9598
setIsErrorDialogOpen(true);
9699
};
97100

98101
const onErrorDialogClose = () => {
99102
setIsErrorDialogOpen(false);
103+
const onClose = errorDialogOnClose.current;
104+
errorDialogOnClose.current = undefined;
105+
onClose?.();
100106
};
101107

102108
const renderPageContent = (): ReactNode => {

webui/src/pages/admin-dashboard/extension-admin.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,20 @@ export const ExtensionAdmin: FunctionComponent = () => {
8282
return;
8383
}
8484

85-
await deleteExtension({
86-
namespace: extension.namespace,
87-
extension: extension.name,
88-
targetPlatformVersions: targetPlatformVersions?.map(({ version, targetPlatform }) => ({
89-
version,
90-
targetPlatform
91-
}))
92-
});
93-
await refetch();
85+
try {
86+
await deleteExtension({
87+
namespace: extension.namespace,
88+
extension: extension.name,
89+
targetPlatformVersions: targetPlatformVersions?.map(({ version, targetPlatform }) => ({
90+
version,
91+
targetPlatform
92+
}))
93+
});
94+
await refetch();
95+
} catch (err) {
96+
const isConflict = (err as { status?: number }).status === 409;
97+
handleError(err, isConflict ? { onClose: () => refetch() } : undefined);
98+
}
9499
};
95100

96101
return (

webui/src/pages/admin-dashboard/extension-remove-dialog.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,13 @@ export const ExtensionRemoveDialog: FunctionComponent<ExtensionRemoveDialogProps
3737
try {
3838
setWorking(true);
3939
await props.onRemove(props.targetPlatformVersions);
40-
setDialogOpen(false);
4140
} catch (err) {
41+
// onRemove is expected to handle its own errors; this is a defensive fallback so an
42+
// unexpected rejection still surfaces to the user rather than being swallowed.
4243
handleError(err);
4344
} finally {
4445
setWorking(false);
46+
setDialogOpen(false);
4547
}
4648
};
4749

webui/src/pages/user/user-settings-delete-extension.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,20 @@ export const UserSettingsDeleteExtension: FunctionComponent<UserSettingsDeleteEx
6363
return;
6464
}
6565

66-
await service.deleteExtensions(abortController.current, {
67-
namespace: extension.namespace,
68-
extension: extension.name,
69-
targetPlatformVersions: targetPlatformVersions?.map(({ version, targetPlatform }) => ({
70-
version,
71-
targetPlatform
72-
}))
73-
});
74-
await findExtension();
66+
try {
67+
await service.deleteExtensions(abortController.current, {
68+
namespace: extension.namespace,
69+
extension: extension.name,
70+
targetPlatformVersions: targetPlatformVersions?.map(({ version, targetPlatform }) => ({
71+
version,
72+
targetPlatform
73+
}))
74+
});
75+
await findExtension();
76+
} catch (err) {
77+
const isConflict = (err as { status?: number }).status === 409;
78+
handleError(err, isConflict ? { onClose: () => findExtension() } : undefined);
79+
}
7580
};
7681

7782
return (

0 commit comments

Comments
 (0)