From 90392fe4320b6152b9735c51e04b57b390423a05 Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Fri, 21 Feb 2020 16:10:15 +0200 Subject: [PATCH] Change entries to also not be a complex fetch type. --- src/App.tsx | 2 +- src/Debug.tsx | 6 +-- src/SyncGate.tsx | 29 +++----------- src/store/actions.ts | 21 ++-------- src/store/construct.ts | 26 ++++++------ src/store/index.test.ts | 22 +++++----- src/store/reducers.ts | 89 ++++++++++++++++------------------------- 7 files changed, 73 insertions(+), 122 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index d1e0ceb..6983d8d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -172,7 +172,7 @@ class App extends React.PureComponent { public props: { credentials: store.CredentialsData; - entries: store.EntriesType; + entries: store.EntriesData; fetchCount: number; errors: ImmutableList; }; diff --git a/src/Debug.tsx b/src/Debug.tsx index eea4ef8..30b725e 100644 --- a/src/Debug.tsx +++ b/src/Debug.tsx @@ -5,7 +5,7 @@ import TextField from '@material-ui/core/TextField'; import Container from './widgets/Container'; import { useSelector } from 'react-redux'; -import { StoreState, CredentialsData, UserInfoData } from './store'; +import { StoreState, CredentialsData, UserInfoData, EntriesListData } from './store'; interface PropsType { etesync: CredentialsData; @@ -67,8 +67,8 @@ export default function Debug(props: PropsType) { const cryptoManager = journal.getCryptoManager(derived, keyPair); let prevUid: string | null = null; - const entries = journalEntries.get(journalUid)!; - const syncEntries = entries.value!.map((entry: EteSync.Entry) => { + const entries = journalEntries.get(journalUid)! as EntriesListData; + const syncEntries = entries.map((entry: EteSync.Entry) => { const syncEntry = entry.getSyncEntry(cryptoManager, prevUid); prevUid = entry.uid; diff --git a/src/SyncGate.tsx b/src/SyncGate.tsx index ed8f01e..bad8b6e 100644 --- a/src/SyncGate.tsx +++ b/src/SyncGate.tsx @@ -21,7 +21,7 @@ import Pim from './Pim'; import * as EteSync from 'etesync'; import { CURRENT_VERSION } from 'etesync'; -import { store, SettingsType, JournalsData, EntriesType, StoreState, CredentialsData, UserInfoData } from './store'; +import { store, SettingsType, JournalsData, EntriesData, StoreState, CredentialsData, UserInfoData } from './store'; import { addJournal, fetchAll, fetchEntries, fetchUserInfo, createUserInfo } from './store/actions'; export interface SyncInfoJournal { @@ -40,7 +40,7 @@ interface PropsType { type PropsTypeInner = RouteComponentProps<{}> & PropsType & { settings: SettingsType; journals: JournalsData; - entries: EntriesType; + entries: EntriesData; userInfo: UserInfoData; fetchCount: number; }; @@ -68,7 +68,7 @@ const syncInfoSelector = createSelector( const journalEntries = entries.get(journal.uid); let prevUid: string | null = null; - if (!journalEntries || !journalEntries.value) { + if (!journalEntries) { return ret; } @@ -77,7 +77,7 @@ const syncInfoSelector = createSelector( const collectionInfo = journal.getInfo(cryptoManager); - const syncEntries = journalEntries.value.map((entry: EteSync.Entry) => { + const syncEntries = journalEntries.map((entry: EteSync.Entry) => { const syncEntry = entry.getSyncEntry(cryptoManager, prevUid); prevUid = entry.uid; @@ -88,7 +88,7 @@ const syncInfoSelector = createSelector( entries: syncEntries, collection: collectionInfo, journal, - journalEntries: journalEntries.value, + journalEntries, }); }, Map() @@ -154,26 +154,9 @@ class SyncGate extends React.PureComponent { const entryArrays = this.props.entries; const journals = this.props.journals; - { - const errors: Array<{journal: string, error: Error}> = []; - this.props.entries.forEach((entry, journal) => { - if (entry.error) { - errors.push({ journal, error: entry.error }); - } - }); - - if (errors.length > 0) { - return ( -
    - {errors.map((error, idx) => (
  • {error.journal}: {error.error.toString()}
  • ))} -
- ); - } - } - if ((this.props.userInfo === null) || (journals === null) || ((this.props.fetchCount > 0) && - ((entryArrays.size === 0) || !entryArrays.every((x: any) => (x.value !== null)))) + ((entryArrays.size === 0) || entryArrays.some((x) => (x.size === 0)))) ) { return (); } diff --git a/src/store/actions.ts b/src/store/actions.ts index 1d05ffe..2fd3439 100644 --- a/src/store/actions.ts +++ b/src/store/actions.ts @@ -3,7 +3,7 @@ import { Action, createAction, createActions } from 'redux-actions'; import * as EteSync from 'etesync'; import { UserInfo } from 'etesync'; -import { CredentialsData, EntriesType, SettingsType } from './'; +import { CredentialsData, EntriesData, SettingsType } from './'; export const { fetchCredentials } = createActions({ FETCH_CREDENTIALS: (username: string, password: string, server: string) => { @@ -168,21 +168,7 @@ export const createUserInfo = createAction( } ); -export function fetchJournalEntries(etesync: CredentialsData, currentEntries: EntriesType, journal: EteSync.Journal) { - return (dispatch: any) => { - let prevUid: string | null = null; - const entries = currentEntries.get(journal.uid); - if (entries && entries.value) { - const last = entries.value.last() as EteSync.Entry; - prevUid = (last) ? last.uid : null; - } - - return dispatch(fetchEntries(etesync, journal.uid, prevUid)); - }; -} - - -export function fetchAll(etesync: CredentialsData, currentEntries: EntriesType) { +export function fetchAll(etesync: CredentialsData, currentEntries: EntriesData) { return (dispatch: any) => { return new Promise((resolve, reject) => { dispatch(fetchListJournal(etesync)).then((journalsAction: Action) => { @@ -193,7 +179,7 @@ export function fetchAll(etesync: CredentialsData, currentEntries: EntriesType) } Promise.all(journals.map((journal) => { - const prevUid = currentEntries.get(journal.uid)?.value?.last(undefined)?.uid ?? null; + const prevUid = currentEntries.get(journal.uid)?.last(undefined)?.uid ?? null; // FIXME: expose it in a non-hacky way. if (prevUid && (prevUid === (journal as any)._json.lastUid)) { @@ -206,6 +192,7 @@ export function fetchAll(etesync: CredentialsData, currentEntries: EntriesType) }; } + export const addError = createAction( 'ADD_ERRORS', (_etesync: CredentialsData, error: Error) => { diff --git a/src/store/construct.ts b/src/store/construct.ts index 67848c8..d113c12 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, - CredentialsDataRemote, EntriesType, SettingsType, + JournalsData, EntriesData, UserInfoData, + CredentialsDataRemote, SettingsType, fetchCount, journals, entries, credentials, userInfo, settingsReducer, encryptionKeyReducer, errorsReducer, } from './reducers'; @@ -19,7 +19,7 @@ export interface StoreState { encryptionKey: {key: string}; cache: { journals: JournalsData; - entries: EntriesType; + entries: EntriesData; userInfo: UserInfoData; }; errors: List; @@ -64,24 +64,24 @@ const journalsDeserialize = (state: []) => { return newState.asImmutable(); }; -const entriesSerialize = (state: FetchType) => { - if ((state === null) || (state.value == null)) { +const entriesSerialize = (state: List) => { + if (state === null) { return null; } - return state.value.map((x) => x.serialize()).toJS(); + return state.map((x) => x.serialize()).toJS(); }; -const entriesDeserialize = (state: EteSync.EntryJson[]): FetchType => { +const entriesDeserialize = (state: EteSync.EntryJson[]): List | null => { if (state === null) { - return new EntriesFetchRecord({ value: null }); + return null; } - return new EntriesFetchRecord({ value: List(state.map((x: any) => { + return List(state.map((x) => { const ret = new EteSync.Entry(); ret.deserialize(x); return ret; - })) }); + })); }; const userInfoSerialize = (state: UserInfoData) => { @@ -102,10 +102,10 @@ const userInfoDeserialize = (state: EteSync.UserInfoJson) => { return ret; }; -const cacheSerialize = (state: any, key: string) => { +const cacheSerialize = (state: any, key: string | number) => { if (key === 'entries') { const ret = {}; - state.forEach((value: FetchType, mapKey: string) => { + state.forEach((value: List, mapKey: string) => { ret[mapKey] = entriesSerialize(value); }); return ret; @@ -118,7 +118,7 @@ const cacheSerialize = (state: any, key: string) => { return state; }; -const cacheDeserialize = (state: any, key: string) => { +const cacheDeserialize = (state: any, key: string | number) => { if (key === 'entries') { const ret = {}; Object.keys(state).forEach((mapKey) => { diff --git a/src/store/index.test.ts b/src/store/index.test.ts index 0cf793d..f3c05ee 100644 --- a/src/store/index.test.ts +++ b/src/store/index.test.ts @@ -1,5 +1,5 @@ import { addEntries, fetchEntries } from './actions'; -import { entries, EntriesTypeImmutable } from './reducers'; +import { entries, EntriesData } from './reducers'; import { Map } from 'immutable'; @@ -7,7 +7,7 @@ import * as EteSync from 'etesync'; it('Entries reducer', () => { const jId = '24324324324'; - let state = Map({}) as EntriesTypeImmutable; + let state = Map({}) as EntriesData; const entry = new EteSync.Entry(); entry.deserialize({ @@ -25,26 +25,26 @@ it('Entries reducer', () => { let entry2; state = entries(state, action as any); - journal = state.get(jId) as any; - entry2 = journal.value.get(0); + journal = state.get(jId)!; + entry2 = journal.get(0)!; expect(entry2.serialize()).toEqual(entry.serialize()); // We replace if there's no prevUid state = entries(state, action as any); - journal = state.get(jId) as any; - entry2 = journal.value.get(0); + journal = state.get(jId)!; + entry2 = journal.get(0)!; expect(entry2.serialize()).toEqual(entry.serialize()); - expect(journal.value.size).toBe(1); + expect(journal.size).toBe(1); // We extend if prevUid is set action.meta.prevUid = entry.uid; state = entries(state, action as any); - journal = state.get(jId) as any; - expect(journal.value.size).toBe(2); + journal = state.get(jId)!; + expect(journal.size).toBe(2); // Creating entries should also work the same action.type = addEntries.toString(); state = entries(state, action as any); - journal = state.get(jId) as any; - expect(journal.value.size).toBe(3); + journal = state.get(jId)!; + expect(journal.size).toBe(3); }); diff --git a/src/store/reducers.ts b/src/store/reducers.ts index 3c5a3a0..5eb0c4f 100644 --- a/src/store/reducers.ts +++ b/src/store/reducers.ts @@ -1,18 +1,12 @@ import { Action, ActionMeta, ActionFunctionAny, combineActions, handleAction, handleActions } from 'redux-actions'; import { shallowEqual } from 'react-redux'; -import { List, Map as ImmutableMap, Record } from 'immutable'; +import { List, Map as ImmutableMap } from 'immutable'; import * as EteSync from 'etesync'; import * as actions from './actions'; -interface FetchTypeInterface { - value: T | null; - fetching?: boolean; - error?: Error; -} - export interface CredentialsDataRemote { serviceApiUrl: string; credentials: EteSync.Credentials; @@ -22,51 +16,13 @@ export interface CredentialsData extends CredentialsDataRemote { encryptionKey: string; } -export type FetchType = FetchTypeInterface; - -function fetchTypeRecord() { - return Record>({ - value: null as T | null, - error: undefined, - }); -} - export type JournalsData = ImmutableMap; -export type EntriesData = List; -export const EntriesFetchRecord = fetchTypeRecord(); -export type EntriesTypeImmutable = ImmutableMap>>; -export type EntriesType = ImmutableMap>; +export type EntriesListData = List; +export type EntriesData = ImmutableMap; export type UserInfoData = EteSync.UserInfo; -function fetchTypeIdentityReducer( - state: Record> = fetchTypeRecord()(), action: any, extend = false) { - if (action.error) { - return state.set('error', action.payload); - } else { - const payload = (action.payload === undefined) ? null : action.payload; - - state = state.set('error', undefined); - - if (action.payload === undefined) { - return state; - } - - let value = state.get('value', null); - if (extend && (value !== null)) { - if (payload !== null) { - value = value.concat(payload); - } - } else if (payload !== null) { - value = List(payload); - } else { - value = null; - } - return state.set('value', value); - } -} - export const encryptionKeyReducer = handleActions( { [actions.deriveKey.toString()]: (_state: {key: string | null}, action: any) => ( @@ -105,22 +61,47 @@ export const credentials = handleActions( {} as CredentialsDataRemote ); -function fetchCreateEntriesReducer(state: EntriesTypeImmutable, action: any) { +function entriesListSetExtend( + state: List | undefined, action: Action, extend = false) { + state = state ?? List([]); + + if (action.error) { + return state; + } else { + const payload = action.payload ?? null; + + if (!payload) { + return state; + } + + if (extend && (state !== null)) { + if (payload !== null) { + state = state.concat(payload); + } + } else if (payload !== null) { + state = List(payload); + } + return state; + } +} + +function fetchCreateEntriesReducer(state: EntriesData, action: any) { const prevState = state.get(action.meta.journal); - const extend = action.meta.prevUid != null; + const extend = action.meta.prevUid !== null; return state.set(action.meta.journal, - fetchTypeIdentityReducer(prevState, action, extend)); + entriesListSetExtend(prevState, action, extend)); } export const entries = handleActions( { [actions.fetchEntries.toString()]: fetchCreateEntriesReducer, [actions.addEntries.toString()]: fetchCreateEntriesReducer, - [actions.addJournal.toString()]: (state: EntriesTypeImmutable, action: any) => { + [actions.addJournal.toString()]: (state: EntriesData, action: any) => { const journal = action.meta.item.uid; - const prevState = state.get(journal); - return state.set(journal, - fetchTypeIdentityReducer(prevState, { payload: [] }, false)); + return state.set(journal, List([])); + }, + [actions.logout.toString()]: (state: EntriesData, _action: any) => { + return state.clear(); }, }, ImmutableMap({})