63 lines
1.9 KiB
TypeScript
63 lines
1.9 KiB
TypeScript
import type { AppState, HistorySnapshot } from "@editor/state";
|
|
import type { CommandContext } from "./command";
|
|
import { commandIds } from "./ids";
|
|
import type { CommandId, CommandPayloads } from "./payloads";
|
|
import type { CommandRegistry } from "./registry";
|
|
|
|
export type Dispatch = <TCommandId extends CommandId>(commandId: TCommandId, payload: CommandPayloads[TCommandId]) => AppState;
|
|
|
|
export type CommandDispatcher = {
|
|
dispatch: Dispatch;
|
|
};
|
|
|
|
export function createCommandDispatcher(options: {
|
|
registry: CommandRegistry;
|
|
getState: () => AppState;
|
|
setState: (state: AppState) => void;
|
|
}): CommandDispatcher {
|
|
return {
|
|
dispatch(commandId, payload) {
|
|
const command = options.registry.get(commandId);
|
|
if (!command) {
|
|
throw new Error(`Unknown command: ${commandId}`);
|
|
}
|
|
|
|
const currentState = options.getState();
|
|
const context: CommandContext = { state: currentState };
|
|
const executedState = command.execute(context, payload);
|
|
if (executedState === currentState) {
|
|
return currentState;
|
|
}
|
|
const nextState = shouldRecordHistory(commandId, currentState, executedState) ? recordHistory(currentState, executedState) : executedState;
|
|
options.setState(nextState);
|
|
return nextState;
|
|
},
|
|
};
|
|
}
|
|
|
|
function shouldRecordHistory(commandId: CommandId, currentState: AppState, nextState: AppState) {
|
|
if (commandId === commandIds.historyUndo || commandId === commandIds.historyRedo) return false;
|
|
return currentState.document !== nextState.document;
|
|
}
|
|
|
|
function recordHistory(currentState: AppState, nextState: AppState): AppState {
|
|
return {
|
|
...nextState,
|
|
history: {
|
|
past: [...currentState.history.past, snapshot(currentState)].slice(-100),
|
|
future: [],
|
|
},
|
|
};
|
|
}
|
|
|
|
function snapshot(state: AppState): HistorySnapshot {
|
|
return {
|
|
document: state.document,
|
|
editor: {
|
|
...state.editor,
|
|
brushPreview: undefined,
|
|
brushStrokePreview: undefined,
|
|
},
|
|
};
|
|
}
|