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.
master
Tom Hacohen 5 years ago
parent 5301b546df
commit 118d26ce83

@ -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<Error>;
@ -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 {
</Drawer>
<ErrorBoundary>
<LoginGate credentials={this.props.credentials} />
<LoginGate />
</ErrorBoundary>
</div>
</BrowserRouter>
@ -268,31 +269,10 @@ class App extends React.PureComponent {
}
private refresh() {
store.store.dispatch<any>(actions.fetchAll(this.props.credentials.value!, this.props.entries));
store.store.dispatch<any>(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),

@ -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<EteSync.UserInfo>();
const [error, setError] = React.useState<Error>();
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<Error>();
function onFormSubmit(username: string, password: string, serviceApiUrl?: string) {
async function onFormSubmit(username: string, password: string, serviceApiUrl?: string) {
serviceApiUrl = serviceApiUrl ? serviceApiUrl : C.serviceApiBase;
store.dispatch<any>(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) {
<h2>Please Log In</h2>
<LoginForm
onSubmit={onFormSubmit}
error={props.credentials.error}
loading={props.credentials.fetching}
error={fetchError}
loading={fetchCount > 0}
/>
<hr style={style.divider} />
<ExternalLink style={style.isSafe} href="https://www.etesync.com/faq/#signed-pages">
@ -127,13 +136,13 @@ export default function LoginGate(props: PropsType) {
</ul>
</Container>
);
} else if (props.credentials.value.encryptionKey === null) {
} else if (credentials === null) {
return (
<EncryptionPart credentials={props.credentials} />
<EncryptionPart credentials={remoteCredentials} />
);
}
return (
<SyncGate etesync={props.credentials.value} />
<SyncGate etesync={credentials} />
);
}

@ -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);
}

@ -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: {

@ -34,9 +34,6 @@ interface BaseModel {
uid: string;
}
export type CredentialsType = FetchType<CredentialsData>;
export type CredentialsTypeRemote = FetchType<CredentialsDataRemote>;
export type JournalsData = ImmutableMap<string, EteSync.Journal>;
export const JournalsFetchRecord = fetchTypeRecord<JournalsData>();
export type JournalsType = FetchType<JournalsData>;
@ -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 = <T extends Record<any>, V extends BaseModel>(state: T, action: any) => {

Loading…
Cancel
Save