From 6e3bb42f96baf9bb332d09df175f59f2d1376e73 Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Fri, 7 Aug 2020 18:09:55 +0300 Subject: [PATCH] Item batch: clean up reducers and update store on upload --- src/Collections/ImportDialog.tsx | 5 ++++- src/Pim/helpers.tsx | 7 +++++-- src/store/actions.ts | 20 ++++++++++++++++++++ src/store/index.ts | 8 ++++++-- src/store/reducers.ts | 29 +++++++++++++++++++++++++---- 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/Collections/ImportDialog.tsx b/src/Collections/ImportDialog.tsx index 796d576..b60ac85 100644 --- a/src/Collections/ImportDialog.tsx +++ b/src/Collections/ImportDialog.tsx @@ -22,6 +22,8 @@ import { ContactType, EventType, TaskType, PimType } from "../pim-types"; import { useCredentials } from "../credentials"; import { CachedCollection } from "../Pim/helpers"; import { getCollectionManager } from "../etebase-helpers"; +import { useAsyncDispatch } from "../store"; +import { itemBatch } from "../store/actions"; const CHUNK_SIZE = 40; @@ -33,6 +35,7 @@ interface PropsType { export default function ImportDialog(props: PropsType) { const etebase = useCredentials()!; + const dispatch = useAsyncDispatch(); const [loading, setLoading] = React.useState(false); const [itemsProcessed, setItemsProccessed] = React.useState(); @@ -74,7 +77,7 @@ export default function ImportDialog(props: PropsType) { const chunks = arrayToChunkIterator(eteItems, CHUNK_SIZE); for (const chunk of chunks) { - await itemMgr.batch(chunk); + await dispatch(itemBatch(collection, itemMgr, chunk)); } setItemsProccessed(items.length); diff --git a/src/Pim/helpers.tsx b/src/Pim/helpers.tsx index d4b6fbc..d4a0f0f 100644 --- a/src/Pim/helpers.tsx +++ b/src/Pim/helpers.tsx @@ -11,6 +11,8 @@ import * as Etebase from "etebase"; import { PimType } from "../pim-types"; import { getCollectionManager } from "../etebase-helpers"; +import { asyncDispatch } from "../store"; +import { itemBatch } from "../store/actions"; export const defaultColor = "#8BC34A"; @@ -91,7 +93,7 @@ export async function itemSave(etebase: Etebase.Account, collection: Etebase.Col eteItem = await itemMgr.create(meta, content); } - await itemMgr.batch([eteItem]); + await asyncDispatch(itemBatch(collection, itemMgr, [eteItem])); } export async function itemDelete(etebase: Etebase.Account, collection: Etebase.Collection, items: Map>, item: PimType, collectionUid: string) { @@ -105,7 +107,8 @@ export async function itemDelete(etebase: Etebase.Account, collection: Etebase.C meta.mtime = mtime; await eteItem.setMeta(meta); await eteItem.delete(); - await itemMgr.batch([eteItem]); + + await asyncDispatch(itemBatch(collection, itemMgr, [eteItem])); } interface PimFabPropsType { diff --git a/src/store/actions.ts b/src/store/actions.ts index 1150dec..54121cc 100644 --- a/src/store/actions.ts +++ b/src/store/actions.ts @@ -88,6 +88,7 @@ export const setCacheItem = createAction( return { colUid: col.uid, itemUid: item.uid, + deleted: item.isDeleted, }; } ); @@ -101,6 +102,25 @@ export const unsetCacheItem = createAction( return { colUid, itemUid, + deleted: true, + }; + } +); + +export const itemBatch = createAction( + "ITEM_BATCH", + async (_col: Etebase.Collection, itemMgr: Etebase.CollectionItemManager, items: Etebase.CollectionItem[], deps?: Etebase.CollectionItem[]) => { + await itemMgr.batch(items, deps); + const ret = []; + for (const item of items) { + ret.push(Etebase.toBase64(await itemMgr.cacheSave(item))); + } + return ret; + }, + (col: Etebase.Collection, _itemMgr: Etebase.CollectionItemManager, items: Etebase.CollectionItem[], _deps?: Etebase.CollectionItem[]) => { + return { + colUid: col.uid, + items: items, }; } ); diff --git a/src/store/index.ts b/src/store/index.ts index 6730d53..8d0125e 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -25,11 +25,15 @@ if (process.env.NODE_ENV === "development") { middleware.push(createLogger()); } +export function asyncDispatch(action: ActionMeta, V>): Promise> { + return store.dispatch(action) as any; +} + export function useAsyncDispatch() { const dispatch = useDispatch(); - return function asyncDispatch(action: ActionMeta, V>): Promise> { + return function (action: any): any { return dispatch(action) as any; - }; + } as typeof asyncDispatch; } export const store = createStore( diff --git a/src/store/reducers.ts b/src/store/reducers.ts index 0db8f25..1647210 100644 --- a/src/store/reducers.ts +++ b/src/store/reducers.ts @@ -5,6 +5,8 @@ import { Action, ActionMeta, ActionFunctionAny, combineActions, handleAction, ha import { List, Map as ImmutableMap } from "immutable"; +import * as Etebase from "etebase"; + import * as actions from "./actions"; interface BaseModel { @@ -114,15 +116,34 @@ export const collections = handleActions( export const items = handleActions( { - [actions.setCacheItem.toString()]: (state: CacheItemsData, action: ActionMeta) => { + [combineActions( + actions.setCacheItem, + actions.unsetCacheItem + ).toString()]: (state: CacheItemsData, action: ActionMeta) => { if (action.payload !== undefined) { - return state.setIn([action.meta.colUid, action.meta.itemUid], action.payload); + if (action.meta.deleted) { + return state.removeIn([action.meta.colUid, action.meta.itemUid]); + } else { + return state.setIn([action.meta.colUid, action.meta.itemUid], action.payload); + } } return state; }, - [actions.unsetCacheItem.toString()]: (state: CacheItemsData, action: ActionMeta) => { + [actions.itemBatch.toString()]: (state: CacheItemsData, action_: any) => { + // Fails without it for some reason + const action = action_ as ActionMeta; if (action.payload !== undefined) { - return state.removeIn([action.meta.colUid, action.meta.itemUid]); + return state.withMutations((state) => { + let i = 0; + for (const item of action.meta.items) { + if (item.isDeleted) { + state.removeIn([action.meta.colUid, item.uid]); + } else { + state.setIn([action.meta.colUid, item.uid], action.payload[i]); + } + i++; + } + }); } return state; },