Skip to content

Commit 3aed6a5

Browse files
committed
[WIP] redux extension integration
1 parent 12a9f16 commit 3aed6a5

File tree

2 files changed

+226
-0
lines changed

2 files changed

+226
-0
lines changed

src/utils/reactive.ts

+26
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
import { scheduleRevalidate } from '@/utils/runtime';
77
import { isFn, isTag, isTagLike } from '@/utils/shared';
8+
import { supportChromeExtension } from './redux-devtools';
89

910
export const asyncOpcodes = new WeakSet<tagOp>();
1011
// List of DOM operations for each tag
@@ -308,3 +309,28 @@ export function inNewTrackingFrame(callback: () => void) {
308309
callback();
309310
currentTracker = existingTracker;
310311
}
312+
313+
supportChromeExtension({
314+
get() {
315+
const cells = {};
316+
DEBUG_CELLS.forEach((cell, index) => {
317+
cells[`${cell._debugName}:${index}`] = cell._value;
318+
});
319+
return cells;
320+
},
321+
skipDispatch: 0,
322+
set() {
323+
console.log('set', ...arguments);
324+
},
325+
on(timeLine: string, fn: () => any) {
326+
console.log('on', timeLine, fn);
327+
setTimeout(() => {
328+
// debugger;
329+
fn.call(this, 'updates', {})
330+
331+
}, 2000);
332+
},
333+
trigger() {
334+
console.log('trigger', ...arguments);
335+
}
336+
});

src/utils/redux-devtools.ts

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// inspired by https://www.npmjs.com/package/freezer-redux-devtools?activeTab=code
2+
var ActionTypes = {
3+
INIT: '@@INIT',
4+
PERFORM_ACTION: 'PERFORM_ACTION',
5+
TOGGLE_ACTION: 'TOGGLE_ACTION'
6+
};
7+
8+
type Listener = () => void;
9+
10+
/**
11+
* Redux middleware to make freezer and devtools
12+
* talk to each other.
13+
* @param {Freezer} State Freezer's app state.
14+
*/
15+
export function FreezerMiddleware( State ){
16+
return function( next ){
17+
return function StoreEnhancer( someReducer, someState ){
18+
var commitedState = State.get(),
19+
lastAction = 0,
20+
/**
21+
* Freezer reducer will trigger events on any
22+
* devtool action to synchronize freezer's and
23+
* devtool's states.
24+
*
25+
* @param {Object} state Current devtool state.
26+
* @param {Object} action Action being dispatched.
27+
* @return {Object} Freezer state after the action.
28+
*/
29+
reducer = function( state, action ){
30+
if( action.type == ActionTypes.INIT ){
31+
State.set( state || commitedState );
32+
}
33+
else if( lastAction != ActionTypes.PERFORM_ACTION ) {
34+
// Flag that we are dispatching to not
35+
// to dispatch the same action twice
36+
State.skipDispatch = 1;
37+
State.trigger.apply( State, [ action.type ].concat( action.arguments || [] ) );
38+
}
39+
// The only valid state is freezer's one.
40+
return State.get();
41+
},
42+
store = next( reducer ),
43+
liftedStore = store.liftedStore,
44+
dtStore = store.devToolsStore || store.liftedStore,
45+
46+
toolsDispatcher = dtStore.dispatch
47+
;
48+
49+
// Override devTools store's dispatch, to set commitedState
50+
// on Commit action.
51+
dtStore.dispatch = function( action ){
52+
lastAction = action.type;
53+
54+
// If we are using redux-devtools we need to reset the state
55+
// to the last valid one manually
56+
if( liftedStore && lastAction == ActionTypes.TOGGLE_ACTION ){
57+
var states = dtStore.getState().computedStates,
58+
nextValue = states[ action.id - 1].state
59+
;
60+
61+
State.set( nextValue );
62+
}
63+
64+
toolsDispatcher.apply( dtStore, arguments );
65+
66+
return action;
67+
};
68+
69+
// Dispatch any freezer "fluxy" event to let the devTools
70+
// know about the update.
71+
State.on('afterAll', function( reactionName ){
72+
if( reactionName == 'update')
73+
return;
74+
75+
// We don't dispatch if the flag is true
76+
if( this.skipDispatch )
77+
this.skipDispatch = 0;
78+
else {
79+
var args = [].slice.call( arguments, 1 );
80+
store.dispatch({ type: reactionName, args: args });
81+
}
82+
});
83+
84+
return store;
85+
};
86+
};
87+
}
88+
89+
/**
90+
* Binds freezer store to the chrome's redux-devtools extension.
91+
* @param {Freezer} State Freezer's app state
92+
*/
93+
export function supportChromeExtension( State ){
94+
var devtools = window.__REDUX_DEVTOOLS_EXTENSION__
95+
? window.__REDUX_DEVTOOLS_EXTENSION__()
96+
: (f) => f;
97+
98+
compose(
99+
FreezerMiddleware( State ),
100+
devtools
101+
)(createStore)( function( state ){
102+
return state;
103+
});
104+
}
105+
106+
107+
/**
108+
* Creates a valid redux store. Copied directly from redux.
109+
* https://github.com/rackt/redux
110+
*/
111+
function createStore(reducer: any, initialState: any) {
112+
113+
114+
if (typeof reducer !== 'function') {
115+
throw new Error('Expected the reducer to be a function.');
116+
}
117+
118+
var currentReducer = reducer;
119+
var currentState = initialState;
120+
var listeners: Listener[] = [];
121+
var isDispatching = false;
122+
var ActionTypes = {
123+
INIT: '@@redux/INIT'
124+
};
125+
126+
function getState() {
127+
return currentState;
128+
}
129+
130+
function subscribe(listener: Listener) {
131+
listeners.push(listener);
132+
var isSubscribed = true;
133+
134+
return function unsubscribe() {
135+
if (!isSubscribed) {
136+
return;
137+
}
138+
139+
isSubscribed = false;
140+
var index = listeners.indexOf(listener);
141+
listeners.splice(index, 1);
142+
};
143+
}
144+
145+
function dispatch(action: { type: string | undefined }) {
146+
if (typeof action.type === 'undefined') {
147+
throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
148+
}
149+
150+
if (isDispatching) {
151+
throw new Error('Reducers may not dispatch actions.');
152+
}
153+
154+
try {
155+
isDispatching = true;
156+
currentState = currentReducer(currentState, action);
157+
} finally {
158+
isDispatching = false;
159+
}
160+
161+
listeners.slice().forEach(function (listener) {
162+
return listener();
163+
});
164+
return action;
165+
}
166+
167+
function replaceReducer(nextReducer: any) {
168+
currentReducer = nextReducer;
169+
dispatch({ type: ActionTypes.INIT });
170+
}
171+
172+
// When a store is created, an "INIT" action is dispatched so that every
173+
// reducer returns their initial state. This effectively populates
174+
// the initial state tree.
175+
dispatch({ type: ActionTypes.INIT });
176+
177+
return {
178+
dispatch: dispatch,
179+
subscribe: subscribe,
180+
getState: getState,
181+
replaceReducer: replaceReducer
182+
};
183+
}
184+
185+
/**
186+
* Composes single-argument functions from right to left.
187+
* Copied directly from redux.
188+
* https://github.com/rackt/redux
189+
*/
190+
function compose() {
191+
for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
192+
funcs[_key] = arguments[_key];
193+
}
194+
195+
return function (arg: any) {
196+
return funcs.reduceRight(function (composed, f) {
197+
return f(composed);
198+
}, arg);
199+
};
200+
}

0 commit comments

Comments
 (0)