@@ -5,97 +5,129 @@ import routes from '../models/routes';
55/* eslint-disable max-len */
66/* eslint-disable no-param-reassign */
77import componentActionsRecord from '../models/masterState' ;
8+ import { Status } from '../types/backendTypes' ;
9+ import Tree from '../models/tree' ;
810const circularComponentTable = new Set ( ) ;
911
1012/**
11- * This file contains necessary functionality for time-travel feature
13+ * This file contains necessary functionality for time-travel feature.
1214 *
13- * Default Export:
14- * @function timeJump
15- * @param origin The latest snapshot, linked to the fiber (changes to origin will change app)
16- * @param mode The current mode (i.e. jumping, time-traveling, or paused)
17- * @returns A function that takes a target snapshot and a boolean flag checking for firstCall, then invokes `jump` on that target snapshot
15+ * The target snapshot portrays some past state we want to travel to `jump` recursively and setState for any stateful component.
16+ *
17+ * @param mode - The current mode (i.e. jumping, time-traveling, or paused)
18+ * @returns A function that takes a `target` snapshot and a boolean flag checking for `firstCall`, then invokes `jump` on that target snapshot
1819 *
19- * The target snapshot portrays some past state we want to travel to.
20- * `jump` recursively sets state for any stateful components.
2120 */
22- export default function timeJump ( mode ) {
23- // Recursively change state of tree
24- // Set the state of the origin tree if the component is stateful
25- async function jump ( target ) : Promise < void > {
26- if ( ! target ) return ;
27- // Base Case: if has visited, return
28- if ( circularComponentTable . has ( target ) ) {
29- return ;
21+ export default function timeJump ( mode : Status ) {
22+ // payload from index.ts is assigned to target
23+ /**
24+ * @param target - The target snapshot to re-render
25+ * @param firstCall - A boolean flag checking for `firstCall`
26+ */
27+ return ( target : Tree , firstCall = false ) : void => {
28+ // Setting mode disables setState from posting messages to window
29+ mode . jumping = true ;
30+ // Clearn the circularComponentTable
31+ if ( firstCall ) circularComponentTable . clear ( ) ;
32+ // Determine if user is navigating to another site
33+ const navigating : boolean = routes . navigate ( target . route ) ;
34+
35+ if ( navigating ) {
36+ // Initiate popStateHandler to aid the removeListener for 'popstate'
37+ const popStateHandler = ( ) => {
38+ initiateJump ( target , mode ) ;
39+ } ;
40+ // removeEventListener('popstate', popStateHandler);
41+ // Background will "perform" popstate till get to the correct history location?
42+ addEventListener ( 'popstate' , popStateHandler ) ;
3043 } else {
31- circularComponentTable . add ( target ) ;
32- }
33- // ------------------------STATELESS/ROOT COMPONENT-------------------------
34- // Since stateless component has no data to update, continue to traverse its child nodes:
35- if ( target . state === 'stateless' || target . state === 'root' ) {
36- target . children . forEach ( ( child ) => jump ( child ) ) ;
37- return ;
44+ // Intiate the jump
45+ initiateJump ( target , mode ) ;
3846 }
47+ } ;
48+ }
3949
40- // Destructure component data:
41- const { index, state, hooksIndex, hooksState } = target . componentData ;
42- // ------------------------STATEFUL CLASS COMPONENT-------------------------
43- // for stateful class components
44- // check if it is a stateful class component
45- // if yes, find the component by its index and assign it to a variable
46- // call that components setState method to reset state to the state at the time of the jump snapshot
47- //index can be zero => falsy value => DO NOT REMOVE UNDEFINED
48- if ( index !== undefined ) {
49- // Obtain component data & its update method at the given index
50- const classComponent = componentActionsRecord . getComponentByIndex ( index ) ;
51- if ( classComponent && classComponent . setState ) {
52- await classComponent . setState (
53- // prevState contains the states of the snapshots we are jumping FROM, not jumping TO
54- ( prevState ) => state ,
55- ) ;
56- }
57- // Iterate through new children after state has been set
58- target . children . forEach ( ( child ) => jump ( child ) ) ;
59- return ;
50+ /**
51+ * This function initiate the request for jump and will pause the jump when user moves mouse over the body of the document.
52+ * @param target - The target snapshot to re-render
53+ * @param mode - The current mode (i.e. jumping, time-traveling, or paused)
54+ */
55+ async function initiateJump ( target , mode ) : Promise < void > {
56+ console . log ( 'JUMP' ) ;
57+ updateTreeState ( target ) . then ( ( ) => {
58+ document . body . onmouseover = ( ) => {
59+ mode . jumping = false ;
60+ } ;
61+ } ) ;
62+ }
63+
64+ /**
65+ * This recursive function receives the target snapshot and will update the state of the fiber tree if the component is statefu
66+ * @param target - The target snapshot to re-render
67+ */
68+ async function updateTreeState ( target ) : Promise < void > {
69+ if ( ! target ) return ;
70+ // Base Case: if has visited, return
71+ if ( circularComponentTable . has ( target ) ) {
72+ return ;
73+ } else {
74+ circularComponentTable . add ( target ) ;
75+ }
76+ // console.log(target.name);
77+ // ------------------------STATELESS/ROOT COMPONENT-------------------------
78+ // Since stateless component has no data to update, continue to traverse its child nodes:
79+ if ( target . state === 'stateless' || target . state === 'root' ) {
80+ target . children . forEach ( ( child ) => updateTreeState ( child ) ) ;
81+ return ;
82+ }
83+
84+ // Destructure component data:
85+ const { index, state, hooksIndex, hooksState } = target . componentData ;
86+ // ------------------------STATEFUL CLASS COMPONENT-------------------------
87+ // for stateful class components
88+ // check if it is a stateful class component
89+ // if yes, find the component by its index and assign it to a variable
90+ // call that components setState method to reset state to the state at the time of the jump snapshot
91+ //index can be zero => falsy value => DO NOT REMOVE UNDEFINED
92+ if ( index !== undefined ) {
93+ // Obtain component data & its update method at the given index
94+ const classComponent = componentActionsRecord . getComponentByIndex ( index ) ;
95+ // If the user navigate to another page during jumps, Routes methods will popState until find a match => this cause changes in componentActionRecord => keep the if statement, otherwise will run into Uncaught Promise type error.
96+ if ( classComponent ?. setState ) {
97+ // Update component state
98+ await classComponent . setState (
99+ // prevState contains the states of the snapshots we are jumping FROM, not jumping TO
100+ ( prevState ) => state ,
101+ ) ;
60102 }
103+ // Else statement is to ensure if a mismatch, this popstate is not the correct componentActionRecord. Return immediately to avoid traverse the entire tree
104+ else return ;
61105
62- // ----------------------STATEFUL FUNCTIONAL COMPONENT----------------------
63- // check if component states are set with hooks
64- // if yes, grab all relevant components for this snapshot by its index
65- // call dispatch on each component passing in the corresponding currState value
66- //index can be zero => falsy value => DO NOT REMOVE UNDEFINED
67- if ( hooksIndex !== undefined ) {
68- // Obtain component data & its update method at the given index
69- const functionalComponent = componentActionsRecord . getComponentByIndexHooks ( hooksIndex ) ;
106+ // Iterate through new children after state has been set
107+ target . children . forEach ( ( child ) => updateTreeState ( child ) ) ;
108+ return ;
109+ }
110+
111+ // ----------------------STATEFUL FUNCTIONAL COMPONENT----------------------
112+ // check if component states are set with hooks
113+ // if yes, grab all relevant components for this snapshot by its index
114+ // call dispatch on each component passing in the corresponding currState value
115+ //index can be zero => falsy value => DO NOT REMOVE UNDEFINED
116+ if ( hooksIndex !== undefined ) {
117+ // Obtain component data & its update method at the given index
118+ const functionalComponent = componentActionsRecord . getComponentByIndexHooks ( hooksIndex ) ;
119+ // If the user navigate to another page during jumps, Routes methods will popState until find a match => this cause changes in componentActionRecord => keep the if statement, otherwise will run into Uncaught Promise type error.
120+ if ( functionalComponent [ 0 ] ?. dispatch ) {
121+ // Update component state
70122 for ( let i in functionalComponent ) {
71123 await functionalComponent [ i ] . dispatch ( Object . values ( hooksState ) [ i ] ) ;
72124 }
73- // Iterate through new children after state has been set
74- target . children . forEach ( ( child ) => jump ( child ) ) ;
75- return ;
76125 }
77- }
126+ // Else statement is to ensure if a mismatch, this popstate is not the correct componentActionRecord. Return immediately to avoid traverse the entire tree
127+ else return ;
78128
79- // payload from index.ts is assigned to target
80- return ( target , firstCall = false ) => {
81- // * Setting mode disables setState from posting messages to window
82- mode . jumping = true ;
83- if ( firstCall ) circularComponentTable . clear ( ) ;
84- const navigating : boolean = routes . navigate ( target . route ) ;
85- if ( navigating ) {
86- addEventListener ( 'popstate' , ( event ) => {
87- jump ( target ) . then ( ( ) => {
88- document . body . onmouseover = ( ) => {
89- mode . jumping = false ;
90- } ;
91- } ) ;
92- } ) ;
93- } else {
94- jump ( target ) . then ( ( ) => {
95- document . body . onmouseover = ( ) => {
96- mode . jumping = false ;
97- } ;
98- } ) ;
99- }
100- } ;
129+ // Iterate through new children after state has been set
130+ target . children . forEach ( ( child ) => updateTreeState ( child ) ) ;
131+ return ;
132+ }
101133}
0 commit comments