Toast notifications.

This commit is contained in:
Mike Cao 2020-08-31 15:57:59 -07:00
parent 2837251db7
commit aec012fb79
6 changed files with 69 additions and 1 deletions

1
assets/times.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>

After

Width:  |  Height:  |  Size: 468 B

View File

@ -0,0 +1,26 @@
import React, { useEffect } from 'react';
import ReactDOM from 'react-dom';
import { useSpring, animated } from 'react-spring';
import styles from './Toast.module.css';
import Icon from 'components/common/Icon';
import Close from 'assets/times.svg';
export default function Toast({ message, timeout = 3000, onClose }) {
const props = useSpring({
opacity: 1,
transform: 'translate3d(0,0px,0)',
from: { opacity: 0, transform: 'translate3d(0,-40px,0)' },
});
useEffect(() => {
setTimeout(onClose, timeout);
}, []);
return ReactDOM.createPortal(
<animated.div className={styles.toast} style={props} onClick={onClose}>
<div className={styles.message}>{message}</div>
<Icon className={styles.close} icon={<Close />} size="small" />
</animated.div>,
document.getElementById('__modals'),
);
}

View File

@ -0,0 +1,25 @@
.toast {
position: absolute;
top: 30px;
left: 0;
right: 0;
width: 300px;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 16px;
color: var(--gray50);
background: var(--green400);
margin: auto;
z-index: 2;
cursor: pointer;
}
.message {
font-size: var(--font-size-normal);
}
.close {
margin-left: 20px;
}

View File

@ -14,12 +14,14 @@ import Plus from 'assets/plus.svg';
import Trash from 'assets/trash.svg'; import Trash from 'assets/trash.svg';
import Check from 'assets/check.svg'; import Check from 'assets/check.svg';
import styles from './AccountSettings.module.css'; import styles from './AccountSettings.module.css';
import Toast from '../common/Toast';
export default function AccountSettings() { export default function AccountSettings() {
const [addAccount, setAddAccount] = useState(); const [addAccount, setAddAccount] = useState();
const [editAccount, setEditAccount] = useState(); const [editAccount, setEditAccount] = useState();
const [deleteAccount, setDeleteAccount] = useState(); const [deleteAccount, setDeleteAccount] = useState();
const [saved, setSaved] = useState(0); const [saved, setSaved] = useState(0);
const [message, setMessage] = useState();
const { data } = useFetch(`/api/accounts`, {}, { update: [saved] }); const { data } = useFetch(`/api/accounts`, {}, { update: [saved] });
const Checkmark = ({ is_admin }) => (is_admin ? <Icon icon={<Check />} size="medium" /> : null); const Checkmark = ({ is_admin }) => (is_admin ? <Icon icon={<Check />} size="medium" /> : null);
@ -52,6 +54,7 @@ export default function AccountSettings() {
function handleSave() { function handleSave() {
setSaved(state => state + 1); setSaved(state => state + 1);
setMessage('Saved successfully.');
handleClose(); handleClose();
} }
@ -97,6 +100,7 @@ export default function AccountSettings() {
/> />
</Modal> </Modal>
)} )}
{message && <Toast message={message} onClose={() => setMessage(null)} />}
</> </>
); );
} }

View File

@ -5,12 +5,19 @@ import Button from 'components/common/Button';
import ChangePasswordForm from '../forms/ChangePasswordForm'; import ChangePasswordForm from '../forms/ChangePasswordForm';
import Modal from 'components/common/Modal'; import Modal from 'components/common/Modal';
import Dots from 'assets/ellipsis-h.svg'; import Dots from 'assets/ellipsis-h.svg';
import Toast from '../common/Toast';
export default function ProfileSettings() { export default function ProfileSettings() {
const user = useSelector(state => state.user); const user = useSelector(state => state.user);
const [changePassword, setChangePassword] = useState(false); const [changePassword, setChangePassword] = useState(false);
const [message, setMessage] = useState();
const { user_id } = user; const { user_id } = user;
function handleSave() {
setChangePassword(false);
setMessage('Saved successfully.');
}
return ( return (
<> <>
<PageHeader> <PageHeader>
@ -27,11 +34,12 @@ export default function ProfileSettings() {
<Modal title="Change password"> <Modal title="Change password">
<ChangePasswordForm <ChangePasswordForm
values={{ user_id }} values={{ user_id }}
onSave={() => setChangePassword(false)} onSave={handleSave}
onClose={() => setChangePassword(false)} onClose={() => setChangePassword(false)}
/> />
</Modal> </Modal>
)} )}
{message && <Toast message={message} onClose={() => setMessage(null)} />}
</> </>
); );
} }

View File

@ -17,6 +17,7 @@ import Code from 'assets/code.svg';
import Link from 'assets/link.svg'; import Link from 'assets/link.svg';
import styles from './WebsiteSettings.module.css'; import styles from './WebsiteSettings.module.css';
import useFetch from '../../hooks/useFetch'; import useFetch from '../../hooks/useFetch';
import Toast from '../common/Toast';
export default function WebsiteSettings() { export default function WebsiteSettings() {
const [editWebsite, setEditWebsite] = useState(); const [editWebsite, setEditWebsite] = useState();
@ -25,6 +26,7 @@ export default function WebsiteSettings() {
const [showCode, setShowCode] = useState(); const [showCode, setShowCode] = useState();
const [showUrl, setShowUrl] = useState(); const [showUrl, setShowUrl] = useState();
const [saved, setSaved] = useState(0); const [saved, setSaved] = useState(0);
const [message, setMessage] = useState();
const { data } = useFetch(`/api/websites`, {}, { update: [saved] }); const { data } = useFetch(`/api/websites`, {}, { update: [saved] });
const Buttons = row => ( const Buttons = row => (
@ -66,6 +68,7 @@ export default function WebsiteSettings() {
function handleSave() { function handleSave() {
setSaved(state => state + 1); setSaved(state => state + 1);
setMessage('Saved successfully.');
handleClose(); handleClose();
} }
@ -127,6 +130,7 @@ export default function WebsiteSettings() {
<ShareUrlForm values={showUrl} onClose={handleClose} /> <ShareUrlForm values={showUrl} onClose={handleClose} />
</Modal> </Modal>
)} )}
{message && <Toast message={message} onClose={() => setMessage(null)} />}
</> </>
); );
} }