mirror of
https://github.com/zoriya/flood.git
synced 2026-06-14 23:00:17 +00:00
client: simplify AlertStore and fix "error" alerts
This commit is contained in:
@@ -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',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user