diff --git a/src/SideMenu/index.tsx b/src/SideMenu/index.tsx index 8be80a0..deeb080 100644 --- a/src/SideMenu/index.tsx +++ b/src/SideMenu/index.tsx @@ -115,7 +115,7 @@ class SideMenu extends React.PureComponent { const mapStateToProps = (state: StoreState, _props: PropsType) => { return { journals: state.cache.journals, - userInfo: state.cache.userInfo.value, + userInfo: state.cache.userInfo, }; }; diff --git a/src/SyncGate.tsx b/src/SyncGate.tsx index 1e61194..a1efda3 100644 --- a/src/SyncGate.tsx +++ b/src/SyncGate.tsx @@ -22,7 +22,7 @@ import Pim from './Pim'; import * as EteSync from 'etesync'; import { CURRENT_VERSION } from 'etesync'; -import { store, SettingsType, JournalsType, EntriesType, StoreState, CredentialsData, UserInfoType } from './store'; +import { store, SettingsType, JournalsType, EntriesType, StoreState, CredentialsData, UserInfoData } from './store'; import { addJournal, fetchAll, fetchEntries, fetchUserInfo, createUserInfo } from './store/actions'; export interface SyncInfoJournal { @@ -42,7 +42,7 @@ type PropsTypeInner = RouteComponentProps<{}> & PropsType & { settings: SettingsType; journals: JournalsType; entries: EntriesType; - userInfo: UserInfoType; + userInfo: UserInfoData; fetchCount: number; }; @@ -50,7 +50,7 @@ const syncInfoSelector = createSelector( (props: PropsTypeInner) => props.etesync, (props: PropsTypeInner) => props.journals.value!, (props: PropsTypeInner) => props.entries, - (props: PropsTypeInner) => props.userInfo.value!, + (props: PropsTypeInner) => props.userInfo, (etesync, journals, entries, userInfo) => { const derived = etesync.encryptionKey; const userInfoCryptoManager = userInfo.getCryptoManager(etesync.encryptionKey); @@ -132,10 +132,14 @@ class SyncGate extends React.PureComponent { }); }; - const sync = () => { - if (this.props.userInfo.value) { + if (this.props.userInfo) { + syncAll(); + } else { + const fetching = fetchUserInfo(this.props.etesync, me); + fetching.payload?.then(() => { + store.dispatch(fetching); syncAll(); - } else { + }).catch(() => { const userInfo = new EteSync.UserInfo(me, CURRENT_VERSION); const keyPair = EteSync.AsymmetricCryptoManager.generateKeyPair(); const cryptoManager = userInfo.getCryptoManager(this.props.etesync.encryptionKey); @@ -143,14 +147,7 @@ class SyncGate extends React.PureComponent { userInfo.setKeyPair(cryptoManager, keyPair); store.dispatch(createUserInfo(this.props.etesync, userInfo)).then(syncAll); - } - }; - - if (this.props.userInfo.value) { - syncAll(); - } else { - const fetching = store.dispatch(fetchUserInfo(this.props.etesync, me)) as any; - fetching.then(sync).catch(() => sync()); + }); } } @@ -158,9 +155,7 @@ class SyncGate extends React.PureComponent { const entryArrays = this.props.entries; const journals = this.props.journals.value; - if (this.props.userInfo.error) { - return ; - } else if (this.props.journals.error) { + if (this.props.journals.error) { return ; } else { const errors: Array<{journal: string, error: Error}> = []; @@ -179,7 +174,7 @@ class SyncGate extends React.PureComponent { } } - if ((this.props.userInfo.value === null) || (journals === null) || + if ((this.props.userInfo === null) || (journals === null) || ((this.props.fetchCount > 0) && ((entryArrays.size === 0) || !entryArrays.every((x: any) => (x.value !== null)))) ) { @@ -208,7 +203,7 @@ class SyncGate extends React.PureComponent { @@ -220,7 +215,7 @@ class SyncGate extends React.PureComponent { render={({ location, history }) => ( { render={() => ( )} /> diff --git a/src/store/construct.ts b/src/store/construct.ts index c8d9ff8..e760df7 100644 --- a/src/store/construct.ts +++ b/src/store/construct.ts @@ -7,8 +7,8 @@ import { List, Map as ImmutableMap } from 'immutable'; import * as EteSync from 'etesync'; import { - JournalsData, FetchType, EntriesData, EntriesFetchRecord, UserInfoData, JournalsFetchRecord, UserInfoFetchRecord, - CredentialsDataRemote, JournalsType, EntriesType, UserInfoType, SettingsType, + JournalsData, FetchType, EntriesData, EntriesFetchRecord, UserInfoData, JournalsFetchRecord, + CredentialsDataRemote, JournalsType, EntriesType, SettingsType, fetchCount, journals, entries, credentials, userInfo, settingsReducer, encryptionKeyReducer, errorsReducer, } from './reducers'; @@ -20,7 +20,7 @@ export interface StoreState { cache: { journals: JournalsType; entries: EntriesType; - userInfo: UserInfoType; + userInfo: UserInfoData; }; errors: List; } @@ -84,12 +84,12 @@ const entriesDeserialize = (state: EteSync.EntryJson[]): FetchType })) }); }; -const userInfoSerialize = (state: FetchType) => { - if ((state === null) || (state.value == null)) { +const userInfoSerialize = (state: UserInfoData) => { + if (state === null) { return null; } - return state.value.serialize(); + return state.serialize(); }; const userInfoDeserialize = (state: EteSync.UserInfoJson) => { @@ -128,7 +128,7 @@ const cacheDeserialize = (state: any, key: string) => { } else if (key === 'journals') { return new JournalsFetchRecord({ value: journalsDeserialize(state) }); } else if (key === 'userInfo') { - return new UserInfoFetchRecord({ value: userInfoDeserialize(state) }); + return userInfoDeserialize(state); } return state; diff --git a/src/store/reducers.ts b/src/store/reducers.ts index aaddf2e..df98b19 100644 --- a/src/store/reducers.ts +++ b/src/store/reducers.ts @@ -1,4 +1,5 @@ import { Action, ActionFunctionAny, combineActions, handleAction, handleActions } from 'redux-actions'; +import { shallowEqual } from 'react-redux'; import { List, Map as ImmutableMap, Record } from 'immutable'; @@ -45,9 +46,6 @@ export type EntriesTypeImmutable = ImmutableMap>; export type UserInfoData = EteSync.UserInfo; -export const UserInfoFetchRecord = fetchTypeRecord(); -export type UserInfoType = FetchType; -export type UserInfoTypeImmutable = Record; function fetchTypeIdentityReducer( state: Record> = fetchTypeRecord()(), action: any, extend = false) { @@ -213,29 +211,35 @@ export const journals = handleActions( new JournalsFetchRecord() ); -export const userInfo = handleAction( - combineActions( - actions.fetchUserInfo, - actions.createUserInfo - ), - (state: Record> = fetchTypeRecord()(), action: any) => { - if (action.error) { - return state.set('error', action.payload); - } else { - let payload = (action.payload === undefined) ? null : action.payload; +export const userInfo = handleActions( + { + [combineActions( + actions.fetchUserInfo, + actions.createUserInfo + ).toString()]: (state: UserInfoData | null, action: any) => { + if (action.error) { + return state; + } else { + let payload = action.payload ?? null; - state = state.set('error', undefined); + if (payload === null) { + return state; + } - if (payload === null) { - return state; - } + payload = action.meta?.userInfo ?? payload; - payload = (action.meta === undefined) ? payload : action.meta.userInfo; + if (!state || !shallowEqual(state.serialize(), payload.serialize())) { + return payload; + } - return state.set('value', payload); - } + return state; + } + }, + [actions.logout.toString()]: (_state: any, _action: any) => { + return null; + }, }, - new UserInfoFetchRecord() + null ); const fetchActions = [