diff --git a/lib/ops/entries-store-ops.js b/lib/ops/entries-store-ops.js --- a/lib/ops/entries-store-ops.js +++ b/lib/ops/entries-store-ops.js @@ -1,6 +1,13 @@ // @flow -import type { RawEntryInfo } from '../types/entry-types.js'; +import type { BaseStoreOpsHandlers } from './base-ops.js'; +import { daysToEntriesFromEntryInfos } from '../reducers/entry-reducer.js'; +import type { + EntryStore, + RawEntryInfo, + RawEntryInfos, +} from '../types/entry-types.js'; +import { values } from '../utils/objects.js'; export type ReplaceEntryOperation = { +type: 'replace_entry', @@ -26,7 +33,7 @@ export type ClientDBEntryInfo = { +id: string, - +entryInfo: string, + +entry: string, }; export type ClientDBReplaceEntryOperation = { @@ -38,3 +45,91 @@ | ClientDBReplaceEntryOperation | RemoveEntriesOperation | RemoveAllEntriesOperation; + +function convertEntryInfoIntoClientDBEntryInfo({ + id, + entry, +}: { + +id: string, + +entry: RawEntryInfo, +}): ClientDBEntryInfo { + return { + id, + entry: JSON.stringify(entry), + }; +} + +const entryStoreOpsHandlers: BaseStoreOpsHandlers< + EntryStore, + EntryStoreOperation, + ClientDBEntryStoreOperation, + RawEntryInfos, + ClientDBEntryInfo, +> = { + processStoreOperations( + entryStore: EntryStore, + ops: $ReadOnlyArray, + ): EntryStore { + if (ops.length === 0) { + return entryStore; + } + + const processedEntryStore = { + ...entryStore, + }; + for (const operation of ops) { + if (operation.type === 'replace_entry') { + processedEntryStore.entryInfos = { + ...processedEntryStore.entryInfos, + [operation.payload.id]: operation.payload.entry, + }; + } else if (operation.type === 'remove_entries') { + const idsToRemove = new Set(operation.payload.ids); + processedEntryStore.entryInfos = Object.fromEntries( + Object.entries(processedEntryStore.entryInfos).filter( + ([id]) => !idsToRemove.has(id), + ), + ); + } else if (operation.type === 'remove_all_entries') { + processedEntryStore.entryInfos = {}; + } + } + + console.log(JSON.stringify(processedEntryStore, null, 2)); + processedEntryStore.daysToEntries = daysToEntriesFromEntryInfos( + values(processedEntryStore.entryInfos), + ); + + return processedEntryStore; + }, + + convertOpsToClientDBOps( + ops: ?$ReadOnlyArray, + ): $ReadOnlyArray { + if (!ops) { + return []; + } + return ops.map(operation => { + if ( + operation.type === 'remove_entries' || + operation.type === 'remove_all_entries' + ) { + return operation; + } + return { + type: 'replace_entry', + payload: convertEntryInfoIntoClientDBEntryInfo(operation.payload), + }; + }); + }, + + translateClientDBData( + clientDBEntries: $ReadOnlyArray, + ): RawEntryInfos { + return Object.fromEntries( + clientDBEntries.map(({ id, entry }) => [id, JSON.parse(entry)]), + ); + }, +}; + +export { convertEntryInfoIntoClientDBEntryInfo, entryStoreOpsHandlers }; diff --git a/lib/ops/entries-store-ops.test.js b/lib/ops/entries-store-ops.test.js new file mode 100644 --- /dev/null +++ b/lib/ops/entries-store-ops.test.js @@ -0,0 +1,170 @@ +// @flow + +import { entryStoreOpsHandlers } from './entries-store-ops.js'; + +const entryStore = { + entryInfos: { + 'A3F2E633-320D-4CDC-A0A7-48325A07E321|87868': { + id: 'A3F2E633-320D-4CDC-A0A7-48325A07E321|87868', + threadID: 'A3F2E633-320D-4CDC-A0A7-48325A07E321|87695', + text: 'aaa', + year: 2024, + month: 5, + day: 21, + creationTime: 1716287287704, + creatorID: 'ACDC8087-8540-4C97-A0AB-B1A0254C4405', + deleted: false, + }, + 'localb59fc932-56c2-4ada-9179-36f5f0cb4855': { + localID: 'localb59fc932-56c2-4ada-9179-36f5f0cb4855', + threadID: 'A3F2E633-320D-4CDC-A0A7-48325A07E321|87695', + text: '', + year: 2024, + month: 5, + day: 22, + creationTime: 1716287380610, + creatorID: 'ACDC8087-8540-4C97-A0AB-B1A0254C4405', + deleted: false, + }, + }, + daysToEntries: { + '2024-05-21': ['A3F2E633-320D-4CDC-A0A7-48325A07E321|87868'], + '2024-05-22': ['localb59fc932-56c2-4ada-9179-36f5f0cb4855'], + }, + lastUserInteractionCalendar: 1716287380611, +}; + +describe('entryStoreOpsHandlers', () => { + describe('processStoreOperations', () => { + it('should process replace operation', () => { + const newEntry = { + id: '123', + threadID: 'A3F2E633-320D-4CDC-A0A7-48325A07E321|87695', + text: 'aaa', + year: 2024, + month: 5, + day: 22, + creationTime: 1716287287704, + creatorID: 'ACDC8087-8540-4C97-A0AB-B1A0254C4405', + deleted: false, + }; + expect( + entryStoreOpsHandlers.processStoreOperations(entryStore, [ + { + type: 'replace_entry', + payload: { + id: '123', + entry: newEntry, + }, + }, + ]), + ).toEqual({ + entryInfos: { + ...entryStore.entryInfos, + ['123']: newEntry, + }, + daysToEntries: { + ...entryStore.daysToEntries, + '2024-05-22': ['123', 'localb59fc932-56c2-4ada-9179-36f5f0cb4855'], + }, + lastUserInteractionCalendar: 1716287380611, + }); + }); + + it('should process remove operation', () => { + expect( + entryStoreOpsHandlers.processStoreOperations(entryStore, [ + { + type: 'remove_entries', + payload: { + ids: ['localb59fc932-56c2-4ada-9179-36f5f0cb4855'], + }, + }, + ]), + ).toEqual({ + entryInfos: { + ['A3F2E633-320D-4CDC-A0A7-48325A07E321|87868']: + entryStore.entryInfos['A3F2E633-320D-4CDC-A0A7-48325A07E321|87868'], + }, + daysToEntries: { + '2024-05-21': ['A3F2E633-320D-4CDC-A0A7-48325A07E321|87868'], + }, + lastUserInteractionCalendar: 1716287380611, + }); + }); + + it('should process remove all operation', () => { + expect( + entryStoreOpsHandlers.processStoreOperations(entryStore, [ + { + type: 'remove_all_entries', + }, + ]), + ).toEqual({ + entryInfos: {}, + daysToEntries: {}, + lastUserInteractionCalendar: 1716287380611, + }); + }); + }); + + describe('convertOpsToClientDBOps', () => { + it('should not modify remove entries operation', () => { + const operation = { + type: 'remove_entries', + payload: { ids: ['1', '2', '3'] }, + }; + expect( + entryStoreOpsHandlers.convertOpsToClientDBOps([operation]), + ).toEqual([operation]); + }); + + it('should not modify remove all entries operations', () => { + const operation = { + type: 'remove_all_entries', + }; + expect( + entryStoreOpsHandlers.convertOpsToClientDBOps([operation]), + ).toEqual([operation]); + }); + + it('should modify replace entry operation', () => { + const key = 'localb59fc932-56c2-4ada-9179-36f5f0cb4855'; + const operation = { + type: 'replace_entry', + payload: { + id: key, + entry: entryStore.entryInfos[key], + }, + }; + expect( + entryStoreOpsHandlers.convertOpsToClientDBOps([operation]), + ).toEqual([ + { + type: 'replace_entry', + payload: { + id: key, + entry: JSON.stringify(entryStore.entryInfos[key]), + }, + }, + ]); + }); + }); + + describe('translateClientDBData', () => { + it('should convert client DB entries', () => { + const key = 'A3F2E633-320D-4CDC-A0A7-48325A07E321|87868'; + const clientDBEntries = [ + { + id: key, + entry: JSON.stringify(entryStore.entryInfos[key]), + }, + ]; + const translated = + entryStoreOpsHandlers.translateClientDBData(clientDBEntries); + expect(translated).toEqual({ + [key]: entryStore.entryInfos[key], + }); + }); + }); +});