Set up the user interface state model and set up the admin user onboarding #283
|
@ -0,0 +1,48 @@
|
||||||
|
import React, { createContext, PropsWithChildren, useEffect, useReducer } from "react";
|
||||||
|
import { Tabletop } from "visions-types";
|
||||||
|
import { assertNever } from "../plugins/Candela";
|
||||||
|
|
||||||
|
type AuthState = { type: "NoAdmin" } | { type: "Unauthed" } | { type: "Authed", username: string };
|
||||||
|
|
||||||
|
type TabletopState = {
|
||||||
|
auth: AuthState;
|
||||||
|
tabletop: Tabletop;
|
||||||
|
}
|
||||||
|
|
||||||
|
type StateAction = { type: "SetAuthState", state: AuthState }
|
||||||
|
| { type: "HandleMessage" };
|
||||||
|
|
||||||
|
const initialState = (): TabletopState => (
|
||||||
|
{
|
||||||
|
auth: { type: "Unauthed" },
|
||||||
|
tabletop: { backgroundColor: { red: 0, green: 0, blue: 0 }, backgroundImage: undefined }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const AppContext = createContext<TabletopState>(initialState());
|
||||||
|
|
||||||
|
interface StateProviderProps { }
|
||||||
|
|
||||||
|
export const StateProvider = ({ children }: PropsWithChildren<StateProviderProps>) => {
|
||||||
|
const [state, dispatch] = useReducer(stateReducer, initialState());
|
||||||
|
|
||||||
|
return <AppContext.Provider value={initialState()}>
|
||||||
|
{children}
|
||||||
|
</AppContext.Provider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateReducer = (state: TabletopState, action: StateAction): TabletopState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case "SetAuthState": {
|
||||||
|
return { ...state, auth: action.state };
|
||||||
|
}
|
||||||
|
case "HandleMessage": {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
assertNever(action);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,19 +2,17 @@ import React, { createContext, PropsWithChildren, useEffect, useReducer } from "
|
||||||
import useWebSocket from "react-use-websocket";
|
import useWebSocket from "react-use-websocket";
|
||||||
import { Message, Tabletop } from "visions-types";
|
import { Message, Tabletop } from "visions-types";
|
||||||
|
|
||||||
type TabletopState = {
|
type WebsocketState = { }
|
||||||
tabletop: Tabletop;
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialState = (): TabletopState => ({ tabletop: { backgroundColor: { red: 0, green: 0, blue: 0 }, backgroundImage: undefined } });
|
export const WebsocketContext = createContext<WebsocketState>({});
|
||||||
|
|
||||||
export const WebsocketContext = createContext<TabletopState>(initialState());
|
|
||||||
|
|
||||||
interface WebsocketProviderProps {
|
interface WebsocketProviderProps {
|
||||||
websocketUrl: string;
|
websocketUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WebsocketProvider = ({ websocketUrl, children }: PropsWithChildren<WebsocketProviderProps>) => {
|
export const WebsocketProvider = ({ websocketUrl, children }: PropsWithChildren<WebsocketProviderProps>) => {
|
||||||
|
return <div> {children} </div>;
|
||||||
|
/*
|
||||||
const { lastMessage } = useWebSocket(websocketUrl);
|
const { lastMessage } = useWebSocket(websocketUrl);
|
||||||
|
|
||||||
const [state, dispatch] = useReducer(handleMessage, initialState());
|
const [state, dispatch] = useReducer(handleMessage, initialState());
|
||||||
|
@ -29,8 +27,10 @@ export const WebsocketProvider = ({ websocketUrl, children }: PropsWithChildren<
|
||||||
return (<WebsocketContext.Provider value={state}>
|
return (<WebsocketContext.Provider value={state}>
|
||||||
{children}
|
{children}
|
||||||
</WebsocketContext.Provider>);
|
</WebsocketContext.Provider>);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
const handleMessage = (state: TabletopState, message: Message): TabletopState => {
|
const handleMessage = (state: TabletopState, message: Message): TabletopState => {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
|
@ -42,3 +42,4 @@ const handleMessage = (state: TabletopState, message: Message): TabletopState =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -8,12 +8,6 @@
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth > div {
|
|
||||||
border: var(--border-standard);
|
|
||||||
border-radius: var(--border-radius-standard);
|
|
||||||
padding: var(--padding-m);
|
|
||||||
}
|
|
||||||
|
|
||||||
.auth__input-line {
|
.auth__input-line {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { PropsWithChildren, ReactNode, useContext, useEffect, useState } from 'react';
|
import React, { PropsWithChildren, ReactNode, useContext, useEffect, useState } from 'react';
|
||||||
import { Client } from '../../client';
|
import { Client } from '../../client';
|
||||||
|
import { AppContext } from '../../components/StateProvider';
|
||||||
import { assertNever } from '../../plugins/Candela';
|
import { assertNever } from '../../plugins/Candela';
|
||||||
import './Authentication.css';
|
import './Authentication.css';
|
||||||
|
|
||||||
|
@ -7,16 +8,14 @@ interface AuthenticationProps {
|
||||||
client: Client;
|
client: Client;
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthState = "NoAdmin" | "Unauthed" | "Authed";
|
|
||||||
|
|
||||||
export const Authentication = ({ client, children }: PropsWithChildren<AuthenticationProps>) => {
|
export const Authentication = ({ client, children }: PropsWithChildren<AuthenticationProps>) => {
|
||||||
// No admin password set: prompt for the admin password
|
// No admin password set: prompt for the admin password
|
||||||
// Password set, nobody logged in: prompt for login
|
// Password set, nobody logged in: prompt for login
|
||||||
// User logged in: show the children
|
// User logged in: show the children
|
||||||
|
|
||||||
let [state, setState] = useState<AuthState>("Unauthed");
|
let { auth } = useContext(AppContext);
|
||||||
|
|
||||||
switch (state) {
|
switch (auth.type) {
|
||||||
case "NoAdmin": {
|
case "NoAdmin": {
|
||||||
return <div className="auth">
|
return <div className="auth">
|
||||||
<div className="card">
|
<div className="card">
|
||||||
|
@ -43,7 +42,7 @@ export const Authentication = ({ client, children }: PropsWithChildren<Authentic
|
||||||
return <div> {children} </div>;
|
return <div> {children} </div>;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
assertNever(state);
|
assertNever(auth);
|
||||||
return <div></div>;
|
return <div></div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { Client, PlayingField } from '../../client';
|
import { Client, PlayingField } from '../../client';
|
||||||
|
import { AppContext } from '../../components/StateProvider';
|
||||||
import { TabletopElement } from '../../components/Tabletop/Tabletop';
|
import { TabletopElement } from '../../components/Tabletop/Tabletop';
|
||||||
import { ThumbnailElement } from '../../components/Thumbnail/Thumbnail';
|
import { ThumbnailElement } from '../../components/Thumbnail/Thumbnail';
|
||||||
import { WebsocketContext } from '../../components/WebsocketProvider';
|
import { WebsocketContext } from '../../components/WebsocketProvider';
|
||||||
|
@ -10,7 +11,7 @@ interface GmViewProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GmView = ({ client }: GmViewProps) => {
|
export const GmView = ({ client }: GmViewProps) => {
|
||||||
const { tabletop } = useContext(WebsocketContext);
|
const { tabletop } = useContext(AppContext);
|
||||||
|
|
||||||
const [images, setImages] = useState<string[]>([]);
|
const [images, setImages] = useState<string[]>([]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { WebsocketContext } from '../../components/WebsocketProvider';
|
||||||
import { Client } from '../../client';
|
import { Client } from '../../client';
|
||||||
import { TabletopElement } from '../../components/Tabletop/Tabletop';
|
import { TabletopElement } from '../../components/Tabletop/Tabletop';
|
||||||
import Candela from '../../plugins/Candela';
|
import Candela from '../../plugins/Candela';
|
||||||
|
import { AppContext } from '../../components/StateProvider';
|
||||||
|
|
||||||
const TEST_CHARSHEET_UUID = "12df9c09-1f2f-4147-8eda-a97bd2a7a803";
|
const TEST_CHARSHEET_UUID = "12df9c09-1f2f-4147-8eda-a97bd2a7a803";
|
||||||
|
|
||||||
|
@ -12,7 +13,7 @@ interface PlayerViewProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PlayerView = ({ client }: PlayerViewProps) => {
|
export const PlayerView = ({ client }: PlayerViewProps) => {
|
||||||
const { tabletop } = useContext(WebsocketContext);
|
const { tabletop } = useContext(AppContext);
|
||||||
|
|
||||||
const [charsheet, setCharsheet] = useState(undefined);
|
const [charsheet, setCharsheet] = useState(undefined);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue