Change entries to also not be a complex fetch type.

master
Tom Hacohen 5 years ago
parent 2946be464c
commit 90392fe432

@ -172,7 +172,7 @@ class App extends React.PureComponent {
public props: {
credentials: store.CredentialsData;
entries: store.EntriesType;
entries: store.EntriesData;
fetchCount: number;
errors: ImmutableList<Error>;
};

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

@ -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<string, SyncInfoJournal>()
@ -154,26 +154,9 @@ class SyncGate extends React.PureComponent<PropsTypeInner> {
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 (
<ul>
{errors.map((error, idx) => (<li key={idx}>{error.journal}: {error.error.toString()}</li>))}
</ul>
);
}
}
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 (<LoadingIndicator style={{ display: 'block', margin: '40px auto' }} />);
}

@ -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<boolean>((resolve, reject) => {
dispatch(fetchListJournal(etesync)).then((journalsAction: Action<EteSync.Journal[]>) => {
@ -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) => {

@ -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<Error>;
@ -64,24 +64,24 @@ const journalsDeserialize = (state: []) => {
return newState.asImmutable();
};
const entriesSerialize = (state: FetchType<EntriesData>) => {
if ((state === null) || (state.value == null)) {
const entriesSerialize = (state: List<EteSync.Entry>) => {
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<EntriesData> => {
const entriesDeserialize = (state: EteSync.EntryJson[]): List<EteSync.Entry> | 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<EntriesData>, mapKey: string) => {
state.forEach((value: List<EteSync.Entry>, 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) => {

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

@ -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<T> {
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<T> = FetchTypeInterface<T>;
function fetchTypeRecord<T>() {
return Record<FetchTypeInterface<T>>({
value: null as T | null,
error: undefined,
});
}
export type JournalsData = ImmutableMap<string, EteSync.Journal>;
export type EntriesData = List<EteSync.Entry>;
export const EntriesFetchRecord = fetchTypeRecord<EntriesData>();
export type EntriesTypeImmutable = ImmutableMap<string, Record<FetchType<EntriesData>>>;
export type EntriesType = ImmutableMap<string, FetchType<EntriesData>>;
export type EntriesListData = List<EteSync.Entry>;
export type EntriesData = ImmutableMap<string, EntriesListData>;
export type UserInfoData = EteSync.UserInfo;
function fetchTypeIdentityReducer(
state: Record<FetchType<any>> = fetchTypeRecord<any>()(), 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<any> | undefined, action: Action<EteSync.Entry[]>, 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({})

Loading…
Cancel
Save