client: simplify AlertStore and fix "error" alerts

This commit is contained in:
Jesse Chan
2020-11-13 23:33:53 +08:00
parent e6181ef2c9
commit 7a14bd2e19
6 changed files with 55 additions and 105 deletions
@@ -46,9 +46,11 @@ const ClientActions = {
err
? {
id: 'general.error.unknown',
type: 'error',
}
: {
id: 'alert.settings.saved',
type: 'success',
},
);
}
@@ -46,9 +46,11 @@ const SettingActions = {
err
? {
id: 'general.error.unknown',
type: 'error',
}
: {
id: 'alert.settings.saved',
type: 'success',
},
);
}
+10 -20
View File
@@ -27,11 +27,9 @@ const {baseURI} = ConfigStore;
const emitTorrentAddedAlert = (count: number) => {
AlertStore.add({
accumulation: {
id: 'alert.torrent.add',
value: count,
},
id: 'alert.torrent.add',
type: 'success',
count,
});
};
@@ -80,20 +78,16 @@ const TorrentActions = {
.then(
() => {
AlertStore.add({
accumulation: {
id: 'alert.torrent.remove',
value: options.hashes.length,
},
id: 'alert.torrent.remove',
type: 'success',
count: options.hashes.length,
});
},
() => {
AlertStore.add({
accumulation: {
id: 'alert.torrent.remove.failed',
value: options.hashes.length,
},
id: 'alert.torrent.remove.failed',
type: 'error',
count: options.hashes.length,
});
},
),
@@ -160,20 +154,16 @@ const TorrentActions = {
.then(
() => {
AlertStore.add({
accumulation: {
id: 'alert.torrent.move',
value: options.hashes.length,
},
id: 'alert.torrent.move',
type: 'success',
count: options.hashes.length,
});
},
() => {
AlertStore.add({
accumulation: {
id: 'alert.torrent.move.failed',
value: options.hashes.length,
},
id: 'alert.torrent.move.failed',
type: 'error',
count: options.hashes.length,
});
},
);
@@ -1,18 +1,23 @@
import {FC} from 'react';
import {FormattedMessage} from 'react-intl';
import classnames from 'classnames';
import {observer} from 'mobx-react';
import AlertStore from '../../stores/AlertStore';
import CircleCheckmarkIcon from '../icons/CircleCheckmarkIcon';
import CircleExclamationIcon from '../icons/CircleExclamationIcon';
interface AlertProps {
id: string;
count?: number;
type?: 'success' | 'error';
}
const Alert: FC<AlertProps> = (props: AlertProps) => {
const {id, count, type} = props;
const Alert: FC<AlertProps> = observer((props: AlertProps) => {
const {id} = props;
const {count, type} = AlertStore.alerts[id] || {};
if (id == null || count == null || type == null) {
return null;
}
const alertClasses = classnames('alert', {
'is-success': type === 'success',
@@ -38,11 +43,6 @@ const Alert: FC<AlertProps> = (props: AlertProps) => {
</span>
</li>
);
};
Alert.defaultProps = {
count: 0,
type: 'success',
};
});
export default Alert;
@@ -6,19 +6,7 @@ import Alert from './Alert';
import AlertStore from '../../stores/AlertStore';
const Alerts: FC = observer(() => {
const {alerts, accumulation} = AlertStore;
const sortedAlerts = Object.keys(alerts)
.sort()
.map((id) => {
const alert = alerts[id];
if (alert.accumulation) {
alert.count = accumulation[alert.accumulation.id];
}
return alert;
});
const {sortedAlerts} = AlertStore;
return (
<TransitionGroup>
@@ -26,7 +14,7 @@ const Alerts: FC = observer(() => {
<CSSTransition classNames="alerts__list" timeout={{enter: 250, exit: 250}}>
<ul className="alerts__list" key="alerts-list">
{sortedAlerts.map((alert) => (
<Alert {...alert} key={alert.id} />
<Alert key={alert.id} id={alert.id} />
))}
</ul>
</CSSTransition>
+29 -61
View File
@@ -1,87 +1,55 @@
import {makeAutoObservable} from 'mobx';
interface Accumulation {
id: string;
value: number;
}
import {computed, extendObservable, makeAutoObservable} from 'mobx';
import sort from 'fast-sort';
export interface Alert {
id: string;
accumulation?: Accumulation;
type: 'success' | 'error';
count?: number;
duration?: number;
timer: number;
updated: number;
}
const DEFAULT_DURATION = 5 * 1000;
class AlertStore {
accumulation: Record<string, number> = {};
alerts: Record<string, Alert> = {};
@computed get sortedAlerts(): Array<Alert> {
return sort(Object.values(this.alerts)).asc((alert) => alert.updated);
}
constructor() {
makeAutoObservable(this);
}
accumulate(alert: Alert) {
if (alert.accumulation == null) {
return;
}
add(alert: Pick<Alert, 'id' | 'type' | 'count' | 'duration'>) {
const curAlert = this.alerts[alert.id];
const {id, value} = alert.accumulation;
if (curAlert != null) {
clearTimeout(curAlert.timer);
if (this.accumulation[id] == null) {
this.accumulation[id] = value;
} else {
this.accumulation[id] += value;
}
}
add(alert: Alert) {
const newAlert: Alert = {
...alert,
id: alert.id || `${Date.now()}`,
duration: alert.duration || DEFAULT_DURATION,
};
this.accumulate(newAlert);
this.scheduleCleanse(newAlert);
this.alerts[newAlert.id] = newAlert;
}
removeExpired = (alert: Alert) => {
const {accumulation} = alert;
if (accumulation) {
this.removeAccumulation(alert);
if (this.accumulation[accumulation.id] === 0) {
delete this.accumulation[accumulation.id];
delete this.alerts[alert.id];
if (alert.count != null) {
curAlert.count = (curAlert.count ?? 0) + alert.count;
}
curAlert.timer = this.scheduleClose(alert.id, alert.duration);
curAlert.updated = Date.now();
} else {
delete this.alerts[alert.id];
extendObservable(this.alerts, {
[alert.id]: {
...alert,
timer: this.scheduleClose(alert.id, alert.duration),
updated: Date.now(),
},
});
}
};
removeAccumulation(alert: Alert) {
if (alert.accumulation == null) {
return;
}
const {id, value} = alert.accumulation;
if (this.accumulation[id] == null) {
return;
}
this.accumulation[id] -= value;
}
scheduleCleanse(alert: Alert) {
setTimeout(() => this.removeExpired(alert), alert.duration);
scheduleClose(id: string, duration = DEFAULT_DURATION): number {
return window.setTimeout(() => {
delete this.alerts[id];
}, duration);
}
}