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
75 changes: 0 additions & 75 deletions ui/src/common/SettingsDialog.tsx

This file was deleted.

107 changes: 40 additions & 67 deletions ui/src/layout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,17 @@ import AccountCircle from '@mui/icons-material/AccountCircle';
import Chat from '@mui/icons-material/Chat';
import DevicesOther from '@mui/icons-material/DevicesOther';
import ExitToApp from '@mui/icons-material/ExitToApp';
import Brightness4 from '@mui/icons-material/Brightness4';
import Brightness7 from '@mui/icons-material/Brightness7';
import BrightnessAuto from '@mui/icons-material/BrightnessAuto';
import GitHubIcon from '@mui/icons-material/GitHub';
import MenuIcon from '@mui/icons-material/Menu';
import Apps from '@mui/icons-material/Apps';
import SupervisorAccount from '@mui/icons-material/SupervisorAccount';
import SettingsIcon from '@mui/icons-material/Settings';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import React, {CSSProperties} from 'react';
import {Link} from 'react-router';
import {useMediaQuery} from '@mui/material';
import {ThemeKey} from './theme';

const themeIcons: Record<ThemeKey, React.ReactElement> = {
dark: <Brightness4 />,
light: <Brightness7 />,
system: <BrightnessAuto />,
};

const useStyles = makeStyles()((theme: Theme) => ({
appBar: {
Expand Down Expand Up @@ -76,29 +70,13 @@ interface IProps {
name: string;
admin: boolean;
version: string;
themeMode: ThemeKey;
toggleTheme: VoidFunction;
showSettings: VoidFunction;
logout: VoidFunction;
style: CSSProperties;
setNavOpen: (open: boolean) => void;
}

const Header = ({
version,
name,
loggedIn,
admin,
toggleTheme,
logout,
style,
setNavOpen,
showSettings,
themeMode,
}: IProps) => {
const Header = ({version, name, loggedIn, admin, logout, style, setNavOpen}: IProps) => {
const {classes} = useStyles();
const themeLabel = `Toggle theme (current: ${themeMode})`;
const themeIcon = themeIcons[themeMode];
return (
<AppBar
sx={{position: {xs: 'sticky', sm: 'fixed'}}}
Expand All @@ -124,41 +102,14 @@ const Header = ({
</a>
</div>
{loggedIn && (
<Buttons
admin={admin}
name={name}
logout={logout}
setNavOpen={setNavOpen}
showSettings={showSettings}
/>
<Buttons admin={admin} name={name} logout={logout} setNavOpen={setNavOpen} />
)}
<div>
<IconButton
onClick={toggleTheme}
color="inherit"
size="large"
title={themeLabel}
aria-label={themeLabel}>
{themeIcon}
</IconButton>

<a
href="https://github.com/gotify/server"
className={classes.link}
target="_blank"
rel="noopener noreferrer">
<IconButton color="inherit" size="large">
<GitHubIcon />
</IconButton>
</a>
</div>
</Toolbar>
</AppBar>
);
};

const Buttons = ({
showSettings,
name,
admin,
logout,
Expand All @@ -168,9 +119,10 @@ const Buttons = ({
admin: boolean;
logout: VoidFunction;
setNavOpen: (open: boolean) => void;
showSettings: VoidFunction;
}) => {
const {classes} = useStyles();
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const userDropDown = Boolean(anchorEl);

return (
<div className={classes.menuButtons}>
Expand Down Expand Up @@ -198,17 +150,38 @@ const Buttons = ({
<ResponsiveButton
icon={<AccountCircle />}
label={name}
onClick={showSettings}
id="changepw"
color="inherit"
/>
<ResponsiveButton
icon={<ExitToApp />}
label="Logout"
onClick={logout}
id="logout"
onClick={(e) => setAnchorEl(e.currentTarget)}
id="user-menu-button"
aria-controls={userDropDown ? 'user-menu' : undefined}
aria-haspopup="true"
aria-expanded={userDropDown ? 'true' : undefined}
color="inherit"
/>
<Menu
id="user-menu"
anchorEl={anchorEl}
open={userDropDown}
onClose={() => setAnchorEl(null)}
anchorOrigin={{vertical: 'bottom', horizontal: 'right'}}
transformOrigin={{vertical: 'top', horizontal: 'right'}}>
<MenuItem component={Link} to="/settings" onClick={() => setAnchorEl(null)}>
<ListItemIcon>
<SettingsIcon fontSize="small" />
</ListItemIcon>
<ListItemText>Settings</ListItemText>
</MenuItem>
<MenuItem
id="logout"
onClick={() => {
setAnchorEl(null);
logout();
}}>
<ListItemIcon>
<ExitToApp fontSize="small" />
</ListItemIcon>
<ListItemText>Logout</ListItemText>
</MenuItem>
</Menu>
</div>
);
};
Expand All @@ -218,7 +191,7 @@ const ResponsiveButton: React.FC<{
sx?: ButtonProps['sx'];
label: string;
id?: string;
onClick?: () => void;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
icon: React.ReactNode;
}> = ({icon, label, ...rest}) => {
const matches = useMediaQuery('(max-width:1000px)');
Expand Down
26 changes: 11 additions & 15 deletions ui/src/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import {HashRouter, Navigate, Route, Routes} from 'react-router';
import Header from './Header';
import Navigation from './Navigation';
import ScrollUpButton from '../common/ScrollUpButton';
import SettingsDialog from '../common/SettingsDialog';
import ElevationForm from '../common/ElevationForm';
import * as config from '../config';
import Applications from '../application/Applications';
import Clients from '../client/Clients';
import Plugins from '../plugin/Plugins';
import Login from '../user/Login';
import Messages from '../message/Messages';
import Settings from '../user/Settings';
import Users from '../user/Users';
import {observer} from 'mobx-react-lite';
import {ConnectionErrorBanner} from '../common/ConnectionErrorBanner';
Expand Down Expand Up @@ -76,15 +76,8 @@ const Layout = observer(() => {
);
const {version} = config.get('version');
const [navOpen, setNavOpen] = React.useState(false);
const [showSettings, setShowSettings] = React.useState(false);

const toggleTheme = () => {
const nextMap: Record<ThemeKey, ThemeKey> = {
dark: 'light',
light: 'system',
system: 'dark',
};
const next = nextMap[currentTheme];
const setTheme = (next: ThemeKey) => {
setCurrentTheme(next);
localStorage.setItem(localStorageThemeKey, next);
};
Expand Down Expand Up @@ -119,9 +112,6 @@ const Layout = observer(() => {
style={{top: !connectionErrorMessage ? 0 : 64}}
version={version}
loggedIn={loggedIn}
themeMode={currentTheme}
toggleTheme={toggleTheme}
showSettings={() => setShowSettings(true)}
logout={logout}
setNavOpen={setNavOpen}
/>
Expand All @@ -148,6 +138,15 @@ const Layout = observer(() => {
path="/users"
element={authed(elevated(<Users />))}
/>
<Route
path="/settings"
element={authed(
<Settings
themeMode={currentTheme}
setTheme={setTheme}
/>
)}
/>
<Route path="/plugins" element={authed(<Plugins />)} />
<Route
path="/plugins/:id"
Expand All @@ -162,9 +161,6 @@ const Layout = observer(() => {
</Routes>
</main>
</div>
{showSettings && (
<SettingsDialog fClose={() => setShowSettings(false)} />
)}
<ScrollUpButton />
<SnackbarProvider />
</div>
Expand Down
4 changes: 3 additions & 1 deletion ui/src/tests/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ export const login = async (page: Page, user = 'admin', pass = 'admin'): Promise
await page.type($loginForm.input('.password'), pass);
await page.click($loginForm.button('.login'));
await waitForExists(page, selector.heading(), 'All Messages');
await waitForExists(page, 'button', 'logout');
};

export const logout = async (page: Page): Promise<void> => {
await page.waitForSelector('#user-menu-button');
await page.click('#user-menu-button');
await page.waitForSelector('#logout');
await page.click('#logout');
await waitForExists(page, selector.heading(), 'Login');
expect(page.url()).toContain('/login');
Expand Down
1 change: 0 additions & 1 deletion ui/src/tests/oidc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ const loginWithOIDC = async (page: Page, user: DexUser): Promise<void> => {

const expectLoggedIn = async (page: Page): Promise<void> => {
await waitForExists(page, selector.heading(), 'All Messages');
await page.waitForSelector('#logout');
};

const oidcError = async (page: Page): Promise<string> => {
Expand Down
10 changes: 7 additions & 3 deletions ui/src/tests/user.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Page} from 'puppeteer';
import {newTest, GotifyTest} from './setup';
import {clearField, count, innerText, waitForExists, waitToDisappear} from './utils';
import {clearField, clickByText, count, innerText, waitForExists, waitToDisappear} from './utils';
import {afterAll, beforeAll, describe, expect, it} from 'vitest';
import * as auth from './authentication';
import * as selector from './selector';
Expand Down Expand Up @@ -120,8 +120,12 @@ describe('User', () => {
expect(await count(page, $table.rows())).toBe(3);
});
it('changes password of current user', async () => {
const $changepw = selector.form('#changepw-dialog');
await page.click('#changepw');
const $changepw = selector.form('#changepw-form');
await page.waitForSelector('#user-menu-button');
await page.click('#user-menu-button');
await clickByText(page, 'a', 'Settings');
await waitToDisappear(page, '.MuiBackdrop-root');
await waitForExists(page, selector.heading(), 'Settings');
await page.waitForSelector($changepw.selector());
await page.type($changepw.input('.newpass'), 'changed');
await page.click($changepw.button('.change'));
Expand Down
Loading
Loading