Page MenuHomePhabricator

D11132.diff
No OneTemporary

D11132.diff

diff --git a/lib/types/redux-types.js b/lib/types/redux-types.js
--- a/lib/types/redux-types.js
+++ b/lib/types/redux-types.js
@@ -1366,11 +1366,13 @@
};
export type ActionPayload = ?(Object | Array<*> | $ReadOnlyArray<*> | string);
+export type DispatchSource = 'tunnelbroker' | 'tab-sync';
export type SuperAction = {
type: string,
payload?: ActionPayload,
loadingInfo?: LoadingInfo,
error?: boolean,
+ dispatchSource?: DispatchSource,
};
type ThunkedAction = (dispatch: Dispatch) => void;
export type PromisedAction = (dispatch: Dispatch) => Promise<void>;
diff --git a/web/redux/redux-setup.js b/web/redux/redux-setup.js
--- a/web/redux/redux-setup.js
+++ b/web/redux/redux-setup.js
@@ -121,17 +121,17 @@
export type Action =
| BaseAction
- | { type: 'UPDATE_NAV_INFO', payload: Partial<WebNavInfo> }
+ | { +type: 'UPDATE_NAV_INFO', +payload: Partial<WebNavInfo> }
| {
- type: 'UPDATE_WINDOW_DIMENSIONS',
- payload: WindowDimensions,
+ +type: 'UPDATE_WINDOW_DIMENSIONS',
+ +payload: WindowDimensions,
}
| {
- type: 'UPDATE_WINDOW_ACTIVE',
- payload: boolean,
+ +type: 'UPDATE_WINDOW_ACTIVE',
+ +payload: boolean,
}
- | { +type: 'SET_CRYPTO_STORE', payload: CryptoStore }
- | { +type: 'SET_INITIAL_REDUX_STATE', payload: InitialReduxState };
+ | { +type: 'SET_CRYPTO_STORE', +payload: CryptoStore }
+ | { +type: 'SET_INITIAL_REDUX_STATE', +payload: InitialReduxState };
function reducer(oldState: AppState | void, action: Action): AppState {
invariant(oldState, 'should be set');
@@ -163,6 +163,7 @@
});
}
return validateStateAndProcessDBOperations(
+ action,
oldState,
{
...state,
@@ -184,6 +185,7 @@
);
} else if (action.type === updateWindowDimensionsActionType) {
return validateStateAndProcessDBOperations(
+ action,
oldState,
{
...state,
@@ -193,6 +195,7 @@
);
} else if (action.type === updateWindowActiveActionType) {
return validateStateAndProcessDBOperations(
+ action,
oldState,
{
...state,
@@ -341,10 +344,16 @@
communityPickerStore,
};
- return validateStateAndProcessDBOperations(oldState, state, storeOperations);
+ return validateStateAndProcessDBOperations(
+ action,
+ oldState,
+ state,
+ storeOperations,
+ );
}
function validateStateAndProcessDBOperations(
+ action: Action,
oldState: AppState,
state: AppState,
storeOperations: StoreOperations,
@@ -455,10 +464,19 @@
};
}
- void processDBStoreOperations(
- storeOperations,
- state.currentUserInfo?.id ?? null,
- );
+ // The operations were already dispatched from the main tab
+
+ // For now the `dispatchSource` field is not included in any of the
+ // redux actions and this causes flow to throw an error.
+ // As soon as one of the actions is updated, this fix (and the corresponding
+ // one in tab-synchronization.js) can be removed.
+ // $FlowFixMe
+ if (action.dispatchSource !== 'tab-sync') {
+ void processDBStoreOperations(
+ storeOperations,
+ state.currentUserInfo?.id ?? null,
+ );
+ }
return state;
}
diff --git a/web/redux/tab-synchronization.js b/web/redux/tab-synchronization.js
new file mode 100644
--- /dev/null
+++ b/web/redux/tab-synchronization.js
@@ -0,0 +1,36 @@
+// @flow
+
+import type { Middleware, Store } from 'redux';
+
+import type { Action, AppState } from './redux-setup.js';
+
+const WEB_REDUX_CHANNEL = new BroadcastChannel('shared-redux');
+
+const tabSynchronizationMiddleware: Middleware<AppState, Action> =
+ () => next => action => {
+ const result = next(action);
+ // For now the `dispatchSource` field is not included in any of the
+ // redux actions and this causes flow to throw an error.
+ // As soon as one of the actions is updated, this fix (and the corresponding
+ // one in redux-setup.js) can be removed.
+ // $FlowFixMe
+ if (action.dispatchSource === 'tunnelbroker') {
+ WEB_REDUX_CHANNEL.postMessage(action);
+ }
+ return result;
+ };
+
+function synchronizeStoreWithOtherTabs(store: Store<AppState, Action>) {
+ WEB_REDUX_CHANNEL.onmessage = event => {
+ // We can be sure that we only pass actions through the broadcast channel.
+ // Additionally we know that this is one of the actions that contains the
+ // `dispatchSource` field.
+ const action: Action = ({
+ ...event.data,
+ dispatchSource: 'tab-sync',
+ }: any);
+ store.dispatch(action);
+ };
+}
+
+export { tabSynchronizationMiddleware, synchronizeStoreWithOtherTabs };
diff --git a/web/root.js b/web/root.js
--- a/web/root.js
+++ b/web/root.js
@@ -29,6 +29,10 @@
import InitialReduxStateGate from './redux/initial-state-gate.js';
import { persistConfig } from './redux/persist.js';
import { type AppState, type Action, reducer } from './redux/redux-setup.js';
+import {
+ synchronizeStoreWithOtherTabs,
+ tabSynchronizationMiddleware,
+} from './redux/tab-synchronization.js';
import history from './router-history.js';
import Socket from './socket.react.js';
@@ -38,8 +42,11 @@
const store: Store<AppState, Action> = createStore(
persistedReducer,
defaultWebState,
- composeWithDevTools({})(applyMiddleware(thunk, reduxLoggerMiddleware)),
+ composeWithDevTools({})(
+ applyMiddleware(thunk, reduxLoggerMiddleware, tabSynchronizationMiddleware),
+ ),
);
+synchronizeStoreWithOtherTabs(store);
const persistor = persistStore(store);
const RootProvider = (): React.Node => (

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 17, 1:21 PM (21 h, 32 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2525519
Default Alt Text
D11132.diff (5 KB)

Event Timeline