diff --git a/src/_node/file/actions.ts b/src/_node/file/actions.ts index b5e6f4c8..5211fbb6 100644 --- a/src/_node/file/actions.ts +++ b/src/_node/file/actions.ts @@ -14,7 +14,38 @@ import { } from "../"; import { FileSystemApis } from "./FileSystemApis"; -const create = () => {}; +const create = async ({ + projectContext, + fileTree, + fileHandlers, + parentUid, + name, + kind, +}: { + projectContext: TProjectContext; + fileTree: TFileNodeTreeData; + fileHandlers: TFileHandlerCollection; + parentUid: TNodeUid; + name: string; + kind: "file" | "directory"; +}): Promise => { + return new Promise(async (resolve, reject) => { + try { + const done = await FileSystemApis[ + projectContext + ].createSingleDirectoryOrFile({ + fileTree, + fileHandlers, + parentUid, + name, + kind, + }); + resolve(done); + } catch (err) { + reject(err); + } + }); +}; const remove = async ({ projectContext, fileTree, @@ -23,29 +54,114 @@ const remove = async ({ }: { projectContext: TProjectContext; fileTree: TFileNodeTreeData; - fileHandlers?: TFileHandlerCollection; + fileHandlers: TFileHandlerCollection; uids: TNodeUid[]; }): Promise => { - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { try { let allDone = true; - uids.map((uid) => { - const done = FileSystemApis[projectContext].removeSingleDirectoryOrFile( - { - uid, + await Promise.all( + uids.map(async (uid) => { + const done = await FileSystemApis[ + projectContext + ].removeSingleDirectoryOrFile({ fileTree, - fileHandlers: fileHandlers || {}, - }, - ); - if (!done) allDone = false; - }); + fileHandlers, + uid, + }); + if (!done) allDone = false; + }), + ); resolve(allDone); } catch (err) { reject(err); } }); }; +const rename = async ({ + projectContext, + fileTree, + fileHandlers, + uids, + parentUid, + newName, +}: { + projectContext: TProjectContext; + fileTree: TFileNodeTreeData; + fileHandlers: TFileHandlerCollection; + uids: TNodeUid[]; + parentUid: TNodeUid; + newName: string; +}): Promise => { + return new Promise(async (resolve, reject) => { + try { + const done = await FileSystemApis[ + projectContext + ].moveSingleDirectoryOrFile({ + fileTree, + fileHandlers, + uid: uids[0], + targetUid: parentUid, + newName, + isCopy: false, + }); + resolve(done); + } catch (err) { + reject(err); + } + }); +}; +const cut = ({ + dispatch, + uids, + fileTree, + currentFileUid, + nodeTree, +}: { + dispatch: Dispatch; + uids: TNodeUid[]; + fileTree: TFileNodeTreeData; + currentFileUid: TNodeUid; + nodeTree: TNodeTreeData; +}) => { + dispatch( + setClipboardData({ + panel: "file", + type: "cut", + uids, + fileType: fileTree[currentFileUid].data.type, + data: [], + fileUid: currentFileUid, + prevNodeTree: nodeTree, + }), + ); +}; +const copy = ({ + dispatch, + uids, + fileTree, + currentFileUid, + nodeTree, +}: { + dispatch: Dispatch; + uids: TNodeUid[]; + fileTree: TFileNodeTreeData; + currentFileUid: string; + nodeTree: TNodeTreeData; +}) => { + dispatch( + setClipboardData({ + panel: "file", + type: "copy", + uids, + fileType: fileTree[currentFileUid].data.type, + data: [], + fileUid: currentFileUid, + prevNodeTree: nodeTree, + }), + ); +}; const move = async ({ projectContext, fileTree, @@ -129,137 +245,7 @@ const move = async ({ }); */ }); }; -const cut = ({ - dispatch, - uids, - fileTree, - currentFileUid, - nodeTree, -}: { - dispatch: Dispatch; - uids: TNodeUid[]; - fileTree: TFileNodeTreeData; - currentFileUid: TNodeUid; - nodeTree: TNodeTreeData; -}) => { - dispatch( - setClipboardData({ - panel: "file", - type: "cut", - uids, - fileType: fileTree[currentFileUid].data.type, - data: [], - fileUid: currentFileUid, - prevNodeTree: nodeTree, - }), - ); -}; -const copy = ({ - dispatch, - uids, - fileTree, - currentFileUid, - nodeTree, -}: { - dispatch: Dispatch; - uids: TNodeUid[]; - fileTree: TFileNodeTreeData; - currentFileUid: string; - nodeTree: TNodeTreeData; -}) => { - dispatch( - setClipboardData({ - panel: "file", - type: "copy", - uids, - fileType: fileTree[currentFileUid].data.type, - data: [], - fileUid: currentFileUid, - prevNodeTree: nodeTree, - }), - ); -}; -const rename = ({ - dispatch, - projectContext, - fileHandlers, - fileTree, - uids, - newName, -}: { - dispatch: Dispatch; - projectContext: TProjectContext; - fileHandlers: any; - fileTree: TFileNodeTreeData; - uids: TNodeUid[]; - newName: string; -}): Promise => { - return new Promise((resolve, reject) => { - resolve(true); - /* const renameUid = uids[0]; - const node = fileTree[renameUid]; - if (node === undefined) { - return false; - } - const type = node.data.kind; - const nodeData = node.data as TFileNodeData; - const _orgName = - type === "directory" - ? `${nodeData.name}` - : `${nodeData.name}${nodeData.ext}`; - - const _newName = type === "directory" ? `${newName}` : `${newName}`; - const parentUid = node.parentUid; - const newUid = `${parentUid}/${_newName}`; - - (async () => { - if (projectContext === "local") { - const handler = fileHandlers[renameUid], - parentHandler = fileHandlers[ - parentUid as TNodeUid - ] as FileSystemDirectoryHandle; - - if ( - !(await verifyFileHandlerPermission(handler)) || - !(await verifyFileHandlerPermission(parentHandler)) - ) { - return; - } - - try { - await moveLocalFF( - handler, - parentHandler, - parentHandler, - _newName, - false, - true, - ); - await parentHandler.removeEntry(handler.name, { recursive: true }); - resolve(true); - } catch (err) { - return; - } - } else if (projectContext === "idb") { - const parentNode = fileTree[parentUid as TNodeUid]; - if (parentNode === undefined) { - return false; - } - const parentNodeData = parentNode.data as TFileNodeData; - try { - await moveIDBFF(nodeData, parentNodeData, _newName, false); - resolve(true); - } catch (err) { - return; - } - } - })(); - - // update redux - dispatch(setCurrentFileUid(newUid)); - dispatch(updateFileTreeViewState({ convertedUids: [[renameUid, newUid]] })); */ - }); -}; +const duplicate = () => {}; export const doFileActions = async ( params: TFileApiPayload, @@ -270,21 +256,30 @@ export const doFileActions = async ( const { projectContext, action, - uids, fileTree, fileHandlers, - dispatch, - currentFileUid, - nodeTree, + + parentUid, + name, + kind, + + uids, + clipboardData, targetNode, - newName, } = params; let allDone = true; switch (action) { case "create": - create(); + allDone = await create({ + projectContext, + fileTree, + fileHandlers, + parentUid, + name, + kind, + }); break; case "remove": allDone = await remove({ @@ -318,14 +313,14 @@ export const doFileActions = async ( }); */ break; case "rename": - /* rename({ - dispatch, + allDone = await rename({ projectContext, - fileHandlers, fileTree, + fileHandlers, uids, - newName, - }); */ + parentUid, + newName: name, + }); break; default: break; diff --git a/src/_node/file/apis.ts b/src/_node/file/apis.ts index 85716880..d34a06b3 100644 --- a/src/_node/file/apis.ts +++ b/src/_node/file/apis.ts @@ -118,41 +118,39 @@ export const loadIDBProject = async ( if (entry.startsWith(StagePreviewPathPrefix) || entry[0] === ".") return; - try { - // build c_handler - const c_uid = _path.join(p_uid, entry) as string; - const c_path = _path.join(p_path, entry) as string; - const stats = await _getIDBDirectoryOrFileStat(c_path); - const c_kind = stats.type === "DIRECTORY" ? "directory" : "file"; - - const nameArr = entry.split("."); - const c_ext = nameArr.length > 1 ? nameArr.pop() : undefined; - const c_name = nameArr.join("."); - - const c_content = - c_kind === "directory" ? undefined : await _readIDBFile(c_path); - - const c_handlerInfo: TFileHandlerInfo = { - uid: c_uid, - parentUid: p_uid, - children: [], - - path: c_path, - kind: c_kind, - name: c_kind === "directory" ? entry : c_name, - - ext: c_ext, - content: c_content, - }; - - // update handlerObj & dirHandlers - handlerObj[c_uid] = c_handlerInfo; - handlerObj[p_uid].children.push(c_uid); - c_kind === "directory" && dirHandlers.push(c_handlerInfo); - - // remove c_uid from deletedUids array - delete deletedUidsObj[c_uid]; - } catch (err) {} + // build c_handler + const c_uid = _path.join(p_uid, entry) as string; + const c_path = _path.join(p_path, entry) as string; + const stats = await _getIDBDirectoryOrFileStat(c_path); + const c_kind = stats.type === "DIRECTORY" ? "directory" : "file"; + + const nameArr = entry.split("."); + const c_ext = nameArr.length > 1 ? nameArr.pop() : undefined; + const c_name = nameArr.join("."); + + const c_content = + c_kind === "directory" ? undefined : await _readIDBFile(c_path); + + const c_handlerInfo: TFileHandlerInfo = { + uid: c_uid, + parentUid: p_uid, + children: [], + + path: c_path, + kind: c_kind, + name: c_kind === "directory" ? entry : c_name, + + ext: c_ext, + content: c_content, + }; + + // update handlerObj & dirHandlers + handlerObj[c_uid] = c_handlerInfo; + handlerObj[p_uid].children.push(c_uid); + c_kind === "directory" && dirHandlers.push(c_handlerInfo); + + // remove c_uid from deletedUids array + delete deletedUidsObj[c_uid]; }), ); } diff --git a/src/_node/file/types.ts b/src/_node/file/types.ts index 103b8c12..24c001e3 100644 --- a/src/_node/file/types.ts +++ b/src/_node/file/types.ts @@ -1,11 +1,8 @@ -import { Dispatch } from "react"; - import JSZip from "jszip"; import { TOsType } from "@_redux/global"; import { TFileActionType, TProjectContext } from "@_redux/main/fileTree"; import { TClipboardData } from "@_redux/main/processor"; -import { AnyAction } from "@reduxjs/toolkit"; import { TBasicNodeData, TNode, TNodeTreeData, TNodeUid } from "../"; @@ -79,46 +76,46 @@ export type TFileApiPayloadBase = { projectContext: TProjectContext; action: TFileActionType; fileTree: TFileNodeTreeData; - fileHandlers?: TFileHandlerCollection; + fileHandlers: TFileHandlerCollection; osType?: TOsType; }; export type TFileApiPayload = TFileApiPayloadBase & ( | { - action: Extract< - TFileActionType, - "remove" | "cut" | "copy" | "move" | "rename" - >; - uids: TNodeUid[]; + action: Extract; + parentUid: TNodeUid; + name: string; } | { - action: Exclude< - TFileActionType, - "remove" | "cut" | "copy" | "move" | "rename" - >; - uids?: never; + action: Exclude; + parentUid?: never; + name?: never; } ) & ( | { - action: Extract; - currentFileUid: TNodeUid; - nodeTree: TNodeTreeData; + action: Extract; + kind: "file" | "directory"; } | { - action: Exclude; - currentFileUid?: never; - nodeTree?: never; + action: Exclude; + kind?: never; } ) & ( | { - action: Extract; - dispatch: Dispatch; + action: Extract< + TFileActionType, + "remove" | "cut" | "copy" | "move" | "rename" + >; + uids: TNodeUid[]; } | { - action: Exclude; - dispatch?: never; + action: Exclude< + TFileActionType, + "remove" | "cut" | "copy" | "move" | "rename" + >; + uids?: never; } ) & ( @@ -134,16 +131,6 @@ export type TFileApiPayload = TFileApiPayloadBase & targetNode: TFileNode; } | { action: Exclude; targetNode?: never } - ) & - ( - | { - action: Extract; - newName: string; - } - | { - action: Exclude; - newName?: never; - } ); export type TZipFileInfo = { diff --git a/src/_node/node/type/html.ts b/src/_node/node/type/html.ts index 286da856..f4ad560e 100644 --- a/src/_node/node/type/html.ts +++ b/src/_node/node/type/html.ts @@ -41,11 +41,4 @@ export type THtmlParserResponse = { contentInApp: string; nodeTree: TNodeTreeData; htmlDom: Document; -}; - -// -------------------- -export type THtmlPageSettings = { - title: string; - favicon: string[]; - scripts: TNode[]; -}; +}; \ No newline at end of file diff --git a/src/_redux/main/fileTree/event/types.ts b/src/_redux/main/fileTree/event/types.ts index 8d5801d0..a42f80c5 100644 --- a/src/_redux/main/fileTree/event/types.ts +++ b/src/_redux/main/fileTree/event/types.ts @@ -1,15 +1,26 @@ -import { TNodeUid } from "@_node/types"; - export type TFileEventReducerState = { fileAction: TFileAction; }; -export type TFileAction = { - action: TFileActionType; - payload?: { - uids: TNodeUid[]; - }; -}; +export type TFileAction = { action: TFileActionType } & ( + | { + action: Extract; + payload: { + path: string; + }; + } + | { + action: Extract; + payload: { + orgPath: string; + newPath: string; + }; + } + | { + action: null; + payload?: never; + } +); export type TFileActionType = | "create" | "remove" diff --git a/src/components/main/actionsPanel/workspaceTreeView/WorkspaceTreeView.tsx b/src/components/main/actionsPanel/workspaceTreeView/WorkspaceTreeView.tsx index 31f993e9..52646ebb 100644 --- a/src/components/main/actionsPanel/workspaceTreeView/WorkspaceTreeView.tsx +++ b/src/components/main/actionsPanel/workspaceTreeView/WorkspaceTreeView.tsx @@ -105,82 +105,6 @@ export default function WorkspaceTreeView() { openFileUid, }); useDefaultFileCreate(); - useEffect(() => { - if (!didUndo && !didRedo) return; - - // isHms === true ? undo : redo - /* if (didUndo) { - const { type, param1, param2 } = fileAction; - if (type === "create") { - _delete([param1]); - } else if (type === "rename") { - const { parentUid } = param1; - const { orgName, newName } = param2; - const currentUid = `${parentUid}/${newName}`; - (async () => { - addTemporaryNodes(currentUid); - await _rename(currentUid, orgName); - removeTemporaryNodes(currentUid); - })(); - } else if (type === "cut") { - const _uids: { uid: TNodeUid; parentUid: TNodeUid; name: string }[] = - param1; - const _targetUids: TNodeUid[] = param2; - - const uids: TNodeUid[] = []; - const targetUids: TNodeUid[] = []; - - _targetUids.map((targetUid, index) => { - const { uid, parentUid, name } = _uids[index]; - uids.push(`${targetUid}/${name}`); - targetUids.push(parentUid); - }); - _cut(uids, targetUids); - } else if (type === "copy") { - const _uids: { uid: TNodeUid; name: string }[] = param1; - const _targetUids: TNodeUid[] = param2; - - const uids: TNodeUid[] = []; - _targetUids.map((targetUid, index) => { - const { name } = _uids[index]; - uids.push(`${targetUid}/${name}`); - }); - _delete(uids); - } else if (type === "remove") { - } - } else { - const { type, param1, param2 } = lastFileAction; - if (type === "create") { - _create(param2); - } else if (type === "rename") { - const { uid } = param1; - const { newName } = param2; - (async () => { - addTemporaryNodes(uid); - await _rename(uid, newName); - removeTemporaryNodes(uid); - })(); - } else if (type === "cut") { - const _uids: { uid: TNodeUid; name: string }[] = param1; - const targetUids: TNodeUid[] = param2; - - const uids: TNodeUid[] = _uids.map((_uid) => _uid.uid); - _cut(uids, targetUids); - } else if (type === "copy") { - const _uids: { uid: TNodeUid; name: string }[] = param1; - const targetUids: TNodeUid[] = param2; - - const uids: TNodeUid[] = []; - const names: string[] = []; - _uids.map((_uid) => { - uids.push(_uid.uid); - names.push(_uid.name); - }); - _copy(uids, names, targetUids); - } else if (type === "remove") { - } - } */ - }, [didUndo, didRedo]); // open default initial html file useEffect(() => { @@ -355,7 +279,6 @@ export default function WorkspaceTreeView() { ); props.context.startDragging(); }; - const debouncedExpand = useCallback( debounce(cb_expandNode, AutoExpandDelayOnDnD), [cb_expandNode], @@ -368,7 +291,6 @@ export default function WorkspaceTreeView() { const onMouseEnter = () => dispatch(setHoveredFileUid(props.item.index as TNodeUid)); - const onMouseLeave = () => dispatch(setHoveredFileUid("")); return ( diff --git a/src/components/main/actionsPanel/workspaceTreeView/hooks/useCmdk.ts b/src/components/main/actionsPanel/workspaceTreeView/hooks/useCmdk.ts index 7c04b068..c6a50b84 100644 --- a/src/components/main/actionsPanel/workspaceTreeView/hooks/useCmdk.ts +++ b/src/components/main/actionsPanel/workspaceTreeView/hooks/useCmdk.ts @@ -3,7 +3,6 @@ import { useCallback, useEffect } from "react"; import { AddFileActionPrefix } from "@_constants/main"; import { isAddFileAction } from "@_node/helpers"; import { useAppState } from "@_redux/useAppState"; -import { TFileNodeType } from "@_types/main"; import { useNodeActionsHandler } from "./useNodeActionsHandler"; @@ -31,7 +30,7 @@ export const useCmdk = ({ }: IUseCmdk) => { const { activePanel, currentCommand } = useAppState(); - const { createTmpNode, onDelete } = useNodeActionsHandler({ + const { onAdd, onRemove } = useNodeActionsHandler({ invalidNodes, addInvalidNodes, removeInvalidNodes, @@ -43,12 +42,10 @@ export const useCmdk = ({ const onAddNode = useCallback( (actionName: string) => { - const nodeType = actionName.slice(AddFileActionPrefix.length + 1); - createTmpNode( - nodeType === "folder" ? "*folder" : (nodeType as TFileNodeType), - ); + const type = actionName.slice(AddFileActionPrefix.length + 1); + onAdd(type === "folder" ? true : false, type); }, - [createTmpNode], + [onAdd], ); useEffect(() => { @@ -63,7 +60,7 @@ export const useCmdk = ({ switch (currentCommand.action) { case "Delete": - onDelete(); + onRemove(); break; /* case "Cut": onCut(); diff --git a/src/components/main/actionsPanel/workspaceTreeView/hooks/useNodeActionsHandler.ts b/src/components/main/actionsPanel/workspaceTreeView/hooks/useNodeActionsHandler.ts index ad7a6091..5a435c60 100644 --- a/src/components/main/actionsPanel/workspaceTreeView/hooks/useNodeActionsHandler.ts +++ b/src/components/main/actionsPanel/workspaceTreeView/hooks/useNodeActionsHandler.ts @@ -8,14 +8,16 @@ import { FileChangeAlertMessage, RednerableFileTypes, RootNodeUid, - TmpNodeUid, + TmpFileNodeUidWhenAddNew, } from "@_constants/main"; import { callFileApi } from "@_node/apis"; import { _createIDBDirectory, + _path, _writeIDBFile, confirmAlert, TFileNodeData, + TFileNodeTreeData, } from "@_node/file"; import { getValidNodeUids } from "@_node/helpers"; import { TNode, TNodeTreeData, TNodeUid } from "@_node/types"; @@ -25,16 +27,14 @@ import { expandFileTreeNodes, setCurrentFileUid, setDoingFileAction, + setFileAction, setFileTree, setPrevRenderableFileUid, + TFileAction, } from "@_redux/main/fileTree"; import { setCurrentFileContent } from "@_redux/main/nodeTree/event"; -import { - setNavigatorDropdownType, - setShowCodeView, -} from "@_redux/main/processor"; +import { setShowCodeView } from "@_redux/main/processor"; import { useAppState } from "@_redux/useAppState"; -import { TFileNodeType } from "@_types/main"; interface IUseNodeActionsHandler { invalidNodes: { @@ -79,13 +79,53 @@ export const useNodeActionsHandler = ({ reloadCurrentProject, } = useContext(MainContext); + // current project - reload trigger const [reloadCurrentProjectTrigger, setReloadCurrentProjectTrigger] = useState(false); useEffect(() => { reloadCurrentProject(); }, [reloadCurrentProjectTrigger]); - const onDelete = useCallback(async () => { + // Add & Remove + const onAdd = useCallback( + async (isDirectory: boolean, ext: string) => { + const _fileTree = structuredClone(fileTree) as TNodeTreeData; + + // validate `focusedItem` + let node = _fileTree[focusedItem]; + if (!node) return; + if (node.isEntity) { + node = _fileTree[node.parentUid as TNodeUid]; + } + + // expand the path to `focusedItem` + node.uid !== RootNodeUid && + !expandedItemsObj[node.uid] && + dispatch(expandFileTreeNodes([node.uid])); + + // add tmp node + const tmpNode: TNode = { + uid: _path.join(node.uid, TmpFileNodeUidWhenAddNew), + parentUid: node.uid, + displayName: isDirectory + ? "Untitled" + : ext === "html" + ? "Untitled" + : "Untitled", + isEntity: !isDirectory, + children: [], + data: { + valid: false, + ext, + }, + }; + node.children.unshift(tmpNode.uid); + _fileTree[tmpNode.uid] = tmpNode; + dispatch(setFileTree(_fileTree as TFileNodeTreeData)); + }, + [fileTree, focusedItem, expandedItemsObj], + ); + const onRemove = useCallback(async () => { const uids = selectedItems.filter((uid) => !invalidNodes[uid]); if (uids.length === 0) return; @@ -129,243 +169,12 @@ export const useNodeActionsHandler = ({ fileHandlers, ]); - // Add - const createFFNode = useCallback( - async (parentUid: TNodeUid, ffType: TFileNodeType, ffName: string) => { - /* let newName: string = ""; - - if (project.context === "local") { - const parentHandler = fileHandlers[ - parentUid - ] as FileSystemDirectoryHandle; - if (!(await verifyFileHandlerPermission(parentHandler))) { - removeRunningActions(["fileTreeView-create"]); - return; - } - - newName = await generateNewName(parentHandler, ffType, ffName); - - // create the directory with generated name - try { - await parentHandler.getDirectoryHandle(newName, { - create: true, - }); - } catch (err) { - removeRunningActions(["fileTreeView-create"]); - return; - } - } else if (project.context === "idb") { - const parentNode = fileTree[parentUid]; - const parentNodeData = parentNode.data as TFileNodeData; - - newName = await generateNewName(undefined, ffType, ffName); - - // create the directory or file with generated name - try { - if (ffType === "*folder") { - await _createIDBDirectory(`${parentNodeData.path}/${newName}`); - } else { - await _writeIDBFile(`${parentNodeData.path}/${newName}`, ""); - } - } catch (err) { - removeRunningActions(["fileTreeView-create"]); - return; - } - } */ - - /* const action: TFileAction = { - type: "create", - param1: `${parentUid}/${newName}`, - param2: { parentUid, name: newName, type: ffType }, - }; - dispatch(setFileAction(action)); */ - - removeRunningActions(["fileTreeView-create"]); - }, - [addRunningActions, removeRunningActions, project.context, fileHandlers], - ); - const createTmpNode = useCallback( - async (ffNodeType: TFileNodeType) => { - const tmpTree = JSON.parse(JSON.stringify(fileTree)) as TNodeTreeData; - - // validate - let node = tmpTree[focusedItem]; - if (node === undefined) return; - if (node.isEntity) { - node = tmpTree[node.parentUid as TNodeUid]; - } - - // expand the focusedItem - node.uid !== RootNodeUid && - expandedItemsObj[node.uid] === undefined && - dispatch(expandFileTreeNodes([node.uid])); - - // add tmp node - const tmpNode: TNode = { - uid: `${node.uid}/${TmpNodeUid}`, - parentUid: node.uid, - displayName: - ffNodeType === "*folder" - ? "Untitled" - : ffNodeType === "html" - ? "Untitled" - : "Untitled", - isEntity: ffNodeType !== "*folder", - children: [], - data: { - valid: false, - type: ffNodeType, - sourceCodeLocation: { - startCol: 0, - endCol: 0, - endLine: 0, - startLine: 0, - endOffset: 0, - startOffset: 0, - }, - }, - }; - - node.children.unshift(tmpNode.uid); - tmpTree[tmpNode.uid] = tmpNode; - // setFFTree(tmpTree) - - addInvalidNodes(tmpNode.uid); - await createFFNode( - node.uid as TNodeUid, - tmpNode.data.type, - tmpNode.displayName, - ); - removeInvalidNodes(tmpNode.uid); - dispatch(setNavigatorDropdownType("project")); - - if (ffNodeType !== "*folder") { - openFileUid.current = `${node.uid}/${tmpNode.displayName}.${ffNodeType}`; - dispatch(setCurrentFileUid(openFileUid.current)); - } - }, - [ - fileTree, - focusedItem, - expandedItemsObj, - addInvalidNodes, - createFFNode, - removeInvalidNodes, - ], - ); - - // Rename - const onRename = useCallback( - async (uid: TNodeUid, newName: string) => { - // validate - const node = fileTree[uid]; - if (node === undefined || node.displayName === newName) return; - const parentNode = fileTree[node.parentUid as TNodeUid]; - if (parentNode === undefined) return; - await callFileApi( - { - projectContext: project.context, - dispatch, - action: "rename", - fileTree, - uids: [uid], - newName, - fileHandlers, - }, - () => { - LogAllow && console.error("error while cutting file system"); - }, - (allDone: boolean) => { - reloadCurrentProject(); - LogAllow && - console.log( - allDone ? "all is successfully removed" : "some is not removed", - ); - }, - ); - }, - [ - addRunningActions, - removeRunningActions, - project.context, - addInvalidNodes, - removeInvalidNodes, - fileTree, - fileHandlers, - ], - ); - const cb_startRenamingNode = useCallback( - (uid: TNodeUid) => { - // validate - if (invalidNodes[uid]) { - removeInvalidNodes(uid); - return; - } - addInvalidNodes(uid); - }, - [invalidNodes, addInvalidNodes, removeInvalidNodes], - ); - const cb_abortRenamingNode = useCallback( - (item: TreeItem) => { - const node = item.data as TNode; - const nodeData = node.data as TFileNodeData; - if (!nodeData.valid) { - const tmpTree = structuredClone(fileTree); - tmpTree[node.parentUid as TNodeUid].children = tmpTree[ - node.parentUid as TNodeUid - ].children.filter((c_uid: TNodeUid) => c_uid !== node.uid); - delete tmpTree[item.data.uid]; - dispatch(setFileTree(tmpTree)); - } - removeInvalidNodes(node.uid); - }, - [fileTree, removeInvalidNodes], - ); - const cb_renameNode = useCallback( - async (item: TreeItem, newName: string) => { - const node = item.data as TNode; - const nodeData = node.data as TFileNodeData; - - // if (invalidNodes[node.uid]) return; - - if (nodeData.valid) { - const file = fileTree[node.uid]; - const fileData = file.data; - if (file && fileData.changed) { - const message = `Your changes will be lost if you don't save them. Are you sure you want to continue without saving?`; - if (!confirmAlert(message)) return; - } - - addTemporaryNodes(file.uid); - await onRename(file.uid, newName); - removeTemporaryNodes(file.uid); - } else { - await createFFNode( - node.parentUid as TNodeUid, - nodeData.ext as TFileNodeType, - newName, - ); - } - removeInvalidNodes(node.uid); - }, - [ - invalidNodes, - onRename, - addTemporaryNodes, - removeTemporaryNodes, - fileTree, - fileHandlers, - osType, - createFFNode, - removeInvalidNodes, - ], - ); - + // Cut & Copy & Paste & Duplicate const onCut = useCallback(async () => { const uids = selectedItems.filter((uid) => !invalidNodes[uid]); if (uids.length === 0) return; - await callFileApi( + /* await callFileApi( { projectContext: project.context, dispatch, @@ -378,76 +187,11 @@ export const useNodeActionsHandler = ({ () => { LogAllow && console.error("error while cutting file system"); }, - ); + ); */ }, [selectedItems, fileTree[currentFileUid], nodeTree]); - - const cb_moveNode = useCallback( - async (uids: string[], targetUid: TNodeUid, copy: boolean = false) => { - // validate - const targetNode = fileTree[targetUid]; - if (targetNode === undefined) { - return; - } - const validatedUids = getValidNodeUids(fileTree, uids, targetUid); - if (validatedUids.length === 0) { - return; - } - // confirm files changes - const hasChangedFile = validatedUids.some((uid) => { - const _file = fileTree[uid]; - const _fileData = _file.data as TFileNodeData; - return _file && _fileData.changed; - }); - if (hasChangedFile && !confirmAlert(FileChangeAlertMessage)) return; - addRunningActions(["fileTreeView-move"]); - addInvalidNodes(...validatedUids); - await callFileApi( - { - projectContext: project.context, - action: "move", - fileHandlers, - uids, - fileTree, - targetNode, - clipboardData, - }, - () => { - LogAllow && console.error("error while pasting file system"); - }, - (allDone: boolean) => { - reloadCurrentProject(); - LogAllow && - console.log( - allDone ? "all is successfully past" : "some is not past", - ); - }, - ); - - /* if (_uids.some((result) => !result)) { - // addMessage(movingError); - } */ - - /* const action: TFileAction = { - type: copy ? "copy" : "cut", - // param1: _uids, - // param2: _uids.map(() => targetUid), - }; - dispatch(setFileAction(action)); */ - - removeRunningActions(["fileTreeView-move"]); - }, - [ - addRunningActions, - removeRunningActions, - project.context, - invalidNodes, - addInvalidNodes, - fileTree, - fileHandlers, - ], - ); - - const cb_duplicateNode = useCallback(async () => { + const onCopy = useCallback(() => {}, []); + const onPaste = useCallback(() => {}, []); + const onDuplicate = useCallback(async () => { const uids = selectedItems.filter((uid) => !invalidNodes[uid]); if (uids.length === 0) return; @@ -519,6 +263,123 @@ export const useNodeActionsHandler = ({ fileHandlers, ]); + // cb + const cb_startRenamingNode = useCallback( + (uid: TNodeUid) => { + // validate + if (invalidNodes[uid]) { + removeInvalidNodes(uid); + return; + } + addInvalidNodes(uid); + }, + [invalidNodes, addInvalidNodes, removeInvalidNodes], + ); + const cb_abortRenamingNode = useCallback( + (item: TreeItem) => { + const node = item.data as TNode; + const nodeData = node.data as TFileNodeData; + if (!nodeData.valid) { + // remove newly added node + const _fileTree = structuredClone(fileTree); + _fileTree[node.parentUid as TNodeUid].children = _fileTree[ + node.parentUid as TNodeUid + ].children.filter((c_uid) => c_uid !== node.uid); + delete _fileTree[item.data.uid]; + dispatch(setFileTree(_fileTree)); + } + removeInvalidNodes(node.uid); + }, + [fileTree, removeInvalidNodes], + ); + const cb_renameNode = useCallback( + async (item: TreeItem, newName: string) => { + const node = item.data as TNode; + const nodeData = node.data as TFileNodeData; + + if (nodeData.valid) { + // rename a file/directory + const file = fileTree[node.uid]; + if (!file) return; + const fileData = file.data; + if (fileData.changed && !confirmAlert(FileChangeAlertMessage)) return; + const parentNode = fileTree[node.parentUid as TNodeUid]; + if (!parentNode) return; + const parentNodeData = parentNode.data; + const name = node.isEntity ? `${newName}.${nodeData.ext}` : newName; + const path = _path.join(parentNodeData.path, name); + + dispatch(setDoingFileAction(true)); + addInvalidNodes(node.uid); + await callFileApi( + { + projectContext: project.context, + action: "rename", + fileTree, + fileHandlers, + uids: [node.uid], + parentUid: node.parentUid as TNodeUid, + name, + }, + () => { + LogAllow && console.error("error while renaming file system"); + }, + (done: boolean) => { + LogAllow && + console.log(done ? "successfully renamed" : "not renamed"); + // add to event history + const _fileAction: TFileAction = { + action: "rename", + payload: { orgPath: nodeData.path, newPath: path }, + }; + dispatch(setFileAction(_fileAction)); + }, + ); + removeInvalidNodes(node.uid); + dispatch(setDoingFileAction(false)); + } else { + // create a new file/directory + const parentNode = fileTree[node.parentUid as TNodeUid]; + if (!parentNode) return; + const parentNodeData = parentNode.data; + const name = node.isEntity ? `${newName}.${nodeData.ext}` : newName; + const path = _path.join(parentNodeData.path, name); + + dispatch(setDoingFileAction(true)); + addInvalidNodes(node.uid); + await callFileApi( + { + projectContext: project.context, + action: "create", + fileTree, + fileHandlers, + parentUid: node.parentUid as TNodeUid, + name, + kind: node.isEntity ? "file" : "directory", + }, + () => { + LogAllow && console.error("error while creating file system"); + }, + (done: boolean) => { + LogAllow && + console.log(done ? "successfully created" : "not created"); + // add to event history + const _fileAction: TFileAction = { + action: "create", + payload: { path }, + }; + dispatch(setFileAction(_fileAction)); + }, + ); + removeInvalidNodes(node.uid); + dispatch(setDoingFileAction(false)); + } + + // reload the current project + setReloadCurrentProjectTrigger(true); + }, + [addInvalidNodes, removeInvalidNodes, fileTree, fileHandlers, osType], + ); const cb_readNode = useCallback( (uid: TNodeUid) => { addRunningActions(["fileTreeView-read"]); @@ -572,19 +433,85 @@ export const useNodeActionsHandler = ({ showCodeView, ], ); + const cb_moveNode = useCallback( + async (uids: string[], targetUid: TNodeUid, copy: boolean = false) => { + // validate + const targetNode = fileTree[targetUid]; + if (targetNode === undefined) { + return; + } + const validatedUids = getValidNodeUids(fileTree, uids, targetUid); + if (validatedUids.length === 0) { + return; + } + // confirm files changes + const hasChangedFile = validatedUids.some((uid) => { + const _file = fileTree[uid]; + const _fileData = _file.data as TFileNodeData; + return _file && _fileData.changed; + }); + if (hasChangedFile && !confirmAlert(FileChangeAlertMessage)) return; + addRunningActions(["fileTreeView-move"]); + addInvalidNodes(...validatedUids); + await callFileApi( + { + projectContext: project.context, + action: "move", + fileHandlers, + uids, + fileTree, + targetNode, + clipboardData, + }, + () => { + LogAllow && console.error("error while pasting file system"); + }, + (allDone: boolean) => { + reloadCurrentProject(); + LogAllow && + console.log( + allDone ? "all is successfully past" : "some is not past", + ); + }, + ); + + /* if (_uids.some((result) => !result)) { + // addMessage(movingError); + } */ + + /* const action: TFileAction = { + type: copy ? "copy" : "cut", + // param1: _uids, + // param2: _uids.map(() => targetUid), + }; + dispatch(setFileAction(action)); */ + + removeRunningActions(["fileTreeView-move"]); + }, + [ + addRunningActions, + removeRunningActions, + project.context, + invalidNodes, + addInvalidNodes, + fileTree, + fileHandlers, + ], + ); return { - onDelete, + onAdd, + onRemove, + + onCut, + onCopy, + onPaste, + onDuplicate, - createFFNode, - createTmpNode, cb_startRenamingNode, cb_abortRenamingNode, cb_renameNode, - onRename, - onCut, - cb_moveNode, - cb_duplicateNode, cb_readNode, + cb_moveNode, }; }; diff --git a/src/constants/main.ts b/src/constants/main.ts index b82042b7..f3e44a35 100644 --- a/src/constants/main.ts +++ b/src/constants/main.ts @@ -18,6 +18,7 @@ export const RednerableFileTypes: { export const AddActionPrefix = "AddAction"; export const AddFileActionPrefix = `${AddActionPrefix}-File`; +export const TmpFileNodeUidWhenAddNew = "tmp:node:uid"; export const AddNodeActionPrefix = `${AddActionPrefix}-Node`; export const RenameActionPrefix = "RenameAction"; @@ -33,4 +34,3 @@ export const NodePathSplitter: string = "?"; export const FileChangeAlertMessage = `Your changes will be lost if you don't save them. Are you sure you want to continue without saving?`; // ---------------------- -export const TmpNodeUid = "tmp:node:uid"; diff --git a/src/pages/main/MainPage.tsx b/src/pages/main/MainPage.tsx index 9032850c..306fe18b 100644 --- a/src/pages/main/MainPage.tsx +++ b/src/pages/main/MainPage.tsx @@ -146,14 +146,6 @@ export default function MainPage() { dropCodeView, } = usePanels(); - // file event hms - useEffect(() => { - // reset fileAction in the new history - /* fileEventFutureLength === 0 && - fileAction.type !== null && - dispatch(setFileAction({ type: null })); */ - }, [fileEventFutureLength]); - // web-tab close event handler useEffect(() => { window.onbeforeunload = isUnsavedProject(fileTree) ? () => "changed" : null; @@ -161,7 +153,6 @@ export default function MainPage() { window.onbeforeunload = null; }; }, [fileTree]); - // open Jumpstart menu on startup useEffect(() => { Object.keys(cmdkReferenceJumpstart).length !== 0 && onJumpstart(); diff --git a/src/pages/main/processor/hooks/useFileTreeEvent.ts b/src/pages/main/processor/hooks/useFileTreeEvent.ts index 921d830d..7ff4ee22 100644 --- a/src/pages/main/processor/hooks/useFileTreeEvent.ts +++ b/src/pages/main/processor/hooks/useFileTreeEvent.ts @@ -3,7 +3,90 @@ import { useEffect } from "react"; import { useAppState } from "@_redux/useAppState"; export const useFileTreeEvent = () => { - const { fileAction } = useAppState(); + const { fileAction, fileEventFutureLength } = useAppState(); - useEffect(() => {}, [fileAction]); + // file event hms + useEffect(() => { + // reset fileAction in the new history + /* fileEventFutureLength === 0 && + fileAction.type !== null && + dispatch(setFileAction({ type: null })); */ + }, [fileEventFutureLength]); + + useEffect(() => { + console.log({ fileAction }); + }, [fileAction]); + + // isHms === true ? undo : redo + /* if (didUndo) { + const { type, param1, param2 } = fileAction; + if (type === "create") { + _delete([param1]); + } else if (type === "rename") { + const { parentUid } = param1; + const { orgName, newName } = param2; + const currentUid = `${parentUid}/${newName}`; + (async () => { + addTemporaryNodes(currentUid); + await _rename(currentUid, orgName); + removeTemporaryNodes(currentUid); + })(); + } else if (type === "cut") { + const _uids: { uid: TNodeUid; parentUid: TNodeUid; name: string }[] = + param1; + const _targetUids: TNodeUid[] = param2; + + const uids: TNodeUid[] = []; + const targetUids: TNodeUid[] = []; + + _targetUids.map((targetUid, index) => { + const { uid, parentUid, name } = _uids[index]; + uids.push(`${targetUid}/${name}`); + targetUids.push(parentUid); + }); + _cut(uids, targetUids); + } else if (type === "copy") { + const _uids: { uid: TNodeUid; name: string }[] = param1; + const _targetUids: TNodeUid[] = param2; + + const uids: TNodeUid[] = []; + _targetUids.map((targetUid, index) => { + const { name } = _uids[index]; + uids.push(`${targetUid}/${name}`); + }); + _delete(uids); + } else if (type === "remove") { + } + } else { + const { type, param1, param2 } = lastFileAction; + if (type === "create") { + _create(param2); + } else if (type === "rename") { + const { uid } = param1; + const { newName } = param2; + (async () => { + addTemporaryNodes(uid); + await _rename(uid, newName); + removeTemporaryNodes(uid); + })(); + } else if (type === "cut") { + const _uids: { uid: TNodeUid; name: string }[] = param1; + const targetUids: TNodeUid[] = param2; + + const uids: TNodeUid[] = _uids.map((_uid) => _uid.uid); + _cut(uids, targetUids); + } else if (type === "copy") { + const _uids: { uid: TNodeUid; name: string }[] = param1; + const targetUids: TNodeUid[] = param2; + + const uids: TNodeUid[] = []; + const names: string[] = []; + _uids.map((_uid) => { + uids.push(_uid.uid); + names.push(_uid.name); + }); + _copy(uids, names, targetUids); + } else if (type === "remove") { + } + } */ }; diff --git a/src/types/main.ts b/src/types/main.ts index f370525d..a557ba08 100644 --- a/src/types/main.ts +++ b/src/types/main.ts @@ -1,16 +1,10 @@ -import { THtmlPageSettings, TNodeUid } from "@_node/index"; import { TProjectContext } from "@_redux/main/fileTree"; -export type TFileInfo = THtmlPageSettings | null | undefined; export type TSession = { "recent-project-context": TProjectContext[]; "recent-project-name": string[]; "recent-project-handler": (FileSystemDirectoryHandle | null)[]; }; -export type TCodeChange = { - uid: TNodeUid; - content: string; -}; export type TFileNodeType = "*folder" | "html" | ""; // file reference