mirror of
https://github.com/QingWei-Li/notea.git
synced 2024-12-05 08:24:46 +03:00
refactor: remove local tree
This commit is contained in:
parent
b6edd1a5d5
commit
2f10fe2292
@ -1,43 +1,46 @@
|
||||
import SidebarListItem from './sidebar-list-item'
|
||||
import { NoteTreeState, TreeModel } from 'containers/tree'
|
||||
import { NoteTreeState } from 'containers/tree'
|
||||
import Tree, {
|
||||
ItemId,
|
||||
mutateTree,
|
||||
TreeDestinationPosition,
|
||||
TreeSourcePosition,
|
||||
} from '@atlaskit/tree'
|
||||
import { useEffect, useCallback } from 'react'
|
||||
import { NoteState } from 'containers/note'
|
||||
import IconPlus from 'heroicons/react/outline/Plus'
|
||||
import router from 'next/router'
|
||||
import HotkeyTooltip from 'components/hotkey-tooltip'
|
||||
import SidebarItemButton from './sidebar-item-button'
|
||||
|
||||
const SideBarList = () => {
|
||||
const { tree, updateTree, initTree, moveTree } = NoteTreeState.useContainer()
|
||||
const { initAllNotes } = NoteState.useContainer()
|
||||
const { tree, initTree, moveItem, mutateItem } = NoteTreeState.useContainer()
|
||||
|
||||
useEffect(() => {
|
||||
initTree().then(() => initAllNotes())
|
||||
}, [initAllNotes, initTree])
|
||||
initTree()
|
||||
}, [initTree])
|
||||
|
||||
const onExpand = useCallback(
|
||||
(itemId: ItemId) => {
|
||||
updateTree(mutateTree(tree, itemId, { isExpanded: true }) as TreeModel)
|
||||
(id: ItemId) => {
|
||||
mutateItem({
|
||||
id,
|
||||
isExpanded: true,
|
||||
})
|
||||
},
|
||||
[tree, updateTree]
|
||||
[mutateItem]
|
||||
)
|
||||
|
||||
const onCollapse = useCallback(
|
||||
(itemId: ItemId) => {
|
||||
updateTree(mutateTree(tree, itemId, { isExpanded: false }) as TreeModel)
|
||||
(id: ItemId) => {
|
||||
mutateItem({
|
||||
id,
|
||||
isExpanded: false,
|
||||
})
|
||||
},
|
||||
[tree, updateTree]
|
||||
[mutateItem]
|
||||
)
|
||||
|
||||
const onDragEnd = useCallback(
|
||||
(source: TreeSourcePosition, destination?: TreeDestinationPosition) => {
|
||||
moveTree({
|
||||
moveItem({
|
||||
source,
|
||||
destination,
|
||||
}).catch((e) => {
|
||||
@ -45,7 +48,7 @@ const SideBarList = () => {
|
||||
console.error('更新错误', e)
|
||||
})
|
||||
},
|
||||
[moveTree]
|
||||
[moveItem]
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC } from 'react'
|
||||
import { FC, useEffect } from 'react'
|
||||
import FilterModal from 'components/filter-modal/filter-modal'
|
||||
import FilterModalInput from 'components/filter-modal/filter-modal-input'
|
||||
import FilterModalList from 'components/filter-modal/filter-modal-list'
|
||||
@ -12,9 +12,14 @@ const Trash: FC = () => {
|
||||
closeModal,
|
||||
filterNotes,
|
||||
keyword,
|
||||
list,
|
||||
initTrash,
|
||||
filterData,
|
||||
} = TrashState.useContainer()
|
||||
|
||||
useEffect(() => {
|
||||
initTrash()
|
||||
}, [initTrash])
|
||||
|
||||
return (
|
||||
<FilterModal open={isOpen} onClose={closeModal}>
|
||||
<FilterModalInput
|
||||
@ -23,7 +28,7 @@ const Trash: FC = () => {
|
||||
keyword={keyword}
|
||||
/>
|
||||
<FilterModalList
|
||||
items={list}
|
||||
items={filterData}
|
||||
ItemComponent={(item: NoteModel) => (
|
||||
<TrashItem note={item} keyword={keyword} key={item.id} />
|
||||
)}
|
||||
|
@ -96,10 +96,6 @@ const useNote = () => {
|
||||
[cache, post]
|
||||
)
|
||||
|
||||
const initAllNotes = useCallback(() => {
|
||||
noteWorker.current?.checkAllNotes()
|
||||
}, [noteWorker])
|
||||
|
||||
const removeNote = useCallback(
|
||||
async (id: string) => {
|
||||
await post(`${id}/meta`, {
|
||||
@ -117,7 +113,6 @@ const useNote = () => {
|
||||
removeNote,
|
||||
setNote,
|
||||
updateNoteMeta,
|
||||
initAllNotes,
|
||||
loading,
|
||||
}
|
||||
}
|
||||
|
@ -2,32 +2,45 @@ import { useState, useCallback } from 'react'
|
||||
import { createContainer } from 'unstated-next'
|
||||
import { noteStore, NoteStoreItem } from 'utils/local-store'
|
||||
import escapeStringRegexp from 'escape-string-regexp'
|
||||
import useFetch from 'use-http'
|
||||
import { map } from 'lodash'
|
||||
|
||||
function useTrashData() {
|
||||
const [list, setList] = useState<NoteStoreItem[]>()
|
||||
const [noteIds, setNoteIds] = useState<string[]>()
|
||||
const [keyword, setKeyword] = useState<string>()
|
||||
const { get, data } = useFetch('/api/trash')
|
||||
const [filterData, setFilterData] = useState<NoteStoreItem[]>()
|
||||
|
||||
const filterNotes = useCallback(async (keyword?: string) => {
|
||||
setKeyword(keyword)
|
||||
const initTrash = useCallback(async () => {
|
||||
await get()
|
||||
setNoteIds(data)
|
||||
}, [data, get])
|
||||
|
||||
if (!keyword) {
|
||||
setList([])
|
||||
return
|
||||
}
|
||||
const filterNotes = useCallback(
|
||||
async (keyword?: string) => {
|
||||
setKeyword(keyword)
|
||||
|
||||
const data = [] as NoteStoreItem[]
|
||||
const re = new RegExp(escapeStringRegexp(keyword))
|
||||
const data = [] as NoteStoreItem[]
|
||||
const re = keyword ? new RegExp(escapeStringRegexp(keyword)) : false
|
||||
|
||||
await noteStore.iterate<NoteStoreItem, void>((note) => {
|
||||
if (re.test(note.rawContent || '') || re.test(note.title || '')) {
|
||||
data.push(note)
|
||||
}
|
||||
})
|
||||
map(noteIds, async (id) => {
|
||||
const note = await noteStore.getItem<NoteStoreItem>(id)
|
||||
if (!note) return
|
||||
if (
|
||||
!re ||
|
||||
re.test(note.rawContent || '') ||
|
||||
re.test(note.title || '')
|
||||
) {
|
||||
data.push(note)
|
||||
}
|
||||
})
|
||||
|
||||
setList(data)
|
||||
}, [])
|
||||
setFilterData(data)
|
||||
},
|
||||
[noteIds]
|
||||
)
|
||||
|
||||
return { list, keyword, filterNotes }
|
||||
return { filterData, keyword, filterNotes, initTrash }
|
||||
}
|
||||
|
||||
function useFilterModal() {
|
||||
|
@ -6,15 +6,14 @@ import {
|
||||
TreeItem,
|
||||
TreeSourcePosition,
|
||||
} from '@atlaskit/tree'
|
||||
import { isEmpty, forEach, union, map } from 'lodash'
|
||||
import { isEmpty, forEach, union, map, without } from 'lodash'
|
||||
import { genId } from 'packages/shared'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { NOTE_DELETED } from 'shared/meta'
|
||||
import { createContainer } from 'unstated-next'
|
||||
import { uiStore } from 'utils/local-store'
|
||||
import { NoteModel } from './note'
|
||||
import { useNoteWorker } from 'workers/note'
|
||||
import useFetch from 'use-http'
|
||||
import { TreeItemMutation } from '@atlaskit/tree/dist/types/utils/tree'
|
||||
|
||||
export interface TreeItemModel extends TreeItem {
|
||||
id: string
|
||||
@ -36,22 +35,6 @@ export const DEFAULT_TREE: TreeModel = {
|
||||
},
|
||||
}
|
||||
|
||||
const saveLocalTree = (data: TreeModel) => {
|
||||
const items: any = {}
|
||||
|
||||
forEach(data.items, (item) => {
|
||||
items[item.id] = {
|
||||
isExpanded: item.isExpanded,
|
||||
id: item.id,
|
||||
}
|
||||
})
|
||||
|
||||
uiStore.setItem('tree_items', {
|
||||
...data,
|
||||
items,
|
||||
})
|
||||
}
|
||||
|
||||
const useNoteTree = (initData: TreeModel = DEFAULT_TREE) => {
|
||||
const { post } = useFetch('/api/tree')
|
||||
const [tree, setTree] = useState<TreeModel>(initData)
|
||||
@ -62,84 +45,71 @@ const useNoteTree = (initData: TreeModel = DEFAULT_TREE) => {
|
||||
treeRef.current = tree
|
||||
}, [tree])
|
||||
|
||||
const updateTree = useCallback((data: TreeModel) => {
|
||||
setTree(data)
|
||||
saveLocalTree(data)
|
||||
}, [])
|
||||
|
||||
const initTree = useCallback(async () => {
|
||||
const localTree =
|
||||
(await uiStore.getItem<TreeModel>('tree_items')) || DEFAULT_TREE
|
||||
if (!noteWorker) return
|
||||
const curTree = treeRef.current
|
||||
const newItems = {} as TreeModel['items']
|
||||
|
||||
await Promise.all(
|
||||
map(curTree.items, async (item) => {
|
||||
if (!item.isExpanded && localTree.items[item.id]?.isExpanded) {
|
||||
item.isExpanded = true
|
||||
}
|
||||
|
||||
newItems[item.id] = {
|
||||
...item,
|
||||
data: await noteWorker.current?.fetchNote(item.id),
|
||||
data: await noteWorker?.fetchNote(item.id),
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
updateTree({
|
||||
setTree({
|
||||
...curTree,
|
||||
items: newItems,
|
||||
})
|
||||
}, [noteWorker, updateTree])
|
||||
noteWorker?.checkAllNotes(newItems)
|
||||
}, [noteWorker])
|
||||
|
||||
const addToTree = useCallback(
|
||||
(item: NoteModel) => {
|
||||
const newItems: TreeModel['items'] = {}
|
||||
const curTree = treeRef.current
|
||||
const curItem = curTree.items[item.id]
|
||||
const parentItem = treeRef.current.items[item.pid || 'root']
|
||||
const addToTree = useCallback((item: NoteModel) => {
|
||||
const newItems: TreeModel['items'] = {}
|
||||
const curTree = treeRef.current
|
||||
const curItem = curTree.items[item.id]
|
||||
const parentItem = treeRef.current.items[item.pid || 'root']
|
||||
|
||||
parentItem.children = union(parentItem.children, [item.id])
|
||||
parentItem.children = union(parentItem.children, [item.id])
|
||||
|
||||
if (!curItem) {
|
||||
newItems[item.id] = {
|
||||
id: item.id,
|
||||
data: item,
|
||||
children: [],
|
||||
}
|
||||
} else if (curItem.data?.title !== item.title) {
|
||||
newItems[item.id] = {
|
||||
...curItem,
|
||||
data: item,
|
||||
}
|
||||
if (!curItem) {
|
||||
newItems[item.id] = {
|
||||
id: item.id,
|
||||
data: item,
|
||||
children: [],
|
||||
}
|
||||
|
||||
if (!isEmpty(newItems)) {
|
||||
updateTree({
|
||||
...curTree,
|
||||
items: {
|
||||
...curTree.items,
|
||||
...newItems,
|
||||
},
|
||||
})
|
||||
} else if (curItem.data?.title !== item.title) {
|
||||
newItems[item.id] = {
|
||||
...curItem,
|
||||
data: item,
|
||||
}
|
||||
},
|
||||
[updateTree]
|
||||
)
|
||||
}
|
||||
|
||||
const removeFromTree = useCallback(
|
||||
(itemId: string) => {
|
||||
updateTree(
|
||||
mutateTree(treeRef.current, itemId, {
|
||||
data: {
|
||||
...treeRef.current.items[itemId].data,
|
||||
deleted: NOTE_DELETED.DELETED,
|
||||
},
|
||||
}) as TreeModel
|
||||
)
|
||||
},
|
||||
[updateTree]
|
||||
)
|
||||
if (!isEmpty(newItems)) {
|
||||
setTree({
|
||||
...curTree,
|
||||
items: {
|
||||
...curTree.items,
|
||||
...newItems,
|
||||
},
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
const removeFromTree = useCallback((id: string) => {
|
||||
forEach(treeRef.current.items, (item) => {
|
||||
if (item.children.includes(id)) {
|
||||
setTree(
|
||||
mutateTree(treeRef.current, item.id, {
|
||||
children: without(item.children, id),
|
||||
}) as TreeModel
|
||||
)
|
||||
return false
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const genNewId = useCallback(() => {
|
||||
let newId = genId()
|
||||
@ -149,7 +119,7 @@ const useNoteTree = (initData: TreeModel = DEFAULT_TREE) => {
|
||||
return newId
|
||||
}, [])
|
||||
|
||||
const moveTree = useCallback(
|
||||
const moveItem = useCallback(
|
||||
async (data: {
|
||||
source: TreeSourcePosition
|
||||
destination?: TreeDestinationPosition
|
||||
@ -162,24 +132,43 @@ const useNoteTree = (initData: TreeModel = DEFAULT_TREE) => {
|
||||
treeRef.current,
|
||||
data.source,
|
||||
data.destination
|
||||
)
|
||||
) as TreeModel
|
||||
|
||||
updateTree(newTree as TreeModel)
|
||||
await post('move', data)
|
||||
setTree(newTree)
|
||||
await post({
|
||||
action: 'move',
|
||||
data,
|
||||
})
|
||||
|
||||
return
|
||||
},
|
||||
[post, updateTree]
|
||||
[post]
|
||||
)
|
||||
|
||||
const mutateItem = useCallback(
|
||||
async (data: TreeItemMutation) => {
|
||||
const tree = mutateTree(
|
||||
treeRef.current,
|
||||
data.id as string,
|
||||
data
|
||||
) as TreeModel
|
||||
setTree(tree)
|
||||
await post({
|
||||
action: 'mutate',
|
||||
data,
|
||||
})
|
||||
},
|
||||
[post]
|
||||
)
|
||||
|
||||
return {
|
||||
tree,
|
||||
addToTree,
|
||||
removeFromTree,
|
||||
updateTree,
|
||||
initTree,
|
||||
genNewId,
|
||||
moveTree,
|
||||
moveItem,
|
||||
mutateItem,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { jsonToMeta, metaToJson } from 'services/meta'
|
||||
import { useAuth } from 'services/middlewares/auth'
|
||||
import { useStore } from 'services/middlewares/store'
|
||||
import { getPathNoteById } from 'services/note-path'
|
||||
import { NOTE_DELETED } from 'shared/meta'
|
||||
|
||||
export default api()
|
||||
.use(useAuth)
|
||||
@ -18,6 +19,16 @@ export default api()
|
||||
|
||||
if (oldMeta) {
|
||||
meta = new Map([...oldMeta, ...meta])
|
||||
|
||||
// 处理删除情况
|
||||
const { deleted } = req.body
|
||||
if (oldMeta.get('deleted') !== deleted) {
|
||||
if (deleted === NOTE_DELETED.DELETED) {
|
||||
await req.treeStore.removeItem(id)
|
||||
} else if (deleted === NOTE_DELETED.NORMAL) {
|
||||
await req.treeStore.addItem(id, req.body.pid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await req.store.copyObject(notePath, notePath, {
|
||||
|
25
pages/api/trash.ts
Normal file
25
pages/api/trash.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { api } from 'services/api'
|
||||
import { useAuth } from 'services/middlewares/auth'
|
||||
import { useStore } from 'services/middlewares/store'
|
||||
|
||||
export default api()
|
||||
.use(useAuth)
|
||||
.use(useStore)
|
||||
.get(async (req, res) => {
|
||||
res.json(await req.treeStore.trash.get())
|
||||
})
|
||||
.post(async (req, res) => {
|
||||
const { action, data } = req.body
|
||||
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
// todo 真删除
|
||||
console.log(data)
|
||||
break
|
||||
|
||||
default:
|
||||
return res.APIError.NOT_SUPPORTED.throw('action not found')
|
||||
}
|
||||
|
||||
res.end()
|
||||
})
|
28
pages/api/tree.ts
Normal file
28
pages/api/tree.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { api } from 'services/api'
|
||||
import { useAuth } from 'services/middlewares/auth'
|
||||
import { useStore } from 'services/middlewares/store'
|
||||
|
||||
export default api()
|
||||
.use(useAuth)
|
||||
.use(useStore)
|
||||
.get(async (req, res) => {
|
||||
res.json(await req.treeStore.get())
|
||||
})
|
||||
.post(async (req, res) => {
|
||||
const { action, data } = req.body
|
||||
|
||||
switch (action) {
|
||||
case 'move':
|
||||
await req.treeStore.moveItem(data.source, data.destination)
|
||||
break
|
||||
|
||||
case 'mutate':
|
||||
await req.treeStore.mutateItem(data.id, data)
|
||||
break
|
||||
|
||||
default:
|
||||
return res.APIError.NOT_SUPPORTED.throw('action not found')
|
||||
}
|
||||
|
||||
res.end()
|
||||
})
|
@ -1,10 +0,0 @@
|
||||
import { api } from 'services/api'
|
||||
import { useAuth } from 'services/middlewares/auth'
|
||||
import { useStore } from 'services/middlewares/store'
|
||||
|
||||
export default api()
|
||||
.use(useAuth)
|
||||
.use(useStore)
|
||||
.get(async (req, res) => {
|
||||
res.json(await req.treeStore.get())
|
||||
})
|
@ -1,14 +0,0 @@
|
||||
import { api } from 'services/api'
|
||||
import { useAuth } from 'services/middlewares/auth'
|
||||
import { useStore } from 'services/middlewares/store'
|
||||
|
||||
export default api()
|
||||
.use(useAuth)
|
||||
.use(useStore)
|
||||
.post(async (req, res) => {
|
||||
const { source, destination } = req.body
|
||||
|
||||
await req.treeStore.moveItem(source, destination)
|
||||
|
||||
res.end()
|
||||
})
|
@ -2,6 +2,10 @@ export function getPathTree() {
|
||||
return `tree`
|
||||
}
|
||||
|
||||
export function getPathTrash() {
|
||||
return `trash`
|
||||
}
|
||||
|
||||
export function getPathNoteById(id: string) {
|
||||
return `notes/${id}`
|
||||
}
|
||||
|
45
services/trash.ts
Normal file
45
services/trash.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { TreeItemModel, TreeModel } from 'containers/tree'
|
||||
import { StoreProvider } from 'packages/store/src'
|
||||
import { getPathTrash } from './note-path'
|
||||
|
||||
export class TrashStore {
|
||||
store: StoreProvider
|
||||
trashPath: string
|
||||
|
||||
constructor(store: StoreProvider) {
|
||||
this.store = store
|
||||
this.trashPath = getPathTrash()
|
||||
}
|
||||
|
||||
async get() {
|
||||
const res = await this.store.getObject(this.trashPath)
|
||||
|
||||
if (!res) {
|
||||
return this.set({})
|
||||
}
|
||||
|
||||
return JSON.parse(res) as TreeModel['items']
|
||||
}
|
||||
|
||||
async set(items: TreeModel['items']) {
|
||||
await this.store.putObject(this.trashPath, JSON.stringify(items))
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
async addItem(item: TreeItemModel) {
|
||||
const items = await this.get()
|
||||
|
||||
items[item.id] = item
|
||||
|
||||
return this.set(items)
|
||||
}
|
||||
|
||||
async removeItem(id: string) {
|
||||
const items = await this.get()
|
||||
|
||||
delete items[id]
|
||||
|
||||
return this.set(items)
|
||||
}
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
import { moveItemOnTree } from '@atlaskit/tree'
|
||||
import { moveItemOnTree, mutateTree } from '@atlaskit/tree'
|
||||
import { TreeItemMutation } from '@atlaskit/tree/dist/types/utils/tree'
|
||||
import { DEFAULT_TREE, TreeModel } from 'containers/tree'
|
||||
import { forEach, pull, union } from 'lodash'
|
||||
import { StoreProvider } from 'packages/store/src'
|
||||
import { getPathTree } from './note-path'
|
||||
import { TrashStore } from './trash'
|
||||
|
||||
interface movePosition {
|
||||
parentId: string
|
||||
@ -12,10 +14,12 @@ interface movePosition {
|
||||
export class TreeStore {
|
||||
store: StoreProvider
|
||||
treePath: string
|
||||
trash: TrashStore
|
||||
|
||||
constructor(store: StoreProvider) {
|
||||
this.store = store
|
||||
this.treePath = getPathTree()
|
||||
this.trash = new TrashStore(store)
|
||||
}
|
||||
|
||||
async get() {
|
||||
@ -71,4 +75,10 @@ export class TreeStore {
|
||||
|
||||
return this.set(tree)
|
||||
}
|
||||
|
||||
async mutateItem(id: string, data: TreeItemMutation) {
|
||||
const tree = await this.get()
|
||||
|
||||
return this.set(mutateTree(tree, id, data) as TreeModel)
|
||||
}
|
||||
}
|
||||
|
@ -16,5 +16,5 @@ export function useNoteWorker() {
|
||||
}
|
||||
}, [])
|
||||
|
||||
return NoteWorkerApiRef
|
||||
return NoteWorkerApiRef.current
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { expose } from 'comlink'
|
||||
import { noteStore, NoteStoreItem, uiStore } from 'utils/local-store'
|
||||
import { noteStore, NoteStoreItem } from 'utils/local-store'
|
||||
import { keys, pull } from 'lodash'
|
||||
import { NoteModel } from 'containers/note'
|
||||
import removeMarkdown from 'remove-markdown'
|
||||
@ -20,14 +20,8 @@ const noteWorker: NoteWorkerApi = {
|
||||
/**
|
||||
* 清除本地存储中未使用的 note
|
||||
*/
|
||||
async function checkAllNotes() {
|
||||
const tree = await uiStore.getItem<TreeModel>('tree_items')
|
||||
|
||||
if (!tree) return
|
||||
|
||||
delete tree.items.root
|
||||
|
||||
const noteIds = keys(tree.items)
|
||||
async function checkAllNotes(items: TreeModel['items']) {
|
||||
const noteIds = keys(items)
|
||||
const localNoteIds = await noteStore.keys()
|
||||
const unusedNoteIds = pull(localNoteIds, ...noteIds)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user