diff --git a/src/lib/db/store.spec.ts b/src/lib/db/store.spec.ts index 1f1b9aa74e..8fc09d020c 100644 --- a/src/lib/db/store.spec.ts +++ b/src/lib/db/store.spec.ts @@ -1,7 +1,7 @@ import { createDbStoreCreator, getStorageDataList } from './store' import { MemoryLiteStorage } from 'ltstrg' import { renderHook, act } from '@testing-library/react-hooks' -import { NoteStorage } from './types' +import { NoteStorage, NoteDoc } from './types' import { getFolderId } from './utils' import { RouterProvider } from '../router' @@ -201,4 +201,110 @@ describe('DbStore', () => { // TODO: routing test when current folder or current folder's parent folder is deleted. }) + + describe('#createNote', () => { + it('creates a note and registers in the map', async () => { + // Given + const { result } = prepareDbStore() + let storage: NoteStorage + let noteDoc: NoteDoc | undefined + await act(async () => { + await result.current.initialize() + storage = await result.current.createStorage('test') + await result.current.createFolder(storage.id, '/test') + + // When + noteDoc = await result.current.createNote(storage.id, { + title: 'testNote' + }) + }) + + // Then + expect(result.current.storageMap[storage!.id]!.noteMap).toEqual({ + [noteDoc!._id]: expect.objectContaining({ title: 'testNote' }) + }) + }) + + it('creates a note and register its tag', async () => { + // Given + const { result } = prepareDbStore() + let storage: NoteStorage + let noteDoc: NoteDoc | undefined + await act(async () => { + await result.current.initialize() + storage = await result.current.createStorage('test') + await result.current.createFolder(storage.id, '/test') + + // When + noteDoc = await result.current.createNote(storage.id, { + title: 'testNote', + tags: ['testTag'] + }) + }) + + // Then + expect( + result.current.storageMap[storage!.id]!.tagMap[noteDoc!.tags[0]]! + .noteIdSet + ).toMatchObject(new Set([noteDoc!._id])) + }) + }) + + describe('#updateNote', () => { + it('update a Note and handle tag removal', async () => { + // Given + const { result } = prepareDbStore() + let storage: NoteStorage + let noteDoc: NoteDoc | undefined + await act(async () => { + await result.current.initialize() + storage = await result.current.createStorage('test') + await result.current.createFolder(storage.id, '/test') + noteDoc = await result.current.createNote(storage.id, { + title: 'testNote', + tags: ['tag1', 'tag2'] + }) + + // When + noteDoc = await result.current.updateNote(storage.id, noteDoc!._id, { + tags: ['tag2'] + }) + }) + + // Then + expect( + result.current.storageMap[storage!.id]!.tagMap['tag1']!.noteIdSet.size + ).toEqual(0) + }) + + it('update a Note and handle tag addition', async () => { + // Given + const { result } = prepareDbStore() + let storage: NoteStorage + let noteDoc: NoteDoc | undefined + await act(async () => { + await result.current.initialize() + storage = await result.current.createStorage('test') + await result.current.createFolder(storage.id, '/test') + noteDoc = await result.current.createNote(storage.id, { + title: 'testNote', + tags: ['tag1'] + }) + await result.current.createNote(storage.id, { + title: 'testNote', + tags: ['tag2'] + }) + + // When + noteDoc = await result.current.updateNote(storage.id, noteDoc!._id, { + tags: ['tag1', 'tag2'] + }) + }) + + // Then + expect( + result.current.storageMap[storage!.id]!.tagMap['tag2']!.noteIdSet.size + ).toEqual(2) + }) + }) }) diff --git a/src/lib/db/store.ts b/src/lib/db/store.ts index bc50f4d95f..2a1a1fa0d5 100644 --- a/src/lib/db/store.ts +++ b/src/lib/db/store.ts @@ -4,7 +4,8 @@ import { ObjectMap, NoteDoc, NoteDocEditibleProps, - PopulatedFolderDoc + PopulatedFolderDoc, + PopulatedTagDoc } from './types' import { useState, useCallback } from 'react' import { createStoreContext } from '../utils/context' @@ -23,6 +24,8 @@ import { produce } from 'immer' import { useRouter } from '../router' import { values } from '../db/utils' import { storageDataListKey } from '../localStorageKeys' +import { TAG_ID_PREFIX } from './consts' +import { difference } from 'ramda' export interface DbStore { initialized: boolean @@ -213,6 +216,7 @@ export function createDbStoreCreator( parentFolderPathnamesToCheck ) : [] + const folder: PopulatedFolderDoc = storage.folderMap[noteDoc.folderPathname] == null ? ({ @@ -228,7 +232,28 @@ export function createDbStoreCreator( ]) } - // TODO: Reflect tags + const modifiedTags = ((await Promise.all( + noteDoc.tags.map(async tag => { + if (storage.tagMap[tag] == null) { + return { + ...(await storage.db.getTag(tag)!), + noteIdSet: new Set([noteDoc._id]) + } as PopulatedTagDoc + } else { + return { + ...storage.tagMap[tag]!, + noteIdSet: new Set([ + ...storage.tagMap[tag]!.noteIdSet.values(), + noteDoc._id + ]) + } + } + }) + )) as PopulatedTagDoc[]).reduce((acc, tag) => { + acc[tag._id.replace(TAG_ID_PREFIX, '')] = tag + return acc + }, {}) + setStorageMap( produce((draft: ObjectMap) => { draft[storageId]!.noteMap[noteDoc._id] = noteDoc @@ -241,9 +266,12 @@ export function createDbStoreCreator( } }) draft[storageId]!.folderMap[noteDoc.folderPathname] = folder + draft[storageId]!.tagMap = { + ...storage.tagMap, + ...modifiedTags + } }) ) - return noteDoc }, [storageMap] @@ -288,7 +316,41 @@ export function createDbStoreCreator( ]) } - // TODO: Reflect tags + const removedTags: ObjectMap = difference( + storage.noteMap[noteDoc._id]!.tags, + noteDoc.tags + ).reduce((acc, tag) => { + const newNoteIdSet = new Set(storage.tagMap[tag]!.noteIdSet) + newNoteIdSet.delete(noteDoc._id) + acc[tag] = { + ...storage.tagMap[tag]!, + noteIdSet: newNoteIdSet + } + return acc + }, {}) + + const modifiedTags: ObjectMap = ((await Promise.all( + noteDoc.tags.map(async tag => { + if (storage.tagMap[tag] == null) { + return { + ...(await storage.db.getTag(tag)!), + noteIdSet: new Set([noteDoc._id]) + } as PopulatedTagDoc + } else { + return { + ...storage.tagMap[tag]!, + noteIdSet: new Set([ + ...storage.tagMap[tag]!.noteIdSet.values(), + noteDoc._id + ]) + } + } + }) + )) as PopulatedTagDoc[]).reduce((acc, tag) => { + acc[tag._id.replace(TAG_ID_PREFIX, '')] = tag + return acc + }, {}) + setStorageMap( produce((draft: ObjectMap) => { draft[storageId]!.noteMap[noteDoc._id] = noteDoc @@ -301,6 +363,11 @@ export function createDbStoreCreator( } }) draft[storageId]!.folderMap[noteDoc.folderPathname] = folder + draft[storageId]!.tagMap = { + ...storage.tagMap, + ...removedTags, + ...modifiedTags + } }) ) diff --git a/webpack.config.ts b/webpack.config.ts index e443db2d7b..56bb01000e 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -6,7 +6,10 @@ import ErrorOverlayPlugin from 'error-overlay-webpack-plugin' import dotenv from 'dotenv' import CopyPlugin from 'copy-webpack-plugin' -const { parsed } = dotenv.config() +let { parsed } = dotenv.config() +if (parsed == null) { + parsed = {} +} module.exports = { entry: [