Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32158982
redux-setup.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
redux-setup.js
View Options
// @flow
import
{
AppState
as
NativeAppState
,
Alert
}
from
'react-native'
;
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
,
keyserverAuthActionTypes
,
deleteKeyserverAccountActionTypes
,
identityRegisterActionTypes
,
}
from
'lib/actions/user-actions.js'
;
import
{
setNewSessionActionType
}
from
'lib/keyserver-conn/keyserver-conn-types.js'
;
import
type
{
ThreadStoreOperation
}
from
'lib/ops/thread-store-ops.js'
;
import
{
threadStoreOpsHandlers
}
from
'lib/ops/thread-store-ops.js'
;
import
{
reduceLoadingStatuses
}
from
'lib/reducers/loading-reducer.js'
;
import
baseReducer
from
'lib/reducers/master-reducer.js'
;
import
{
invalidSessionDowngrade
,
invalidSessionRecovery
,
identityInvalidSessionDowngrade
,
}
from
'lib/shared/session-utils.js'
;
import
{
isStaff
}
from
'lib/shared/staff-utils.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
{
reduxLoggerMiddleware
}
from
'lib/utils/action-logger.js'
;
import
{
resetUserSpecificState
}
from
'lib/utils/reducers-utils.js'
;
import
{
updateDimensionsActiveType
,
updateConnectivityActiveType
,
updateDeviceCameraInfoActionType
,
updateDeviceOrientationActionType
,
backgroundActionTypes
,
setReduxStateActionType
,
setStoreLoadedActionType
,
type
Action
,
setLocalSettingsActionType
,
}
from
'./action-types.js'
;
import
{
defaultState
}
from
'./default-state.js'
;
import
{
remoteReduxDevServerConfig
}
from
'./dev-tools.js'
;
import
{
persistConfig
,
setPersistor
}
from
'./persist.js'
;
import
{
onStateDifference
}
from
'./redux-debug-utils.js'
;
import
{
processDBStoreOperations
}
from
'./redux-utils.js'
;
import
{
nonUserSpecificFieldsNative
}
from
'./state-types.js'
;
import
type
{
AppState
}
from
'./state-types.js'
;
import
{
getGlobalNavContext
}
from
'../navigation/icky-global.js'
;
import
{
activeMessageListSelector
}
from
'../navigation/nav-selectors.js'
;
import
reactotron
from
'../reactotron.js'
;
import
{
AppOutOfDateAlertDetails
}
from
'../utils/alert-messages.js'
;
import
{
isStaffRelease
}
from
'../utils/staff-utils.js'
;
import
{
getDevServerHostname
}
from
'../utils/url-utils.js'
;
function
reducer
(
state
:
AppState
=
defaultState
,
inputAction
:
Action
)
{
let
action
=
inputAction
;
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
:
$ReadOnlyArray
<
string
>
=
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
:
$ReadOnlyArray
<
string
>
=
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
.
payload
.
keyserverID
,
)
)
{
return
{
...
state
,
loadingStatuses
:
reduceLoadingStatuses
(
state
.
loadingStatuses
,
action
),
};
}
else
if
(
action
.
type
===
deleteKeyserverAccountActionTypes
.
success
)
{
const
{
currentUserInfo
,
preRequestUserState
}
=
action
.
payload
;
const
newKeyserverIDs
=
[];
for
(
const
keyserverID
of
action
.
payload
.
keyserverIDs
)
{
if
(
invalidSessionDowngrade
(
state
,
currentUserInfo
,
preRequestUserState
,
keyserverID
,
)
)
{
continue
;
}
newKeyserverIDs
.
push
(
keyserverID
);
}
if
(
newKeyserverIDs
.
length
===
0
)
{
return
{
...
state
,
loadingStatuses
:
reduceLoadingStatuses
(
state
.
loadingStatuses
,
action
),
};
}
action
=
{
...
action
,
payload
:
{
...
action
.
payload
,
keyserverIDs
:
newKeyserverIDs
,
},
};
}
else
if
(
action
.
type
===
logOutActionTypes
.
success
||
action
.
type
===
deleteAccountActionTypes
.
success
)
{
const
{
currentUserInfo
,
preRequestUserState
}
=
action
.
payload
;
if
(
identityInvalidSessionDowngrade
(
state
,
currentUserInfo
,
preRequestUserState
,
)
)
{
return
{
...
state
,
loadingStatuses
:
reduceLoadingStatuses
(
state
.
loadingStatuses
,
action
),
};
}
state
=
resetUserSpecificState
(
state
,
defaultState
,
nonUserSpecificFieldsNative
,
);
}
else
if
(
action
.
type
===
identityRegisterActionTypes
.
success
)
{
state
=
resetUserSpecificState
(
state
,
defaultState
,
nonUserSpecificFieldsNative
,
);
}
if
(
(
action
.
type
===
setNewSessionActionType
&&
action
.
payload
.
sessionChange
.
currentUserInfo
&&
invalidSessionRecovery
(
state
,
action
.
payload
.
sessionChange
.
currentUserInfo
,
action
.
payload
.
authActionSource
,
))
||
((
action
.
type
===
logInActionTypes
.
success
||
action
.
type
===
siweAuthActionTypes
.
success
)
&&
invalidSessionRecovery
(
state
,
action
.
payload
.
currentUserInfo
,
action
.
payload
.
authActionSource
,
))
||
(
action
.
type
===
keyserverAuthActionTypes
.
success
&&
invalidSessionRecovery
(
state
,
action
.
payload
.
preRequestUserInfo
,
action
.
payload
.
authActionSource
,
))
)
{
return
state
;
}
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
===
updateDeviceCameraInfoActionType
)
{
return
{
...
state
,
deviceCameraInfo
:
{
...
state
.
deviceCameraInfo
,
...
action
.
payload
,
},
};
}
else
if
(
action
.
type
===
updateDeviceOrientationActionType
)
{
return
{
...
state
,
deviceOrientation
:
action
.
payload
,
};
}
else
if
(
action
.
type
===
setLocalSettingsActionType
)
{
return
{
...
state
,
localSettings
:
{
...
state
.
localSettings
,
...
action
.
payload
},
};
}
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
),
onStateDifference
,
);
state
=
baseReducerResult
.
state
;
const
{
storeOperations
}
=
baseReducerResult
;
const
{
draftStoreOperations
,
threadStoreOperations
,
messageStoreOperations
,
reportStoreOperations
,
userStoreOperations
,
keyserverStoreOperations
,
communityStoreOperations
,
integrityStoreOperations
,
}
=
storeOperations
;
const
fixUnreadActiveThreadResult
=
fixUnreadActiveThread
(
state
,
action
);
state
=
fixUnreadActiveThreadResult
.
state
;
const
threadStoreOperationsWithUnreadFix
=
[
...
threadStoreOperations
,
...
fixUnreadActiveThreadResult
.
threadStoreOperations
,
];
void
processDBStoreOperations
({
draftStoreOperations
,
messageStoreOperations
,
threadStoreOperations
:
threadStoreOperationsWithUnreadFix
,
reportStoreOperations
,
userStoreOperations
,
keyserverStoreOperations
,
communityStoreOperations
,
integrityStoreOperations
,
});
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'
)
{
Alert
.
alert
(
AppOutOfDateAlertDetails
.
title
,
AppOutOfDateAlertDetails
.
message
,
[{
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
activeThreadInfo
=
state
.
threadStore
.
threadInfos
[
activeThread
];
const
updatedActiveThreadInfo
=
{
...
activeThreadInfo
,
currentUser
:
{
...
activeThreadInfo
.
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-java
Expires
Sun, Dec 7, 4:24 PM (6 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5839547
Default Alt Text
redux-setup.js (13 KB)
Attached To
Mode
rCOMM Comm
Attached
Detach File
Event Timeline
Log In to Comment