Merge pull request #515 from Tert0/master

Add Delete All Button confirmation
This commit is contained in:
Jannis Mattheis 2022-10-08 13:42:02 +00:00 committed by GitHub
commit fb7d910a1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 67 additions and 90 deletions

View File

@ -6,7 +6,7 @@
"singleQuote": true, "singleQuote": true,
"trailingComma": "es5", "trailingComma": "es5",
"bracketSpacing": false, "bracketSpacing": false,
"jsxBracketSameLine": true, "bracketSameLine": true,
"arrowParens": "always", "arrowParens": "always",
"parser": "typescript" "parser": "typescript"
} }

View File

@ -34,8 +34,7 @@ export default class AddDialog extends Component<IProps, IState> {
open={true} open={true}
onClose={fClose} onClose={fClose}
aria-labelledby="form-dialog-title" aria-labelledby="form-dialog-title"
id="app-dialog" id="app-dialog">
>
<DialogTitle id="form-dialog-title">Create an application</DialogTitle> <DialogTitle id="form-dialog-title">Create an application</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText> <DialogContentText>
@ -70,8 +69,7 @@ export default class AddDialog extends Component<IProps, IState> {
disabled={!submitEnabled} disabled={!submitEnabled}
onClick={submitAndClose} onClick={submitAndClose}
color="primary" color="primary"
variant="contained" variant="contained">
>
Create Create
</Button> </Button>
</div> </div>

View File

@ -53,13 +53,11 @@ class Applications extends Component<Stores<'appStore'>> {
id="create-app" id="create-app"
variant="contained" variant="contained"
color="primary" color="primary"
onClick={() => (this.createDialog = true)} onClick={() => (this.createDialog = true)}>
>
Create Application Create Application
</Button> </Button>
} }
maxWidth={1000} maxWidth={1000}>
>
<Grid item xs={12}> <Grid item xs={12}>
<Paper elevation={6}> <Paper elevation={6}>
<Table id="app-table"> <Table id="app-table">

View File

@ -44,8 +44,7 @@ export default class UpdateDialog extends Component<IProps, IState> {
open={true} open={true}
onClose={fClose} onClose={fClose}
aria-labelledby="form-dialog-title" aria-labelledby="form-dialog-title"
id="app-dialog" id="app-dialog">
>
<DialogTitle id="form-dialog-title">Update an application</DialogTitle> <DialogTitle id="form-dialog-title">Update an application</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText> <DialogContentText>
@ -80,8 +79,7 @@ export default class UpdateDialog extends Component<IProps, IState> {
disabled={!submitEnabled} disabled={!submitEnabled}
onClick={submitAndClose} onClick={submitAndClose}
color="primary" color="primary"
variant="contained" variant="contained">
>
Update Update
</Button> </Button>
</div> </div>

View File

@ -28,8 +28,7 @@ export default class AddDialog extends Component<IProps, {name: string}> {
open={true} open={true}
onClose={fClose} onClose={fClose}
aria-labelledby="form-dialog-title" aria-labelledby="form-dialog-title"
id="client-dialog" id="client-dialog">
>
<DialogTitle id="form-dialog-title">Create a client</DialogTitle> <DialogTitle id="form-dialog-title">Create a client</DialogTitle>
<DialogContent> <DialogContent>
<TextField <TextField
@ -47,16 +46,14 @@ export default class AddDialog extends Component<IProps, {name: string}> {
<Button onClick={fClose}>Cancel</Button> <Button onClick={fClose}>Cancel</Button>
<Tooltip <Tooltip
placement={'bottom-start'} placement={'bottom-start'}
title={submitEnabled ? '' : 'name is required'} title={submitEnabled ? '' : 'name is required'}>
>
<div> <div>
<Button <Button
className="create" className="create"
disabled={!submitEnabled} disabled={!submitEnabled}
onClick={submitAndClose} onClick={submitAndClose}
color="primary" color="primary"
variant="contained" variant="contained">
>
Create Create
</Button> </Button>
</div> </div>

View File

@ -48,12 +48,10 @@ class Clients extends Component<Stores<'clientStore'>> {
id="create-client" id="create-client"
variant="contained" variant="contained"
color="primary" color="primary"
onClick={() => (this.showDialog = true)} onClick={() => (this.showDialog = true)}>
>
Create Client Create Client
</Button> </Button>
} }>
>
<Grid item xs={12}> <Grid item xs={12}>
<Paper elevation={6}> <Paper elevation={6}>
<Table id="client-table"> <Table id="client-table">

View File

@ -41,8 +41,7 @@ export default class UpdateDialog extends Component<IProps, IState> {
open={true} open={true}
onClose={fClose} onClose={fClose}
aria-labelledby="form-dialog-title" aria-labelledby="form-dialog-title"
id="client-dialog" id="client-dialog">
>
<DialogTitle id="form-dialog-title">Update a Client</DialogTitle> <DialogTitle id="form-dialog-title">Update a Client</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText> <DialogContentText>
@ -69,8 +68,7 @@ export default class UpdateDialog extends Component<IProps, IState> {
disabled={!submitEnabled} disabled={!submitEnabled}
onClick={submitAndClose} onClick={submitAndClose}
color="primary" color="primary"
variant="contained" variant="contained">
>
Update Update
</Button> </Button>
</div> </div>

View File

@ -23,8 +23,7 @@ export default function ConfirmDialog({title, text, fClose, fOnSubmit}: IProps)
open={true} open={true}
onClose={fClose} onClose={fClose}
aria-labelledby="form-dialog-title" aria-labelledby="form-dialog-title"
className="confirm-dialog" className="confirm-dialog">
>
<DialogTitle id="form-dialog-title">{title}</DialogTitle> <DialogTitle id="form-dialog-title">{title}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText>{text}</DialogContentText> <DialogContentText>{text}</DialogContentText>
@ -38,8 +37,7 @@ export default function ConfirmDialog({title, text, fClose, fOnSubmit}: IProps)
autoFocus autoFocus
color="primary" color="primary"
variant="contained" variant="contained"
className="confirm" className="confirm">
>
Yes Yes
</Button> </Button>
</DialogActions> </DialogActions>

View File

@ -16,8 +16,7 @@ export const ConnectionErrorBanner = ({height, retry, message}: ConnectionErrorB
width: '100%', width: '100%',
zIndex: 1300, zIndex: 1300,
position: 'relative', position: 'relative',
}} }}>
>
<Typography align="center" variant="h6" style={{lineHeight: `${height}px`}}> <Typography align="center" variant="h6" style={{lineHeight: `${height}px`}}>
{message}{' '} {message}{' '}
<Button variant="outlined" onClick={retry}> <Button variant="outlined" onClick={retry}>

View File

@ -36,8 +36,7 @@ class ScrollUpButton extends Component {
display: this.state.display, display: this.state.display,
opacity: this.state.opacity, opacity: this.state.opacity,
}} }}
onClick={this.scrollUp} onClick={this.scrollUp}>
>
<KeyboardArrowUp /> <KeyboardArrowUp />
</Fab> </Fab>
); );

View File

@ -31,8 +31,7 @@ class SettingsDialog extends Component<IProps & Stores<'currentUser'>> {
open={true} open={true}
onClose={fClose} onClose={fClose}
aria-labelledby="form-dialog-title" aria-labelledby="form-dialog-title"
id="changepw-dialog" id="changepw-dialog">
>
<DialogTitle id="form-dialog-title">Change Password</DialogTitle> <DialogTitle id="form-dialog-title">Change Password</DialogTitle>
<DialogContent> <DialogContent>
<TextField <TextField
@ -55,8 +54,7 @@ class SettingsDialog extends Component<IProps & Stores<'currentUser'>> {
disabled={pass.length === 0} disabled={pass.length === 0}
onClick={submitAndClose} onClick={submitAndClose}
color="primary" color="primary"
variant="contained" variant="contained">
>
Change Change
</Button> </Button>
</div> </div>

View File

@ -106,8 +106,7 @@ class Header extends Component<IProps> {
</Link> </Link>
<a <a
href={'https://github.com/gotify/server/releases/tag/v' + version} href={'https://github.com/gotify/server/releases/tag/v' + version}
className={classes.link} className={classes.link}>
>
<Typography variant="button" color="inherit"> <Typography variant="button" color="inherit">
@{version} @{version}
</Typography> </Typography>
@ -123,8 +122,7 @@ class Header extends Component<IProps> {
href="https://github.com/gotify/server" href="https://github.com/gotify/server"
className={classes.link} className={classes.link}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer">
>
<IconButton color="inherit"> <IconButton color="inherit">
<GitHubIcon /> <GitHubIcon />
</IconButton> </IconButton>

View File

@ -57,8 +57,7 @@ class Navigation extends Component<
onClick={() => setNavOpen(false)} onClick={() => setNavOpen(false)}
className={`${classes.link} item`} className={`${classes.link} item`}
to={'/messages/' + app.id} to={'/messages/' + app.id}
key={app.id} key={app.id}>
>
<ListItem button> <ListItem button>
<ListItemText primary={app.name} /> <ListItemText primary={app.name} />
</ListItem> </ListItem>
@ -79,8 +78,7 @@ class Navigation extends Component<
classes={{root: classes.root, paper: classes.drawerPaper}} classes={{root: classes.root, paper: classes.drawerPaper}}
navOpen={navOpen} navOpen={navOpen}
setNavOpen={setNavOpen} setNavOpen={setNavOpen}
id="message-navigation" id="message-navigation">
>
<div className={classes.toolbar} /> <div className={classes.toolbar} />
<Link className={classes.link} to="/" onClick={() => setNavOpen(false)}> <Link className={classes.link} to="/" onClick={() => setNavOpen(false)}>
<ListItem button disabled={!loggedIn} className="all"> <ListItem button disabled={!loggedIn} className="all">
@ -96,8 +94,7 @@ class Navigation extends Component<
onClick={() => { onClick={() => {
requestPermission(); requestPermission();
this.setState({showRequestNotification: false}); this.setState({showRequestNotification: false});
}} }}>
>
Enable Notifications Enable Notifications
</Button> </Button>
) : null} ) : null}

View File

@ -11,6 +11,7 @@ import {inject, Stores} from '../inject';
import {observable} from 'mobx'; import {observable} from 'mobx';
import ReactInfinite from 'react-infinite'; import ReactInfinite from 'react-infinite';
import {IMessage} from '../types'; import {IMessage} from '../types';
import ConfirmDialog from '../common/ConfirmDialog';
type IProps = RouteComponentProps<{id: string}>; type IProps = RouteComponentProps<{id: string}>;
@ -22,6 +23,8 @@ interface IState {
class Messages extends Component<IProps & Stores<'messagesStore' | 'appStore'>, IState> { class Messages extends Component<IProps & Stores<'messagesStore' | 'appStore'>, IState> {
@observable @observable
private heights: Record<string, number> = {}; private heights: Record<string, number> = {};
@observable
private deleteAll = false;
private static appId(props: IProps) { private static appId(props: IProps) {
if (props === undefined) { if (props === undefined) {
@ -70,8 +73,7 @@ class Messages extends Component<IProps & Stores<'messagesStore' | 'appStore'>,
disabled={!hasMessages} disabled={!hasMessages}
color="primary" color="primary"
onClick={() => messagesStore.refreshByApp(appId)} onClick={() => messagesStore.refreshByApp(appId)}
style={{marginRight: 5}} style={{marginRight: 5}}>
>
Refresh Refresh
</Button> </Button>
<Button <Button
@ -79,21 +81,20 @@ class Messages extends Component<IProps & Stores<'messagesStore' | 'appStore'>,
variant="contained" variant="contained"
disabled={!hasMessages} disabled={!hasMessages}
color="primary" color="primary"
onClick={() => messagesStore.removeByApp(appId)} onClick={() => {
> this.deleteAll = true;
}}>
Delete All Delete All
</Button> </Button>
</div> </div>
} }>
>
{hasMessages ? ( {hasMessages ? (
<div style={{width: '100%'}} id="messages"> <div style={{width: '100%'}} id="messages">
<ReactInfinite <ReactInfinite
key={appId} key={appId}
useWindowAsScrollContainer useWindowAsScrollContainer
preloadBatchSize={window.innerHeight * 3} preloadBatchSize={window.innerHeight * 3}
elementHeight={messages.map((m) => this.heights[m.id] || 1)} elementHeight={messages.map((m) => this.heights[m.id] || 1)}>
>
{messages.map(this.renderMessage)} {messages.map(this.renderMessage)}
</ReactInfinite> </ReactInfinite>
@ -108,6 +109,15 @@ class Messages extends Component<IProps & Stores<'messagesStore' | 'appStore'>,
) : ( ) : (
this.label('No messages') this.label('No messages')
)} )}
{this.deleteAll && (
<ConfirmDialog
title="Confirm Delete"
text={'Delete all messages?'}
fClose={() => (this.deleteAll = false)}
fOnSubmit={() => messagesStore.removeByApp(appId)}
/>
)}
</DefaultPage> </DefaultPage>
); );
} }

View File

@ -79,8 +79,7 @@ class PluginDetailView extends Component<IProps & Stores<'pluginStore'>, IState>
name={'Configurer'} name={'Configurer'}
description={'This is the configuration panel for this plugin.'} description={'This is the configuration panel for this plugin.'}
icon={Build} icon={Build}
refresh={this.refreshConfigurer.bind(this)} refresh={this.refreshConfigurer.bind(this)}>
>
<ConfigurerPanel <ConfigurerPanel
pluginInfo={pluginInfo} pluginInfo={pluginInfo}
initialConfig={ initialConfig={
@ -100,8 +99,7 @@ class PluginDetailView extends Component<IProps & Stores<'pluginStore'>, IState>
name={'Displayer'} name={'Displayer'}
description={'This is the information generated by the plugin.'} description={'This is the information generated by the plugin.'}
refresh={this.refreshDisplayer.bind(this)} refresh={this.refreshDisplayer.bind(this)}
icon={Subject} icon={Subject}>
>
<DisplayerPanel <DisplayerPanel
pluginInfo={pluginInfo} pluginInfo={pluginInfo}
displayText={ displayText={
@ -147,8 +145,7 @@ const PanelWrapper: React.FC<IPanelWrapperProps> = ({
style={{float: 'right'}} style={{float: 'right'}}
onClick={() => { onClick={() => {
refresh(); refresh();
}} }}>
>
<Refresh /> <Refresh />
</Button> </Button>
) : null} ) : null}
@ -202,8 +199,7 @@ class ConfigurerPanel extends Component<IConfigurerPanelProps, {unsavedChanges:
this.props.save(newConfig!).then(() => { this.props.save(newConfig!).then(() => {
this.setState({unsavedChanges: null}); this.setState({unsavedChanges: null});
}); });
}} }}>
>
<Typography variant="button">Save</Typography> <Typography variant="button">Save</Typography>
</Button> </Button>
</div> </div>
@ -264,8 +260,7 @@ class PluginInfo extends Component<{pluginInfo: IPlugin}> {
href={url} href={url}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="custom-route" className="custom-route">
>
{url} {url}
</a> </a>
))(`${config.get('url')}plugin/${id}/custom/${token}/`)} ))(`${config.get('url')}plugin/${id}/custom/${token}/`)}

View File

@ -42,8 +42,7 @@ class SnackBarHandler extends Component<Stores<'snackManager'>> {
key="close" key="close"
aria-label="Close" aria-label="Close"
color="inherit" color="inherit"
onClick={this.closeCurrentSnack} onClick={this.closeCurrentSnack}>
>
<Close /> <Close />
</IconButton> </IconButton>
} }

View File

@ -219,6 +219,8 @@ describe('Messages', () => {
it('deletes all linux messages', async () => { it('deletes all linux messages', async () => {
await navigate('Linux'); await navigate('Linux');
await page.click('#delete-all'); await page.click('#delete-all');
await page.waitForSelector(selector.$confirmDialog.selector());
await page.click(selector.$confirmDialog.button('.confirm'));
await page.waitForSelector('#delete-all:disabled'); await page.waitForSelector('#delete-all:disabled');
await expectMessages({ await expectMessages({
all: [windows3, backup1, windows1], all: [windows3, backup1, windows1],
@ -250,6 +252,9 @@ describe('Messages', () => {
it('deletes all messages', async () => { it('deletes all messages', async () => {
await navigate('All Messages'); await navigate('All Messages');
await page.click('#delete-all'); await page.click('#delete-all');
await page.waitForSelector(selector.$confirmDialog.selector());
await page.click(selector.$confirmDialog.button('.confirm'));
await page.waitForSelector('#delete-all:disabled');
await expectMessages({ await expectMessages({
all: [], all: [],
windows: [], windows: [],
@ -269,6 +274,8 @@ describe('Messages', () => {
it('deletes all backup messages and navigates to all messages', async () => { it('deletes all backup messages and navigates to all messages', async () => {
await navigate('Backup'); await navigate('Backup');
await page.click('#delete-all'); await page.click('#delete-all');
await page.waitForSelector(selector.$confirmDialog.selector());
await page.click(selector.$confirmDialog.button('.confirm'));
await page.waitForSelector('#delete-all:disabled'); await page.waitForSelector('#delete-all:disabled');
await navigate('All Messages'); await navigate('All Messages');
await createMessage(backup3, backupServerToken); await createMessage(backup3, backupServerToken);

View File

@ -44,8 +44,7 @@ export default class AddEditDialog extends Component<IProps, IState> {
open={true} open={true}
onClose={fClose} onClose={fClose}
aria-labelledby="form-dialog-title" aria-labelledby="form-dialog-title"
id="add-edit-user-dialog" id="add-edit-user-dialog">
>
<DialogTitle id="form-dialog-title"> <DialogTitle id="form-dialog-title">
{isEdit ? 'Edit ' + this.props.name : 'Add a user'} {isEdit ? 'Edit ' + this.props.name : 'Add a user'}
</DialogTitle> </DialogTitle>
@ -91,16 +90,14 @@ export default class AddEditDialog extends Component<IProps, IState> {
? '' ? ''
: 'password is required' : 'password is required'
: 'name is required' : 'name is required'
} }>
>
<div> <div>
<Button <Button
className="save-create" className="save-create"
disabled={!passPresent || !namePresent} disabled={!passPresent || !namePresent}
onClick={submitAndClose} onClick={submitAndClose}
color="primary" color="primary"
variant="contained" variant="contained">
>
{isEdit ? 'Save' : 'Create'} {isEdit ? 'Save' : 'Create'}
</Button> </Button>
</div> </div>

View File

@ -52,8 +52,7 @@ class Login extends Component<Stores<'currentUser'>> {
color="primary" color="primary"
disabled={!!this.props.currentUser.connectionErrorMessage} disabled={!!this.props.currentUser.connectionErrorMessage}
style={{marginTop: 15, marginBottom: 5}} style={{marginTop: 15, marginBottom: 5}}
onClick={this.login} onClick={this.login}>
>
Login Login
</Button> </Button>
</form> </form>
@ -81,8 +80,7 @@ class Login extends Component<Stores<'currentUser'>> {
id="register" id="register"
variant="contained" variant="contained"
color="primary" color="primary"
onClick={() => (this.registerDialog = true)} onClick={() => (this.registerDialog = true)}>
>
Register Register
</Button> </Button>
); );

View File

@ -41,8 +41,7 @@ export default class RegistrationDialog extends Component<IProps, IState> {
open={true} open={true}
onClose={fClose} onClose={fClose}
aria-labelledby="form-dialog-title" aria-labelledby="form-dialog-title"
id="add-edit-user-dialog" id="add-edit-user-dialog">
>
<DialogTitle id="form-dialog-title">Registration</DialogTitle> <DialogTitle id="form-dialog-title">Registration</DialogTitle>
<DialogContent> <DialogContent>
<TextField <TextField
@ -75,16 +74,14 @@ export default class RegistrationDialog extends Component<IProps, IState> {
? '' ? ''
: 'password is required' : 'password is required'
: 'name is required' : 'name is required'
} }>
>
<div> <div>
<Button <Button
className="save-create" className="save-create"
disabled={!passPresent || !namePresent} disabled={!passPresent || !namePresent}
onClick={submitAndClose} onClick={submitAndClose}
color="primary" color="primary"
variant="contained" variant="contained">
>
Register Register
</Button> </Button>
</div> </div>

View File

@ -75,12 +75,10 @@ class Users extends Component<WithStyles<'wrapper'> & Stores<'userStore'>> {
id="create-user" id="create-user"
variant="contained" variant="contained"
color="primary" color="primary"
onClick={() => (this.createDialog = true)} onClick={() => (this.createDialog = true)}>
>
Create User Create User
</Button> </Button>
} }>
>
<Grid item xs={12}> <Grid item xs={12}>
<Paper elevation={6}> <Paper elevation={6}>
<Table id="user-table"> <Table id="user-table">