Sawtaytoes
4/9/2019 - 11:52 PM

Undo Hack Fix

function undo<T>(undoNotifier: Observable<any>, redoNotifier: Observable<any> = EMPTY) {
  return (source: Observable<T>) => merge(
    undoNotifier.pipe(map(() => UNDO_TOKEN)),
    redoNotifier.pipe(map(() => REDO_TOKEN)),
    source,
  ).pipe(
    scan<T, { state: T[], subtractor: number, redos: T[]|null }>((d, x) => {
      let { state, subtractor, redos } = d;
      if (x === UNDO_TOKEN) {
        // We were notified of an "undo". pop state.
        if (state && state.length > 1) {
          d.subtractor += state[state.length - 1] - state[state.length - 2];
          redos = redos || (d.redos = []);
          redos.push(state.pop());
        }
      } else if (x === REDO_TOKEN) {
        if (redos && redos.length > 0) {
          d.subtractor -= redos[redos.length - 1] - state[state.length - 1];
          state.push(redos.pop());
        }
      } else {
        if (redos) {
          // clear our redos as new history is written
          redos.length = 0;
        }
        state = state || (d.state = []);
        // It's not an "undo", push state
        state.push(x - subtractor);
      }
      return d;
    }, { state: null, subtractor: 0, redos: null }),
    // we only care about state past here
    map(x => x.state),
    // Don't emit if we don't have state
    filter(x => x !== null),
    // Take the last value from state
    map(state => state[state.length - 1]),
  )
}