Merge pull request #160 from 11cafe/feat/folder-drag-drop
feat: folder drag drop
This commit is contained in:
+18
-23
@@ -261,6 +261,22 @@ def file_handle(name, file, existFlowIds, fileList):
|
||||
else:
|
||||
fileList.append(fileInfo)
|
||||
|
||||
def folder_handle(path, existFlowIds):
|
||||
fileList = []
|
||||
for item in os.listdir(path):
|
||||
item_path = os.path.join(path, item)
|
||||
if os.path.isfile(item_path) and item_path.endswith('.json'):
|
||||
with open(item_path, 'r') as f:
|
||||
file_handle(item, f, existFlowIds, fileList)
|
||||
|
||||
elif os.path.isdir(item_path):
|
||||
fileList.append({
|
||||
'name': item,
|
||||
'list': folder_handle(item_path, existFlowIds)
|
||||
})
|
||||
return fileList
|
||||
|
||||
|
||||
# Scan all files and subfolders in the local save directory.
|
||||
# For files, compare the extra.workspace_info.id in the json format file with the flow of the current DB to determine whether it is a flow that needs to be added;
|
||||
# For subfolders, scan the json files in the subfolder and use the same processing method as the file to determine whether it is a flow that needs to be added;
|
||||
@@ -270,29 +286,8 @@ async def scan_local_new_files(request):
|
||||
path = reqJson['path']
|
||||
existFlowIds = reqJson['existFlowIds']
|
||||
|
||||
fileList = []
|
||||
folderList = []
|
||||
|
||||
for item in os.listdir(path):
|
||||
item_path = os.path.join(path, item)
|
||||
if os.path.isfile(item_path) and item_path.endswith('.json'):
|
||||
with open(item_path, 'r') as f:
|
||||
file_handle(item, f, existFlowIds, fileList)
|
||||
|
||||
elif os.path.isdir(item_path):
|
||||
folder = {
|
||||
'name': item,
|
||||
'list': []
|
||||
}
|
||||
for sub_item in os.listdir(item_path):
|
||||
sub_item_path = os.path.join(item_path, sub_item)
|
||||
if os.path.isfile(sub_item_path) and sub_item_path.endswith('.json'):
|
||||
with open(sub_item_path, 'r') as f:
|
||||
file_handle(sub_item, f, existFlowIds, folder['list'])
|
||||
|
||||
if len(folder['list']) > 0:
|
||||
folderList.append(folder)
|
||||
return web.Response(text=json.dumps({'fileList': fileList, 'folderList': folderList}), content_type='application/json')
|
||||
fileList = folder_handle(path, existFlowIds)
|
||||
return web.Response(text=json.dumps(fileList), content_type='application/json')
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/workspace/delete_folder")
|
||||
|
||||
+11
-1
@@ -122,6 +122,16 @@ export async function openWorkflowsFolder() {
|
||||
}
|
||||
}
|
||||
|
||||
export type ScanLocalFile = {
|
||||
name: string;
|
||||
json: string;
|
||||
};
|
||||
export type ScanLocalFolder = {
|
||||
name: string;
|
||||
list: ScanLocalResult[];
|
||||
};
|
||||
export type ScanLocalResult = ScanLocalFile | ScanLocalFolder;
|
||||
|
||||
export async function scanLocalNewFiles(path: string, existFlowIds: string[]) {
|
||||
try {
|
||||
const response = await fetch("/workspace/scan_local_new_files", {
|
||||
@@ -134,7 +144,7 @@ export async function scanLocalNewFiles(path: string, existFlowIds: string[]) {
|
||||
existFlowIds,
|
||||
}),
|
||||
});
|
||||
const result = await response.json();
|
||||
const result: ScanLocalResult[] = await response.json();
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("Error scan local new files:", error);
|
||||
|
||||
+3
-5
@@ -154,11 +154,9 @@ export default function App() {
|
||||
await userSettingsTable?.getSetting("myWorkflowsDir");
|
||||
const allFlows = await workflowsTable?.listAll();
|
||||
const existFlowIds = (allFlows && allFlows.map((flow) => flow.id)) || [];
|
||||
const { fileList, folderList } = await scanLocalNewFiles(
|
||||
myWorkflowsDir!,
|
||||
existFlowIds,
|
||||
);
|
||||
await syncNewFlowOfLocalDisk(fileList, folderList);
|
||||
const fileList = await scanLocalNewFiles(myWorkflowsDir!, existFlowIds);
|
||||
if (!fileList || fileList.length === 0) return;
|
||||
await syncNewFlowOfLocalDisk(fileList);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,13 +4,20 @@ import {
|
||||
isFolder,
|
||||
workflowsTable,
|
||||
} from "../db-tables/WorkspaceDB";
|
||||
import { useState, memo, useContext, useEffect, MouseEvent } from "react";
|
||||
import {
|
||||
useState,
|
||||
memo,
|
||||
useContext,
|
||||
useEffect,
|
||||
MouseEvent,
|
||||
DragEvent,
|
||||
} from "react";
|
||||
import { IconChevronDown, IconChevronRight } from "@tabler/icons-react";
|
||||
import { RecentFilesContext } from "../WorkspaceContext";
|
||||
import WorkflowListItem from "./WorkflowListItem";
|
||||
import FilesListFolderItemRightClickMenu from "./FilesListFolderItemRightClickMenu";
|
||||
import { ESortTypes, sortTypeLocalStorageKey } from "./types";
|
||||
import { Folder, Workflow } from "../types/dbTypes";
|
||||
import ItemsList from "./ItemsList";
|
||||
|
||||
type Props = {
|
||||
folder: Folder;
|
||||
@@ -22,8 +29,12 @@ export default memo(function FilesListFolderItem({ folder }: Props) {
|
||||
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const { colorMode } = useColorMode();
|
||||
const { draggingFile, refreshFolderStamp, onRefreshFilesList } =
|
||||
useContext(RecentFilesContext);
|
||||
const {
|
||||
draggingFile,
|
||||
setDraggingFile,
|
||||
refreshFolderStamp,
|
||||
onRefreshFilesList,
|
||||
} = useContext(RecentFilesContext);
|
||||
const activeStyle =
|
||||
colorMode === "light"
|
||||
? { backgroundColor: "#E2E8F0" }
|
||||
@@ -45,6 +56,24 @@ export default memo(function FilesListFolderItem({ folder }: Props) {
|
||||
setMenuPosition({ x: event.clientX, y: event.clientY });
|
||||
setIsMenuOpen(true);
|
||||
};
|
||||
const handleDrop = async (e: DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!draggingFile) return setIsActive(false);
|
||||
if (isFolder(draggingFile)) {
|
||||
if (draggingFile.id === folder.id) return setIsActive(false);
|
||||
await foldersTable?.update({
|
||||
id: draggingFile.id,
|
||||
parentFolderID: folder.id,
|
||||
});
|
||||
} else if (!isFolder(draggingFile)) {
|
||||
await workflowsTable?.updateFlow(draggingFile.id, {
|
||||
parentFolderID: folder.id,
|
||||
});
|
||||
}
|
||||
await onRefreshFilesList?.();
|
||||
setIsActive(false);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (!!folder.isCollapse === isCollapsed) return;
|
||||
foldersTable?.update({
|
||||
@@ -62,24 +91,22 @@ export default memo(function FilesListFolderItem({ folder }: Props) {
|
||||
setIsCollapsed(!isCollapsed);
|
||||
}}
|
||||
onContextMenu={handleContextMenu}
|
||||
draggable="true"
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsActive(true);
|
||||
}}
|
||||
onDragLeave={() => {
|
||||
setIsActive(false);
|
||||
}}
|
||||
onDrop={async () => {
|
||||
if (draggingFile && !isFolder(draggingFile)) {
|
||||
await workflowsTable?.updateFlow(draggingFile.id, {
|
||||
parentFolderID: folder.id,
|
||||
});
|
||||
await onRefreshFilesList?.();
|
||||
}
|
||||
onDragLeave={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsActive(false);
|
||||
}}
|
||||
onDragStart={() => setDraggingFile?.(folder)}
|
||||
onDrop={handleDrop}
|
||||
_hover={activeStyle}
|
||||
style={isActive || isMenuOpen ? activeStyle : undefined}
|
||||
className="droppable"
|
||||
>
|
||||
<HStack gap={1}>
|
||||
{isCollapsed ? (
|
||||
@@ -97,23 +124,16 @@ export default memo(function FilesListFolderItem({ folder }: Props) {
|
||||
</svg>
|
||||
</HStack>
|
||||
<Text>{folder.name}</Text>
|
||||
|
||||
<FilesListFolderItemRightClickMenu
|
||||
isopen={isMenuOpen}
|
||||
menuPosition={menuPosition}
|
||||
onClose={() => setIsMenuOpen(false)}
|
||||
folder={folder}
|
||||
/>
|
||||
</HStack>
|
||||
<FilesListFolderItemRightClickMenu
|
||||
isopen={isMenuOpen}
|
||||
menuPosition={menuPosition}
|
||||
onClose={() => setIsMenuOpen(false)}
|
||||
folder={folder}
|
||||
/>
|
||||
{!isCollapsed && (
|
||||
<Stack ml={4} gap={0}>
|
||||
{children.map((file) => {
|
||||
if (isFolder(file)) {
|
||||
return <FilesListFolderItem key={file.id} folder={file} />;
|
||||
} else {
|
||||
return <WorkflowListItem key={file.id} workflow={file} />;
|
||||
}
|
||||
})}
|
||||
<ItemsList items={children} />
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import { DragEvent, useContext, useEffect, useState } from "react";
|
||||
import { RecentFilesContext } from "../WorkspaceContext";
|
||||
import {
|
||||
foldersTable,
|
||||
isFolder,
|
||||
workflowsTable,
|
||||
} from "../db-tables/WorkspaceDB";
|
||||
import { Folder, Workflow } from "../types/dbTypes";
|
||||
import FilesListFolderItem from "./FilesListFolderItem";
|
||||
import WorkflowListItem from "./WorkflowListItem";
|
||||
import { Box } from "@chakra-ui/react";
|
||||
import { folderOnTopLocalStorageKey } from "./types";
|
||||
import { userSettingsTable } from "../db-tables/WorkspaceDB";
|
||||
|
||||
export default function ItemsList({
|
||||
items,
|
||||
}: {
|
||||
items: Array<Folder | Workflow>;
|
||||
}) {
|
||||
const [folderOnTop, setFolderOnTop] = useState(false);
|
||||
const folders = items.filter(isFolder);
|
||||
const workflows = items.filter((item): item is Workflow => !isFolder(item));
|
||||
const parentFolderID = workflows[0]?.parentFolderID;
|
||||
|
||||
const [isDraggingOver, setIsDraggingOver] = useState(false);
|
||||
const { onRefreshFilesList, draggingFile, refreshFolderStamp } =
|
||||
useContext(RecentFilesContext);
|
||||
|
||||
const handleDragLeave = (e: DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const targetIsDroppable =
|
||||
e.relatedTarget instanceof Element &&
|
||||
e.relatedTarget.classList.contains("droppable") &&
|
||||
e.relatedTarget !== e.currentTarget;
|
||||
const targetIsChild = e.currentTarget.contains(e.relatedTarget as Node);
|
||||
// fixes a bug where the dragleave event is fired when dragging over a child element, causing elements to flicker
|
||||
if (targetIsDroppable || !targetIsChild) {
|
||||
setIsDraggingOver(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDrop = async (e: DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!draggingFile) return setIsDraggingOver(false);
|
||||
if (isFolder(draggingFile)) {
|
||||
if (draggingFile.id === parentFolderID) return setIsDraggingOver(false);
|
||||
await foldersTable?.update({
|
||||
id: draggingFile.id,
|
||||
parentFolderID: parentFolderID,
|
||||
});
|
||||
} else {
|
||||
await workflowsTable?.updateFlow(draggingFile.id, {
|
||||
parentFolderID: parentFolderID,
|
||||
});
|
||||
}
|
||||
await onRefreshFilesList?.();
|
||||
setIsDraggingOver(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
userSettingsTable?.getSetting("foldersOnTop").then((res) => {
|
||||
setFolderOnTop(res ?? false);
|
||||
});
|
||||
}, [refreshFolderStamp]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{folderOnTop &&
|
||||
folders.map((folder) => (
|
||||
<FilesListFolderItem key={folder.id} folder={folder} />
|
||||
))}
|
||||
<Box
|
||||
border={isDraggingOver ? "2px dashed #718096" : undefined}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsDraggingOver(true);
|
||||
}}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={handleDrop}
|
||||
className="droppable"
|
||||
>
|
||||
{folderOnTop
|
||||
? workflows.map((workflow) => (
|
||||
<WorkflowListItem key={workflow.id} workflow={workflow} />
|
||||
))
|
||||
: items.map((n) => {
|
||||
if (isFolder(n)) {
|
||||
return <FilesListFolderItem folder={n} key={n.id} />;
|
||||
}
|
||||
return <WorkflowListItem key={n.id} workflow={n} />;
|
||||
})}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -12,12 +12,21 @@ import {
|
||||
Box,
|
||||
Flex,
|
||||
Tooltip,
|
||||
Switch,
|
||||
} from "@chakra-ui/react";
|
||||
import { useEffect, useState, useRef, useCallback, useContext } from "react";
|
||||
import {
|
||||
useEffect,
|
||||
useState,
|
||||
useRef,
|
||||
useCallback,
|
||||
useContext,
|
||||
ChangeEvent,
|
||||
} from "react";
|
||||
import {
|
||||
workflowsTable,
|
||||
isFolder,
|
||||
foldersTable,
|
||||
userSettingsTable,
|
||||
} from "../db-tables/WorkspaceDB";
|
||||
import {
|
||||
IconChevronDown,
|
||||
@@ -28,19 +37,18 @@ import {
|
||||
import { RecentFilesContext, WorkspaceContext } from "../WorkspaceContext";
|
||||
import RecentFilesDrawerMenu from "./RecentFilesDrawerMenu";
|
||||
import { sortFileItem } from "../utils";
|
||||
import WorkflowListItem from "./WorkflowListItem";
|
||||
import MultipleSelectionOperation from "./MultipleSelectionOperation";
|
||||
import { ESortTypes, sortTypeLocalStorageKey } from "./types";
|
||||
// @ts-ignore
|
||||
// @ts-expect-error ComfyUI import
|
||||
import { app } from "/scripts/app.js";
|
||||
import { insertWorkflowToCanvas3 } from "./InsertWorkflowToCanvas";
|
||||
import FilesListFolderItem from "./FilesListFolderItem";
|
||||
import { useDebounce } from "../customHooks/useDebounce";
|
||||
import SearchInput from "../components/SearchInput";
|
||||
import { openWorkflowsFolder } from "../Api";
|
||||
import { Folder, Workflow } from "../types/dbTypes";
|
||||
import ImportFileButton from "./ImportFileButton";
|
||||
import MyTagsRow from "./MyTagsRow";
|
||||
import ItemsList from "./ItemsList";
|
||||
|
||||
type Props = {
|
||||
onClose: () => void;
|
||||
@@ -68,6 +76,12 @@ export default function RecentFilesDrawer({ onClose, onClickNewFlow }: Props) {
|
||||
(window.localStorage.getItem(sortTypeLocalStorageKey) as ESortTypes) ??
|
||||
ESortTypes.RECENTLY_MODIFIED,
|
||||
);
|
||||
const [folderOnTop, setFolderOnTop] = useState(false);
|
||||
useEffect(() => {
|
||||
userSettingsTable?.getSetting("foldersOnTop").then((res) => {
|
||||
setFolderOnTop(res ?? false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const loadLatestWorkflows = async () => {
|
||||
const all = (await workflowsTable?.listFolderContent()) ?? [];
|
||||
@@ -112,6 +126,18 @@ export default function RecentFilesDrawer({ onClose, onClickNewFlow }: Props) {
|
||||
!isFilter && setRefreshFolderStamp(Date.now());
|
||||
};
|
||||
|
||||
const onFolderOnTopChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const state = e.target.checked;
|
||||
userSettingsTable
|
||||
?.upsert({
|
||||
foldersOnTop: state,
|
||||
})
|
||||
.then(() => {
|
||||
setFolderOnTop(state);
|
||||
!isFilter && setRefreshFolderStamp(Date.now());
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
filterFlows();
|
||||
}, [debounceSearchValue, selectedTag]);
|
||||
@@ -129,7 +155,7 @@ export default function RecentFilesDrawer({ onClose, onClickNewFlow }: Props) {
|
||||
|
||||
useEffect(() => {
|
||||
loadLatestWorkflows();
|
||||
const handleDrop = async (e: any) => {
|
||||
const handleDrop = async (e: { canvasX: number; canvasY: number }) => {
|
||||
if (draggingWorkflowID.current) {
|
||||
const flow = await workflowsTable?.get(draggingWorkflowID.current);
|
||||
flow &&
|
||||
@@ -319,18 +345,20 @@ export default function RecentFilesDrawer({ onClose, onClickNewFlow }: Props) {
|
||||
</MenuOptionGroup>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
<Flex gap={2} align="center">
|
||||
<Text>Folders on Top</Text>
|
||||
<Switch
|
||||
isChecked={folderOnTop}
|
||||
onChange={onFolderOnTopChange}
|
||||
/>
|
||||
</Flex>
|
||||
</HStack>
|
||||
<SearchInput
|
||||
searchValue={searchValue}
|
||||
onUpdateSearchValue={onUpdateSearchValue}
|
||||
/>
|
||||
<Flex overflowY={"auto"} overflowX={"hidden"} direction="column">
|
||||
{currentRenderingData.map((n) => {
|
||||
if (isFolder(n)) {
|
||||
return <FilesListFolderItem folder={n} key={n.id} />;
|
||||
}
|
||||
return <WorkflowListItem key={n.id} workflow={n} />;
|
||||
})}
|
||||
<ItemsList items={currentRenderingData} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Card>
|
||||
|
||||
@@ -11,10 +11,9 @@ import {
|
||||
Tooltip,
|
||||
} from "@chakra-ui/react";
|
||||
import { IconExternalLink } from "@tabler/icons-react";
|
||||
import { isFolder, workflowsTable } from "../db-tables/WorkspaceDB";
|
||||
import { formatTimestamp, openWorkflowInNewTab, isImageFormat } from "../utils";
|
||||
import AddTagToWorkflowPopover from "./AddTagToWorkflowPopover";
|
||||
import { useState, memo, ChangeEvent, useContext } from "react";
|
||||
import { MouseEvent, useState, memo, ChangeEvent, useContext } from "react";
|
||||
import WorkflowListItemRightClickMenu from "./WorkflowListItemRightClickMenu";
|
||||
import DeleteConfirm from "../components/DeleteConfirm";
|
||||
import { RecentFilesContext, WorkspaceContext } from "../WorkspaceContext";
|
||||
@@ -25,7 +24,6 @@ type Props = {
|
||||
};
|
||||
export default memo(function WorkflowListItem({ workflow }: Props) {
|
||||
const { colorMode } = useColorMode();
|
||||
const [isDraggingOver, setIsDraggingOver] = useState(false);
|
||||
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const {
|
||||
@@ -34,8 +32,6 @@ export default memo(function WorkflowListItem({ workflow }: Props) {
|
||||
onMultiSelectFlow,
|
||||
onDeleteFlow,
|
||||
multiSelectedFlowsID,
|
||||
onRefreshFilesList,
|
||||
draggingFile,
|
||||
} = useContext(RecentFilesContext);
|
||||
const isChecked =
|
||||
multiSelectedFlowsID &&
|
||||
@@ -43,7 +39,7 @@ export default memo(function WorkflowListItem({ workflow }: Props) {
|
||||
multiSelectedFlowsID.includes(workflow.id);
|
||||
const { curFlowID, loadWorkflowID } = useContext(WorkspaceContext);
|
||||
const isSelected = curFlowID === workflow.id;
|
||||
const handleContextMenu = (event: any) => {
|
||||
const handleContextMenu = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
setMenuPosition({ x: event.clientX, y: event.clientY });
|
||||
setIsMenuOpen(true);
|
||||
@@ -58,30 +54,12 @@ export default memo(function WorkflowListItem({ workflow }: Props) {
|
||||
<Box
|
||||
flexShrink={1}
|
||||
flexGrow={1}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
setIsDraggingOver(true);
|
||||
}}
|
||||
onDragLeave={() => {
|
||||
setIsDraggingOver(false);
|
||||
}}
|
||||
onDrop={async () => {
|
||||
if (draggingFile && !isFolder(draggingFile)) {
|
||||
await workflowsTable?.updateFlow(draggingFile.id, {
|
||||
parentFolderID: workflow.parentFolderID,
|
||||
});
|
||||
await onRefreshFilesList?.();
|
||||
}
|
||||
setIsDraggingOver(false);
|
||||
}}
|
||||
backgroundColor={
|
||||
isSelected ? "teal.200" : isMenuOpen ? hoverBgColor : undefined
|
||||
}
|
||||
color={isSelected && !isMultiSelecting ? "#333" : undefined}
|
||||
draggable={!isMultiSelecting}
|
||||
onDragStart={(e) => {
|
||||
setDraggingFile && setDraggingFile(workflow);
|
||||
}}
|
||||
onDragStart={() => setDraggingFile?.(workflow)}
|
||||
borderRadius={6}
|
||||
px={1}
|
||||
py={1}
|
||||
@@ -120,9 +98,6 @@ export default memo(function WorkflowListItem({ workflow }: Props) {
|
||||
</Text>
|
||||
</Stack>
|
||||
</HStack>
|
||||
{isDraggingOver && (
|
||||
<Box width={"100%"} mt={2} height={"2px"} backgroundColor={"#4299E1"} />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
|
||||
@@ -23,7 +23,10 @@ export class FoldersTable extends TableBase<Folder> {
|
||||
name: string;
|
||||
parentFolderID?: string;
|
||||
}): Promise<Folder> {
|
||||
const uniqueName = await this.generateUniqueName(input.name);
|
||||
const uniqueName = await this.generateUniqueName(
|
||||
input.name,
|
||||
input.parentFolderID,
|
||||
);
|
||||
const folder: Folder = {
|
||||
id: uuidv4(),
|
||||
name: uniqueName,
|
||||
@@ -46,6 +49,10 @@ export class FoldersTable extends TableBase<Folder> {
|
||||
if (folder == null) {
|
||||
return;
|
||||
}
|
||||
const nameChanged = input.name != undefined && input.name != folder.name;
|
||||
const parentFolderChanged =
|
||||
input.parentFolderID != undefined &&
|
||||
input.parentFolderID != folder.parentFolderID;
|
||||
const newRecord = {
|
||||
...folder,
|
||||
...input,
|
||||
@@ -53,11 +60,17 @@ export class FoldersTable extends TableBase<Folder> {
|
||||
if (input.name != null) {
|
||||
newRecord.updateTime = Date.now();
|
||||
}
|
||||
if (parentFolderChanged) {
|
||||
input.name = await this.generateUniqueName(
|
||||
newRecord.name,
|
||||
newRecord.parentFolderID ?? undefined,
|
||||
);
|
||||
}
|
||||
await indexdb.folders.update(input.id, input);
|
||||
this.saveDiskDB();
|
||||
|
||||
// folder moved or renamed - move all workflows to the right directory(not required when folded state changes)
|
||||
if (input.name != null || input.parentFolderID != null) {
|
||||
if (nameChanged || parentFolderChanged) {
|
||||
validateOrSaveAllJsonFileMyWorkflows(true);
|
||||
}
|
||||
}
|
||||
@@ -109,10 +122,10 @@ export class FoldersTable extends TableBase<Folder> {
|
||||
this.saveDiskDB();
|
||||
}
|
||||
|
||||
public async generateUniqueName(name?: string) {
|
||||
public async generateUniqueName(name?: string, parentFolderID?: string) {
|
||||
let newFlowName = name ?? "New folder";
|
||||
const folderNameList = await this.listAll().then((list) =>
|
||||
list.map((f) => f.name),
|
||||
list.filter((f) => f.parentFolderID == parentFolderID).map((f) => f.name),
|
||||
);
|
||||
if (folderNameList.includes(newFlowName)) {
|
||||
let num = 2;
|
||||
|
||||
@@ -113,10 +113,7 @@ export class WorkflowsTable extends TableBase<Workflow> {
|
||||
}
|
||||
await this.saveDiskDB();
|
||||
// save to my_workflows/
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(input, "name") ||
|
||||
Object.prototype.hasOwnProperty.call(input, "parentFolderID")
|
||||
) {
|
||||
if ("name" in input || "parentFolderID" in input) {
|
||||
// renamed file or moved file folder
|
||||
await deleteJsonFileMyWorkflows(before);
|
||||
await saveJsonFileMyWorkflows(after);
|
||||
|
||||
@@ -54,6 +54,7 @@ export type UserSettings = {
|
||||
};
|
||||
autoSave?: boolean;
|
||||
twoWaySync?: boolean;
|
||||
foldersOnTop?: boolean;
|
||||
};
|
||||
|
||||
export interface PanelPosition {
|
||||
|
||||
+10
-9
@@ -1,5 +1,5 @@
|
||||
// @ts-ignore
|
||||
import { deleteFile, updateFile } from "./Api";
|
||||
import { ScanLocalFile, ScanLocalFolder, ScanLocalResult, deleteFile, updateFile } from "./Api";
|
||||
import { ESortTypes } from "./RecentFilesDrawer/types";
|
||||
import {
|
||||
workflowsTable,
|
||||
@@ -157,6 +157,7 @@ export async function validateOrSaveAllJsonFileMyWorkflows(
|
||||
for (const workflow of flowList) {
|
||||
const fullPath = await generateFilePathAbsolute(workflow);
|
||||
if (workflow.filePath != fullPath) {
|
||||
console.log(workflow.filePath, fullPath);
|
||||
// file path changed
|
||||
workflow.filePath != null &&
|
||||
(await deleteFile(workflow.filePath, deleteEmptyFolder));
|
||||
@@ -546,16 +547,15 @@ export function getFileUrl(relativePath: string) {
|
||||
}
|
||||
|
||||
export async function syncNewFlowOfLocalDisk(
|
||||
singleFlowList: Workflow[],
|
||||
folderList: {
|
||||
name: string;
|
||||
list: Workflow[];
|
||||
}[],
|
||||
scanList: ScanLocalResult[],
|
||||
parentFolderID?: string,
|
||||
) {
|
||||
if (singleFlowList.length) {
|
||||
await workflowsTable?.batchCreateFlows(singleFlowList, true);
|
||||
const fileList = scanList.filter((s): s is ScanLocalFile => "json" in s);
|
||||
if (fileList.length) {
|
||||
await workflowsTable?.batchCreateFlows(fileList, true, parentFolderID);
|
||||
}
|
||||
|
||||
const folderList = scanList.filter((s): s is ScanLocalFolder => "list" in s);
|
||||
if (folderList.length) {
|
||||
const currentFolderList = await foldersTable?.listAll();
|
||||
|
||||
@@ -571,11 +571,12 @@ export async function syncNewFlowOfLocalDisk(
|
||||
} else {
|
||||
const newFolder = await foldersTable?.create({
|
||||
name: folder.name,
|
||||
parentFolderID,
|
||||
});
|
||||
folderId = newFolder?.id;
|
||||
}
|
||||
|
||||
await workflowsTable?.batchCreateFlows(folder.list, true, folderId);
|
||||
await syncNewFlowOfLocalDisk(folder.list, folderId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user