import React, { useState, useEffect, useMemo } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import * as actions from 'loot-core/src/client/actions'; import { View, Text, Button, ButtonWithLoading, Stack, ExternalLink } from 'loot-design/src/components/common'; import { styles, colors } from 'loot-design/src/style'; import Delete from 'loot-design/src/svg/Delete'; import Loading from 'loot-design/src/svg/v1/AnimatedLoading'; function compileMessage(message, actions, setLoading, onRemove) { return ( {message.split(/\n\n/).map((paragraph, idx) => { let parts = paragraph.split(/(\[[^\]]*\]\([^)]*\))/g); return ( {parts.map((part, idx) => { let match = part.match(/\[([^\]]*)\]\(([^)]*)\)/); if (match) { let [_, text, href] = match; if (href[0] === '#') { let actionName = href.slice(1); return ( // eslint-disable-next-line { e.preventDefault(); if (actions[actionName]) { setLoading(true); await actions[actionName](); onRemove(); } }} > {text} ); } return ( {match[1]} ); } return {part}; })} ); })} ); } function Notification({ notification, onRemove }) { let { id, type, title, message, messageActions, sticky, internal, button } = notification; let [loading, setLoading] = useState(false); let [overlayLoading, setOverlayLoading] = useState(false); useEffect(() => { if (type === 'error' && internal) { console.error('Internal error:', internal); } if (!sticky) { setTimeout(onRemove, 6500); } }, []); let positive = type === 'message'; let error = type === 'error'; let processedMessage = useMemo( () => compileMessage(message, messageActions, setOverlayLoading, onRemove), [message, messageActions] ); return ( {title && ( {title} )} {processedMessage} {button && ( { setLoading(true); await button.action(); onRemove(); setLoading(false); }} style={{ backgroundColor: 'transparent', border: `1px solid ${ positive ? colors.g5 : error ? colors.r4 : colors.y3 }`, color: 'currentColor', fontSize: 14, flexShrink: 0, '&:hover, &:active': { backgroundColor: positive ? colors.g9 : error ? colors.r10 : colors.y9 } }} > {button.title} )} {sticky && ( )} {overlayLoading && ( )} ); } function Notifications({ notifications, removeNotification, style }) { return ( {notifications.map(note => ( { if (note.onClose) { note.onClose(); } removeNotification(note.id); }} /> ))} ); } export default connect( state => ({ notifications: state.notifications.notifications }), dispatch => bindActionCreators(actions, dispatch) )(Notifications);