mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 20:59:03 +00:00
Redesign notifications
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
33
ui/src/components/messages/Message.tsx
Normal file
33
ui/src/components/messages/Message.tsx
Normal file
@@ -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<HTMLDivElement>
|
||||
) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className="bg-white shadow-md border rounded p-2 h-[84px] relative"
|
||||
>
|
||||
<button onClick={onDismiss} className="absolute top-3 right-2">
|
||||
<DismissIcon className="w-[10px] h-[10px] text-gray-700" />
|
||||
</button>
|
||||
<h1 className="font-semibold text-sm">{header}</h1>
|
||||
<div className="text-sm">{content}</div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default Message
|
||||
@@ -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 (
|
||||
<Message floating {...props}>
|
||||
<Message.Content>
|
||||
<Message.Header>{header}</Message.Header>
|
||||
{content}
|
||||
<StyledProgress
|
||||
percent={percent}
|
||||
size="tiny"
|
||||
attached="bottom"
|
||||
indicating
|
||||
/>
|
||||
</Message.Content>
|
||||
</Message>
|
||||
)
|
||||
}
|
||||
|
||||
MessageProgress.propTypes = {
|
||||
header: PropTypes.string,
|
||||
content: PropTypes.any,
|
||||
percent: PropTypes.number,
|
||||
}
|
||||
|
||||
export default MessageProgress
|
||||
30
ui/src/components/messages/MessageProgress.tsx
Normal file
30
ui/src/components/messages/MessageProgress.tsx
Normal file
@@ -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<HTMLDivElement>
|
||||
) => {
|
||||
let color = '#dc2625'
|
||||
if (percent > 33) color = '#fbbf24'
|
||||
if (percent > 66) color = '#56e263'
|
||||
|
||||
return (
|
||||
<MessagePlain header={header} content={content} {...props} ref={ref}>
|
||||
<div className="absolute bottom-0 left-0 right-0 h-[3px] rounded-b overflow-hidden">
|
||||
<div
|
||||
className="h-full transition-all duration-200"
|
||||
style={{ width: `${percent}%`, backgroundColor: color }}
|
||||
></div>
|
||||
</div>
|
||||
</MessagePlain>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default MessageProgress
|
||||
@@ -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<React.SetStateAction<Message[]>>
|
||||
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<Message[]>([])
|
||||
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 => <div {...props} ref={x => x && ref(x)} />
|
||||
|
||||
switch (message.type.toLowerCase()) {
|
||||
case 'message':
|
||||
switch (message.type) {
|
||||
case NotificationType.Message:
|
||||
return props => (
|
||||
<Message
|
||||
as={RefDiv}
|
||||
<MessagePlain
|
||||
onDismiss={() => {
|
||||
dismissMessage(message)
|
||||
}}
|
||||
floating
|
||||
{...message.props}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
case 'progress':
|
||||
case NotificationType.Progress:
|
||||
return props => (
|
||||
<MessageProgress
|
||||
as={RefDiv}
|
||||
onDismiss={() => {
|
||||
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 (
|
||||
<Container>
|
||||
{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 (
|
||||
<animated.div key={key} style={style}>
|
||||
@@ -26,6 +26,7 @@ export interface Message {
|
||||
key: string
|
||||
type: NotificationType
|
||||
timeout?: number
|
||||
onDismiss?: () => void
|
||||
props: {
|
||||
header: string
|
||||
content: string
|
||||
|
||||
1
ui/src/components/messages/icons/dismissIcon.svg
Normal file
1
ui/src/components/messages/icons/dismissIcon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 20 20"><g stroke="currentColor" stroke-width="3.5" fill="none" fill-rule="evenodd"><path d="m1.515 1.515 16.97 16.97M18.485 1.515l-16.97 16.97"/></g></svg>
|
||||
|
After Width: | Height: | Size: 173 B |
@@ -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`,
|
||||
|
||||
Reference in New Issue
Block a user