Closes #842 - Toast messages hanging
This commit is contained in:
@ -10,20 +10,16 @@ type Props = {
|
||||
};
|
||||
@observer
|
||||
class Toasts extends React.Component<Props> {
|
||||
handleClose = (index: number) => {
|
||||
this.props.ui.removeToast(index);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { ui } = this.props;
|
||||
|
||||
return (
|
||||
<List>
|
||||
{ui.toasts.map((toast, index) => (
|
||||
{ui.orderedToasts.map(toast => (
|
||||
<Toast
|
||||
key={index}
|
||||
onRequestClose={this.handleClose.bind(this, index)}
|
||||
key={toast.id}
|
||||
toast={toast}
|
||||
onRequestClose={() => ui.removeToast(toast.id)}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
|
@ -15,7 +15,7 @@ class Toast extends React.Component<Props> {
|
||||
timeout: TimeoutID;
|
||||
|
||||
static defaultProps = {
|
||||
closeAfterMs: 3000,
|
||||
closeAfterMs: 30000,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@ -37,15 +37,17 @@ class Toast extends React.Component<Props> {
|
||||
: toast.message.toString();
|
||||
|
||||
return (
|
||||
<Container onClick={onRequestClose} type={toast.type}>
|
||||
<Message>{message}</Message>
|
||||
</Container>
|
||||
<li>
|
||||
<Container onClick={onRequestClose} type={toast.type}>
|
||||
<Message>{message}</Message>
|
||||
</Container>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const Container = styled.li`
|
||||
display: flex;
|
||||
const Container = styled.div`
|
||||
display: inline-block;
|
||||
align-items: center;
|
||||
animation: ${fadeAndScaleIn} 100ms ease;
|
||||
margin: 8px 0;
|
||||
|
@ -71,7 +71,7 @@ class Notifications extends React.Component<Props> {
|
||||
};
|
||||
|
||||
showSuccessMessage = debounce(() => {
|
||||
this.props.ui.showToast('Notifications updated');
|
||||
this.props.ui.showToast('Notifications saved');
|
||||
}, 500);
|
||||
|
||||
render() {
|
||||
|
@ -2,11 +2,11 @@
|
||||
import * as React from 'react';
|
||||
import { observable } from 'mobx';
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import AuthStore from 'stores/AuthStore';
|
||||
import UiStore from 'stores/UiStore';
|
||||
import Checkbox from 'components/Checkbox';
|
||||
import Button from 'components/Button';
|
||||
import CenteredContent from 'components/CenteredContent';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import HelpText from 'components/HelpText';
|
||||
@ -31,33 +31,29 @@ class Security extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
handleSubmit = async (ev: SyntheticEvent<*>) => {
|
||||
ev.preventDefault();
|
||||
handleChange = async (ev: SyntheticInputEvent<*>) => {
|
||||
switch (ev.target.name) {
|
||||
case 'sharing':
|
||||
this.sharing = ev.target.checked;
|
||||
break;
|
||||
case 'documentEmbeds':
|
||||
this.documentEmbeds = ev.target.checked;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
await this.props.auth.updateTeam({
|
||||
sharing: this.sharing,
|
||||
documentEmbeds: this.documentEmbeds,
|
||||
});
|
||||
this.props.ui.showToast('Settings saved', 'success');
|
||||
this.showSuccessMessage();
|
||||
};
|
||||
|
||||
handleChange = (ev: SyntheticInputEvent<*>) => {
|
||||
switch (ev.target.name) {
|
||||
case 'sharing':
|
||||
return (this.sharing = ev.target.checked);
|
||||
case 'documentEmbeds':
|
||||
return (this.documentEmbeds = ev.target.checked);
|
||||
default:
|
||||
}
|
||||
};
|
||||
|
||||
get isValid() {
|
||||
return this.form && this.form.checkValidity();
|
||||
}
|
||||
showSuccessMessage = debounce(() => {
|
||||
this.props.ui.showToast('Settings saved');
|
||||
}, 500);
|
||||
|
||||
render() {
|
||||
const { isSaving } = this.props.auth;
|
||||
|
||||
return (
|
||||
<CenteredContent>
|
||||
<PageTitle title="Security" />
|
||||
@ -67,25 +63,20 @@ class Security extends React.Component<Props> {
|
||||
knowledgebase.
|
||||
</HelpText>
|
||||
|
||||
<form onSubmit={this.handleSubmit} ref={ref => (this.form = ref)}>
|
||||
<Checkbox
|
||||
label="Public document sharing"
|
||||
name="sharing"
|
||||
checked={this.sharing}
|
||||
onChange={this.handleChange}
|
||||
note="When enabled documents can be shared publicly by any team member"
|
||||
/>
|
||||
<Checkbox
|
||||
label="Rich service embeds"
|
||||
name="documentEmbeds"
|
||||
checked={this.documentEmbeds}
|
||||
onChange={this.handleChange}
|
||||
note="Convert links to supported services into rich embeds within your documents"
|
||||
/>
|
||||
<Button type="submit" disabled={isSaving || !this.isValid}>
|
||||
{isSaving ? 'Saving…' : 'Save'}
|
||||
</Button>
|
||||
</form>
|
||||
<Checkbox
|
||||
label="Public document sharing"
|
||||
name="sharing"
|
||||
checked={this.sharing}
|
||||
onChange={this.handleChange}
|
||||
note="When enabled documents can be shared publicly by any team member"
|
||||
/>
|
||||
<Checkbox
|
||||
label="Rich service embeds"
|
||||
name="documentEmbeds"
|
||||
checked={this.documentEmbeds}
|
||||
onChange={this.handleChange}
|
||||
note="Convert links to supported services into rich embeds within your documents"
|
||||
/>
|
||||
</CenteredContent>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
// @flow
|
||||
import { observable, action } from 'mobx';
|
||||
import { v4 } from 'uuid';
|
||||
import { orderBy } from 'lodash';
|
||||
import { observable, action, computed } from 'mobx';
|
||||
import Document from 'models/Document';
|
||||
import Collection from 'models/Collection';
|
||||
import type { Toast } from '../types';
|
||||
@ -12,7 +14,7 @@ class UiStore {
|
||||
@observable progressBarVisible: boolean = false;
|
||||
@observable editMode: boolean = false;
|
||||
@observable mobileSidebarVisible: boolean = false;
|
||||
@observable toasts: Toast[] = [];
|
||||
@observable toasts: Map<string, Toast> = new Map();
|
||||
|
||||
@action
|
||||
setActiveModal = (name: string, props: ?Object): void => {
|
||||
@ -85,14 +87,23 @@ class UiStore {
|
||||
showToast = (
|
||||
message: string,
|
||||
type?: 'warning' | 'error' | 'info' | 'success' = 'success'
|
||||
): void => {
|
||||
this.toasts.push({ message, type });
|
||||
) => {
|
||||
const id = v4();
|
||||
const createdAt = new Date().toISOString();
|
||||
this.toasts.set(id, { message, type, createdAt, id });
|
||||
return id;
|
||||
};
|
||||
|
||||
@action
|
||||
removeToast = (index: number): void => {
|
||||
this.toasts.splice(index, 1);
|
||||
removeToast = (id: string) => {
|
||||
this.toasts.delete(id);
|
||||
};
|
||||
|
||||
@computed
|
||||
get orderedToasts(): Toast[] {
|
||||
// $FlowIssue
|
||||
return orderBy(Array.from(this.toasts.values()), 'createdAt', 'desc');
|
||||
}
|
||||
}
|
||||
|
||||
export default UiStore;
|
||||
|
@ -2,6 +2,8 @@
|
||||
import Document from 'models/Document';
|
||||
|
||||
export type Toast = {
|
||||
id: string,
|
||||
createdAt: string,
|
||||
message: string,
|
||||
type: 'warning' | 'error' | 'info' | 'success',
|
||||
};
|
||||
|
Reference in New Issue
Block a user