Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3510060
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
18 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/native/redux/default-state.js b/native/redux/default-state.js
new file mode 100644
index 000000000..afb4e6ef8
--- /dev/null
+++ b/native/redux/default-state.js
@@ -0,0 +1,90 @@
+// @flow
+
+import { Platform } from 'react-native';
+import Orientation from 'react-native-orientation-locker';
+
+import { defaultEnabledApps } from 'lib/types/enabled-apps.js';
+import { defaultCalendarQuery } from 'lib/types/entry-types.js';
+import { defaultCalendarFilters } from 'lib/types/filter-types.js';
+import { defaultConnectionInfo } from 'lib/types/socket-types.js';
+import { defaultNotifPermissionAlertInfo } from 'lib/utils/push-alerts.js';
+import { ashoatKeyserverID } from 'lib/utils/validation-utils.js';
+
+import { defaultDimensionsInfo } from './dimensions-updater.react.js';
+import type { AppState } from './state-types.js';
+import { defaultNavInfo } from '../navigation/default-state.js';
+import { defaultDeviceCameraInfo } from '../types/camera.js';
+import { defaultConnectivityInfo } from '../types/connectivity.js';
+import { defaultGlobalThemeInfo } from '../types/themes.js';
+import { defaultURLPrefix, natNodeServer } from '../utils/url-utils.js';
+
+const defaultState = ({
+ navInfo: defaultNavInfo,
+ currentUserInfo: null,
+ draftStore: { drafts: {} },
+ entryStore: {
+ entryInfos: {},
+ daysToEntries: {},
+ lastUserInteractionCalendar: 0,
+ },
+ threadStore: {
+ threadInfos: {},
+ },
+ userStore: {
+ userInfos: {},
+ inconsistencyReports: [],
+ },
+ messageStore: {
+ messages: {},
+ threads: {},
+ local: {},
+ currentAsOf: { [ashoatKeyserverID]: 0 },
+ },
+ storeLoaded: false,
+ loadingStatuses: {},
+ calendarFilters: defaultCalendarFilters,
+ deviceToken: null,
+ dataLoaded: false,
+ customServer: natNodeServer,
+ notifPermissionAlertInfo: defaultNotifPermissionAlertInfo,
+ actualizedCalendarQuery: defaultCalendarQuery(Platform.OS),
+ watchedThreadIDs: [],
+ lifecycleState: 'active',
+ enabledApps: defaultEnabledApps,
+ reportStore: {
+ enabledReports: {
+ crashReports: __DEV__,
+ inconsistencyReports: __DEV__,
+ mediaReports: __DEV__,
+ },
+ queuedReports: [],
+ },
+ nextLocalID: 0,
+ _persist: null,
+ dimensions: defaultDimensionsInfo,
+ connectivity: defaultConnectivityInfo,
+ globalThemeInfo: defaultGlobalThemeInfo,
+ deviceCameraInfo: defaultDeviceCameraInfo,
+ deviceOrientation: Orientation.getInitialOrientation(),
+ frozen: false,
+ userPolicies: {},
+ commServicesAccessToken: null,
+ inviteLinksStore: {
+ links: {},
+ },
+ keyserverStore: {
+ keyserverInfos: {
+ [ashoatKeyserverID]: {
+ updatesCurrentAsOf: 0,
+ urlPrefix: defaultURLPrefix,
+ connection: defaultConnectionInfo,
+ lastCommunicatedPlatformDetails: null,
+ },
+ },
+ },
+ localSettings: {
+ isBackupEnabled: false,
+ },
+}: AppState);
+
+export { defaultState };
diff --git a/native/redux/redux-setup.js b/native/redux/redux-setup.js
index b067a1804..771b9cd17 100644
--- a/native/redux/redux-setup.js
+++ b/native/redux/redux-setup.js
@@ -1,509 +1,424 @@
// @flow
import { AppState as NativeAppState, Platform, Alert } from 'react-native';
-import Orientation from 'react-native-orientation-locker';
import { createStore, applyMiddleware, type Store, compose } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import thunk from 'redux-thunk';
import { setClientDBStoreActionType } from 'lib/actions/client-db-store-actions.js';
import { siweAuthActionTypes } from 'lib/actions/siwe-actions.js';
import {
logOutActionTypes,
deleteAccountActionTypes,
logInActionTypes,
} from 'lib/actions/user-actions.js';
import type { ThreadStoreOperation } from 'lib/ops/thread-store-ops.js';
import { threadStoreOpsHandlers } from 'lib/ops/thread-store-ops.js';
import baseReducer from 'lib/reducers/master-reducer.js';
import {
invalidSessionDowngrade,
invalidSessionRecovery,
} from 'lib/shared/session-utils.js';
import { isStaff } from 'lib/shared/staff-utils.js';
-import { defaultEnabledApps } from 'lib/types/enabled-apps.js';
-import { defaultCalendarQuery } from 'lib/types/entry-types.js';
-import { defaultCalendarFilters } from 'lib/types/filter-types.js';
import type { Dispatch, BaseAction } from 'lib/types/redux-types.js';
import { rehydrateActionType } from 'lib/types/redux-types.js';
import type { SetSessionPayload } from 'lib/types/session-types.js';
-import { defaultConnectionInfo } from 'lib/types/socket-types.js';
import { reduxLoggerMiddleware } from 'lib/utils/action-logger.js';
import { setNewSessionActionType } from 'lib/utils/action-utils.js';
-import { defaultNotifPermissionAlertInfo } from 'lib/utils/push-alerts.js';
-import { ashoatKeyserverID } from 'lib/utils/validation-utils.js';
import {
updateDimensionsActiveType,
updateConnectivityActiveType,
updateThemeInfoActionType,
updateDeviceCameraInfoActionType,
updateDeviceOrientationActionType,
updateThreadLastNavigatedActionType,
backgroundActionTypes,
setReduxStateActionType,
setStoreLoadedActionType,
type Action,
setLocalSettingsActionType,
} from './action-types.js';
+import { defaultState } from './default-state.js';
import { remoteReduxDevServerConfig } from './dev-tools.js';
-import { defaultDimensionsInfo } from './dimensions-updater.react.js';
import { persistConfig, setPersistor } from './persist.js';
import { processDBStoreOperations } from './redux-utils.js';
import type { AppState } from './state-types.js';
import reduceGlobalThemeInfo from './theme-reducer.js';
-import { defaultNavInfo } from '../navigation/default-state.js';
import { getGlobalNavContext } from '../navigation/icky-global.js';
import { activeMessageListSelector } from '../navigation/nav-selectors.js';
import reactotron from '../reactotron.js';
-import { defaultDeviceCameraInfo } from '../types/camera.js';
-import { defaultConnectivityInfo } from '../types/connectivity.js';
-import { defaultGlobalThemeInfo } from '../types/themes.js';
import { isStaffRelease } from '../utils/staff-utils.js';
-import {
- defaultURLPrefix,
- natNodeServer,
- setCustomServer,
- getDevServerHostname,
-} from '../utils/url-utils.js';
-
-const defaultState = ({
- navInfo: defaultNavInfo,
- currentUserInfo: null,
- draftStore: { drafts: {} },
- entryStore: {
- entryInfos: {},
- daysToEntries: {},
- lastUserInteractionCalendar: 0,
- },
- threadStore: {
- threadInfos: {},
- },
- userStore: {
- userInfos: {},
- inconsistencyReports: [],
- },
- messageStore: {
- messages: {},
- threads: {},
- local: {},
- currentAsOf: { [ashoatKeyserverID]: 0 },
- },
- storeLoaded: false,
- loadingStatuses: {},
- calendarFilters: defaultCalendarFilters,
- deviceToken: null,
- dataLoaded: false,
- customServer: natNodeServer,
- notifPermissionAlertInfo: defaultNotifPermissionAlertInfo,
- actualizedCalendarQuery: defaultCalendarQuery(Platform.OS),
- watchedThreadIDs: [],
- lifecycleState: 'active',
- enabledApps: defaultEnabledApps,
- reportStore: {
- enabledReports: {
- crashReports: __DEV__,
- inconsistencyReports: __DEV__,
- mediaReports: __DEV__,
- },
- queuedReports: [],
- },
- nextLocalID: 0,
- _persist: null,
- dimensions: defaultDimensionsInfo,
- connectivity: defaultConnectivityInfo,
- globalThemeInfo: defaultGlobalThemeInfo,
- deviceCameraInfo: defaultDeviceCameraInfo,
- deviceOrientation: Orientation.getInitialOrientation(),
- frozen: false,
- userPolicies: {},
- commServicesAccessToken: null,
- inviteLinksStore: {
- links: {},
- },
- keyserverStore: {
- keyserverInfos: {
- [ashoatKeyserverID]: {
- updatesCurrentAsOf: 0,
- urlPrefix: defaultURLPrefix,
- connection: defaultConnectionInfo,
- lastCommunicatedPlatformDetails: null,
- },
- },
- },
- localSettings: {
- isBackupEnabled: false,
- },
-}: AppState);
+import { setCustomServer, getDevServerHostname } from '../utils/url-utils.js';
function reducer(state: AppState = defaultState, action: Action) {
if (action.type === setReduxStateActionType) {
return action.payload.state;
}
// We want to alert staff/developers if there's a difference between the keys
// we expect to see REHYDRATED and the keys that are actually REHYDRATED.
// Context: https://linear.app/comm/issue/ENG-2127/
if (
action.type === rehydrateActionType &&
(__DEV__ ||
isStaffRelease ||
(state.currentUserInfo &&
state.currentUserInfo.id &&
isStaff(state.currentUserInfo.id)))
) {
// 1. Construct set of keys expected to be REHYDRATED
const defaultKeys = Object.keys(defaultState);
const expectedKeys = defaultKeys.filter(
each => !persistConfig.blacklist.includes(each),
);
const expectedKeysSet = new Set(expectedKeys);
// 2. Construct set of keys actually REHYDRATED
const rehydratedKeys = Object.keys(action.payload ?? {});
const rehydratedKeysSet = new Set(rehydratedKeys);
// 3. Determine the difference between the two sets
const expectedKeysNotRehydrated = expectedKeys.filter(
each => !rehydratedKeysSet.has(each),
);
const rehydratedKeysNotExpected = rehydratedKeys.filter(
each => !expectedKeysSet.has(each),
);
// 4. Display alerts with the differences between the two sets
if (expectedKeysNotRehydrated.length > 0) {
Alert.alert(
`EXPECTED KEYS NOT REHYDRATED: ${JSON.stringify(
expectedKeysNotRehydrated,
)}`,
);
}
if (rehydratedKeysNotExpected.length > 0) {
Alert.alert(
`REHYDRATED KEYS NOT EXPECTED: ${JSON.stringify(
rehydratedKeysNotExpected,
)}`,
);
}
}
if (
(action.type === setNewSessionActionType &&
invalidSessionDowngrade(
state,
action.payload.sessionChange.currentUserInfo,
action.payload.preRequestUserState,
)) ||
(action.type === logOutActionTypes.success &&
invalidSessionDowngrade(
state,
action.payload.currentUserInfo,
action.payload.preRequestUserState,
)) ||
(action.type === deleteAccountActionTypes.success &&
invalidSessionDowngrade(
state,
action.payload.currentUserInfo,
action.payload.preRequestUserState,
))
) {
return state;
}
if (
(action.type === setNewSessionActionType &&
action.payload.sessionChange.currentUserInfo &&
invalidSessionRecovery(
state,
action.payload.sessionChange.currentUserInfo,
action.payload.logInActionSource,
)) ||
((action.type === logInActionTypes.success ||
action.type === siweAuthActionTypes.success) &&
invalidSessionRecovery(
state,
action.payload.currentUserInfo,
action.payload.logInActionSource,
))
) {
return state;
}
state = {
...state,
globalThemeInfo: reduceGlobalThemeInfo(state.globalThemeInfo, action),
};
if (action.type === setCustomServer) {
return {
...state,
customServer: action.payload,
};
} else if (action.type === updateDimensionsActiveType) {
return {
...state,
dimensions: {
...state.dimensions,
...action.payload,
},
};
} else if (action.type === updateConnectivityActiveType) {
return {
...state,
connectivity: action.payload,
};
} else if (action.type === updateThemeInfoActionType) {
// Handled above by reduceGlobalThemeInfo
return state;
} else if (action.type === updateDeviceCameraInfoActionType) {
return {
...state,
deviceCameraInfo: {
...state.deviceCameraInfo,
...action.payload,
},
};
} else if (action.type === updateDeviceOrientationActionType) {
return {
...state,
deviceOrientation: action.payload,
};
} else if (action.type === updateThreadLastNavigatedActionType) {
const { threadID, time } = action.payload;
if (state.messageStore.threads[threadID]) {
const updatedThreads = {
[threadID]: {
...state.messageStore.threads[threadID],
lastNavigatedTo: time,
},
};
state = {
...state,
messageStore: {
...state.messageStore,
threads: {
...state.messageStore.threads,
...updatedThreads,
},
},
};
processDBStoreOperations({
draftStoreOperations: [],
messageStoreOperations: [
{
type: 'replace_threads',
payload: {
threads: updatedThreads,
},
},
],
threadStoreOperations: [],
reportStoreOperations: [],
});
}
return state;
} else if (action.type === setLocalSettingsActionType) {
return {
...state,
localSettings: { ...state.localSettings, ...action.payload },
};
} else if (
action.type === logOutActionTypes.started ||
action.type === logOutActionTypes.success ||
action.type === deleteAccountActionTypes.success
) {
return {
...state,
localSettings: { isBackupEnabled: false },
};
}
if (action.type === setNewSessionActionType) {
sessionInvalidationAlert(action.payload);
}
if (action.type === setStoreLoadedActionType) {
return {
...state,
storeLoaded: true,
};
}
if (action.type === setClientDBStoreActionType) {
state = {
...state,
storeLoaded: true,
};
const currentLoggedInUserID = state.currentUserInfo?.anonymous
? undefined
: state.currentUserInfo?.id;
const actionCurrentLoggedInUserID = action.payload.currentUserID;
if (
!currentLoggedInUserID ||
!actionCurrentLoggedInUserID ||
actionCurrentLoggedInUserID !== currentLoggedInUserID
) {
// If user is logged out now, was logged out at the time action was
// dispatched or their ID changed between action dispatch and a
// call to reducer we ignore the SQLite data since it is not valid
return state;
}
}
const baseReducerResult = baseReducer(state, (action: BaseAction));
state = baseReducerResult.state;
const { storeOperations } = baseReducerResult;
const {
draftStoreOperations,
threadStoreOperations,
messageStoreOperations,
reportStoreOperations,
} = storeOperations;
const fixUnreadActiveThreadResult = fixUnreadActiveThread(state, action);
state = fixUnreadActiveThreadResult.state;
const threadStoreOperationsWithUnreadFix = [
...threadStoreOperations,
...fixUnreadActiveThreadResult.threadStoreOperations,
];
processDBStoreOperations({
draftStoreOperations,
messageStoreOperations,
threadStoreOperations: threadStoreOperationsWithUnreadFix,
reportStoreOperations,
});
return state;
}
function sessionInvalidationAlert(payload: SetSessionPayload) {
if (
!payload.sessionChange.cookieInvalidated ||
!payload.preRequestUserState ||
!payload.preRequestUserState.currentUserInfo ||
payload.preRequestUserState.currentUserInfo.anonymous
) {
return;
}
if (payload.error === 'client_version_unsupported') {
const app = Platform.select({
ios: 'App Store',
android: 'Play Store',
});
Alert.alert(
'App out of date',
'Your app version is pretty old, and the server doesn’t know how to ' +
`speak to it anymore. Please use the ${app} app to update!`,
[{ text: 'OK' }],
{ cancelable: true },
);
} else {
Alert.alert(
'Session invalidated',
'We’re sorry, but your session was invalidated by the server. ' +
'Please log in again.',
[{ text: 'OK' }],
{ cancelable: true },
);
}
}
// Makes sure a currently focused thread is never unread. Note that we consider
// a backgrounded NativeAppState to actually be active if it last changed to
// inactive more than 10 seconds ago. This is because there is a delay when
// NativeAppState is updating in response to a foreground, and actions don't get
// processed more than 10 seconds after a backgrounding anyways. However we
// don't consider this for action types that can be expected to happen while the
// app is backgrounded.
type FixUnreadActiveThreadResult = {
+state: AppState,
+threadStoreOperations: $ReadOnlyArray<ThreadStoreOperation>,
};
function fixUnreadActiveThread(
state: AppState,
action: *,
): FixUnreadActiveThreadResult {
const navContext = getGlobalNavContext();
const activeThread = activeMessageListSelector(navContext);
if (
!activeThread ||
!state.threadStore.threadInfos[activeThread]?.currentUser.unread ||
(NativeAppState.currentState !== 'active' &&
(appLastBecameInactive + 10000 >= Date.now() ||
backgroundActionTypes.has(action.type)))
) {
return { state, threadStoreOperations: [] };
}
const updatedActiveThreadInfo = {
...state.threadStore.threadInfos[activeThread],
currentUser: {
...state.threadStore.threadInfos[activeThread].currentUser,
unread: false,
},
};
const threadStoreOperations = [
{
type: 'replace',
payload: {
id: activeThread,
threadInfo: updatedActiveThreadInfo,
},
},
];
const updatedThreadStore = threadStoreOpsHandlers.processStoreOperations(
state.threadStore,
threadStoreOperations,
);
return {
state: { ...state, threadStore: updatedThreadStore },
threadStoreOperations,
};
}
let appLastBecameInactive = 0;
function appBecameInactive() {
appLastBecameInactive = Date.now();
}
const middleware = applyMiddleware(thunk, reduxLoggerMiddleware);
let composeFunc = compose;
if (__DEV__ && global.HermesInternal) {
const { composeWithDevTools } = require('remote-redux-devtools/src/index.js');
composeFunc = composeWithDevTools({
name: 'Redux',
hostname: getDevServerHostname(),
...remoteReduxDevServerConfig,
});
} else if (global.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) {
composeFunc = global.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
name: 'Redux',
});
}
let enhancers;
if (reactotron) {
enhancers = composeFunc(middleware, reactotron.createEnhancer());
} else {
enhancers = composeFunc(middleware);
}
const store: Store<AppState, *> = createStore(
persistReducer(persistConfig, reducer),
defaultState,
enhancers,
);
const persistor = persistStore(store);
setPersistor(persistor);
const unsafeDispatch: any = store.dispatch;
const dispatch: Dispatch = unsafeDispatch;
export { store, dispatch, appBecameInactive };
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Dec 23, 11:01 AM (18 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2676698
Default Alt Text
(18 KB)
Attached To
Mode
rCOMM Comm
Attached
Detach File
Event Timeline
Log In to Comment