-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Major breaking change suggestion #17
Comments
I like the idea of the driver holding the responsibility. Not all drivers are the same, so the driver itself would choose how to display their state. The driver would be able to provide further options that could display input and/or output or anything else that could be useful. Using @staltz example var timeTraveler = renderTimeTravelingDebuggerAt('#container');
// further configuration
makeDOMDriver('#app', {timeTravel: timeTraveler, input: true, output: false});
// pretend that object isn't anymore for custom elements I'm very interested in seeing how this plays out. |
It would also be useful to use time-travel in an end app for reporting at different verbosity levels and for undo/redo management. Firebase timestamps all edits and this could also be done for edits on changes to models outside the Firebase cache. Then when rolling back to a action it's timestamp can be used to index changes made to any model and roll back the model as well. |
I really like this idea @staltz, and it would nicely address issue #14. It would be quite nice to keep the ability to log out internal application state to the time travel bar, but there are ways that can be done. I might even extract a package just for doing that (the stream visualization aspect), and then include it in this package. An option I was toying with after reading this issue this morning is making this change by asking users to replace |
👍 for |
Thinking more about how to go from here to there. My goal for the new API will be that by default, the only thing a user has to change is the By tapping in to This is my thinking right now. I'm willing to bet that not all of this is possible as I imagine it, but here's what I'll try.
|
You don't actually need to do this part, you can render the time travel vtree$ in a newly created div e.g. And since we're talking about this, how about investigating how to render that in a dev tools tab, and not in |
Progress update: https://gfycat.com/NextThirstyGermanspaniel https://github.com/cyclejs/cycle-time-travel/pull/24/files #24 So now all you have to do to use cycle-time-travel is replace It figures out the streams to display by walking the observable tree. It's currently just using the old record/filter approach for time travel, which totally doesn't work for stubbing out responses. The time travelling doesn't work right now. As you can see in the above .gif, it will replay old user intent when you rewind, which causes it to act like it's traveling forward in time. The next step as far as I can see is to restart the Cycle app every time the user rewinds. I can just call |
@Widdershin I think I just came up with a (hopefully) brilliant idea to make time travelling more automatic, with less customizations to do inside application code.
So far we have been tracking streams inside
main
. What if we would track onlysource
streams, and all of them? By source I meanresponses
here:In other words, everything that drivers output. The time travelling panel would then show only these response observables, making it agnostic to
main
's internals. In a way the information it would display would be only "what went into the application". This will mostly represent the user's actions, but it can also represent HTTP responses, or anything coming from drivers.The inspiration to this came when I was thinking about Flux/Redux's actions and how time travelling is all about rewinding/replaying actions. I started thinking about what actions represent, and in Cycle.js the
main
's semantics are clear: input are events from the outside world represented by drivers (user + server + etc) and output is the program's behavior. To implement global time travel we just need to replay driver responses.On a more technical level, to do this, we would need to wrap drivers, for example:
wrapWithTimeTraveling intercepts output Observables, and stores their events in a list. Then, to rewind, we need to somehow restart the program (maybe call
Cycle.run
again?) and make the wrapped output Observable emit events stored in the history. The reason why we need to restart the program is that this approach doesn't touch the app's internal state, so we need to reset it. Probably not super fast approach, but definitely solves the problem. The wrapped output Observable is probably most easily implemented internally as a subject, although there might be a nice way of making it just an Observable.Then there is the problem of how to actually intercept the output Observable from drivers. If the driver is a simple
function (input: Observable): Observable
, then it's dumb easy. But the DOM driver isn't, it'sfunction (vtree$: Observable): SelectableCollection
, where SelectableCollection has.select()
. Somehow wrapWithTimeTraveling would need to be aware that the DOM driver returns SelectableCollection and not a plain Observable, and then mimic that SelectableCollection.On the other hand, we could think of pushing this responsability to drivers, so a driver should provide an implementation of its time traveled variant. This could be a convention for drivers. If they support it, then they would work, if they don't support it, then their outputs simply wouldn't appear in the time traveling panel and it wouldn't be used. This way we can mark some drivers as "Time Traveling Ready™".
Maybe one way of trivially doing this is giving an option to the driver:
I am pretty happy with this approach, also for testing and debugging. It's actually not hard at all to intercept a driver's output and log all events from its output observables to console.log or even some remote service like with websockets. In Flux world this is common to use actions as the full log of "what happened" in the app. In Cycle.js it would be similar, but the log has more low-level data, and doesn't leave anything out.
What do you think?
The text was updated successfully, but these errors were encountered: