Webatrice: show loading screen until protobuf initializes (#4559)

* show loading screen until protobuf initializes

* cleanup

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
This commit is contained in:
Jeremy Letto 2022-02-04 16:03:39 -06:00 committed by GitHub
parent bb16ae09ef
commit 6928a2bd98
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 178 additions and 4 deletions

View file

@ -11,6 +11,7 @@ import {
Server, Server,
Login, Login,
Logs, Logs,
Initialize,
Unsupported Unsupported
} from 'containers'; } from 'containers';
@ -25,9 +26,10 @@ const Routes = () => (
{<Route path={RouteEnum.ROOM} render={() => <Room />} />} {<Route path={RouteEnum.ROOM} render={() => <Room />} />}
<Route path={RouteEnum.SERVER} render={() => <Server />} /> <Route path={RouteEnum.SERVER} render={() => <Server />} />
<Route path={RouteEnum.LOGIN} render={() => <Login />} /> <Route path={RouteEnum.LOGIN} render={() => <Login />} />
<Route path={RouteEnum.INITIALIZE} render={() => <Initialize />} />
<Route path={RouteEnum.UNSUPPORTED} render={() => <Unsupported />} /> <Route path={RouteEnum.UNSUPPORTED} render={() => <Unsupported />} />
<Redirect from="*" to={RouteEnum.LOGIN} /> <Redirect from="*" to={RouteEnum.INITIALIZE} />
</Switch> </Switch>
</div> </div>
); );

View file

@ -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;
}

View file

@ -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
? <Redirect from="*" to={RouteEnum.LOGIN} />
: (
<div className={'Initialize ' + classes.root}>
<div className='Initialize-content'>
<img src={Images.Logo} alt="logo" />
<Typography variant="subtitle1" className='subtitle'>DID YOU KNOW</Typography>
<Typography variant="subtitle2">Cockatrice is run by volunteers</Typography>
<Typography variant="subtitle2">that love card games!</Typography>
</div>
<div className="Initialize-graphics">
<div className="topLeft Initialize-graphics__square" />
<div className="topRight Initialize-graphics__square" />
<div className="bottomRight Initialize-graphics__square" />
<div className="bottomLeft Initialize-graphics__square" />
<div className="topBar Initialize-graphics__bar" />
<div className="bottomBar Initialize-graphics__bar" />
</div>
</div>
);
}
interface InitializeProps {
initialized: boolean;
}
const mapStateToProps = state => ({
initialized: ServerSelectors.getInitialized(state),
});
export default withRouter(connect(mapStateToProps)(Initialize));

View file

@ -7,4 +7,5 @@ export { default as Player } from './Player/Player';
export { default as Server } from './Server/Server'; export { default as Server } from './Server/Server';
export { default as Logs } from './Logs/Logs'; export { default as Logs } from './Logs/Logs';
export { default as Login } from './Login/Login'; export { default as Login } from './Login/Login';
export { default as Initialize } from './Initialize/Initialize';
export { default as Unsupported } from './Unsupported/Unsupported'; export { default as Unsupported } from './Unsupported/Unsupported';

View file

@ -34,7 +34,7 @@ const ResetPasswordForm = ({ onSubmit, userName }) => {
if (!values.newPassword) { if (!values.newPassword) {
errors.newPassword = 'Required'; errors.newPassword = 'Required';
} else if (values.newPassword.length < 8) { } else if (values.newPassword.length < 8) {
errors.password = 'Minimum of 8 characters required'; errors.newPassword = 'Minimum of 8 characters required';
} }
if (!values.passwordAgain) { if (!values.passwordAgain) {

View file

@ -193,11 +193,11 @@ export const materialTheme = createTheme({
fontSize: 14, fontSize: 14,
fontWeight: 'bold', fontWeight: 'bold',
lineHeight: 1.4, lineHeight: 1.4,
color: '#9E9E9E', color: palette.grey[500],
}, },
subtitle2: { subtitle2: {
lineHeight: 1.4, lineHeight: 1.4,
color: '#9E9E9E', color: palette.grey[500],
}, },
// body1: {}, // body1: {},
// body2: {}, // body2: {},

View file

@ -4,6 +4,9 @@ import { WebSocketConnectOptions } from 'types';
import { Types } from './server.types'; import { Types } from './server.types';
export const Actions = { export const Actions = {
initialized: () => ({
type: Types.INITIALIZED
}),
clearStore: () => ({ clearStore: () => ({
type: Types.CLEAR_STORE type: Types.CLEAR_STORE
}), }),

View file

@ -3,6 +3,9 @@ import { Actions } from './server.actions';
import { store } from 'store'; import { store } from 'store';
export const Dispatch = { export const Dispatch = {
initialized: () => {
store.dispatch(Actions.initialized());
},
clearStore: () => { clearStore: () => {
store.dispatch(Actions.clearStore()); store.dispatch(Actions.clearStore());
}, },

View file

@ -39,6 +39,7 @@ export interface AccountActivationParams extends ServerRegisterParams {
} }
export interface ServerState { export interface ServerState {
initialized: boolean;
buddyList: User[]; buddyList: User[];
ignoreList: User[]; ignoreList: User[];
info: ServerStateInfo; info: ServerStateInfo;

View file

@ -6,6 +6,7 @@ import { ServerState } from './server.interfaces'
import { Types } from './server.types'; import { Types } from './server.types';
const initialState: ServerState = { const initialState: ServerState = {
initialized: false,
buddyList: [], buddyList: [],
ignoreList: [], ignoreList: [],
@ -33,6 +34,12 @@ const initialState: ServerState = {
export const serverReducer = (state = initialState, action: any) => { export const serverReducer = (state = initialState, action: any) => {
switch (action.type) { switch (action.type) {
case Types.INITIALIZED: {
return {
...initialState,
initialized: true
}
}
case Types.CLEAR_STORE: { case Types.CLEAR_STORE: {
return { return {
...initialState, ...initialState,

View file

@ -5,6 +5,7 @@ interface State {
} }
export const Selectors = { export const Selectors = {
getInitialized: ({ server }: State) => server.initialized,
getMessage: ({ server }: State) => server.info.message, getMessage: ({ server }: State) => server.info.message,
getName: ({ server }: State) => server.info.name, getName: ({ server }: State) => server.info.name,
getVersion: ({ server }: State) => server.info.version, getVersion: ({ server }: State) => server.info.version,

View file

@ -1,4 +1,5 @@
export const Types = { export const Types = {
INITIALIZED: '[Server] Initialized',
CLEAR_STORE: '[Server] Clear Store', CLEAR_STORE: '[Server] Clear Store',
LOGIN_SUCCESSFUL: '[Server] Login Successful', LOGIN_SUCCESSFUL: '[Server] Login Successful',
LOGIN_FAILED: '[Server] Login Failed', LOGIN_FAILED: '[Server] Login Failed',

View file

@ -10,5 +10,6 @@ export enum RouteEnum {
ACCOUNT = '/account', ACCOUNT = '/account',
ADMINISTRATION = '/administration', ADMINISTRATION = '/administration',
REPLAYS = '/replays', REPLAYS = '/replays',
INITIALIZE = '/initialize',
UNSUPPORTED = '/unsupported', UNSUPPORTED = '/unsupported',
} }

View file

@ -5,6 +5,10 @@ import { sanitizeHtml } from 'websocket/utils';
import NormalizeService from '../utils/NormalizeService'; import NormalizeService from '../utils/NormalizeService';
export class SessionPersistence { export class SessionPersistence {
static initialized() {
ServerDispatch.initialized();
}
static clearStore() { static clearStore() {
ServerDispatch.clearStore(); ServerDispatch.clearStore();
} }

View file

@ -4,6 +4,7 @@ import ProtoFiles from '../ProtoFiles';
import { WebClient } from '../WebClient'; import { WebClient } from '../WebClient';
import { RoomEvents, SessionEvents } from '../events'; import { RoomEvents, SessionEvents } from '../events';
import { SessionPersistence } from '../persistence';
export interface ProtobufEvents { export interface ProtobufEvents {
[event: string]: Function; [event: string]: Function;
@ -135,6 +136,8 @@ export class ProtobufService {
if (err) { if (err) {
throw err; throw err;
} }
SessionPersistence.initialized();
}); });
} }
} }