Skip to content
Merged
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
1 change: 1 addition & 0 deletions webui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This change log covers only the frontend library (webui) of Open VSX.
### Fixed

- Check `Retry-After` http header when receiving `429` responses from the server ([#1637](https://github.com/eclipse/openvsx/pull/1637))
- Menu items link clicks now capture the whole menu item area ([#1598](https://github.com/eclipse/openvsx/pull/1598))

### Dependencies

Expand Down
166 changes: 70 additions & 96 deletions webui/src/default/menu-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/

import { FunctionComponent, PropsWithChildren, useContext } from 'react';
import { FunctionComponent, PropsWithChildren, useContext, useRef } from 'react';
import { Typography, MenuItem, Link, Button, IconButton, Accordion, AccordionSummary, Avatar, AccordionDetails } from '@mui/material';
import { useLocation } from 'react-router-dom';
import { Link as RouteLink } from 'react-router-dom';
import { useLocation, Link as RouteLink } from 'react-router-dom';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import GitHubIcon from '@mui/icons-material/GitHub';
import MenuBookIcon from '@mui/icons-material/MenuBook';
Expand All @@ -31,21 +30,13 @@ import { LogoutForm } from '../pages/user/logout';
import { LoginComponent } from './login';

//-------------------- Mobile View --------------------//

export const MobileMenuItem = styled(MenuItem)({
cursor: 'auto',
'&>a': {
textDecoration: 'none'
}
});

export const itemIcon = {
mr: 1,
width: '16px',
height: '16px',
};

export const MobileMenuItemText: FunctionComponent<PropsWithChildren> = ({ children }) => {
export const MenuItemText: FunctionComponent<PropsWithChildren> = ({ children }) => {
return (
<Typography variant='body2' color='text.primary' sx={{ display: 'flex', alignItems: 'center', textTransform: 'none' }}>
{children}
Expand All @@ -56,6 +47,7 @@ export const MobileMenuItemText: FunctionComponent<PropsWithChildren> = ({ child
export const MobileUserAvatar: FunctionComponent = () => {
const context = useContext(MainContext);
const user = context.user;
const logoutFormRef = useRef<HTMLFormElement>(null);
if (!user) {
return null;
}
Expand All @@ -66,52 +58,46 @@ export const MobileUserAvatar: FunctionComponent = () => {
aria-controls='user-actions'
id='user-avatar'
>
<MobileMenuItemText>
<MenuItemText>
<Avatar
src={user.avatarUrl}
alt={user.loginName}
variant='rounded'
sx={itemIcon} />
{user.loginName}
</MobileMenuItemText>
</MenuItemText>
</AccordionSummary>
<AccordionDetails>
<MobileMenuItem>
<Link href={user.homepage}>
<MobileMenuItemText>
<GitHubIcon sx={itemIcon} />
{user.loginName}
</MobileMenuItemText>
</Link>
</MobileMenuItem>
<MobileMenuItem>
<RouteLink to={UserSettingsRoutes.PROFILE}>
<MobileMenuItemText>
<SettingsIcon sx={itemIcon} />
Settings
</MobileMenuItemText>
</RouteLink>
</MobileMenuItem>
<MenuItem component={Link} href={user.homepage}>
<MenuItemText>
<GitHubIcon sx={itemIcon} />
{user.loginName}
</MenuItemText>
</MenuItem>
<MenuItem component={RouteLink} to={UserSettingsRoutes.PROFILE}>
<MenuItemText>
<SettingsIcon sx={itemIcon} />
Settings
</MenuItemText>
</MenuItem>
{
user.role === 'admin'
? <MobileMenuItem>
<RouteLink to={AdminDashboardRoutes.MAIN}>
<MobileMenuItemText>
<AdminPanelSettingsIcon sx={itemIcon} />
Admin Dashboard
</MobileMenuItemText>
</RouteLink>
</MobileMenuItem>
? <MenuItem component={RouteLink} to={AdminDashboardRoutes.MAIN}>
<MenuItemText>
<AdminPanelSettingsIcon sx={itemIcon} />
Admin Dashboard
</MenuItemText>
</MenuItem>
: null
}
<MobileMenuItem>
<LogoutForm>
<MobileMenuItemText>
<MenuItem onClick={() => logoutFormRef.current?.submit()}>
<LogoutForm ref={logoutFormRef}>
<MenuItemText>
<LogoutIcon sx={itemIcon} />
Log Out
</MobileMenuItemText>
</MenuItemText>
</LogoutForm>
</MobileMenuItem>
</MenuItem>
</AccordionDetails>
</Accordion>;
};
Expand All @@ -125,63 +111,51 @@ export const MobileMenuContent: FunctionComponent = () => {
user ? (
<MobileUserAvatar />
) : (
<MobileMenuItem>
<LoginComponent
loginProviders={loginProviders}
renderButton={(href, onClick) => {
return (<Link href={href} onClick={onClick}>
<MobileMenuItemText>
<AccountBoxIcon sx={itemIcon} />
Log In
</MobileMenuItemText>
</Link>);
}}
/>
</MobileMenuItem>
<LoginComponent
loginProviders={loginProviders}
renderButton={(href, onClick) => (
<MenuItem component={Link} href={href} onClick={onClick}>
<MenuItemText>
<AccountBoxIcon sx={itemIcon} />
Log In
</MenuItemText>
</MenuItem>
)}
/>
)
)}
{loginProviders && !location.pathname.startsWith(UserSettingsRoutes.ROOT) && (
<MobileMenuItem>
<RouteLink to='/user-settings/extensions'>
<MobileMenuItemText>
<PublishIcon sx={itemIcon} />
Publish Extension
</MobileMenuItemText>
</RouteLink>
</MobileMenuItem>
<MenuItem component={RouteLink} to='/user-settings/extensions'>
<MenuItemText>
<PublishIcon sx={itemIcon} />
Publish Extension
</MenuItemText>
</MenuItem>
)}
<MobileMenuItem>
<Link target='_blank' href='https://github.com/eclipse/openvsx'>
<MobileMenuItemText>
<GitHubIcon sx={itemIcon} />
Source Code
</MobileMenuItemText>
</Link>
</MobileMenuItem>
<MobileMenuItem>
<Link href='https://github.com/eclipse/openvsx/wiki'>
<MobileMenuItemText>
<MenuBookIcon sx={itemIcon} />
Documentation
</MobileMenuItemText>
</Link>
</MobileMenuItem>
<MobileMenuItem>
<Link href='https://join.slack.com/t/openvsxworkinggroup/shared_invite/zt-2y07y1ggy-ct3IfJljjGI6xWUQ9llv6A'>
<MobileMenuItemText>
<ForumIcon sx={itemIcon} />
Slack Workspace
</MobileMenuItemText>
</Link>
</MobileMenuItem>
<MobileMenuItem>
<RouteLink to='/about'>
<MobileMenuItemText>
<InfoIcon sx={itemIcon} />
About This Service
</MobileMenuItemText>
</RouteLink>
</MobileMenuItem>
<MenuItem component={Link} href='https://github.com/eclipse/openvsx' target='_blank'>
<MenuItemText>
<GitHubIcon sx={itemIcon} />
Source Code
</MenuItemText>
</MenuItem>
<MenuItem component={Link} href='https://github.com/eclipse/openvsx/wiki'>
<MenuItemText>
<MenuBookIcon sx={itemIcon} />
Documentation
</MenuItemText>
</MenuItem>
<MenuItem component={Link} href='https://join.slack.com/t/openvsxworkinggroup/shared_invite/zt-2y07y1ggy-ct3IfJljjGI6xWUQ9llv6A'>
<MenuItemText>
<ForumIcon sx={itemIcon} />
Slack Workspace
</MenuItemText>
</MenuItem>
<MenuItem component={RouteLink} to='/about'>
<MenuItemText>
<InfoIcon sx={itemIcon} />
About This Service
</MenuItemText>
</MenuItem>
</>;
};

Expand Down
65 changes: 30 additions & 35 deletions webui/src/pages/user/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,19 @@
********************************************************************************/

import { FunctionComponent, useContext, useRef, useState } from 'react';
import { styled } from '@mui/material/styles';
import { Avatar, Menu, Typography, MenuItem, Link, Divider, IconButton } from '@mui/material';
import { Link as RouteLink } from 'react-router-dom';
import { UserSettingsRoutes } from './user-settings';
import { AdminDashboardRoutes } from '../admin-dashboard/admin-dashboard';
import { MainContext } from '../../context';
import { LogoutForm } from './logout';

const AvatarRouteLink = styled(RouteLink)({
cursor: 'pointer',
textDecoration: 'none'
});

const AvatarMenuItem = styled(MenuItem)({ cursor: 'auto' });


export const UserAvatar: FunctionComponent = () => {
const [open, setOpen] = useState<boolean>(false);
const context = useContext(MainContext);
const avatarButton = useRef<any>();
const logoutFormRef = useRef<HTMLFormElement>(null);

const handleAvatarClick = () => {
setOpen(!open);
Expand Down Expand Up @@ -60,43 +53,45 @@ export const UserAvatar: FunctionComponent = () => {
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
onClose={handleClose} >
<AvatarMenuItem>
<Link href={user.homepage} underline='hover'>
<Typography variant='body2' color='text.primary'>
Logged in as
</Typography>
<Typography variant='overline' color='text.primary'>
{user.loginName}
</Typography>
</Link>
</AvatarMenuItem>
<MenuItem
component={Link}
href={user.homepage}
sx={{
display: 'block',
'&:hover': {
textDecoration: 'underline'
}
}} >
<Typography variant='body2' color='text.primary' sx={{mr: 1}}>
Logged in as
</Typography>
<Typography variant='overline' color='text.primary'>
{user.loginName}
</Typography>
</MenuItem>
<Divider />
<AvatarMenuItem>
<AvatarRouteLink onClick={handleClose} to={UserSettingsRoutes.PROFILE}>
<Typography variant='button' color='text.primary'>
Settings
</Typography>
</AvatarRouteLink>
</AvatarMenuItem>
<MenuItem component={RouteLink} to={UserSettingsRoutes.PROFILE} onClick={handleClose}>
<Typography variant='button' color='text.primary'>
Settings
</Typography>
</MenuItem>
{
user.role && user.role === 'admin' ?
<AvatarMenuItem>
<AvatarRouteLink onClick={handleClose} to={AdminDashboardRoutes.MAIN}>
<Typography variant='button' color='text.primary'>
Admin Dashboard
</Typography>
</AvatarRouteLink>
</AvatarMenuItem>
<MenuItem component={RouteLink} to={AdminDashboardRoutes.MAIN} onClick={handleClose}>
<Typography variant='button' color='text.primary'>
Admin Dashboard
</Typography>
</MenuItem>
:
''
}
<AvatarMenuItem>
<LogoutForm>
<MenuItem onClick={() => logoutFormRef.current?.submit()}>
<LogoutForm ref={logoutFormRef}>
<Typography variant='button' sx={{ color: 'primary.dark' }}>
Log Out
</Typography>
</LogoutForm>
</AvatarMenuItem>
</MenuItem>
</Menu>
</>;
};
10 changes: 6 additions & 4 deletions webui/src/pages/user/logout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* SPDX-License-Identifier: EPL-2.0
* ****************************************************************************** */

import { FunctionComponent, PropsWithChildren, useContext, useEffect, useRef, useState } from 'react';
import { PropsWithChildren, useContext, useEffect, useRef, useState, forwardRef } from 'react';
import { Button } from '@mui/material';
import { styled } from '@mui/material/styles';
import { isError, CsrfTokenJson } from '../../extension-registry-types';
Expand All @@ -22,7 +22,7 @@ const LogoutButton = styled(Button)({
padding: 0
});

export const LogoutForm: FunctionComponent<PropsWithChildren> = ({ children }) => {
export const LogoutForm = forwardRef<HTMLFormElement, PropsWithChildren>(({ children }, ref) => {
const [csrf, setCsrf] = useState<string>();
const context = useContext(MainContext);

Expand All @@ -44,10 +44,12 @@ export const LogoutForm: FunctionComponent<PropsWithChildren> = ({ children }) =
}
};

return <form method='post' action={context.service.getLogoutUrl()}>
return <form ref={ref} method='post' action={context.service.getLogoutUrl()}>
{csrf ? <input name='_csrf' type='hidden' value={csrf} /> : null}
<LogoutButton type='submit'>
{children}
</LogoutButton>
</form>;
};
});

LogoutForm.displayName = 'LogoutForm';