From 118d26ce83e06a1c83efe72b5332b92a68394e6b Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Fri, 21 Feb 2020 15:29:28 +0200 Subject: [PATCH] Simplify the fetch credentials structure to not use the fetch record. It was thrashing the redux store. We made the same change for the ios app. --- src/App.tsx | 32 ++++++------------------------ src/LoginGate.tsx | 43 +++++++++++++++++++++++++---------------- src/login/index.tsx | 44 ++++++++++++++++++++++++++++++++++++++++++ src/store/construct.ts | 4 ++-- src/store/reducers.ts | 25 +++++++----------------- 5 files changed, 85 insertions(+), 63 deletions(-) create mode 100644 src/login/index.tsx diff --git a/src/App.tsx b/src/App.tsx index 5e89800..d1e0ceb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,7 +3,6 @@ import { List as ImmutableList } from 'immutable'; import { connect } from 'react-redux'; import { withRouter } from 'react-router'; import { BrowserRouter } from 'react-router-dom'; -import { createSelector } from 'reselect'; import { MuiThemeProvider as ThemeProvider, createMuiTheme } from '@material-ui/core/styles'; // v1.x import amber from '@material-ui/core/colors/amber'; import lightBlue from '@material-ui/core/colors/lightBlue'; @@ -32,6 +31,8 @@ import { RouteResolver } from './routes'; import * as store from './store'; import * as actions from './store/actions'; +import { credentialsSelector } from './login'; + import { History } from 'history'; const muiTheme = createMuiTheme({ @@ -170,7 +171,7 @@ class App extends React.PureComponent { }; public props: { - credentials: store.CredentialsType; + credentials: store.CredentialsData; entries: store.EntriesType; fetchCount: number; errors: ImmutableList; @@ -186,7 +187,7 @@ class App extends React.PureComponent { } public render() { - const credentials = (this.props.credentials) ? this.props.credentials.value : null; + const credentials = this.props.credentials ?? null; const errors = this.props.errors; const fetching = this.props.fetchCount > 0; @@ -251,7 +252,7 @@ class App extends React.PureComponent { - + @@ -268,31 +269,10 @@ class App extends React.PureComponent { } private refresh() { - store.store.dispatch(actions.fetchAll(this.props.credentials.value!, this.props.entries)); + store.store.dispatch(actions.fetchAll(this.props.credentials, this.props.entries)); } } -const credentialsSelector = createSelector( - (state: store.StoreState) => state.credentials.value, - (state: store.StoreState) => state.credentials.error, - (state: store.StoreState) => state.credentials.fetching, - (state: store.StoreState) => state.encryptionKey.key, - (value, error, fetching, encryptionKey) => { - if (value === null) { - return { value, error, fetching }; - } - - return { - error, - fetching, - value: { - ...value, - encryptionKey, - }, - }; - } -); - const mapStateToProps = (state: store.StoreState) => { return { credentials: credentialsSelector(state), diff --git a/src/LoginGate.tsx b/src/LoginGate.tsx index 4751603..1bc893d 100644 --- a/src/LoginGate.tsx +++ b/src/LoginGate.tsx @@ -8,7 +8,7 @@ import SyncGate from './SyncGate'; import LoginForm from './components/LoginForm'; import EncryptionLoginForm from './components/EncryptionLoginForm'; -import { store, CredentialsType } from './store'; +import { store, StoreState, CredentialsDataRemote } from './store'; import { deriveKey, fetchCredentials, fetchUserInfo } from './store/actions'; import * as EteSync from 'etesync'; @@ -16,14 +16,16 @@ import * as C from './constants'; import SignedPagesBadge from './images/signed-pages-badge.svg'; import LoadingIndicator from './widgets/LoadingIndicator'; +import { useCredentials, useRemoteCredentials } from './login'; +import { useSelector } from 'react-redux'; -function EncryptionPart(props: { credentials: CredentialsType }) { +function EncryptionPart(props: { credentials: CredentialsDataRemote }) { const [fetched, setFetched] = React.useState(false); const [userInfo, setUserInfo] = React.useState(); const [error, setError] = React.useState(); - const credentials = props.credentials.value!; + const credentials = props.credentials; React.useEffect(() => { // FIXME: verify the error is a 404 @@ -41,7 +43,7 @@ function EncryptionPart(props: { credentials: CredentialsType }) { } function onEncryptionFormSubmit(encryptionPassword: string) { - const derivedAction = deriveKey(props.credentials.value!.credentials.email, encryptionPassword); + const derivedAction = deriveKey(credentials.credentials.email, encryptionPassword); if (userInfo) { const userInfoCryptoManager = userInfo.getCryptoManager(derivedAction.payload!); try { @@ -81,18 +83,25 @@ function EncryptionPart(props: { credentials: CredentialsType }) { ); } -interface PropsType { - credentials: CredentialsType; -} - -export default function LoginGate(props: PropsType) { +export default function LoginGate() { + const remoteCredentials = useRemoteCredentials(); + const credentials = useCredentials(); + const fetchCount = useSelector((state: StoreState) => state.fetchCount); + const [fetchError, setFetchError] = React.useState(); - function onFormSubmit(username: string, password: string, serviceApiUrl?: string) { + async function onFormSubmit(username: string, password: string, serviceApiUrl?: string) { serviceApiUrl = serviceApiUrl ? serviceApiUrl : C.serviceApiBase; - store.dispatch(fetchCredentials(username, password, serviceApiUrl)); + try { + setFetchError(undefined); + const ret = fetchCredentials(username, password, serviceApiUrl); + await ret.payload; + store.dispatch(ret); + } catch (e) { + setFetchError(e); + } } - if (props.credentials.value === null) { + if (remoteCredentials === null) { const style = { isSafe: { textDecoration: 'none', @@ -109,8 +118,8 @@ export default function LoginGate(props: PropsType) {

Please Log In

0} />
@@ -127,13 +136,13 @@ export default function LoginGate(props: PropsType) { ); - } else if (props.credentials.value.encryptionKey === null) { + } else if (credentials === null) { return ( - + ); } return ( - + ); } diff --git a/src/login/index.tsx b/src/login/index.tsx new file mode 100644 index 0000000..c1fda3a --- /dev/null +++ b/src/login/index.tsx @@ -0,0 +1,44 @@ +import { shallowEqual, useSelector } from 'react-redux'; +import { createSelector } from 'reselect'; + +import * as store from '../store'; + +export const remoteCredentialsSelector = createSelector( + (state: store.StoreState) => state.credentials.credentials, + (state: store.StoreState) => state.credentials.serviceApiUrl, + (credentials, serviceApiUrl) => { + if (!credentials) { + return null; + } + + const ret: store.CredentialsDataRemote = { + credentials, + serviceApiUrl, + }; + return ret; + } +); + +export function useRemoteCredentials() { + return useSelector(remoteCredentialsSelector, shallowEqual); +} + +export const credentialsSelector = createSelector( + (state: store.StoreState) => remoteCredentialsSelector(state), + (state: store.StoreState) => state.encryptionKey.key, + (remoteCredentials, encryptionKey) => { + if (!remoteCredentials || !encryptionKey) { + return null; + } + + const ret: store.CredentialsData = { + ...remoteCredentials, + encryptionKey, + }; + return ret; + } +); + +export function useCredentials() { + return useSelector(credentialsSelector, shallowEqual); +} diff --git a/src/store/construct.ts b/src/store/construct.ts index 17a7508..c8d9ff8 100644 --- a/src/store/construct.ts +++ b/src/store/construct.ts @@ -8,13 +8,13 @@ import { List, Map as ImmutableMap } from 'immutable'; import * as EteSync from 'etesync'; import { JournalsData, FetchType, EntriesData, EntriesFetchRecord, UserInfoData, JournalsFetchRecord, UserInfoFetchRecord, - CredentialsTypeRemote, JournalsType, EntriesType, UserInfoType, SettingsType, + CredentialsDataRemote, JournalsType, EntriesType, UserInfoType, SettingsType, fetchCount, journals, entries, credentials, userInfo, settingsReducer, encryptionKeyReducer, errorsReducer, } from './reducers'; export interface StoreState { fetchCount: number; - credentials: CredentialsTypeRemote; + credentials: CredentialsDataRemote; settings: SettingsType; encryptionKey: {key: string}; cache: { diff --git a/src/store/reducers.ts b/src/store/reducers.ts index 9e8cbd8..aaddf2e 100644 --- a/src/store/reducers.ts +++ b/src/store/reducers.ts @@ -34,9 +34,6 @@ interface BaseModel { uid: string; } -export type CredentialsType = FetchType; -export type CredentialsTypeRemote = FetchType; - export type JournalsData = ImmutableMap; export const JournalsFetchRecord = fetchTypeRecord(); export type JournalsType = FetchType; @@ -97,32 +94,24 @@ export const encryptionKeyReducer = handleActions( export const credentials = handleActions( { [actions.fetchCredentials.toString()]: ( - _state: CredentialsTypeRemote, action: any) => { + state: CredentialsDataRemote, action: any) => { if (action.error) { - return { - value: null, - error: action.payload, - }; + return state; } else if (action.payload === undefined) { - return { - fetching: true, - value: null, - }; + return state; } else { const { encryptionKey, // We don't want to set encryption key here. ...payload } = action.payload; - return { - value: payload, - }; + return payload; } }, - [actions.logout.toString()]: (_state: CredentialsTypeRemote, _action: any) => { - return { out: true, value: null }; + [actions.logout.toString()]: (_state: CredentialsDataRemote, _action: any) => { + return {}; }, }, - { value: null } + {} as CredentialsDataRemote ); const setMapModelReducer = , V extends BaseModel>(state: T, action: any) => {