Split tokens into data and state to avoid data duplication

This commit is contained in:
Mitchell McCaffrey 2020-04-24 18:39:11 +10:00
parent 65478d555b
commit ece974c5d9
4 changed files with 59 additions and 43 deletions

View File

@ -21,8 +21,9 @@ const maxZoom = 5;
function Map({ function Map({
map, map,
mapState, mapState,
onMapTokenChange, tokens,
onMapTokenRemove, onMapTokenStateChange,
onMapTokenStateRemove,
onMapChange, onMapChange,
onMapStateChange, onMapStateChange,
onMapDraw, onMapDraw,
@ -31,13 +32,13 @@ function Map({
}) { }) {
const mapSource = useDataSource(map, defaultMapSources); const mapSource = useDataSource(map, defaultMapSources);
function handleProxyDragEnd(isOnMap, token) { function handleProxyDragEnd(isOnMap, tokenState) {
if (isOnMap && onMapTokenChange) { if (isOnMap && onMapTokenStateChange) {
onMapTokenChange(token); onMapTokenStateChange(tokenState);
} }
if (!isOnMap && onMapTokenRemove) { if (!isOnMap && onMapTokenStateRemove) {
onMapTokenRemove(token); onMapTokenStateRemove(tokenState);
} }
} }
@ -240,10 +241,11 @@ function Map({
}} }}
> >
{mapState && {mapState &&
Object.values(mapState.tokens).map((token) => ( Object.values(mapState.tokens).map((tokenState) => (
<MapToken <MapToken
key={token.id} key={tokenState.id}
token={token} token={tokens.find((token) => token.id === tokenState.tokenId)}
tokenState={tokenState}
tokenSizePercent={tokenSizePercent} tokenSizePercent={tokenSizePercent}
className={`${mapTokenProxyClassName} ${mapTokenMenuClassName}`} className={`${mapTokenProxyClassName} ${mapTokenMenuClassName}`}
/> />
@ -335,7 +337,7 @@ function Map({
/> />
<TokenMenu <TokenMenu
tokenClassName={mapTokenMenuClassName} tokenClassName={mapTokenMenuClassName}
onTokenChange={onMapTokenChange} onTokenChange={onMapTokenStateChange}
tokens={mapState && mapState.tokens} tokens={mapState && mapState.tokens}
/> />
</> </>

View File

@ -9,7 +9,7 @@ import useDataSource from "../../helpers/useDataSource";
import { tokenSources } from "../../tokens"; import { tokenSources } from "../../tokens";
function MapToken({ token, tokenSizePercent, className }) { function MapToken({ token, tokenState, tokenSizePercent, className }) {
const imageSource = useDataSource(token, tokenSources); const imageSource = useDataSource(token, tokenSources);
const imageRef = useRef(); const imageRef = useRef();
@ -19,7 +19,7 @@ function MapToken({ token, tokenSizePercent, className }) {
return ( return (
<Box <Box
style={{ style={{
transform: `translate(${token.x * 100}%, ${token.y * 100}%)`, transform: `translate(${tokenState.x * 100}%, ${tokenState.y * 100}%)`,
width: "100%", width: "100%",
height: "100%", height: "100%",
}} }}
@ -30,7 +30,7 @@ function MapToken({ token, tokenSizePercent, className }) {
> >
<Box <Box
style={{ style={{
width: `${tokenSizePercent * (token.size || 1)}%`, width: `${tokenSizePercent * (tokenState.size || 1)}%`,
}} }}
sx={{ sx={{
position: "absolute", position: "absolute",
@ -54,11 +54,13 @@ function MapToken({ token, tokenSizePercent, className }) {
}} }}
src={imageSource} src={imageSource}
// pass id into the dom element which is then used by the ProxyToken // pass id into the dom element which is then used by the ProxyToken
data-id={token.id} data-id={tokenState.id}
ref={imageRef} ref={imageRef}
/> />
{token.statuses && <TokenStatus statuses={token.statuses} />} {tokenState.statuses && (
{token.label && <TokenLabel label={token.label} />} <TokenStatus statuses={tokenState.statuses} />
)}
{tokenState.label && <TokenLabel label={tokenState.label} />}
</Box> </Box>
</Box> </Box>
</Box> </Box>

View File

@ -1,10 +1,8 @@
import React, { useState, useEffect } from "react"; import React, { useState } from "react";
import { Box } from "theme-ui"; import { Box } from "theme-ui";
import shortid from "shortid"; import shortid from "shortid";
import SimpleBar from "simplebar-react"; import SimpleBar from "simplebar-react";
import { tokens as defaultTokens } from "../../tokens";
import ListToken from "./ListToken"; import ListToken from "./ListToken";
import ProxyToken from "./ProxyToken"; import ProxyToken from "./ProxyToken";
import NumberInput from "../NumberInput"; import NumberInput from "../NumberInput";
@ -13,27 +11,20 @@ import { fromEntries } from "../../helpers/shared";
const listTokenClassName = "list-token"; const listTokenClassName = "list-token";
function Tokens({ onCreateMapToken }) { function Tokens({ onCreateMapTokenState, tokens }) {
const [tokens, setTokens] = useState([]);
useEffect(() => {
const defaultTokensWithIds = [];
for (let defaultToken of defaultTokens) {
defaultTokensWithIds.push({ ...defaultToken, id: defaultToken.name });
}
setTokens(defaultTokensWithIds);
}, []);
const [tokenSize, setTokenSize] = useState(1); const [tokenSize, setTokenSize] = useState(1);
function handleProxyDragEnd(isOnMap, token) { function handleProxyDragEnd(isOnMap, token) {
if (isOnMap && onCreateMapToken) { if (isOnMap && onCreateMapTokenState) {
// Give the token an id // Create a token state from the dragged token
onCreateMapToken({ onCreateMapTokenState({
...token,
id: shortid.generate(), id: shortid.generate(),
tokenId: token.id,
size: tokenSize, size: tokenSize,
label: "", label: "",
statuses: [], statuses: [],
x: token.x,
y: token.y,
}); });
} }
} }

View File

@ -19,6 +19,8 @@ import AuthModal from "../modals/AuthModal";
import AuthContext from "../contexts/AuthContext"; import AuthContext from "../contexts/AuthContext";
import { tokens as defaultTokens } from "../tokens";
function Game() { function Game() {
const { id: gameId } = useParams(); const { id: gameId } = useParams();
const { authenticationStatus, userId } = useContext(AuthContext); const { authenticationStatus, userId } = useContext(AuthContext);
@ -70,7 +72,7 @@ function Game() {
} }
} }
async function handleMapTokenChange(token) { async function handleMapTokenStateChange(token) {
if (mapState === null) { if (mapState === null) {
return; return;
} }
@ -83,18 +85,18 @@ function Game() {
})); }));
for (let peer of Object.values(peers)) { for (let peer of Object.values(peers)) {
const data = { [token.id]: token }; const data = { [token.id]: token };
peer.connection.send({ id: "tokenEdit", data }); peer.connection.send({ id: "tokenStateEdit", data });
} }
} }
function handleMapTokenRemove(token) { function handleMapTokenStateRemove(token) {
setMapState((prevMapState) => { setMapState((prevMapState) => {
const { [token.id]: old, ...rest } = prevMapState.tokens; const { [token.id]: old, ...rest } = prevMapState.tokens;
return { ...prevMapState, tokens: rest }; return { ...prevMapState, tokens: rest };
}); });
for (let peer of Object.values(peers)) { for (let peer of Object.values(peers)) {
const data = { [token.id]: token }; const data = { [token.id]: token };
peer.connection.send({ id: "tokenRemove", data }); peer.connection.send({ id: "tokenStateRemove", data });
} }
} }
@ -196,13 +198,13 @@ function Game() {
if (data.id === "mapState") { if (data.id === "mapState") {
setMapState(data.data); setMapState(data.data);
} }
if (data.id === "tokenEdit") { if (data.id === "tokenStateEdit") {
setMapState((prevMapState) => ({ setMapState((prevMapState) => ({
...prevMapState, ...prevMapState,
tokens: { ...prevMapState.tokens, ...data.data }, tokens: { ...prevMapState.tokens, ...data.data },
})); }));
} }
if (data.id === "tokenRemove") { if (data.id === "tokenStateRemove") {
setMapState((prevMapState) => ({ setMapState((prevMapState) => ({
...prevMapState, ...prevMapState,
tokens: omit(prevMapState.tokens, Object.keys(data.data)), tokens: omit(prevMapState.tokens, Object.keys(data.data)),
@ -317,6 +319,21 @@ function Game() {
} }
}, [stream, peers, handleStreamEnd]); }, [stream, peers, handleStreamEnd]);
/**
* Token data
*/
const [tokens, setTokens] = useState([]);
useEffect(() => {
const defaultTokensWithIds = [];
for (let defaultToken of defaultTokens) {
defaultTokensWithIds.push({
...defaultToken,
id: `__default-${defaultToken.name}`,
});
}
setTokens(defaultTokensWithIds);
}, []);
return ( return (
<> <>
<Flex sx={{ flexDirection: "column", height: "100%" }}> <Flex sx={{ flexDirection: "column", height: "100%" }}>
@ -336,15 +353,19 @@ function Game() {
<Map <Map
map={map} map={map}
mapState={mapState} mapState={mapState}
onMapTokenChange={handleMapTokenChange} tokens={tokens}
onMapTokenRemove={handleMapTokenRemove} onMapTokenStateChange={handleMapTokenStateChange}
onMapTokenStateRemove={handleMapTokenStateRemove}
onMapChange={handleMapChange} onMapChange={handleMapChange}
onMapStateChange={handleMapStateChange} onMapStateChange={handleMapStateChange}
onMapDraw={handleMapDraw} onMapDraw={handleMapDraw}
onMapDrawUndo={handleMapDrawUndo} onMapDrawUndo={handleMapDrawUndo}
onMapDrawRedo={handleMapDrawRedo} onMapDrawRedo={handleMapDrawRedo}
/> />
<Tokens onCreateMapToken={handleMapTokenChange} /> <Tokens
tokens={tokens}
onCreateMapTokenState={handleMapTokenStateChange}
/>
</Flex> </Flex>
</Flex> </Flex>
<Banner isOpen={!!peerError} onRequestClose={() => setPeerError(null)}> <Banner isOpen={!!peerError} onRequestClose={() => setPeerError(null)}>