import React from 'react'; import { localized, UndoRedoStore, SyncbackMetadataTask } from 'mailspring-exports'; import { RetinaImg } from 'mailspring-component-kit'; import { CSSTransitionGroup } from 'react-transition-group'; function isUndoSend(block) { return ( block.tasks.length === 1 && block.tasks[0] instanceof SyncbackMetadataTask && block.tasks[0].value.isUndoSend ); } function getUndoSendExpiration(block) { return block.tasks[0].value.expiration * 1000; } function getDisplayDuration(block) { return isUndoSend(block) ? Math.max(400, getUndoSendExpiration(block) - Date.now()) : 3000; } class Countdown extends React.Component<{ expiration: number }, { x: number }> { animationDuration: string; _tickStart: NodeJS.Timeout; _tick: NodeJS.Timeout; constructor(props) { super(props); this.animationDuration = `${props.expiration - Date.now()}ms`; this.state = { x: 0 }; } componentWillReceiveProps(nextProps) { if (nextProps.expiration !== this.props.expiration) { this.animationDuration = `${nextProps.expiration - Date.now()}ms`; } } componentDidMount() { this._tickStart = setTimeout(() => { this.setState({ x: this.state.x + 1 }); this._tick = setInterval(() => { this.setState({ x: this.state.x + 1 }); }, 1000); }, this.props.expiration % 1000); } componentWillUnmount() { clearTimeout(this._tickStart); clearInterval(this._tick); } render() { // subtract a few ms so we never round up to start time + 1 by accident let diff = Math.min( Math.max(0, this.props.expiration - Date.now()), AppEnv.config.get('core.sending.undoSend') ); return (
{Math.ceil(diff / 1000)}
{diff > 0 && ( )}
); } } const UndoSendContent = ({ block, onMouseEnter, onMouseLeave }) => { return (
{localized('Sending soon...')}
AppEnv.commands.dispatch('core:undo')}> {localized('Undo')}
); }; const BasicContent = ({ block, onMouseEnter, onMouseLeave }) => { return (
{block.description}
AppEnv.commands.dispatch('core:undo')}> {localized('Undo')}
); }; export default class UndoRedoToast extends React.Component<{}, { block: any }> { static displayName = 'UndoRedoToast'; static containerRequired = false; _timeout: NodeJS.Timeout; _unlisten?: () => void; constructor(props) { super(props); this._timeout = null; this._unlisten = null; // Note: we explicitly do /not/ set initial state to the state of // the UndoRedoStore here because "getMostRecent" might be more // than 3000ms old. this.state = { block: null, }; } componentDidMount() { this._unlisten = UndoRedoStore.listen(() => { this.setState({ block: UndoRedoStore.getMostRecent(), }); }); } componentDidUpdate() { this._ensureTimeout(); } componentWillUnmount() { this._clearTimeout(); if (this._unlisten) { this._unlisten(); } } _clearTimeout() { clearTimeout(this._timeout); this._timeout = null; } _ensureTimeout() { this._clearTimeout(); if (this.state.block) { this._timeout = setTimeout(() => { this.setState({ block: null }); }, getDisplayDuration(this.state.block)); } } _onMouseEnter = () => { this._clearTimeout(); }; _onMouseLeave = () => { this._ensureTimeout(); }; render() { const { block } = this.state; const Component = block && (isUndoSend(block) ? UndoSendContent : BasicContent); return ( {block ? ( ) : null} ); } }