Как реализовать redo/undo с вложенным redo/undo?

Рейтинг: 0Ответов: 0Опубликовано: 21.03.2023

Есть данные в стейте которые приходят в экшене SET_DATA, в редюсере я добавляю новые данные в history.current, старые в past. Формат данных можно поменять если я не правильно начал реализовывать.

При нажатии комбинации клавиш срабатывает экшн changeStateHistory('undo'|'redo'), который заменяет history.current нужным значением.С глобальным историей все нормально работает. Но должна быть возможность откатить не всю историю, а только выбранного агента.

State:

//state example
{
  selectedAgent: -1,
  history: {
    past: [],
    current: {
      data: [
        {
          past: [],
          current: {
            agentId: 1,
            agentName: 'Test User1',
          future: []
        },     {
          past: [],
          current: {
            agentId: 2,
            agentName: 'Test User2',
          future: []
        }
      ],
      isModified: false
    },
    future: []
  }
}

Reducer:

const reducer = createReducer(initialState, {
  [ActionTypes.SET_DATA]: (
    state: typeof initialState,
    action: { payload: { agents: IAgentSchedule[]; isMerge: boolean; isSaveToHistory?: boolean; isModified: boolean } },
  ) => {
    const { agents, isMerge, isSaveToHistory, isModified } = action.payload;
    //save changes to history
    if (isSaveToHistory) {
      state.history.past = clone([...state.history.past, state.history.current]);
      state.history.future = [];
    } else {
      state.history.past = [];
      state.history.future = [];
    }

    if (isMerge) {
      state.history.current.data = state.history.current.data.map(agentHistory => {
        if (agentHistory.current.isFixLater) return agentHistory;
        const foundedAgent = agents.find(_agent => _agent.agentId === agentHistory.current.agentId);

        if (foundedAgent) {
          return { past: clone([...agentHistory.past, agentHistory.current]), current: foundedAgent, future: [] };
        }
        return agentHistory;
      });
      return;
    } else {
      //initialize history
      state.history.current.data = agents.map(agent => {
        const agentHistory = state.history.current.data.find(_agent => _agent.current.agentId === agent.agentId);
        if (!agentHistory) return { past: [], current: agent, future: [] };
        if (!agent.isModified) return { past: agentHistory.past, current: agent, future: agentHistory.future };
        return { past: [...agentHistory.past, agentHistory.current], current: agent, future: [] };
      });
    }
  },
  [ActionTypes.CHANGE_STORE_BY_HISTORY]: (state: typeof initialState, action: { payload: 'undo' | 'redo' }) => {
    const isAgentSelected = state.selectedAgent !== -1;

    //change agent history
    if (isAgentSelected) {
      const agentHistory = state.history.current.data.find(
        agentHistory => agentHistory.current.agentId === state.selectedAgent,
      );
      if (!agentHistory) return;
      if (action.payload === 'undo') {
        const previous = agentHistory.past[agentHistory.past.length - 1];
        if (!previous) return;

        agentHistory.past = agentHistory.past.slice(0, agentHistory.past.length - 1);
        agentHistory.current = previous;
        agentHistory.future = [agentHistory.current, ...agentHistory.future];
        return;
      } else if (action.payload === 'redo') {
        const next = agentHistory.future[0];
        if (!next) return;
        const newFuture = agentHistory.future.slice(1);

        agentHistory.past = [...agentHistory.past, agentHistory.current];
        agentHistory.current = next;
        agentHistory.future = newFuture;
        return;
      }
    }

    //change global history
    if (!isAgentSelected) {
      const { past, current, future } = state.history;
      if (action.payload === 'undo') {
        const previous = past[past.length - 1];
        if (!previous) return;

        state.history.past = past.slice(0, past.length - 1);
        state.history.current = previous;
        state.history.future = [current, ...future];
      } else if (action.payload === 'redo') {
        const next = future[0];
        if (!next) return;
        const newFuture = future.slice(1);

        state.history.past = [...past, current];
        state.history.current = next;
        state.history.future = newFuture;
      }
    }
  },
});

export default reducer;

Ответы

Ответов пока нет.