From 5fb6b432afd2f705122ca707aea5de73f551bbbd Mon Sep 17 00:00:00 2001 From: viktorstrate Date: Sun, 18 Jul 2021 15:04:06 +0200 Subject: [PATCH] Redesign notifications --- ui/src/Pages/LoginPage/LoginPage.tsx | 4 -- ui/src/Pages/LoginPage/loginUtilities.tsx | 1 - ui/src/apolloClient.ts | 5 +- ui/src/components/messages/Message.tsx | 33 +++++++++ .../components/messages/MessageProgress.jsx | 36 ---------- .../components/messages/MessageProgress.tsx | 30 ++++++++ .../messages/{Messages.jsx => Messages.tsx} | 72 +++++++------------ .../components/messages/SubscriptionsHook.ts | 1 + .../components/messages/icons/dismissIcon.svg | 1 + ui/src/components/sidebar/SidebarDownload.tsx | 9 +-- 10 files changed, 97 insertions(+), 95 deletions(-) create mode 100644 ui/src/components/messages/Message.tsx delete mode 100644 ui/src/components/messages/MessageProgress.jsx create mode 100644 ui/src/components/messages/MessageProgress.tsx rename ui/src/components/messages/{Messages.jsx => Messages.tsx} (61%) create mode 100644 ui/src/components/messages/icons/dismissIcon.svg diff --git a/ui/src/Pages/LoginPage/LoginPage.tsx b/ui/src/Pages/LoginPage/LoginPage.tsx index a06968ad..969ffbe7 100644 --- a/ui/src/Pages/LoginPage/LoginPage.tsx +++ b/ui/src/Pages/LoginPage/LoginPage.tsx @@ -117,10 +117,6 @@ const LoginForm = () => { ) } -// background-image: radial-gradient(0% 100%, #FF8246 0%, #D6264D 100%); -// border: 2px solid rgba(255,51,0,0.29); -// border-radius: 6px; - type LoginInputs = { username: string password: string diff --git a/ui/src/Pages/LoginPage/loginUtilities.tsx b/ui/src/Pages/LoginPage/loginUtilities.tsx index 7cd1b0c9..f27397f1 100644 --- a/ui/src/Pages/LoginPage/loginUtilities.tsx +++ b/ui/src/Pages/LoginPage/loginUtilities.tsx @@ -1,7 +1,6 @@ import { gql } from '@apollo/client' import { saveTokenCookie } from '../../helpers/authentication' import styled from 'styled-components' -// import { Container as SemanticContainer } from 'semantic-ui-react' export const checkInitialSetupQuery = gql` query CheckInitialSetup { diff --git a/ui/src/apolloClient.ts b/ui/src/apolloClient.ts index 3d28328a..1b200bd5 100644 --- a/ui/src/apolloClient.ts +++ b/ui/src/apolloClient.ts @@ -15,6 +15,7 @@ import urlJoin from 'url-join' import { clearTokenCookie } from './helpers/authentication' import { MessageState } from './components/messages/Messages' import { Message } from './components/messages/SubscriptionsHook' +import { NotificationType } from './__generated__/globalTypes' export const GRAPHQL_ENDPOINT = process.env.REACT_APP_API_ENDPOINT ? urlJoin(process.env.REACT_APP_API_ENDPOINT as string, '/graphql') @@ -103,9 +104,9 @@ const linkError = onError(({ graphQLErrors, networkError }) => { } if (errorMessages.length > 0) { - const newMessages = errorMessages.map(msg => ({ + const newMessages: Message[] = errorMessages.map(msg => ({ key: Math.random().toString(26), - type: 'message', + type: NotificationType.Message, props: { negative: true, ...msg, diff --git a/ui/src/components/messages/Message.tsx b/ui/src/components/messages/Message.tsx new file mode 100644 index 00000000..46a52a24 --- /dev/null +++ b/ui/src/components/messages/Message.tsx @@ -0,0 +1,33 @@ +import React from 'react' +import { forwardRef } from 'react' +import { ReactComponent as DismissIcon } from './icons/dismissIcon.svg' + +export type MessageProps = { + header: string + content?: string + children?: React.ReactNode + onDismiss?(): void +} + +const Message = forwardRef( + ( + { onDismiss, header, children, content }: MessageProps, + ref: React.ForwardedRef + ) => { + return ( +
+ +

{header}

+
{content}
+ {children} +
+ ) + } +) + +export default Message diff --git a/ui/src/components/messages/MessageProgress.jsx b/ui/src/components/messages/MessageProgress.jsx deleted file mode 100644 index 5e533ef3..00000000 --- a/ui/src/components/messages/MessageProgress.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import styled from 'styled-components' -import { Message, Progress } from 'semantic-ui-react' - -const StyledProgress = styled(Progress)` - position: absolute !important; - bottom: 0; - left: 0; - width: 100%; -` - -const MessageProgress = ({ header, content, percent = 0, ...props }) => { - return ( - - - {header} - {content} - - - - ) -} - -MessageProgress.propTypes = { - header: PropTypes.string, - content: PropTypes.any, - percent: PropTypes.number, -} - -export default MessageProgress diff --git a/ui/src/components/messages/MessageProgress.tsx b/ui/src/components/messages/MessageProgress.tsx new file mode 100644 index 00000000..1215ec2c --- /dev/null +++ b/ui/src/components/messages/MessageProgress.tsx @@ -0,0 +1,30 @@ +import React, { forwardRef } from 'react' +import MessagePlain, { MessageProps } from './Message' + +type MessageProgressProps = MessageProps & { + percent?: number +} + +const MessageProgress = forwardRef( + ( + { header, content, percent = 0, ...props }: MessageProgressProps, + ref: React.ForwardedRef + ) => { + let color = '#dc2625' + if (percent > 33) color = '#fbbf24' + if (percent > 66) color = '#56e263' + + return ( + +
+
+
+
+ ) + } +) + +export default MessageProgress diff --git a/ui/src/components/messages/Messages.jsx b/ui/src/components/messages/Messages.tsx similarity index 61% rename from ui/src/components/messages/Messages.jsx rename to ui/src/components/messages/Messages.tsx index d055822b..f1729511 100644 --- a/ui/src/components/messages/Messages.jsx +++ b/ui/src/components/messages/Messages.tsx @@ -1,10 +1,11 @@ import React, { useState } from 'react' import { animated, useTransition } from 'react-spring' -import { Message } from 'semantic-ui-react' import styled from 'styled-components' import { authToken } from '../../helpers/authentication' import MessageProgress from './MessageProgress' -import SubscriptionsHook from './SubscriptionsHook' +import MessagePlain from './Message' +import SubscriptionsHook, { Message } from './SubscriptionsHook' +import { NotificationType } from '../../__generated__/globalTypes' const Container = styled.div` position: fixed; @@ -17,7 +18,14 @@ const Container = styled.div` } ` -export const MessageState = { +type MessageStateType = { + set: React.Dispatch> + get: Message[] + add(message: Message): void + removeKey(key: string): void +} + +export const MessageState: MessageStateType = { set: fn => { console.warn('set function is not defined yet, called with', fn) }, @@ -39,37 +47,30 @@ export const MessageState = { } const Messages = () => { - const [messages, setMessages] = useState([]) + const [messages, setMessages] = useState([]) MessageState.set = setMessages MessageState.get = messages - const [refMap] = useState(() => new WeakMap()) - - const getMessageElement = (message, ref) => { - const dismissMessage = message => { + const getMessageElement = (message: Message): React.FunctionComponent => { + const dismissMessage = (message: Message) => { message.onDismiss && message.onDismiss() setMessages(messages => messages.filter(msg => msg.key != message.key)) } - const RefDiv = props =>
x && ref(x)} /> - - switch (message.type.toLowerCase()) { - case 'message': + switch (message.type) { + case NotificationType.Message: return props => ( - { dismissMessage(message) }} - floating {...message.props} {...props} /> ) - case 'progress': + case NotificationType.Progress: return props => ( { dismissMessage(message) }} @@ -77,36 +78,19 @@ const Messages = () => { {...props} /> ) + default: + throw new Error(`Invalid message type: ${message.type}`) } } - let refHooks = new Map() - messages.forEach(message => { - let resolveFunc = null - - const waitPromise = new Promise(resolve => { - resolveFunc = resolve - }) - - refHooks.set(message.key, { - done: resolveFunc, - promise: waitPromise, - }) - }) - const transitions = useTransition(messages.slice().reverse(), x => x.key, { from: { opacity: 0, height: '0px', }, - enter: item => async next => { - const refPromise = refHooks.get(item.key).promise - await refPromise - - await next({ - opacity: 1, - height: `${refMap.get(item).offsetHeight + 10}px`, - }) + enter: { + opacity: 1, + height: `100px`, }, leave: { opacity: 0, height: '0px' }, }) @@ -114,15 +98,7 @@ const Messages = () => { return ( {transitions.map(({ item, props: style, key }) => { - const getRef = ref => { - refMap.set(item, ref) - if (refHooks.has(item.key)) { - refHooks.get(item.key).done() - } - } - const MessageElement = getMessageElement(item, getRef) - - style.padding = 0 + const MessageElement = getMessageElement(item) return ( diff --git a/ui/src/components/messages/SubscriptionsHook.ts b/ui/src/components/messages/SubscriptionsHook.ts index 9ad17022..fad109dc 100644 --- a/ui/src/components/messages/SubscriptionsHook.ts +++ b/ui/src/components/messages/SubscriptionsHook.ts @@ -26,6 +26,7 @@ export interface Message { key: string type: NotificationType timeout?: number + onDismiss?: () => void props: { header: string content: string diff --git a/ui/src/components/messages/icons/dismissIcon.svg b/ui/src/components/messages/icons/dismissIcon.svg new file mode 100644 index 00000000..cc121dcf --- /dev/null +++ b/ui/src/components/messages/icons/dismissIcon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/components/sidebar/SidebarDownload.tsx b/ui/src/components/sidebar/SidebarDownload.tsx index 15a85b51..f9608221 100644 --- a/ui/src/components/sidebar/SidebarDownload.tsx +++ b/ui/src/components/sidebar/SidebarDownload.tsx @@ -12,6 +12,7 @@ import { sidebarDownloadQuery_media_downloads, } from './__generated__/sidebarDownloadQuery' import { SidebarSection, SidebarSectionTitle } from './SidebarComponents' +import { NotificationType } from '../../__generated__/globalTypes' export const SIDEBAR_DOWNLOAD_QUERY = gql` query sidebarDownloadQuery($mediaId: ID!) { @@ -111,12 +112,12 @@ const downloadMediaShowProgress = const notifyKey = Math.random().toString(26) MessageState.add({ key: notifyKey, - type: 'progress', + type: NotificationType.Progress, onDismiss, props: { header: 'Downloading photo', content: `Starting download`, - progress: 0, + percent: 0, }, }) @@ -133,7 +134,7 @@ const downloadMediaShowProgress = MessageState.add({ key: notifyKey, - type: 'progress', + type: NotificationType.Progress, onDismiss, props: { header: 'Downloading photo', @@ -151,7 +152,7 @@ const downloadMediaShowProgress = MessageState.add({ key: notifyKey, - type: 'progress', + type: NotificationType.Progress, props: { header: 'Downloading photo completed', content: `The photo has been downloaded`,