-
Notifications
You must be signed in to change notification settings - Fork 342
feat: add extension detail page for user-settings and admin dashboard #1939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
a1931a4
add an extension detail page for managing an extension and its versio…
netomi 7dc82d6
add changelog entry, formatting
netomi c4fc6dd
remove accidentally committed code
netomi f01a0aa
remove reference to admin context
netomi c590c37
refactor loading an extension icons into a separate component
netomi a6cc923
use tanstack to load user extension data, fix import for extension ic…
netomi 6f60557
ensure that clicking on the tab always triggers a navigation event
netomi 1dda02b
make mutation operation non retriable
netomi 38e93a2
use nonretriable requests for getExtension as well
netomi 0cdf6e0
fix import
netomi d95120b
restore abortController for get request
netomi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
server/src/main/java/org/eclipse/openvsx/json/TargetPlatformActiveJson.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| /****************************************************************************** | ||
| * Copyright (c) 2026 Contributors to the Eclipse Foundation. | ||
| * | ||
| * See the NOTICE file(s) distributed with this work for additional | ||
| * information regarding copyright ownership. | ||
| * | ||
| * This program and the accompanying materials are made available under the | ||
| * terms of the Eclipse Public License 2.0 which is available at | ||
| * https://www.eclipse.org/legal/epl-2.0. | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| *****************************************************************************/ | ||
| package org.eclipse.openvsx.json; | ||
|
|
||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
|
|
||
| import static org.eclipse.openvsx.util.TargetPlatform.*; | ||
|
|
||
| /** | ||
| * | ||
| * @param targetPlatform Name of the target platform | ||
| * @param active Whether this target platform version is active | ||
| */ | ||
| @Schema( | ||
| name = "TargetPlatformActive", | ||
| description = "Target platform of an extension version and whether it is active" | ||
| ) | ||
| public record TargetPlatformActiveJson( | ||
| @Schema(description = "Name of the target platform", allowableValues = { | ||
| NAME_WIN32_X64, NAME_WIN32_IA32, NAME_WIN32_ARM64, | ||
| NAME_LINUX_X64, NAME_LINUX_ARM64, NAME_LINUX_ARMHF, | ||
| NAME_ALPINE_X64, NAME_ALPINE_ARM64, | ||
| NAME_DARWIN_X64, NAME_DARWIN_ARM64, | ||
| NAME_WEB, NAME_UNIVERSAL | ||
| }) | ||
| String targetPlatform, | ||
| @Schema(description = "Whether this extension version for this target platform is active") | ||
| boolean active | ||
| ) {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
webui/src/components/extension/extension-delete-all-versions-dialog.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| /****************************************************************************** | ||
| * Copyright (c) 2026 Contributors to the Eclipse Foundation. | ||
| * | ||
| * See the NOTICE file(s) distributed with this work for additional | ||
| * information regarding copyright ownership. | ||
| * | ||
| * This program and the accompanying materials are made available under the | ||
| * terms of the Eclipse Public License 2.0 which is available at | ||
| * https://www.eclipse.org/legal/epl-2.0. | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| *****************************************************************************/ | ||
|
|
||
| import { FunctionComponent, useContext, useState } from 'react'; | ||
| import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material'; | ||
| import { ButtonWithProgress } from '../button-with-progress'; | ||
| import { MainContext } from '../../context'; | ||
| import { Extension, VersionTargetPlatforms } from '../../extension-registry-types'; | ||
| import { VersionDeleteTarget } from './extension-version-delete-dialog'; | ||
|
|
||
| export const DeleteAllVersionsDialog: FunctionComponent<DeleteAllVersionsDialogProps> = props => { | ||
| const { handleError } = useContext(MainContext); | ||
| const [working, setWorking] = useState(false); | ||
|
|
||
| const handleRemove = async () => { | ||
| try { | ||
| setWorking(true); | ||
| const targets: VersionDeleteTarget[] = props.versions.flatMap(v => | ||
| v.targetPlatforms.map(({ targetPlatform }) => ({ version: v.version, targetPlatform })) | ||
| ); | ||
| await props.onRemove(targets); | ||
| props.onDeleted(); | ||
| props.onClose(); | ||
| } catch (err) { | ||
| handleError(err); | ||
| } finally { | ||
| setWorking(false); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <Dialog open={props.open} onClose={props.onClose} maxWidth='xs' fullWidth> | ||
| <DialogTitle>Delete all versions of {props.extension.displayName ?? props.extension.name}?</DialogTitle> | ||
| <DialogContent> | ||
| <DialogContentText> | ||
| This will permanently remove {props.versions.length} version | ||
| {props.versions.length === 1 ? '' : 's'} of this extension across all target platforms. This action | ||
| cannot be undone. | ||
| </DialogContentText> | ||
| </DialogContent> | ||
| <DialogActions> | ||
| <Button variant='contained' onClick={props.onClose}> | ||
| Cancel | ||
| </Button> | ||
| <ButtonWithProgress sx={{ ml: 1 }} color='error' working={working} onClick={handleRemove}> | ||
| Delete All Versions | ||
| </ButtonWithProgress> | ||
| </DialogActions> | ||
| </Dialog> | ||
| ); | ||
| }; | ||
|
|
||
| export interface DeleteAllVersionsDialogProps { | ||
| open: boolean; | ||
| onClose: () => void; | ||
| extension: Extension; | ||
| versions: VersionTargetPlatforms[]; | ||
| onRemove: (targets: VersionDeleteTarget[]) => Promise<unknown>; | ||
| onDeleted: () => void; | ||
| } |
101 changes: 101 additions & 0 deletions
101
webui/src/components/extension/extension-detail-view.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| /****************************************************************************** | ||
| * Copyright (c) 2026 Contributors to the Eclipse Foundation. | ||
| * | ||
| * See the NOTICE file(s) distributed with this work for additional | ||
| * information regarding copyright ownership. | ||
| * | ||
| * This program and the accompanying materials are made available under the | ||
| * terms of the Eclipse Public License 2.0 which is available at | ||
| * https://www.eclipse.org/legal/epl-2.0. | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| *****************************************************************************/ | ||
|
|
||
| import { FunctionComponent, ReactNode, useEffect, useState } from 'react'; | ||
| import { Box, Button, Divider, Stack, Typography } from '@mui/material'; | ||
| import { Link as RouteLink } from 'react-router-dom'; | ||
| import { Extension, VERSION_ALIASES, VersionTargetPlatforms } from '../../extension-registry-types'; | ||
| import { ExtensionHeader } from './extension-header'; | ||
| import { ExtensionStatusChips } from './extension-status-chips'; | ||
| import { ExtensionVersionTable } from './extension-version-table'; | ||
| import { DeleteVersionDialog, VersionDeleteTarget } from './extension-version-delete-dialog'; | ||
| import { DeleteAllVersionsDialog } from './extension-delete-all-versions-dialog'; | ||
| import { ExtensionDetailRoutes } from '../../pages/extension-detail/extension-detail-routes'; | ||
| import { createRoute } from '../../utils'; | ||
|
|
||
| export const ExtensionDetailView: FunctionComponent<ExtensionDetailViewProps> = props => { | ||
| const { extension, actions, onRemoveVersion, onVersionDeleted } = props; | ||
|
|
||
| const [page, setPage] = useState(0); | ||
| const [deleteDialogVersion, setDeleteDialogVersion] = useState<VersionTargetPlatforms | null>(null); | ||
| const [deleteAllOpen, setDeleteAllOpen] = useState(false); | ||
|
|
||
| useEffect(() => { | ||
| setPage(0); | ||
| }, [extension]); | ||
|
|
||
| const publicRoute = createRoute([ExtensionDetailRoutes.ROOT, extension.namespace, extension.name]); | ||
| const allVersions = (extension.allTargetPlatformVersions ?? []).filter(v => !VERSION_ALIASES.includes(v.version)); | ||
|
|
||
| return ( | ||
| <Box> | ||
| <ExtensionHeader extension={extension} /> | ||
| {extension.description && ( | ||
| <Typography variant='body1' mb={2}> | ||
| {extension.description} | ||
| </Typography> | ||
| )} | ||
| <ExtensionStatusChips extension={extension} /> | ||
| <Divider sx={{ my: 2 }} /> | ||
| <Stack direction='row' spacing={2} mb={3}> | ||
| <Button variant='outlined' component={RouteLink} to={publicRoute}> | ||
| View in Marketplace | ||
| </Button> | ||
| <Button | ||
| variant='outlined' | ||
| color='error' | ||
| onClick={() => setDeleteAllOpen(true)} | ||
| disabled={allVersions.length === 0}> | ||
| Delete All Versions | ||
| </Button> | ||
| {actions} | ||
| </Stack> | ||
| <Typography variant='h6' gutterBottom> | ||
| Versions | ||
| </Typography> | ||
| <ExtensionVersionTable | ||
| versions={allVersions} | ||
| page={page} | ||
| onPageChange={setPage} | ||
| onDeleteVersion={setDeleteDialogVersion} | ||
| /> | ||
| {deleteDialogVersion && ( | ||
| <DeleteVersionDialog | ||
| open={true} | ||
| onClose={() => setDeleteDialogVersion(null)} | ||
| extension={extension} | ||
| version={deleteDialogVersion} | ||
| onRemove={onRemoveVersion} | ||
| onDeleted={onVersionDeleted} | ||
| /> | ||
| )} | ||
| {deleteAllOpen && ( | ||
| <DeleteAllVersionsDialog | ||
| open={true} | ||
| onClose={() => setDeleteAllOpen(false)} | ||
| extension={extension} | ||
| versions={allVersions} | ||
| onRemove={onRemoveVersion} | ||
| onDeleted={onVersionDeleted} | ||
| /> | ||
| )} | ||
| </Box> | ||
| ); | ||
| }; | ||
|
|
||
| export interface ExtensionDetailViewProps { | ||
| extension: Extension; | ||
| actions?: ReactNode; | ||
| onRemoveVersion: (targets: VersionDeleteTarget[]) => Promise<unknown>; | ||
| onVersionDeleted: () => void; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| /****************************************************************************** | ||
| * Copyright (c) 2026 Contributors to the Eclipse Foundation. | ||
| * | ||
| * See the NOTICE file(s) distributed with this work for additional | ||
| * information regarding copyright ownership. | ||
| * | ||
| * This program and the accompanying materials are made available under the | ||
| * terms of the Eclipse Public License 2.0 which is available at | ||
| * https://www.eclipse.org/legal/epl-2.0. | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| *****************************************************************************/ | ||
|
|
||
| import { FunctionComponent } from 'react'; | ||
| import { Box, Typography } from '@mui/material'; | ||
| import { Extension } from '../../extension-registry-types'; | ||
| import { ExtensionIcon } from './extension-icon'; | ||
|
|
||
| export const ExtensionHeader: FunctionComponent<ExtensionHeaderProps> = ({ extension }) => ( | ||
| <Box display='flex' alignItems='center' gap={2} mb={2}> | ||
| <ExtensionIcon extension={extension} sx={{ width: '4rem', height: '4rem', objectFit: 'contain' }} /> | ||
| <Box> | ||
| <Typography variant='h5'>{extension.displayName ?? extension.name}</Typography> | ||
| <Typography variant='body2' color='text.secondary'> | ||
| {extension.namespace}.{extension.name} · v{extension.version} | ||
| </Typography> | ||
| </Box> | ||
| </Box> | ||
| ); | ||
|
|
||
| export interface ExtensionHeaderProps { | ||
| extension: Extension; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.