Refactor map actions into hook
This commit is contained in:
parent
54bc3502df
commit
ef55f9c3a5
149
src/hooks/useMapActions.ts
Normal file
149
src/hooks/useMapActions.ts
Normal file
@ -0,0 +1,149 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import Action from "../actions/Action";
|
||||
import { DrawingState } from "../types/Drawing";
|
||||
import { FogState } from "../types/Fog";
|
||||
import { MapState } from "../types/MapState";
|
||||
import { Notes } from "../types/Note";
|
||||
import { TokenStates } from "../types/TokenState";
|
||||
|
||||
export type DrawingsAction = {
|
||||
type: "drawings";
|
||||
action: Action<DrawingState>;
|
||||
};
|
||||
export type FogsAction = { type: "fogs"; action: Action<FogState> };
|
||||
export type TokensAction = { type: "tokens"; action: Action<TokenStates> };
|
||||
export type NotesAction = { type: "notes"; action: Action<Notes> };
|
||||
|
||||
export type MapAction =
|
||||
| DrawingsAction
|
||||
| FogsAction
|
||||
| TokensAction
|
||||
| NotesAction;
|
||||
|
||||
export type MapActions = {
|
||||
actions: MapAction[][];
|
||||
actionIndex: number;
|
||||
};
|
||||
|
||||
export type AddActionsEventHandler = (actions: MapAction[]) => void;
|
||||
export type UpdateActionIndexEventHandler = (change: number) => void;
|
||||
export type ResetActionsEventHandler = () => void;
|
||||
|
||||
const defaultMapActions: MapActions = {
|
||||
actions: [],
|
||||
actionIndex: -1,
|
||||
};
|
||||
|
||||
function useMapActions(
|
||||
setCurrentMapState: React.Dispatch<React.SetStateAction<MapState | null>>
|
||||
): [
|
||||
MapActions,
|
||||
AddActionsEventHandler,
|
||||
UpdateActionIndexEventHandler,
|
||||
ResetActionsEventHandler
|
||||
] {
|
||||
const [mapActions, setMapActions] = useState(defaultMapActions);
|
||||
|
||||
function applyMapActionsToState(
|
||||
mapState: MapState,
|
||||
actions: MapAction[]
|
||||
): MapState {
|
||||
for (let mapAction of actions) {
|
||||
if (mapAction.type === "drawings") {
|
||||
mapState.drawShapes = mapAction.action.execute(mapState.drawShapes);
|
||||
} else if (mapAction.type === "fogs") {
|
||||
mapState.fogShapes = mapAction.action.execute(mapState.fogShapes);
|
||||
} else if (mapAction.type === "tokens") {
|
||||
mapState.tokens = mapAction.action.execute(mapState.tokens);
|
||||
} else if (mapAction.type === "notes") {
|
||||
mapState.notes = mapAction.action.execute(mapState.notes);
|
||||
}
|
||||
}
|
||||
return mapState;
|
||||
}
|
||||
|
||||
function undoMapActionsToState(
|
||||
mapState: MapState,
|
||||
actions: MapAction[]
|
||||
): MapState {
|
||||
for (let mapAction of actions) {
|
||||
if (mapAction.type === "drawings") {
|
||||
mapState.drawShapes = mapAction.action.undo(mapState.drawShapes);
|
||||
} else if (mapAction.type === "fogs") {
|
||||
mapState.fogShapes = mapAction.action.undo(mapState.fogShapes);
|
||||
} else if (mapAction.type === "tokens") {
|
||||
mapState.tokens = mapAction.action.undo(mapState.tokens);
|
||||
} else if (mapAction.type === "notes") {
|
||||
mapState.notes = mapAction.action.undo(mapState.notes);
|
||||
}
|
||||
}
|
||||
return mapState;
|
||||
}
|
||||
|
||||
function addActions(actions: MapAction[]) {
|
||||
setMapActions((prevActions) => {
|
||||
const newActions = [
|
||||
...prevActions.actions.slice(0, prevActions.actionIndex + 1),
|
||||
actions,
|
||||
];
|
||||
const newIndex = newActions.length - 1;
|
||||
return {
|
||||
actions: newActions,
|
||||
actionIndex: newIndex,
|
||||
};
|
||||
});
|
||||
|
||||
// Update map state by performing the actions on it
|
||||
setCurrentMapState((prevMapState) => {
|
||||
if (!prevMapState) {
|
||||
return prevMapState;
|
||||
}
|
||||
let state = { ...prevMapState };
|
||||
state = applyMapActionsToState(state, actions);
|
||||
return state;
|
||||
});
|
||||
}
|
||||
|
||||
function updateActionIndex(change: number) {
|
||||
const prevIndex = mapActions.actionIndex;
|
||||
const newIndex = Math.min(
|
||||
Math.max(mapActions.actionIndex + change, -1),
|
||||
mapActions.actions.length - 1
|
||||
);
|
||||
|
||||
setMapActions((prevMapActions) => ({
|
||||
...prevMapActions,
|
||||
actionIndex: newIndex,
|
||||
}));
|
||||
|
||||
// Update map state by either performing the actions or undoing them
|
||||
setCurrentMapState((prevMapState) => {
|
||||
if (!prevMapState) {
|
||||
return prevMapState;
|
||||
}
|
||||
let state = { ...prevMapState };
|
||||
if (prevIndex < newIndex) {
|
||||
// Redo
|
||||
for (let i = prevIndex + 1; i < newIndex + 1; i++) {
|
||||
const actions = mapActions.actions[i];
|
||||
state = applyMapActionsToState(state, actions);
|
||||
}
|
||||
} else {
|
||||
// Undo
|
||||
for (let i = prevIndex; i > newIndex; i--) {
|
||||
const actions = mapActions.actions[i];
|
||||
state = undoMapActionsToState(state, actions);
|
||||
}
|
||||
}
|
||||
return state;
|
||||
});
|
||||
}
|
||||
|
||||
const resetActions = useCallback(() => {
|
||||
setMapActions(defaultMapActions);
|
||||
}, []);
|
||||
|
||||
return [mapActions, addActions, updateActionIndex, resetActions];
|
||||
}
|
||||
|
||||
export default useMapActions;
|
@ -12,8 +12,8 @@ import { omit } from "../helpers/shared";
|
||||
|
||||
import useDebounce from "../hooks/useDebounce";
|
||||
import useNetworkedState from "../hooks/useNetworkedState";
|
||||
import useMapActions from "../hooks/useMapActions";
|
||||
|
||||
// Load session for auto complete
|
||||
import Session, { PeerDataEvent, PeerDataProgressEvent } from "./Session";
|
||||
|
||||
import Action from "../actions/Action";
|
||||
@ -23,7 +23,7 @@ import TokenBar from "../components/token/TokenBar";
|
||||
|
||||
import GlobalImageDrop from "../components/image/GlobalImageDrop";
|
||||
|
||||
import { Map as MapType, MapActions, MapAction } from "../types/Map";
|
||||
import { Map as MapType } from "../types/Map";
|
||||
import { MapState } from "../types/MapState";
|
||||
import {
|
||||
AssetManifest,
|
||||
@ -35,11 +35,6 @@ import { DrawingState } from "../types/Drawing";
|
||||
import { FogState } from "../types/Fog";
|
||||
import { Note } from "../types/Note";
|
||||
|
||||
const defaultMapActions: MapActions = {
|
||||
actions: [],
|
||||
actionIndex: -1,
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {object} NetworkedMapProps
|
||||
* @property {Session} session
|
||||
@ -219,106 +214,12 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
||||
await loadAssetManifestFromMap(newMap, newMapState);
|
||||
}
|
||||
|
||||
const [_, addActions, updateActionIndex, resetActions] =
|
||||
useMapActions(setCurrentMapState);
|
||||
|
||||
function handleMapReset(newMapState: MapState) {
|
||||
setCurrentMapState(newMapState, true, true);
|
||||
setMapActions(defaultMapActions);
|
||||
}
|
||||
|
||||
const [mapActions, setMapActions] = useState(defaultMapActions);
|
||||
|
||||
function applyMapActionsToState(
|
||||
mapState: MapState,
|
||||
actions: MapAction[]
|
||||
): MapState {
|
||||
for (let mapAction of actions) {
|
||||
if (mapAction.type === "drawings") {
|
||||
mapState.drawShapes = mapAction.action.execute(mapState.drawShapes);
|
||||
} else if (mapAction.type === "fogs") {
|
||||
mapState.fogShapes = mapAction.action.execute(mapState.fogShapes);
|
||||
} else if (mapAction.type === "tokens") {
|
||||
mapState.tokens = mapAction.action.execute(mapState.tokens);
|
||||
} else if (mapAction.type === "notes") {
|
||||
mapState.notes = mapAction.action.execute(mapState.notes);
|
||||
}
|
||||
}
|
||||
return mapState;
|
||||
}
|
||||
|
||||
function undoMapActionsToState(
|
||||
mapState: MapState,
|
||||
actions: MapAction[]
|
||||
): MapState {
|
||||
for (let mapAction of actions) {
|
||||
if (mapAction.type === "drawings") {
|
||||
mapState.drawShapes = mapAction.action.undo(mapState.drawShapes);
|
||||
} else if (mapAction.type === "fogs") {
|
||||
mapState.fogShapes = mapAction.action.undo(mapState.fogShapes);
|
||||
} else if (mapAction.type === "tokens") {
|
||||
mapState.tokens = mapAction.action.undo(mapState.tokens);
|
||||
} else if (mapAction.type === "notes") {
|
||||
mapState.notes = mapAction.action.undo(mapState.notes);
|
||||
}
|
||||
}
|
||||
return mapState;
|
||||
}
|
||||
|
||||
function addActions(actions: MapAction[]) {
|
||||
setMapActions((prevActions) => {
|
||||
const newActions = [
|
||||
...prevActions.actions.slice(0, prevActions.actionIndex + 1),
|
||||
actions,
|
||||
];
|
||||
const newIndex = newActions.length - 1;
|
||||
return {
|
||||
actions: newActions,
|
||||
actionIndex: newIndex,
|
||||
};
|
||||
});
|
||||
|
||||
// Update map state by performing the actions on it
|
||||
setCurrentMapState((prevMapState) => {
|
||||
if (!prevMapState) {
|
||||
return prevMapState;
|
||||
}
|
||||
let state = { ...prevMapState };
|
||||
state = applyMapActionsToState(state, actions);
|
||||
return state;
|
||||
});
|
||||
}
|
||||
|
||||
function updateActionIndex(change: number) {
|
||||
const prevIndex = mapActions.actionIndex;
|
||||
const newIndex = Math.min(
|
||||
Math.max(mapActions.actionIndex + change, -1),
|
||||
mapActions.actions.length - 1
|
||||
);
|
||||
|
||||
setMapActions((prevMapActions) => ({
|
||||
...prevMapActions,
|
||||
actionIndex: newIndex,
|
||||
}));
|
||||
|
||||
// Update map state by either performing the actions or undoing them
|
||||
setCurrentMapState((prevMapState) => {
|
||||
if (!prevMapState) {
|
||||
return prevMapState;
|
||||
}
|
||||
let state = { ...prevMapState };
|
||||
if (prevIndex < newIndex) {
|
||||
// Redo
|
||||
for (let i = prevIndex + 1; i < newIndex + 1; i++) {
|
||||
const actions = mapActions.actions[i];
|
||||
state = applyMapActionsToState(state, actions);
|
||||
}
|
||||
} else {
|
||||
// Undo
|
||||
for (let i = prevIndex; i > newIndex; i--) {
|
||||
const actions = mapActions.actions[i];
|
||||
state = undoMapActionsToState(state, actions);
|
||||
}
|
||||
}
|
||||
return state;
|
||||
});
|
||||
resetActions();
|
||||
}
|
||||
|
||||
function handleMapDraw(action: Action<DrawingState>) {
|
||||
@ -341,10 +242,10 @@ function NetworkedMapAndTokens({ session }: { session: Session }) {
|
||||
const previousMapIdRef = useRef<string>();
|
||||
useEffect(() => {
|
||||
if (currentMap && currentMap?.id !== previousMapIdRef.current) {
|
||||
setMapActions(defaultMapActions);
|
||||
resetActions();
|
||||
previousMapIdRef.current = currentMap?.id;
|
||||
}
|
||||
}, [currentMap]);
|
||||
}, [currentMap, resetActions]);
|
||||
|
||||
function handleNoteCreate(notes: Note[]) {
|
||||
setCurrentMapState((prevMapState) => {
|
||||
|
@ -1,10 +1,5 @@
|
||||
import React from "react";
|
||||
import Action from "../actions/Action";
|
||||
import { DrawingState } from "./Drawing";
|
||||
import { FogState } from "./Fog";
|
||||
import { Grid } from "./Grid";
|
||||
import { Notes } from "./Note";
|
||||
import { TokenStates } from "./TokenState";
|
||||
|
||||
export type MapToolId =
|
||||
| "map"
|
||||
@ -60,22 +55,3 @@ export type FileMap = BaseMap & {
|
||||
};
|
||||
|
||||
export type Map = DefaultMap | FileMap;
|
||||
|
||||
export type DrawingsAction = {
|
||||
type: "drawings";
|
||||
action: Action<DrawingState>;
|
||||
};
|
||||
export type FogsAction = { type: "fogs"; action: Action<FogState> };
|
||||
export type TokensAction = { type: "tokens"; action: Action<TokenStates> };
|
||||
export type NotesAction = { type: "notes"; action: Action<Notes> };
|
||||
|
||||
export type MapAction =
|
||||
| DrawingsAction
|
||||
| FogsAction
|
||||
| TokensAction
|
||||
| NotesAction;
|
||||
|
||||
export type MapActions = {
|
||||
actions: MapAction[][];
|
||||
actionIndex: number;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user