From fb7a8f03120d95a0aa7dd7a5c491a4d085ff1438 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Thu, 31 May 2018 12:07:49 -0700 Subject: [PATCH] Toast type (success/warning/etc) --- app/components/Layout/Layout.js | 2 +- app/components/Toasts/Toasts.js | 19 ++++++++++++------- app/components/Toasts/components/Toast.js | 15 +++++++-------- app/menus/ShareMenu.js | 2 +- app/scenes/Settings/Profile.js | 13 ++++++++----- app/scenes/Settings/components/SlackButton.js | 2 +- app/stores/AuthStore.js | 19 +++++++++++++------ app/stores/CollectionsStore.js | 1 - app/stores/UiStore.js | 10 +++++++--- app/types/index.js | 5 +++++ shared/styles/constants.js | 6 +++--- 11 files changed, 58 insertions(+), 36 deletions(-) diff --git a/app/components/Layout/Layout.js b/app/components/Layout/Layout.js index 17e316ba3..99b7a4ed5 100644 --- a/app/components/Layout/Layout.js +++ b/app/components/Layout/Layout.js @@ -112,7 +112,7 @@ class Layout extends React.Component { - + ); } diff --git a/app/components/Toasts/Toasts.js b/app/components/Toasts/Toasts.js index 2e5ff3334..efe25a437 100644 --- a/app/components/Toasts/Toasts.js +++ b/app/components/Toasts/Toasts.js @@ -1,14 +1,18 @@ // @flow import * as React from 'react'; -import { inject, observer } from 'mobx-react'; +import { observer } from 'mobx-react'; import styled from 'styled-components'; import { layout } from 'shared/styles/constants'; import Toast from './components/Toast'; +import UiStore from '../../stores/UiStore'; +type Props = { + ui: UiStore, +}; @observer -class Toasts extends React.Component<*> { - handleClose = index => { - this.props.ui.remove(index); +class Toasts extends React.Component { + handleClose = (index: number) => { + this.props.ui.removeToast(index); }; render() { @@ -16,11 +20,11 @@ class Toasts extends React.Component<*> { return ( - {ui.toasts.map((error, index) => ( + {ui.toasts.map((toast, index) => ( ))} @@ -35,6 +39,7 @@ const List = styled.ol` list-style: none; margin: 0; padding: 0; + z-index: 1000; `; -export default inject('ui')(Toasts); +export default Toasts; diff --git a/app/components/Toasts/components/Toast.js b/app/components/Toasts/components/Toast.js index 6efc2bd91..04bbc84f6 100644 --- a/app/components/Toasts/components/Toast.js +++ b/app/components/Toasts/components/Toast.js @@ -4,12 +4,12 @@ import styled from 'styled-components'; import { darken } from 'polished'; import { color } from 'shared/styles/constants'; import { fadeAndScaleIn } from 'shared/styles/animations'; +import type { Toast as TToast } from '../../../types'; type Props = { onRequestClose: () => void, closeAfterMs: number, - message: string, - type: 'warning' | 'error' | 'info', + toast: TToast, }; class Toast extends React.Component { @@ -17,7 +17,6 @@ class Toast extends React.Component { static defaultProps = { closeAfterMs: 3000, - type: 'warning', }; componentDidMount() { @@ -32,14 +31,14 @@ class Toast extends React.Component { } render() { - const { type, onRequestClose } = this.props; + const { toast, onRequestClose } = this.props; const message = - typeof this.props.message === 'string' - ? this.props.message - : this.props.message.toString(); + typeof toast.message === 'string' + ? toast.message + : toast.message.toString(); return ( - + {message} ); diff --git a/app/menus/ShareMenu.js b/app/menus/ShareMenu.js index 67c3b18f0..c2708d025 100644 --- a/app/menus/ShareMenu.js +++ b/app/menus/ShareMenu.js @@ -4,7 +4,7 @@ import { withRouter } from 'react-router-dom'; import { inject } from 'mobx-react'; import { MoreIcon } from 'outline-icons'; -import { Share } from 'types'; +import type { Share } from 'types'; import CopyToClipboard from 'components/CopyToClipboard'; import SharesStore from 'stores/SharesStore'; import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; diff --git a/app/scenes/Settings/Profile.js b/app/scenes/Settings/Profile.js index 86a8adbe2..15682737d 100644 --- a/app/scenes/Settings/Profile.js +++ b/app/scenes/Settings/Profile.js @@ -6,6 +6,7 @@ import styled from 'styled-components'; import { color, size } from 'shared/styles/constants'; import AuthStore from 'stores/AuthStore'; +import UiStore from 'stores/UiStore'; import ImageUpload from './components/ImageUpload'; import Input, { LabelText } from 'components/Input'; import Button from 'components/Button'; @@ -15,6 +16,7 @@ import Flex from 'shared/components/Flex'; type Props = { auth: AuthStore, + ui: UiStore, }; @observer @@ -41,6 +43,7 @@ class Profile extends React.Component { name: this.name, avatarUrl: this.avatarUrl, }); + this.props.ui.showToast('Profile saved', 'success'); }; handleNameChange = (ev: SyntheticInputEvent<*>) => { @@ -56,7 +59,7 @@ class Profile extends React.Component { }; render() { - const { user } = this.props.auth; + const { user, isSaving } = this.props.auth; if (!user) return null; const avatarUrl = this.avatarUrl || user.avatarUrl; @@ -73,7 +76,7 @@ class Profile extends React.Component { > - Upload new image + Upload @@ -85,8 +88,8 @@ class Profile extends React.Component { onChange={this.handleNameChange} required /> - @@ -101,7 +104,7 @@ const ProfilePicture = styled(Flex)` const avatarStyles = ` width: 80px; height: 80px; - border-radius: 10px; + border-radius: 50%; `; const AvatarContainer = styled(Flex)` diff --git a/app/scenes/Settings/components/SlackButton.js b/app/scenes/Settings/components/SlackButton.js index 6923317f1..cffdd5652 100644 --- a/app/scenes/Settings/components/SlackButton.js +++ b/app/scenes/Settings/components/SlackButton.js @@ -11,7 +11,7 @@ type Props = { auth: AuthStore, scopes?: string[], redirectUri?: string, - state?: string, + state: string, label?: string, }; diff --git a/app/stores/AuthStore.js b/app/stores/AuthStore.js index b21550699..16decad9c 100644 --- a/app/stores/AuthStore.js +++ b/app/stores/AuthStore.js @@ -12,6 +12,7 @@ class AuthStore { @observable user: ?User; @observable team: ?Team; @observable token: ?string; + @observable isSaving: boolean = false; @observable isLoading: boolean = false; @observable isSuspended: boolean = false; @observable suspendedContactEmail: ?string; @@ -50,13 +51,19 @@ class AuthStore { }; @action - updateUser = async (params: { name: string, avatarUrl?: string }) => { - const res = await client.post(`/user.update`, params); - invariant(res && res.data, 'User response not available'); + updateUser = async (params: { name: string, avatarUrl: ?string }) => { + this.isSaving = true; - runInAction('AuthStore#updateUser', () => { - this.user = res.data.user; - }); + try { + const res = await client.post(`/user.update`, params); + invariant(res && res.data, 'User response not available'); + + runInAction('AuthStore#updateUser', () => { + this.user = res.data; + }); + } finally { + this.isSaving = false; + } }; @action diff --git a/app/stores/CollectionsStore.js b/app/stores/CollectionsStore.js index bc2e84ea1..d23f610e1 100644 --- a/app/stores/CollectionsStore.js +++ b/app/stores/CollectionsStore.js @@ -4,7 +4,6 @@ import { client } from 'utils/ApiClient'; import _ from 'lodash'; import invariant from 'invariant'; -import stores from 'stores'; import BaseStore from './BaseStore'; import UiStore from './UiStore'; import Collection from 'models/Collection'; diff --git a/app/stores/UiStore.js b/app/stores/UiStore.js index 7caff9834..c5477a11b 100644 --- a/app/stores/UiStore.js +++ b/app/stores/UiStore.js @@ -2,6 +2,7 @@ import { observable, action } from 'mobx'; import Document from 'models/Document'; import Collection from 'models/Collection'; +import type { Toast } from '../types'; class UiStore { @observable activeModalName: ?string; @@ -11,7 +12,7 @@ class UiStore { @observable progressBarVisible: boolean = false; @observable editMode: boolean = false; @observable mobileSidebarVisible: boolean = false; - @observable toasts: string[] = observable.array([]); + @observable toasts: Toast[] = observable.array([]); /* Actions */ @action @@ -82,8 +83,11 @@ class UiStore { } @action - showToast = (message: string): void => { - this.toasts.push(message); + showToast = ( + message: string, + type?: 'warning' | 'error' | 'info' | 'success' = 'warning' + ): void => { + this.toasts.push({ message, type }); }; @action diff --git a/app/types/index.js b/app/types/index.js index 2ef769b57..f0824f8d9 100644 --- a/app/types/index.js +++ b/app/types/index.js @@ -9,6 +9,11 @@ export type User = { isSuspended?: boolean, }; +export type Toast = { + message: string, + type: 'warning' | 'error' | 'info' | 'success', +}; + export type Share = { id: string, url: string, diff --git a/shared/styles/constants.js b/shared/styles/constants.js index 303d6beee..3615b0736 100644 --- a/shared/styles/constants.js +++ b/shared/styles/constants.js @@ -47,9 +47,9 @@ export const color = { /* Brand */ primary: '#1AB6FF', danger: '#D0021B', - warning: '#f08a24' /* replace */, - success: '#43AC6A' /* replace */, - info: '#a0d3e8' /* replace */, + warning: '#f08a24', + success: '#1AB6FF', + info: '#a0d3e8', offline: '#000000', /* Dark Grays */