From 4059cd20f83fd38a808f9e64f85efa155454739d Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Wed, 29 May 2024 22:06:04 +0100 Subject: [PATCH] refactor duplicated code --- src/index.ts | 120 ++++++++++++++++++++++++++------------------------- 1 file changed, 61 insertions(+), 59 deletions(-) diff --git a/src/index.ts b/src/index.ts index df20097..3dcff0c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,6 +23,10 @@ export interface BaseHistoryState { paused: boolean; } +type HistoryEntryType = State extends BaseHistoryState + ? T + : never; + export interface HistoryAdapterConfig { /** * Maximum number of past states to keep. @@ -156,14 +160,23 @@ type GetInitialState = ( initialData: Data, ) => GetStateType; +type ApplyEntry = ( + state: StateFn["state"], + historyEntry: HistoryEntryType, +) => HistoryEntryType; + type BuildHistoryAdapterConfig = { - undoMutably: (state: StateFn["state"]) => void; - redoMutably: (state: StateFn["state"]) => void; + applyEntry: + | ApplyEntry + | Record<"undo" | "redo", ApplyEntry>; wrapRecipe: >( recipe: (draft: Draft, ...args: Args) => ValidRecipeReturnType, config: WrapRecipeConfig, adapterConfig?: GetConfigType, - ) => (state: Draft>, ...args: Args) => void; + ) => ( + state: Draft>, + ...args: Args + ) => HistoryEntryType>; onCreate?: (config?: GetConfigType) => void; } & (BaseHistoryState extends GetStateType ? { @@ -174,12 +187,23 @@ type BuildHistoryAdapterConfig = { }); function buildCreateHistoryAdapter({ - undoMutably, - redoMutably, + applyEntry, wrapRecipe, onCreate, getInitialState: getInitialStateCustom = getInitialState, }: BuildHistoryAdapterConfig) { + const { undo, redo } = + typeof applyEntry === "function" + ? { undo: applyEntry, redo: applyEntry } + : applyEntry; + function undoMutably(state: StateFn["state"]) { + if (!state.past.length) return; + state.future.unshift(undo(state, state.past.pop() as never)); + } + function redoMutably(state: StateFn["state"]) { + if (!state.future.length) return; + state.past.push(redo(state, state.future.shift() as never)); + } return function createHistoryAdapter( adapterConfig?: GetConfigType, ): HistoryAdapter> { @@ -231,7 +255,24 @@ function buildCreateHistoryAdapter({ const state = selectHistoryState?.(rootState as never) ?? (rootState as Draft>); - finalRecipe(state, ...args); + + const historyEntry = finalRecipe(state, ...args); + + // if paused, don't add to history + if (state.paused) return; + + const undoable = config.isUndoable?.(...args) ?? true; + if (undoable) { + const lengthWithoutFuture = state.past.length + 1; + if ( + adapterConfig?.limit && + lengthWithoutFuture > adapterConfig.limit + ) { + state.past.shift(); + } + state.past.push(historyEntry); + state.future = []; + } }); }, }; @@ -245,17 +286,10 @@ interface HistoryStateFn extends BaseHistoryStateFn { } export const createHistoryAdapter = buildCreateHistoryAdapter({ - undoMutably(state) { - if (!state.past.length) return; - const historyEntry = state.past.pop(); - state.future.unshift(state.present); - state.present = historyEntry; - }, - redoMutably(state) { - if (!state.future.length) return; - const historyEntry = state.future.shift(); - state.past.push(state.present); + applyEntry(state, historyEntry) { + const stateBefore = state.present; state.present = historyEntry; + return stateBefore; }, wrapRecipe: >( @@ -263,13 +297,12 @@ export const createHistoryAdapter = buildCreateHistoryAdapter({ draft: Draft, ...args: Args ) => ValidRecipeReturnType, - config: WrapRecipeConfig, - adapterConfig: HistoryAdapterConfig = {}, ) => (state: Draft>, ...args: Args) => { // we need to get the present state before the recipe is applied // and because the recipe might mutate it, we need the non-draft version - const before = ensureCurrent(state.present); + const before = ensureCurrent(state.present) as Data; + const result = recipe(state.present as Draft, ...args); if (result === nothing) { state.present = undefined as never; @@ -277,18 +310,7 @@ export const createHistoryAdapter = buildCreateHistoryAdapter({ state.present = result as never; } - // if paused, don't add to history - if (state.paused) return; - - const undoable = config.isUndoable?.(...args) ?? true; - if (undoable) { - const lengthWithoutFuture = state.past.length + 1; - if (adapterConfig.limit && lengthWithoutFuture > adapterConfig.limit) { - state.past.shift(); - } - state.past.push(before); - state.future = []; - } + return before; }, }); @@ -308,19 +330,15 @@ export const createPatchHistoryAdapter = onCreate() { enablePatches(); }, - undoMutably(state) { - const historyEntry = state.past.pop(); - if (historyEntry) { + applyEntry: { + undo(state, historyEntry) { applyPatches(state, historyEntry.undo); - state.future.unshift(historyEntry); - } - }, - redoMutably(state) { - const historyEntry = state.future.shift(); - if (historyEntry) { + return historyEntry; + }, + redo(state, historyEntry) { applyPatches(state, historyEntry.redo); - state.past.push(historyEntry); - } + return historyEntry; + }, }, wrapRecipe: >( @@ -328,8 +346,6 @@ export const createPatchHistoryAdapter = draft: Draft, ...args: Args ) => ValidRecipeReturnType, - config: WrapRecipeConfig, - adapterConfig: HistoryAdapterConfig = {}, ) => (state: Draft>, ...args: Args) => { const [{ present }, redo, undo] = produceWithPatches(state, (draft) => { @@ -342,20 +358,6 @@ export const createPatchHistoryAdapter = }); state.present = present; - // if paused, don't add to history - if (state.paused) return; - - const undoable = config.isUndoable?.(...args) ?? true; - if (undoable) { - const lengthWithoutFuture = state.past.length + 1; - if ( - adapterConfig.limit && - lengthWithoutFuture > adapterConfig.limit - ) { - state.past.shift(); - } - state.past.push({ undo, redo }); - state.future = []; - } + return { undo, redo }; }, });