diff --git a/src/app/components/App.jsx b/src/app/components/App.jsx index 15d905f..c27c18a 100644 --- a/src/app/components/App.jsx +++ b/src/app/components/App.jsx @@ -1,4 +1,4 @@ -import React, { useContext, Component } from 'react'; +import React, { Component } from 'react'; import { createGlobalStyle } from 'styled-components'; // containers @@ -48,7 +48,7 @@ class App extends Component { }; this.portToExtension = null; - + this.addActionToView = this.addActionToView.bind(this); this.toTheFuture = this.toTheFuture.bind(this); this.toThePast = this.toThePast.bind(this); @@ -82,6 +82,7 @@ class App extends Component { })); }); }); + } // functionality to change 'play' button to 'stop' setIsPlaying() { @@ -120,16 +121,19 @@ class App extends Component { actionInPlay() { let { isPlayingIndex } = this.state; - if (isPlayingIndex >= this.state.data.length - 1) isPlayingIndex = 0; + const { isPlaying, data } = this.state; + + if (isPlayingIndex >= data.length - 1) isPlayingIndex = 0; this.setState({ isPlayingIndex: isPlayingIndex + 1 }); - const { id, action, state } = this.state.data[isPlayingIndex + 1]; + const { id, action, state } = data[isPlayingIndex + 1]; + this.toTheFuture(); setTimeout(() => { this.setState((prev, props) => { return { ...prev, id, action, state }; }); - if (this.state.isPlaying && isPlayingIndex + 1 < this.state.data.length - 1) { + if (isPlaying && isPlayingIndex + 1 < data.length - 1) { this.actionInPlay(); } else { this.setState({ isPlaying: false }); @@ -153,12 +157,12 @@ class App extends Component { // filter search bar results searchChange(e) { const { data } = this.state; - + // grab user entry from filter bar const compareSearchValue = e.target.value; // set state with compare value - this.setState({ searchField: compareSearchValue }) + this.setState({ searchField: compareSearchValue }); // match results from our filter entry to data const actions = data.filter(function(item) { @@ -200,7 +204,6 @@ class App extends Component { } render() { - console.log(this.state.isPlayingIndex); const { action, id, diff --git a/src/app/components/DetailCards/DetailsNav.jsx b/src/app/components/DetailCards/DetailsNav.jsx index 24c5e65..76a35c4 100644 --- a/src/app/components/DetailCards/DetailsNav.jsx +++ b/src/app/components/DetailCards/DetailsNav.jsx @@ -1,10 +1,8 @@ -import React, { useState } from 'react'; - -let NavLink = require('react-router-dom').NavLink; - +import React from 'react'; // styled component imports import { Buttons, Button, DetailsNavWrapper } from '../../styles/Nav.jsx'; +const { NavLink } = require('react-router-dom'); export default function RightNav(props) { return ( diff --git a/src/app/container/Details.jsx b/src/app/container/Details.jsx index b249d76..af449b5 100644 --- a/src/app/container/Details.jsx +++ b/src/app/container/Details.jsx @@ -15,7 +15,6 @@ import StateDisplay from '../components/DetailCards/State/StateDisplay.jsx' export default function Details(props) { - // destructuring required info that's being passed down from App.jsx // passing these props onto children const { diff --git a/src/browser/chrome/background.js b/src/browser/chrome/background.js index 567364b..522bc25 100644 --- a/src/browser/chrome/background.js +++ b/src/browser/chrome/background.js @@ -1,13 +1,22 @@ const parseAndGenerate = require('./scripts/parser'); +let portToDevtools; +const msgsToPanel = []; + chrome.tabs.onUpdated.addListener((id, info, tab) => { if (tab.status !== 'complete' || tab.url.startsWith('chrome')) return; + // active page action button and inject extension.js chrome.pageAction.show(tab.id); chrome.tabs.executeScript(null, { file: 'extension.js', runAt: 'document_end', }); + + // refresh devtool panel everytime we refresh webpage + // console.log('port: ', portToDevtools); + // if (portToDevtools) portToDevtools.postMessage({ action: 'refresh_devtool' }); + // else msgsToPanel.push({ action: 'refresh_devtool' }); }); @@ -29,7 +38,6 @@ function handleRequest(request) { const syncRequest = new XMLHttpRequest(); syncRequest.open('GET', request.url, false); syncRequest.send(null); - console.log(`Status: ${syncRequest.status} - Size of response: ${syncRequest.responseText.length}`); sendMessageToContent(parseAndGenerate(syncRequest.responseText)); @@ -40,6 +48,18 @@ function handleRequest(request) { // The App on the devtools panel start a connection so that it can // tell us when to start intercepting the script requests. chrome.runtime.onConnect.addListener((port) => { + portToDevtools = port; + + // if (msgsToPanel.length > 0) { + // for (let msg of msgsToPanel) port.postMessage(msg); + // } + // we change the port to null when we disconnect, so that when we refresh + // the page by start recording, we can check if (!port) and not refresh + // the devtools page. + port.onDisconnect.addListener(() => { + portToDevtools = null; + }); + port.onMessage.addListener((msg) => { if (!msg.turnOnDevtool) return; interceptedUrl = msg.url; @@ -64,7 +84,6 @@ function addScriptInterception() { let reqIndex = 0; function sendMessageToContent(codeString) { const index = reqIndex++; - console.log(`Sending request ${index}.`); chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { chrome.tabs.sendMessage(tabs[0].id, { codeString, index }); }); diff --git a/src/browser/chrome/devtools.js b/src/browser/chrome/devtools.js index 21f1723..f66b54d 100644 --- a/src/browser/chrome/devtools.js +++ b/src/browser/chrome/devtools.js @@ -3,7 +3,11 @@ chrome.devtools.panels.create('React Rewind', 'devtools.html', (extensionPanel) => { const port = chrome.runtime.connect({ name: 'devtools' }); - port.onMessage.addListener(() => {}); + port.onMessage.addListener((msg) => { + // console.log('got msg: ', msg); + if (msg.action === 'refresh_devtool') extensionPanel.setPage('devtools.html'); + }); + extensionPanel.onShown.addListener((panelWindow) => { panelWindow.backgroundPort = port; }); diff --git a/src/browser/chrome/scripts/inject_script_tags.js b/src/browser/chrome/scripts/inject_script_tags.js index 45da7e0..e6250fd 100644 --- a/src/browser/chrome/scripts/inject_script_tags.js +++ b/src/browser/chrome/scripts/inject_script_tags.js @@ -2,15 +2,17 @@ // We need this because our content-injected scripts are executed in an "isolated // world" environment. BUT for the scripts below, we want the edited-React libraries // to have access to the their functionalities. -const linkedListScript = document.createElement('script'); -linkedListScript.src = chrome.runtime.getURL('scripts/linked_list.js'); -(document.head || document.documentElement).appendChild(linkedListScript); + +// const linkedListScript = document.createElement('script'); +// linkedListScript.src = chrome.runtime.getURL('scripts/linked_list.js'); +// (document.head || document.documentElement).appendChild(linkedListScript); const timeTravelScript = document.createElement('script'); timeTravelScript.src = chrome.runtime.getURL('scripts/time_travel.js'); (document.head || document.documentElement).appendChild(timeTravelScript); -linkedListScript.onload = timeTravelScript.onload = function removeScriptTag() { +// linkedListScript.onload = +timeTravelScript.onload = function removeScriptTag() { this.remove(); }; diff --git a/src/browser/chrome/scripts/linked_list.js b/src/browser/chrome/scripts/linked_list.js index 20eead2..31404af 100644 --- a/src/browser/chrome/scripts/linked_list.js +++ b/src/browser/chrome/scripts/linked_list.js @@ -1,13 +1,14 @@ class DoublyLinkedListNode { - constructor(value, next = null, previous = null) { + constructor(value, next = null, prev = null) { this.value = value; this.next = next; - this.previous = previous; + this.prev = prev; } } class DoublyLinkedList { constructor() { + this.head = null; this.tail = null; this.current = null; } @@ -16,11 +17,12 @@ class DoublyLinkedList { const newDLLNode = new DoublyLinkedListNode(fiberNode); if (!this.head) { + this.head = newDLLNode; this.tail = newDLLNode; this.current = newDLLNode; } else { this.tail.next = newDLLNode; - newDLLNode.previous = this.tail; + newDLLNode.prev = this.tail; this.tail = newDLLNode; } diff --git a/src/browser/chrome/scripts/parser.js b/src/browser/chrome/scripts/parser.js index ffa50bf..8ee3d2d 100644 --- a/src/browser/chrome/scripts/parser.js +++ b/src/browser/chrome/scripts/parser.js @@ -8,7 +8,7 @@ function useReducerReplacement() { const dispatcher = resolveDispatcher(); function reducerWithTracker(state, action) { const newState = reducer(state, action); - timeTravelTracker[timeTravelTracker.length - 1].actionDispatched = true; + timeTravelLList.tail.value.actionDispatched = true; window.postMessage({ type: 'DISPATCH', data: { @@ -52,7 +52,8 @@ function commitAllHostEffectsReplacement() { switch (primaryEffectTag) { case Placement: { - timeTravelTracker.push({ + // editbyme + timeTravelLList.append({ primaryEffectTag: 'PLACEMENT', effect: _.cloneDeep(nextEffect), }); @@ -72,7 +73,8 @@ function commitAllHostEffectsReplacement() { } case Update: { - timeTravelTracker.push({ + // editbyme + timeTravelLList.append({ primaryEffectTag: 'UPDATE', effect: _.cloneDeep(nextEffect), current: _.cloneDeep(nextEffect.alternate), @@ -84,7 +86,8 @@ function commitAllHostEffectsReplacement() { } case Deletion: { - timeTravelTracker.push({ + // editbyme + timeTravelLList.append({ primaryEffectTag: 'DELETION', effect: _.cloneDeep(nextEffect), }); @@ -167,7 +170,7 @@ const parseAndGenerate = (codeString) => { // parse react code injectableUseReducer = esprima.parseScript(useReducerReplacement.toString()); traverseTree(injectableUseReducer, 'useReducer', ast); - + const code = escodegen.generate(ast); console.log('returning code.'); return code; diff --git a/src/browser/chrome/scripts/time_travel.js b/src/browser/chrome/scripts/time_travel.js index 9086ce3..ede68f8 100644 --- a/src/browser/chrome/scripts/time_travel.js +++ b/src/browser/chrome/scripts/time_travel.js @@ -1,44 +1,76 @@ -let timeTravelTracker = []; -let timeTravelTrackerIndex = null; +class DoublyLinkedListNode { + constructor(value, next = null, prev = null) { + this.value = value; + this.next = next; + this.prev = prev; + } +} + +class DoublyLinkedList { + constructor() { + this.head = null; + this.tail = null; + this.current = null; + } + + append(fiberNode) { + const newDLLNode = new DoublyLinkedListNode(fiberNode); + + if (!this.head) { + this.head = newDLLNode; + this.tail = newDLLNode; + this.current = newDLLNode; + } else { + this.tail.next = newDLLNode; + newDLLNode.prev = this.tail; + this.tail = newDLLNode; + } + + return this; + } +} + + const funcStorage = {}; -let root; +let root = null; +const timeTravelLList = new DoublyLinkedList(); function timeTravel(direction) { - if (timeTravelTrackerIndex === null) { - // First time we call this function. We need to remove the PLACEMENT entry - // from position [0]. This is originated from the initial mount of the App. - timeTravelTracker = timeTravelTracker.slice(1); - timeTravelTrackerIndex = timeTravelTracker.length - 1; - root = getRootContainerInstance(timeTravelTracker[0].effect); + if (!root) { + root = getRootContainerInstance(timeTravelLList.current.value.effect); + timeTravelLList.current = timeTravelLList.tail; } const diff = direction === 'forward' ? 1 : -1; - if ((diff === 1 && timeTravelTrackerIndex === timeTravelTracker.length - 1) - || (diff === -1 && timeTravelTrackerIndex === 0)) { + if ((diff === 1 && timeTravelLList.current.next === null) + || (diff === -1 && timeTravelLList.current.prev === null)) { return; } - if (diff === 1 && timeTravelTrackerIndex !== 0) timeTravelTrackerIndex += 1; + if (diff === 1 && timeTravelLList.current !== timeTravelLList.tail) { + timeTravelLList.current = timeTravelLList.current.next; + } + + const { + commitDeletion, + commitPlacement, + commitWork, + prepareUpdate, + } = funcStorage; while (true) { - const { - commitDeletion, - commitPlacement, - commitWork, - prepareUpdate, - } = funcStorage; - console.log('doing work for ', timeTravelTrackerIndex); - const { primaryEffectTag, effect } = timeTravelTracker[timeTravelTrackerIndex]; + // console.log('doing work for ', timeTravelTrackerIndex); + const { primaryEffectTag, effect } = timeTravelLList.current.value; switch(primaryEffectTag) { case 'UPDATE': { - const { current } = timeTravelTracker[timeTravelTrackerIndex]; + const { current } = timeTravelLList.current.value; // if we are moving forwards, we need to commitWork() the same // way the function was originally called. if (diff === 1) { - commitWork(current, effect); + commitWork(_.cloneDeep(current), _.cloneDeep(effect)); break; } @@ -60,16 +92,16 @@ function timeTravel(direction) { const currentCopy = _.cloneDeep(current); currentCopy.updateQueue = payload; - commitWork(effect, currentCopy); + commitWork(_.cloneDeep(effect), currentCopy); break; } - commitWork(effect, current); + commitWork(_.cloneDeep(effect), _.cloneDeep(current)); break; } case 'PLACEMENT': { if (diff === 1) { - commitPlacement(effect); + commitPlacement(_.cloneDeep(effect)); } else { // commitDeletion() will call unmountHostComponents(), which recursively // deletes all host nodes from the parent. This means that @@ -77,7 +109,6 @@ function timeTravel(direction) { // on commitPlacement() on this same node. This is why we need to clone // the effect fiberNode and call commitDeletion() on that instead. const effectCopy = _.cloneDeep(effect); - console.log(effectCopy); commitDeletion(effectCopy); } break; @@ -88,7 +119,7 @@ function timeTravel(direction) { const effectCopy = _.cloneDeep(effect); commitDeletion(effectCopy); } else { - commitPlacement(effect); + commitPlacement(_.cloneDeep(effect)); } break; } @@ -97,15 +128,17 @@ function timeTravel(direction) { } // break points for the while loop - if (timeTravelTrackerIndex + diff === timeTravelTracker.length - || (diff === -1 && timeTravelTrackerIndex === 0) - || (diff === 1 && timeTravelTracker[timeTravelTrackerIndex].actionDispatched)) { + if ((diff === -1 && timeTravelLList.current.prev === null) + || (diff === 1 && timeTravelLList.current.next === null) + || (diff === 1 && timeTravelLList.current.value.actionDispatched)) { return; } - timeTravelTrackerIndex += diff; + timeTravelLList.current = diff === 1 + ? timeTravelLList.current.next + : timeTravelLList.current.prev; - if (diff === -1 && timeTravelTracker[timeTravelTrackerIndex].actionDispatched) { + if (diff === -1 && timeTravelLList.current.value.actionDispatched) { return; } }