Skip to content
102 changes: 64 additions & 38 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ import { useStateRef } from "./customHooks/useStateRef";
import { indexdb } from "./db-tables/indexdb";
import EnableTwowaySyncConfirm from "./settings/EnableTwowaySyncConfirm";
import { deepJsonDiffCheck } from "./utils/deepJsonDiffCheck";
import {
OPEN_TAB_EVENT,
tabDataManager,
} from "./topbar/multipleTabs/TabDataManager";

export default function App() {
const [curFlowName, setCurFlowName] = useState<string | null>(null);
const [route, setRoute] = useState<WorkspaceRoute>("root");
const [loadingDB, setLoadingDB] = useState(true);
const [flowID, setFlowID] = useState<string | null>(null);
const curFlowID = useRef<string | null>(null);

const [isDirty, setIsDirty] = useState(false);
const workspaceContainerRef = useRef(null);
Expand All @@ -59,23 +61,25 @@ export default function App() {
const developmentEnvLoadFirst = useRef(false);
const toast = useToast();
const [curVersion, setCurVersion] = useStateRef<WorkflowVersion | null>(null);
const saveCurWorkflow = useCallback(async (force = false) => {
if (curFlowID.current) {
if (!force && workflowsTable?.curWorkflow?.saveLock) {
const saveCurWorkflow = useCallback(async (newGraphJson?: string) => {
const flowId = workflowsTable?.curWorkflow?.id;
if (flowId) {
if (workflowsTable?.curWorkflow?.saveLock) {
toast({
title: "The workflow is locked and cannot be saved",
status: "warning",
duration: 3000,
});
return;
}
const graphJson = JSON.stringify(app.graph.serialize());
setIsDirty(false);
const graphJson = newGraphJson || JSON.stringify(app.graph.serialize());
await Promise.all([
workflowsTable?.updateFlow(curFlowID.current, {
workflowsTable?.updateFlow(flowId, {
json: graphJson,
}),
changelogsTable?.create({
workflowID: curFlowID.current,
workflowID: flowId,
json: graphJson,
isAutoSave: false,
}),
Expand All @@ -87,7 +91,6 @@ export default function App() {
duration: 1000,
isClosable: true,
});
setIsDirty(false);
}
}, []);

Expand Down Expand Up @@ -122,9 +125,7 @@ export default function App() {
// curID null is when you deleted current workflow
const id = workflow?.id ?? null;
workflowsTable?.updateCurWorkflow(workflow);
curFlowID.current = id;
setFlowID(id);
setCurFlowName(workflow?.name ?? "");
if (id == null) {
document.title = "ComfyUI";
window.location.hash = "";
Expand Down Expand Up @@ -160,7 +161,7 @@ export default function App() {
latestWfID = localStorage.getItem("curFlowID");
}
if (latestWfID) {
loadWorkflowIDImpl(latestWfID);
loadWorkflowIDImpl(latestWfID!, null, true);
}
fetch("/workspace/deduplicate_workflow_ids");
const twoway = await userSettingsTable?.getSetting("twoWaySync");
Expand Down Expand Up @@ -208,7 +209,7 @@ export default function App() {
};

const checkIsDirty = () => {
if (curFlowID.current == null) return false;
if (workflowsTable?.curWorkflow?.id == null) return false;
const curflow = workflowsTable?.curWorkflow;
if (curflow == null) return false;
if (curflow.json == null) return true;
Expand All @@ -223,7 +224,11 @@ export default function App() {
return !equal;
};

const loadWorkflowIDImpl = async (id: string, versionID?: string | null) => {
const loadWorkflowIDImpl = async (
id: string,
versionID?: string | null,
isInit?: boolean,
) => {
if (app.graph == null) {
return;
}
Expand All @@ -238,46 +243,66 @@ export default function App() {
? await workflowVersionsTable?.get(versionID)
: null;

setCurVersion(version ?? null);

const tabIndex = tabDataManager.tabs.findIndex((tab) => tab.id === id);
if (tabIndex !== -1) {
tabDataManager.changeActiveTab(tabIndex);
if (version) {
tabDataManager.updateTabData(tabIndex, { json: version.json });
}
return;
}

app.ui.dialog.close();
if (version) {
setCurVersion(version);
setRoute("root");
isDirty && setIsDirty(false);

const newTabInfo = {
id: flow.id,
name: flow.name,
json: version ? version.json : flow.json,
isDirty: false,
};

/**
* During initialization,
* because the event listener of OPEN_TAB_EVENT has not been registered yet,
* the newly added tab will be invalid,
* so the tab is added manually during initialization.
*/
if (isInit) {
tabDataManager.addTabData(newTabInfo);
app.loadGraphData(JSON.parse(newTabInfo.json));
setCurFlowIDAndName({
...flow,
json: version.json,
json: newTabInfo.json,
});
app.loadGraphData(JSON.parse(version.json));
} else {
setCurFlowIDAndName(flow);
setCurVersion(null);
app.loadGraphData(JSON.parse(flow.json));
document.dispatchEvent(
new CustomEvent(OPEN_TAB_EVENT, {
detail: {
tabInfo: newTabInfo,
},
}),
);
}
setRoute("root");
isDirty && setIsDirty(false);
};

const loadWorkflowID = async (
id: string | null,
versionID?: string | null,
forceLoad: boolean = false,
) => {
// No current workflow, id is null when you deleted current workflow
if (id === null) {
setCurFlowIDAndName(null);
app.graph.clear();
return;
}
if (
!isDirty ||
forceLoad ||
userSettingsTable?.settings?.autoSave ||
workflowsTable?.curWorkflow == null
) {
loadWorkflowIDImpl(id, versionID);
return;
}
// prompt when auto save disabled and dirty
showSaveOrDiscardCurWorkflowDialog(id);

loadWorkflowIDImpl(id, versionID);
};

const showSaveOrDiscardCurWorkflowDialog = async (newIDToLoad?: string) => {
const buttons = [
newIDToLoad
Expand Down Expand Up @@ -358,13 +383,13 @@ export default function App() {
};

const onExecutedCreateMedia = useCallback((image: any) => {
if (curFlowID.current == null) return;
if (workflowsTable?.curWorkflow?.id == null) return;
let path = image.filename;
if (image.subfolder != null && image.subfolder !== "") {
path = image.subfolder + "/" + path;
}
mediaTable?.create({
workflowID: curFlowID.current,
workflowID: workflowsTable?.curWorkflow?.id,
localPath: path,
});
}, []);
Expand Down Expand Up @@ -491,6 +516,7 @@ export default function App() {
route: route,
curVersion: curVersion,
setCurVersion: setCurVersion,
setCurFlowIDAndName: setCurFlowIDAndName,
}}
>
<div ref={workspaceContainerRef} className="workspace_manager">
Expand All @@ -505,7 +531,7 @@ export default function App() {
zIndex={DRAWER_Z_INDEX}
draggable={false}
>
<Topbar curFlowName={curFlowName} setCurFlowName={setCurFlowName} />
<Topbar />
{loadChild && route === "recentFlows" && (
<Suspense>
<RecentFilesDrawer
Expand Down
2 changes: 2 additions & 0 deletions ui/src/RecentFilesDrawer/MultipleSelectionOperation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import DeleteConfirm from "../components/DeleteConfirm";
import { ChangeEvent } from "react";
import { workflowsTable } from "../db-tables/WorkspaceDB";
import { downloadWorkflowsZip } from "../utils/downloadWorkflowsZip";
import { tabDataManager } from "../topbar/multipleTabs/TabDataManager";

type Props = {
multipleState: boolean;
Expand Down Expand Up @@ -57,6 +58,7 @@ export default function MultipleSelectionOperation(props: Props) {
onDelete={async () => {
await workflowsTable?.batchDeleteFlow(selectedKeys);
batchOperationCallback("batchDelete");
tabDataManager.batchDeleteTabData(selectedKeys);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你觉得这些 tabDataManager.batchDeleteTabData() 或者其他的 tabDataManager操作,是不是放到workflowsTable?.batchDeleteFlow()里面统一操作比较好呢,就不需要每个callsite都写一遍了

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嗯,可以的,不过tab更多的是打开、关闭、切换,纯tab触发,不涉及db操作,所以我只把flow的删除和修改名字的tab处理放到workflowsTable里了。

Copy link
Member

@Weixuanf Weixuanf Apr 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

》 不过tab更多的是打开、关闭、切换,纯tab触发,不涉及db操作,
确实 这个也有道理🤔

}}
/>
</Tooltip>
Expand Down
10 changes: 3 additions & 7 deletions ui/src/RecentFilesDrawer/RecentFilesDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import MyTagsRow from "./MyTagsRow";
import ImportFlowsFileInput from "./ImportFlowsFileInput";
import ItemsList from "./ItemsList";
import { DRAWER_Z_INDEX } from "../const";
import { tabDataManager } from "../topbar/multipleTabs/TabDataManager";

type Props = {
onClose: () => void;
Expand All @@ -51,7 +52,7 @@ export default function RecentFilesDrawer({ onClose, onClickNewFlow }: Props) {
Array<Folder | Workflow>
>([]);
const allFlowsRef = useRef<Array<Workflow>>([]);
const { loadWorkflowID, curFlowID } = useContext(WorkspaceContext);
const { loadWorkflowID } = useContext(WorkspaceContext);
const [selectedTag, setSelectedTag] = useState<string>();
const [multipleState, setMultipleState] = useState(false);
const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
Expand Down Expand Up @@ -115,6 +116,7 @@ export default function RecentFilesDrawer({ onClose, onClickNewFlow }: Props) {
await workflowsTable?.deleteFlow(id);
if (workflowsTable?.curWorkflow?.id === id) {
await loadWorkflowID?.(null);
tabDataManager?.deleteTabData(tabDataManager.activeIndex);
}
await loadLatestWorkflows();
},
Expand Down Expand Up @@ -155,12 +157,6 @@ export default function RecentFilesDrawer({ onClose, onClickNewFlow }: Props) {
const batchOperationCallback = (type: string, value: unknown) => {
switch (type) {
case "batchDelete":
curFlowID &&
workflowsTable?.get(curFlowID).then((res) => {
if (!res) {
loadWorkflowID?.(null);
}
});
loadLatestWorkflows();
setMultipleState(false);
setSelectedKeys([]);
Expand Down
10 changes: 4 additions & 6 deletions ui/src/WorkspaceContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,9 @@ export type JsonDiff = {
export const WorkspaceContext = createContext<{
curFlowID: string | null;
onDuplicateWorkflow?: (flowID: string, newFlowName?: string) => void;
loadWorkflowID: (
id: string | null,
versionID?: string | null,
forceLoad?: boolean,
) => void;
loadWorkflowID: (id: string | null, versionID?: string | null) => void;
setIsDirty: (dirty: boolean) => void;
saveCurWorkflow: (force?: boolean) => Promise<void>;
saveCurWorkflow: (saveCurWorkflow?: string) => Promise<void>;
discardUnsavedChanges: () => Promise<void>;
isDirty: boolean;
loadNewWorkflow: (input?: { json: string; name?: string }) => void;
Expand All @@ -24,6 +20,7 @@ export const WorkspaceContext = createContext<{
route: WorkspaceRoute;
curVersion: WorkflowVersion | null;
setCurVersion: (version: WorkflowVersion | null) => void;
setCurFlowIDAndName: (workflow: Workflow) => void;
}>({
curFlowID: null,
loadWorkflowID: () => {},
Expand All @@ -37,6 +34,7 @@ export const WorkspaceContext = createContext<{
curVersion: null,
setIsDirty: () => {},
setCurVersion: () => {},
setCurFlowIDAndName: () => {},
});

export const RecentFilesContext = createContext<{
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/DropdownTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export default function DropdownTitle() {
parentFolderID: workflow?.parentFolderID,
});

flow && (await loadWorkflowID(flow.id, null, true));
flow && (await loadWorkflowID(flow.id, null));
handleOnCloseModal();
};

Expand Down
26 changes: 11 additions & 15 deletions ui/src/components/EditFlowName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,14 @@ import {
import { useContext, ChangeEvent, useState } from "react";
import { workflowsTable } from "../db-tables/WorkspaceDB";
import { WorkspaceContext } from "../WorkspaceContext";
import { tabDataManager } from "../topbar/multipleTabs/TabDataManager";

type Props = {
displayName: string;
updateFlowName: (newName: string) => void;
isDirty: boolean;
isActive: boolean;
};

export default function EditFlowName({
displayName,
updateFlowName,
isDirty,
}: Props) {
export default function EditFlowName({ displayName, isActive }: Props) {
const { isOpen, onOpen, onClose } = useDisclosure();
const { curFlowID } = useContext(WorkspaceContext);
const [editName, setEditName] = useState(displayName);
Expand Down Expand Up @@ -63,7 +59,10 @@ export default function EditFlowName({
await workflowsTable?.updateName(curFlowID, {
name: trimEditName,
});
updateFlowName(trimEditName);
tabDataManager.updateTabData(tabDataManager.activeIndex, {
name: trimEditName,
});

onCloseModal();
}
}
Expand All @@ -79,17 +78,14 @@ export default function EditFlowName({
<Tooltip label={displayName} placement="bottom">
<Text
as="div"
color="white"
onClick={startEdit}
maxW={200}
fontWeight={500}
color={isActive ? "white" : "#9d9d9d"}
onDoubleClick={startEdit}
maxW="114px"
marginRight="4px"
whiteSpace="nowrap"
overflow="hidden"
textOverflow="ellipsis"
>
<div style={{ width: 8, display: "inline-block" }}>
<Text as="span">{isDirty && "* "}</Text>
</div>
{displayName}
</Text>
</Tooltip>
Expand Down
4 changes: 2 additions & 2 deletions ui/src/components/SpotlightSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,12 @@ export default function SpotlightSearch() {

document.addEventListener("keyup", handleKeyUp);
document.addEventListener("keydown", keydownListener);
window.addEventListener(SHORTCUT_TRIGGER_EVENT, shortcutTrigger);
document.addEventListener(SHORTCUT_TRIGGER_EVENT, shortcutTrigger);

return () => {
document.removeEventListener("keyup", handleKeyUp);
document.removeEventListener("keydown", keydownListener);
window.removeEventListener(SHORTCUT_TRIGGER_EVENT, shortcutTrigger);
document.removeEventListener(SHORTCUT_TRIGGER_EVENT, shortcutTrigger);
};
}, []);

Expand Down
Loading