From 6928a2bd98055d8d906763457f95b9f69a8752c5 Mon Sep 17 00:00:00 2001 From: Jeremy Letto Date: Fri, 4 Feb 2022 16:03:39 -0600 Subject: [PATCH] Webatrice: show loading screen until protobuf initializes (#4559) * show loading screen until protobuf initializes * cleanup Co-authored-by: Jeremy Letto --- .../src/containers/App/AppShellRoutes.tsx | 4 +- .../src/containers/Initialize/Initialize.css | 88 +++++++++++++++++++ .../src/containers/Initialize/Initialize.tsx | 59 +++++++++++++ webclient/src/containers/index.ts | 1 + .../ResetPasswordForm/ResetPasswordForm.tsx | 2 +- webclient/src/material-theme.ts | 4 +- webclient/src/store/server/server.actions.ts | 3 + webclient/src/store/server/server.dispatch.ts | 3 + .../src/store/server/server.interfaces.ts | 1 + webclient/src/store/server/server.reducer.ts | 7 ++ .../src/store/server/server.selectors.ts | 1 + webclient/src/store/server/server.types.ts | 1 + webclient/src/types/routes.tsx | 1 + .../persistence/SessionPersistence.ts | 4 + .../src/websocket/services/ProtobufService.ts | 3 + 15 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 webclient/src/containers/Initialize/Initialize.css create mode 100644 webclient/src/containers/Initialize/Initialize.tsx diff --git a/webclient/src/containers/App/AppShellRoutes.tsx b/webclient/src/containers/App/AppShellRoutes.tsx index 699281ba..3cab196e 100644 --- a/webclient/src/containers/App/AppShellRoutes.tsx +++ b/webclient/src/containers/App/AppShellRoutes.tsx @@ -11,6 +11,7 @@ import { Server, Login, Logs, + Initialize, Unsupported } from 'containers'; @@ -25,9 +26,10 @@ const Routes = () => ( { } />} } /> } /> + } /> } /> - + ); diff --git a/webclient/src/containers/Initialize/Initialize.css b/webclient/src/containers/Initialize/Initialize.css new file mode 100644 index 00000000..d52eafd5 --- /dev/null +++ b/webclient/src/containers/Initialize/Initialize.css @@ -0,0 +1,88 @@ +.Initialize { + position: relative; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + text-align: center; +} + +.Initialize img { + width: 60px; +} + +h6.subtitle { + margin: 20px 0 10px; +} + +.Initialize-graphics { + position: absolute; + height: 100%; + width: 100%; + overflow: hidden; +} + +.Initialize-graphics__square { + position: absolute; + border: 2px solid; + opacity: .05; +} + +.Initialize-graphics__bar { + position: absolute; + opacity: .05; + border-radius: 8px; +} + +.Initialize-graphics__square.topLeft { + transform: rotate(27deg); + top: 38px; + left: 64px; + height: 134px; + width: 100px; + border-radius: 8px; +} + +.Initialize-graphics__square.topRight { + transform: rotate(10deg); + top: 74px; + right: 62px; + height: 50px; + width: 66px; + border-radius: 20px; +} + +.Initialize-graphics__square.bottomLeft { + transform: rotate(120deg); + bottom: 61px; + left: 66px; + height: 50px; + width: 66px; + border-radius: 20px; +} + +.Initialize-graphics__square.bottomRight { + transform: rotate(-24deg); + bottom: 54px; + right: 0; + height: 88px; + width: 66px; + border-radius: 8px; +} + +.Initialize-graphics__bar.bottomBar { + transform: rotate(30deg); + bottom: -4px; + left: -29px; + height: 50px; + width: 222px; +} + +.Initialize-graphics__bar.topBar { + transform: rotate(-330deg); + top: 10px; + right: -49px; + height: 50px; + width: 222px; +} diff --git a/webclient/src/containers/Initialize/Initialize.tsx b/webclient/src/containers/Initialize/Initialize.tsx new file mode 100644 index 00000000..85d9d47e --- /dev/null +++ b/webclient/src/containers/Initialize/Initialize.tsx @@ -0,0 +1,59 @@ +import { useState } from 'react'; +import { connect } from 'react-redux'; +import { Redirect, withRouter } from 'react-router-dom'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; + +import { Images } from 'images'; +import { ServerSelectors } from 'store'; +import { RouteEnum } from 'types'; + +import './Initialize.css'; + +const useStyles = makeStyles(theme => ({ + root: { + '& .Initialize-graphics': { + color: theme.palette.primary.contrastText, + }, + + '& .Initialize-graphics__bar': { + backgroundColor: theme.palette.primary.contrastText, + }, + }, +})); + +const Initialize = ({ initialized }: InitializeProps) => { + const classes = useStyles(); + + return initialized + ? + : ( +
+
+ logo + DID YOU KNOW + Cockatrice is run by volunteers + that love card games! +
+ +
+
+
+
+
+
+
+
+
+ ); +} + +interface InitializeProps { + initialized: boolean; +} + +const mapStateToProps = state => ({ + initialized: ServerSelectors.getInitialized(state), +}); + +export default withRouter(connect(mapStateToProps)(Initialize)); diff --git a/webclient/src/containers/index.ts b/webclient/src/containers/index.ts index de43d8ec..f43de1c8 100644 --- a/webclient/src/containers/index.ts +++ b/webclient/src/containers/index.ts @@ -7,4 +7,5 @@ export { default as Player } from './Player/Player'; export { default as Server } from './Server/Server'; export { default as Logs } from './Logs/Logs'; export { default as Login } from './Login/Login'; +export { default as Initialize } from './Initialize/Initialize'; export { default as Unsupported } from './Unsupported/Unsupported'; diff --git a/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.tsx b/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.tsx index 0731974f..154fe59f 100644 --- a/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.tsx +++ b/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.tsx @@ -34,7 +34,7 @@ const ResetPasswordForm = ({ onSubmit, userName }) => { if (!values.newPassword) { errors.newPassword = 'Required'; } else if (values.newPassword.length < 8) { - errors.password = 'Minimum of 8 characters required'; + errors.newPassword = 'Minimum of 8 characters required'; } if (!values.passwordAgain) { diff --git a/webclient/src/material-theme.ts b/webclient/src/material-theme.ts index 9faec010..c2f78498 100644 --- a/webclient/src/material-theme.ts +++ b/webclient/src/material-theme.ts @@ -193,11 +193,11 @@ export const materialTheme = createTheme({ fontSize: 14, fontWeight: 'bold', lineHeight: 1.4, - color: '#9E9E9E', + color: palette.grey[500], }, subtitle2: { lineHeight: 1.4, - color: '#9E9E9E', + color: palette.grey[500], }, // body1: {}, // body2: {}, diff --git a/webclient/src/store/server/server.actions.ts b/webclient/src/store/server/server.actions.ts index 62b79da6..c528ca50 100644 --- a/webclient/src/store/server/server.actions.ts +++ b/webclient/src/store/server/server.actions.ts @@ -4,6 +4,9 @@ import { WebSocketConnectOptions } from 'types'; import { Types } from './server.types'; export const Actions = { + initialized: () => ({ + type: Types.INITIALIZED + }), clearStore: () => ({ type: Types.CLEAR_STORE }), diff --git a/webclient/src/store/server/server.dispatch.ts b/webclient/src/store/server/server.dispatch.ts index 3e637bbe..d4b1639b 100644 --- a/webclient/src/store/server/server.dispatch.ts +++ b/webclient/src/store/server/server.dispatch.ts @@ -3,6 +3,9 @@ import { Actions } from './server.actions'; import { store } from 'store'; export const Dispatch = { + initialized: () => { + store.dispatch(Actions.initialized()); + }, clearStore: () => { store.dispatch(Actions.clearStore()); }, diff --git a/webclient/src/store/server/server.interfaces.ts b/webclient/src/store/server/server.interfaces.ts index 5846b91d..1de6f00d 100644 --- a/webclient/src/store/server/server.interfaces.ts +++ b/webclient/src/store/server/server.interfaces.ts @@ -39,6 +39,7 @@ export interface AccountActivationParams extends ServerRegisterParams { } export interface ServerState { + initialized: boolean; buddyList: User[]; ignoreList: User[]; info: ServerStateInfo; diff --git a/webclient/src/store/server/server.reducer.ts b/webclient/src/store/server/server.reducer.ts index 89546806..15ae6c18 100644 --- a/webclient/src/store/server/server.reducer.ts +++ b/webclient/src/store/server/server.reducer.ts @@ -6,6 +6,7 @@ import { ServerState } from './server.interfaces' import { Types } from './server.types'; const initialState: ServerState = { + initialized: false, buddyList: [], ignoreList: [], @@ -33,6 +34,12 @@ const initialState: ServerState = { export const serverReducer = (state = initialState, action: any) => { switch (action.type) { + case Types.INITIALIZED: { + return { + ...initialState, + initialized: true + } + } case Types.CLEAR_STORE: { return { ...initialState, diff --git a/webclient/src/store/server/server.selectors.ts b/webclient/src/store/server/server.selectors.ts index 16fa379a..0eca5779 100644 --- a/webclient/src/store/server/server.selectors.ts +++ b/webclient/src/store/server/server.selectors.ts @@ -5,6 +5,7 @@ interface State { } export const Selectors = { + getInitialized: ({ server }: State) => server.initialized, getMessage: ({ server }: State) => server.info.message, getName: ({ server }: State) => server.info.name, getVersion: ({ server }: State) => server.info.version, diff --git a/webclient/src/store/server/server.types.ts b/webclient/src/store/server/server.types.ts index b1adcdf0..f68b8e78 100644 --- a/webclient/src/store/server/server.types.ts +++ b/webclient/src/store/server/server.types.ts @@ -1,4 +1,5 @@ export const Types = { + INITIALIZED: '[Server] Initialized', CLEAR_STORE: '[Server] Clear Store', LOGIN_SUCCESSFUL: '[Server] Login Successful', LOGIN_FAILED: '[Server] Login Failed', diff --git a/webclient/src/types/routes.tsx b/webclient/src/types/routes.tsx index 51dde695..d91e8a7c 100644 --- a/webclient/src/types/routes.tsx +++ b/webclient/src/types/routes.tsx @@ -10,5 +10,6 @@ export enum RouteEnum { ACCOUNT = '/account', ADMINISTRATION = '/administration', REPLAYS = '/replays', + INITIALIZE = '/initialize', UNSUPPORTED = '/unsupported', } diff --git a/webclient/src/websocket/persistence/SessionPersistence.ts b/webclient/src/websocket/persistence/SessionPersistence.ts index d9a772eb..ef598bd9 100644 --- a/webclient/src/websocket/persistence/SessionPersistence.ts +++ b/webclient/src/websocket/persistence/SessionPersistence.ts @@ -5,6 +5,10 @@ import { sanitizeHtml } from 'websocket/utils'; import NormalizeService from '../utils/NormalizeService'; export class SessionPersistence { + static initialized() { + ServerDispatch.initialized(); + } + static clearStore() { ServerDispatch.clearStore(); } diff --git a/webclient/src/websocket/services/ProtobufService.ts b/webclient/src/websocket/services/ProtobufService.ts index 6cdee499..4a659a18 100644 --- a/webclient/src/websocket/services/ProtobufService.ts +++ b/webclient/src/websocket/services/ProtobufService.ts @@ -4,6 +4,7 @@ import ProtoFiles from '../ProtoFiles'; import { WebClient } from '../WebClient'; import { RoomEvents, SessionEvents } from '../events'; +import { SessionPersistence } from '../persistence'; export interface ProtobufEvents { [event: string]: Function; @@ -135,6 +136,8 @@ export class ProtobufService { if (err) { throw err; } + + SessionPersistence.initialized(); }); } }