import type { TimelineEvent, TimelineToken } from "atom.io" import { ingestAtomUpdateEvent, ingestCreationEvent, ingestDisposalEvent, ingestSelectorUpdateEvent, ingestTransactionOutcomeEvent, } from "../events/index.ts" import type { Store } from "../store/index.ts" export function timeTravel( store: Store, action: `redo` | `undo`, token: TimelineToken, ): void { store.logger.info( action === `redo` ? `⏩` : `βͺ`, `timeline`, token.key, action, ) const timelineData = store.timelines.get(token.key) if (!timelineData) { store.logger.error( `🐞`, `timeline`, token.key, `Failed to ${action}. This timeline has not been initialized.`, ) return } if ( (action === `redo` && timelineData.at === timelineData.history.length) || (action === `undo` && timelineData.at === 0) ) { store.logger.warn( `πŸ’`, `timeline`, token.key, `Failed to ${action} at the ${ action === `redo` ? `end` : `beginning` } of timeline "${token.key}". There is nothing to ${action}.`, ) return } timelineData.timeTraveling = action === `redo` ? `into_future` : `into_past` let nextIndex = timelineData.at let events: TimelineEvent[] switch (action) { case `undo`: --nextIndex while ( nextIndex !== 0 && timelineData.history[nextIndex].checkpoint !== true ) { --nextIndex } events = timelineData.history.slice(nextIndex, timelineData.at).reverse() break case `redo`: ++nextIndex events = timelineData.history.slice(timelineData.at, nextIndex) } timelineData.at = nextIndex const applying = action === `redo` ? `newValue` : `oldValue` for (const event of events) { switch (event.type) { case `atom_creation`: { ingestCreationEvent(store, event, applying) break } case `atom_disposal`: { ingestDisposalEvent(store, event, applying) break } case `atom_update`: { ingestAtomUpdateEvent(store, event, applying) break } case `selector_update`: { ingestSelectorUpdateEvent(store, event, applying) break } case `transaction_outcome`: { ingestTransactionOutcomeEvent(store, event, applying) break } } } timelineData.subject.next({ type: `timeline_update`, event: action, at: timelineData.at, length: timelineData.history.length, }) timelineData.timeTraveling = null store.logger.info( `⏸️`, `timeline`, token.key, `"${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`, ) }