1
Fork 0

Improve messages

This commit is contained in:
viktorstrate 2020-02-16 16:52:00 +01:00
parent e7b2bc13d2
commit 34729c763f
7 changed files with 181 additions and 72 deletions

View File

@ -2,7 +2,8 @@ import React, { Component } from 'react'
import gql from 'graphql-tag'
import { Mutation, Query } from 'react-apollo'
import { Redirect } from 'react-router-dom'
import { Button, Form, Message, Container, Header } from 'semantic-ui-react'
import { Button, Form, Message, Header } from 'semantic-ui-react'
import { Container } from './LoginPage'
import { checkInitialSetupQuery, login } from './loginUtilFunctions'

View File

@ -2,7 +2,14 @@ import React, { Component } from 'react'
import gql from 'graphql-tag'
import { Mutation, Query } from 'react-apollo'
import { Redirect } from 'react-router-dom'
import { Button, Form, Message, Container, Header } from 'semantic-ui-react'
import styled from 'styled-components'
import {
Button,
Form,
Message,
Container as SemanticContainer,
Header,
} from 'semantic-ui-react'
import { checkInitialSetupQuery, login } from './loginUtilFunctions'
const authorizeMutation = gql`
@ -15,6 +22,10 @@ const authorizeMutation = gql`
}
`
export const Container = styled(SemanticContainer)`
margin-top: 80px;
`
class LoginPage extends Component {
constructor(props) {
super(props)
@ -49,7 +60,7 @@ class LoginPage extends Component {
<div>
<Container>
<Header as="h1" textAlign="center">
Welcome
Welcome to Photoview
</Header>
<Query query={checkInitialSetupQuery}>
{({ loading, error, data }) => {

View File

@ -6,6 +6,7 @@ import { onError } from 'apollo-link-error'
import { setContext } from 'apollo-link-context'
import { ApolloLink, split } from 'apollo-link'
import { getMainDefinition } from 'apollo-utilities'
import { MessageState } from './components/messages/Messages'
const httpLink = new HttpLink({
uri: process.env.GRAPHQL_ENDPOINT,
@ -44,7 +45,9 @@ const link = split(
)
const linkError = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
let errorMessages = []
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${JSON.stringify(
@ -52,9 +55,49 @@ const linkError = onError(({ graphQLErrors, networkError }) => {
)} Path: ${path}`
)
)
if (graphQLErrors.length == 1) {
errorMessages.push({
header: 'Something went wrong',
content: graphQLErrors[0].message,
})
} else if (graphQLErrors.length > 1) {
errorMessages.push({
header: 'Multiple things went wrong',
content: `Received ${graphQLErrors.length} errors from the server. See the console for more information`,
})
}
}
if (networkError) {
console.log(`[Network error]: ${JSON.stringify(networkError)}`)
localStorage.removeItem('token')
const errors = networkError.result.errors
if (errors.length == 1) {
errorMessages.push({
header: 'Server error',
content: `You are being logged out in an attempt to recover.\n${errors[0].message}`,
})
} else if (errors.length > 1) {
errorMessages.push({
header: 'Multiple server errors',
content: `Received ${graphQLErrors.length} errors from the server. You are being logged out in an attempt to recover.`,
})
}
}
if (errorMessages.length > 0) {
const newMessages = errorMessages.map(msg => ({
key: Math.random().toString(26),
type: 'message',
props: {
negative: true,
...msg,
},
}))
MessageState.set(messages => [...messages, ...newMessages])
}
})

View File

@ -1,22 +1,10 @@
import React, { useState, useEffect } from 'react'
import { useSubscription } from 'react-apollo'
import { useTransition, animated } from 'react-spring'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { Message } from 'semantic-ui-react'
import gql from 'graphql-tag'
import MessageProgress from './MessageProgress'
const syncSubscription = gql`
subscription syncSubscription {
scannerStatusUpdate {
finished
success
message
progress
}
}
`
import MessageProgress from './MessageProgress'
import SubscriptionsHook from './SubscriptionsHook'
const Container = styled.div`
position: fixed;
@ -25,66 +13,27 @@ const Container = styled.div`
width: 500px;
`
export let MessageState = {
set: null,
get: null,
add: message => {
MessageState.set(messages => [...messages, message])
},
}
const Messages = () => {
if (!localStorage.getItem('token')) {
return null
}
const { data, error } = useSubscription(syncSubscription)
console.log('Rendering messages')
const [messages, setMessages] = useState([])
MessageState.set = setMessages
MessageState.get = messages
const [refMap] = useState(() => new WeakMap())
useEffect(() => {
if (error) {
setMessages(state => [
...state,
{
key: Math.random().toString(26),
type: 'message',
props: {
header: 'Network error',
content: error.message,
negative: true,
},
},
])
}
if (!data) return
const update = data.scannerStatusUpdate
const newMessages = [...messages]
if (update.success) {
newMessages[0] = {
key: 'primary',
type: 'progress',
props: {
header: update.finished ? 'Synced' : 'Syncing',
content: update.message,
percent: update.progress,
positive: update.finished,
},
}
if (!update.finished) newMessages[0].props.onDismiss = null
} else {
const key = Math.random().toString(26)
newMessages.push({
key,
type: 'message',
props: {
header: 'Sync error',
content: update.message,
negative: true,
},
})
}
setMessages(newMessages)
}, [data, error])
const getMessageElement = (message, ref) => {
const dismissMessage = key => {
setMessages(messages => messages.filter(msg => msg.key != key))
@ -119,12 +68,35 @@ const Messages = () => {
}
}
let refHooks = new Map()
messages.forEach(message => {
let resolveFunc = null
const waitPromise = new Promise((resolve, reject) => {
resolveFunc = resolve
})
console.log(resolveFunc, waitPromise)
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 => {
console.log('HERE', refMap, item)
const refPromise = refHooks.get(item.key).promise
console.log('promise', refPromise)
await refPromise
console.log('AFTER PROMISE', refMap, item)
await next({
opacity: 1,
height: `${refMap.get(item).offsetHeight + 10}px`,
@ -137,7 +109,11 @@ const Messages = () => {
<Container>
{transitions.map(({ item, props: style, key }) => {
const getRef = ref => {
console.log('GET REF', refMap, refHooks, item.key)
refMap.set(item, ref)
if (refHooks.has(item.key)) {
refHooks.get(item.key).done()
}
}
const MessageElement = getMessageElement(item, getRef)
@ -149,6 +125,7 @@ const Messages = () => {
</animated.div>
)
})}
<SubscriptionsHook messages={messages} setMessages={setMessages} />
</Container>
)
}

View File

@ -0,0 +1,78 @@
import React, { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import { useSubscription } from 'react-apollo'
import gql from 'graphql-tag'
const syncSubscription = gql`
subscription syncSubscription {
scannerStatusUpdate {
finished
success
message
progress
}
}
`
const SubscriptionsHook = ({ messages, setMessages }) => {
const { data, error } = useSubscription(syncSubscription)
useEffect(() => {
if (error) {
setMessages(state => [
...state,
{
key: Math.random().toString(26),
type: 'message',
props: {
header: 'Network error',
content: error.message,
negative: true,
},
},
])
}
if (!data) return
const update = data.scannerStatusUpdate
const newMessages = [...messages]
if (update.success) {
newMessages[0] = {
key: 'primary',
type: 'progress',
props: {
header: update.finished ? 'Synced' : 'Syncing',
content: update.message,
percent: update.progress,
positive: update.finished,
},
}
if (!update.finished) newMessages[0].props.onDismiss = null
} else {
const key = Math.random().toString(26)
newMessages.push({
key,
type: 'message',
props: {
header: 'Sync error',
content: update.message,
negative: true,
},
})
}
setMessages(newMessages)
}, [data, error])
return null
}
SubscriptionsHook.propTypes = {
messages: PropTypes.array.isRequired,
setMessages: PropTypes.func.isRequired,
}
export default SubscriptionsHook

View File

@ -19,7 +19,7 @@ const StyledPhoto = styled(ProtectedImage)`
min-width: 100%;
position: relative;
object-fit: cover;
opacity: ${props => (props.loaded ? 1 : 0)};
opacity: ${({ loaded }) => (loaded ? 1 : 0)};
transition: opacity 300ms;
`
@ -30,7 +30,7 @@ const PhotoImg = photoProps => {
return (
<StyledPhoto
{...photoProps}
loaded={loaded}
loaded={loaded ? 1 : 0}
onLoad={() => {
setLoaded(true)
}}

View File

@ -18,5 +18,4 @@ const Main = () => (
ReactDOM.render(<Main />, document.getElementById('root'))
// TODO: Get Service Worker up and running
registerServiceWorker()