diff --git a/apps/desktop/src/editorHooks/EditorState/index.tsx b/apps/desktop/src/editorHooks/EditorState/index.tsx index 30ceee1e..c7f71d29 100644 --- a/apps/desktop/src/editorHooks/EditorState/index.tsx +++ b/apps/desktop/src/editorHooks/EditorState/index.tsx @@ -7,18 +7,19 @@ import { t } from 'i18next' import type { FC } from 'react' import { useCallback, useEffect, useMemo, useReducer } from 'react' import { editorReducer, initializeState } from './editor-state' -import type { IFile } from '@/helper/filesys' +import { getFileNameFromPath, type IFile } from '@/helper/filesys' import { useEditorStateStore, useEditorStore } from '@/stores' import { useTitleEffect } from '@/hooks/useTitleEffect' import type { KeyBindingProps } from '@remirror/core' import { useGlobalSettingData } from '@/hooks' import { debounce } from 'lodash' +import { updateFileObject } from '@/helper/files' export const useEditorState: FC = ({ active, file }) => { const ctx = useRemirrorContext() const [settingData] = useGlobalSettingData() const helpers = useHelpers() - const { getEditorDelegate, getEditorContent } = useEditorStore() + const { getEditorDelegate, getEditorContent, insertNodeToFolderData } = useEditorStore() const { setIdStateMap } = useEditorStateStore() const curDelegate = getEditorDelegate(file.id) const [state, dispatch] = useReducer( @@ -51,6 +52,9 @@ export const useEditorState: FC = ({ active, file }) => { defaultPath: file.name ?? `${t('file.untitled')}.md`, }).then((path) => { if (path === null) return + const filename = getFileNameFromPath(path) + updateFileObject(file.id, { ...file, path, name: filename }) + insertNodeToFolderData({...file, name: filename, content, path }) invoke('write_file', { filePath: path, content }) dispatch({ type: 'SAVE_CONTENT', @@ -68,7 +72,7 @@ export const useEditorState: FC = ({ active, file }) => { console.error(error) } }, - [active, file, getEditorContent, helpers], + [active, file, getEditorContent, helpers, insertNodeToFolderData], ) const debounceSave = useMemo( diff --git a/apps/desktop/src/helper/files.ts b/apps/desktop/src/helper/files.ts index 0a426d30..9c425056 100644 --- a/apps/desktop/src/helper/files.ts +++ b/apps/desktop/src/helper/files.ts @@ -13,6 +13,10 @@ export function getFileObject(id: string): IFile { return entries[id] } +export function updateFileObject(id: string, file: IFile): void { + entries[id] = file +} + export function setFileObjectByPath(path: string, file: IFile): void { pathEntries[path] = file } diff --git a/apps/desktop/src/helper/filesys.ts b/apps/desktop/src/helper/filesys.ts index c13ba2bb..d9f5da6a 100644 --- a/apps/desktop/src/helper/filesys.ts +++ b/apps/desktop/src/helper/filesys.ts @@ -115,3 +115,14 @@ export function getFileNameFromPath(filePath: string) { return filePath } + +export function getFolderPathFromPath(filePath: string) { + const regex = /^(.*)[\/\\][^\/\\]+$/ + const match = regex.exec(filePath) + + if (match && match.length > 1) { + return match[1] + } + + return filePath +} diff --git a/apps/desktop/src/helper/test/filesys.test.ts b/apps/desktop/src/helper/test/filesys.test.ts index db4371b9..698a2d12 100644 --- a/apps/desktop/src/helper/test/filesys.test.ts +++ b/apps/desktop/src/helper/test/filesys.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { getFileNameFromPath, isMdFile } from '../filesys' +import { getFileNameFromPath, getFolderPathFromPath, isMdFile } from '../filesys' describe('test helper/filesys ', () => { it('getFileNameFromPath', () => { @@ -19,4 +19,12 @@ describe('test helper/filesys ', () => { expect(isMdFile(winPath)).toBe(true) expect(isMdFile(otherPath)).toBe(false) }) + + it('getFolderPathFromPath', () => { + const macPath = '/path/to/myfile.txt' + const winPath = 'C:\\path\\to\\myfile.txt' + + expect(getFolderPathFromPath(macPath)).toBe('/path/to') + expect(getFolderPathFromPath(winPath)).toBe('C:\\path\\to') + }) }) diff --git a/apps/desktop/src/stores/useEditorStore.ts b/apps/desktop/src/stores/useEditorStore.ts index 9e84fc0e..e5613db0 100644 --- a/apps/desktop/src/stores/useEditorStore.ts +++ b/apps/desktop/src/stores/useEditorStore.ts @@ -1,5 +1,5 @@ import { create } from 'zustand' -import { createFile, isMdFile, type IFile } from '@/helper/filesys' +import { createFile, isMdFile, type IFile, getFolderPathFromPath } from '@/helper/filesys' import type { EditorDelegate } from '@linebyline/editor' import { invoke } from '@tauri-apps/api' @@ -22,14 +22,37 @@ const findParentNode = (fileNode: IFile, rootFile: IFile) => { return dfs(rootFile) } +const findParentNodeByPath = (path: string, rootFile: IFile) => { + const folderPath = getFolderPathFromPath(path) + + if (rootFile.path === folderPath) return rootFile + + const dfs = (file: IFile): undefined | IFile => { + if (!file.children) return undefined + + for (let i = 0; i < file.children.length; i++) { + if (file.children[i].path === folderPath) { + return file.children[i] + } else if (file.children[i]) { + const res = dfs(file.children[i]) + if (res) { + return res + } + } + } + } + + return dfs(rootFile) +} + type BaseIFile = { name: string; kind: IFile['kind'] } -function sameFile(current: BaseIFile, target: BaseIFile): boolean { +function isSameFile(current: BaseIFile, target: BaseIFile): boolean { return current.name === target.name && current.kind === target.kind } const hasSameFile = (fileNodeList: IFile[], target: BaseIFile) => { - return !!fileNodeList.find((file) => sameFile(file, target)) + return !!fileNodeList.find((file) => isSameFile(file, target)) } const useEditorStore = create((set, get) => { @@ -72,6 +95,32 @@ const useEditorStore = create((set, get) => { } }, + insertNodeToFolderData: (fileNode) => { + const { folderData } = get() + if (fileNode) { + const parent = fileNode.kind === 'dir' ? fileNode : findParentNodeByPath(fileNode.path!, folderData![0]) + + if (!parent) return false + let sameFile = parent.children?.find((file) => isSameFile(file, fileNode)) + if (sameFile) { + sameFile = { + ...sameFile, + ...fileNode + } + } else { + parent.children!.push(fileNode) + } + + set((state) => { + return { + ...state, + opened: sameFile ? state.opened.filter((id) => id !== sameFile!.id) : state.opened, + folderData: [...(state.folderData || [])], + } + }) + } + }, + deleteFile: (fileNode) => { const { folderData, activeId, delOpenedFile, opened } = get() const parent = findParentNode(fileNode, folderData![0]) @@ -81,7 +130,7 @@ const useEditorStore = create((set, get) => { const newChildren: IFile[] = [] for (let i = 0; i < parent.children.length; i++) { const child = parent.children[i] - if (sameFile(child, fileNode)) { + if (isSameFile(child, fileNode)) { targetFile = child } else { newChildren.push(child) @@ -172,6 +221,7 @@ type EditorStore = { editorCtxMap: Map> setActiveId: (id: string) => void addFile: (file: IFile, target: { name: string; kind: IFile['kind'] }) => boolean | void + insertNodeToFolderData: (fileNode: IFile) => void deleteFile: (file: IFile) => void addOpenedFile: (id: string) => void delOpenedFile: (id: string) => void diff --git a/apps/desktop/vite.config.ts b/apps/desktop/vite.config.ts index 45e78f44..cc4c6ca5 100644 --- a/apps/desktop/vite.config.ts +++ b/apps/desktop/vite.config.ts @@ -32,6 +32,6 @@ export default defineConfig({ test: { environment: 'happy-dom', globals: true, - reporters: ['json', 'html'], + // reporters: ['json', 'html'], }, })