From d0c8982524d4cebaa1f72959e4fabc1df1bbe6d6 Mon Sep 17 00:00:00 2001 From: GREENRAT-K405 Date: Sat, 17 Jan 2026 01:59:29 +0530 Subject: [PATCH 1/5] add pop up --- src/component/TabBar.jsx | 32 +++++++++++++++++++++---- src/component/modals/ConfirmModal.jsx | 19 +++++++++++++++ src/component/modals/confirmModal.css | 34 +++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 src/component/modals/ConfirmModal.jsx create mode 100644 src/component/modals/confirmModal.css diff --git a/src/component/TabBar.jsx b/src/component/TabBar.jsx index 6ce73f1..c73e3f9 100644 --- a/src/component/TabBar.jsx +++ b/src/component/TabBar.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { MdEdit, MdClose, MdAdd, } from 'react-icons/md'; @@ -8,12 +8,22 @@ import localStorageManager from '../graph-builder/local-storage-manager'; import { actionType as T } from '../reducer'; import { newProject, editDetails } from '../toolbarActions/toolbarFunctions'; import './tabBar.css'; +import ConfirmModal from './modals/ConfirmModal'; const TabBar = ({ superState, dispatcher }) => { - const closeTab = (i, e) => { + const [confirmOpen, setConfirmOpen] = useState(false); + const [tabToClose, setTabToClose] = useState(null); + + const handleRequestCloseTab = (i, e) => { e.stopPropagation(); - // eslint-disable-next-line no-alert - if (!window.confirm('Do you confirm to close the tab? This action is irreversable.')) return; + setTabToClose(i); + setConfirmOpen(true); + }; + + const handleConfirmClose = () => { + const i = tabToClose; + setConfirmOpen(false); + setTabToClose(null); localStorageManager.remove(superState.graphs[i] ? superState.graphs[i].graphID : null); dispatcher({ type: T.REMOVE_GRAPH, payload: i }); if (!superState.curGraphIndex && superState.graphs.length === 1) { @@ -21,6 +31,11 @@ const TabBar = ({ superState, dispatcher }) => { dispatcher({ type: T.SET_CUR_INDEX, payload: -1 }); } }; + + const handleCancelClose = () => { + setConfirmOpen(false); + setTabToClose(null); + }; const editCur = (e) => { e.stopPropagation(); editDetails(superState, dispatcher); @@ -80,7 +95,7 @@ const TabBar = ({ superState, dispatcher }) => { ) : <>} + + + + +); + +export default ConfirmModal; diff --git a/src/component/modals/confirmModal.css b/src/component/modals/confirmModal.css new file mode 100644 index 0000000..5402b8b --- /dev/null +++ b/src/component/modals/confirmModal.css @@ -0,0 +1,34 @@ +.confirm-modal-content { + padding: 20px; + text-align: center; +} +.confirm-modal-message { + margin-bottom: 20px; + font-size: 1.1em; +} +.confirm-modal-actions { + display: flex; + justify-content: center; + gap: 20px; +} +.confirm-btn, .cancel-btn { + padding: 8px 20px; + font-size: 1em; + border: none; + border-radius: 4px; + cursor: pointer; +} +.confirm-btn { + background: #d9534f; + color: #fff; +} +.cancel-btn { + background: #f0f0f0; + color: #333; +} +.confirm-btn:hover { + background: #c9302c; +} +.cancel-btn:hover { + background: #e0e0e0; +} From ec5c14ad669b4661fe355a9159299ba0a2595704 Mon Sep 17 00:00:00 2001 From: GREENRAT-K405 Date: Sat, 17 Jan 2026 03:11:12 +0530 Subject: [PATCH 2/5] add pop up to upload directory feature --- src/component/fileBrowser.jsx | 78 ++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/src/component/fileBrowser.jsx b/src/component/fileBrowser.jsx index 4e706a9..61fd757 100644 --- a/src/component/fileBrowser.jsx +++ b/src/component/fileBrowser.jsx @@ -7,17 +7,24 @@ import React, { useEffect, useState } from 'react'; import FileBrowser, { FileRenderers, FolderRenderers } from 'react-keyed-file-browser'; import { readFile, readTextFile } from '../toolbarActions/toolbarFunctions'; import { actionType as T } from '../reducer'; +import ConfirmModal from './modals/ConfirmModal'; import './fileBrowser.css'; const LocalFileBrowser = ({ superState, dispatcher }) => { const fileRef = React.useRef(); + const dirInputRef = React.useRef(); + const tempFilesRef = React.useRef(null); + const [confirmOpen, setConfirmOpen] = useState(false); + const [pendingFolderName, setPendingFolderName] = useState(''); const [dirButton, setDirButton] = useState(false); const [fileState, setFileState] = useState([]); useEffect(() => { - if (navigator.userAgent.indexOf('Edg') !== -1 || navigator.userAgent.indexOf('Chrome') !== -1) { - setDirButton(true); - } + // if (navigator.userAgent.indexOf('Edg') !== -1 || navigator.userAgent.indexOf('Chrome') !== -1) { + // setDirButton(true); + // } + // Force fallback to webkitdirectory to allow custom popup flow + setDirButton(false); dispatcher({ type: T.SET_FILE_REF, payload: fileRef }); }, []); @@ -133,31 +140,32 @@ const LocalFileBrowser = ({ superState, dispatcher }) => { return (
{!dirButton && ( - + )} {dirButton && (
); }; From e783b349349f9c33a9e486a0cd50e9b1cb60f5e5 Mon Sep 17 00:00:00 2001 From: GREENRAT-K405 Date: Sat, 17 Jan 2026 03:41:39 +0530 Subject: [PATCH 3/5] update close tab pop up logic --- src/GraphArea.jsx | 3 ++- src/component/TabBar.jsx | 26 ++++++++++++++------- src/graph-builder/graph-core/5-load-save.js | 13 +++++++++++ src/reducer/actionType.js | 1 + src/reducer/reducer.js | 8 +++++++ 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/GraphArea.jsx b/src/GraphArea.jsx index efe0166..373eb82 100644 --- a/src/GraphArea.jsx +++ b/src/GraphArea.jsx @@ -44,7 +44,8 @@ function Graph({ useEffect(() => active && instance && instance.setCurStatus(), [active && instance]); useEffect(() => { if (active && instance) dispatcher({ type: T.SET_CUR_INSTANCE, payload: instance }); - }, [active && instance]); + if (instance) dispatcher({ type: T.SET_GRAPH_INSTANCE, payload: { graphID, instance } }); + }, [active, instance, graphID, dispatcher]); useEffect(() => { if (ref.current) { diff --git a/src/component/TabBar.jsx b/src/component/TabBar.jsx index c73e3f9..9a3ba89 100644 --- a/src/component/TabBar.jsx +++ b/src/component/TabBar.jsx @@ -14,22 +14,30 @@ const TabBar = ({ superState, dispatcher }) => { const [confirmOpen, setConfirmOpen] = useState(false); const [tabToClose, setTabToClose] = useState(null); + const closeTab = (i) => { + localStorageManager.remove(superState.graphs[i] ? superState.graphs[i].graphID : null); + dispatcher({ type: T.REMOVE_GRAPH, payload: i }); + if (!superState.curGraphIndex && superState.graphs.length === 1) { + dispatcher({ type: T.SET_CUR_INSTANCE, payload: null }); + dispatcher({ type: T.SET_CUR_INDEX, payload: -1 }); + } + }; + const handleRequestCloseTab = (i, e) => { e.stopPropagation(); - setTabToClose(i); - setConfirmOpen(true); + const graph = superState.graphs[i]; + if (graph && graph.instance && !graph.instance.isSaved) { + setTabToClose(i); + setConfirmOpen(true); + } else { + closeTab(i); + } }; const handleConfirmClose = () => { - const i = tabToClose; + closeTab(tabToClose); setConfirmOpen(false); setTabToClose(null); - localStorageManager.remove(superState.graphs[i] ? superState.graphs[i].graphID : null); - dispatcher({ type: T.REMOVE_GRAPH, payload: i }); - if (!superState.curGraphIndex && superState.graphs.length === 1) { - dispatcher({ type: T.SET_CUR_INSTANCE, payload: null }); - dispatcher({ type: T.SET_CUR_INDEX, payload: -1 }); - } }; const handleCancelClose = () => { diff --git a/src/graph-builder/graph-core/5-load-save.js b/src/graph-builder/graph-core/5-load-save.js index 4a78774..e285098 100644 --- a/src/graph-builder/graph-core/5-load-save.js +++ b/src/graph-builder/graph-core/5-load-save.js @@ -13,6 +13,11 @@ class GraphLoadSave extends GraphUndoRedo { constructor(...args) { super(...args); this.autoSaveIntervalId = null; + this.lastSavedActionIndex = 0; + } + + get isSaved() { + return this.curActionIndex === this.lastSavedActionIndex; } registerEvents() { @@ -132,6 +137,7 @@ class GraphLoadSave extends GraphUndoRedo { fileHandle: handle, }]); this.dispatcher({ type: T.SET_FILE_STATE, payload: fS }); + this.lastSavedActionIndex = this.curActionIndex; toast.success('File saved Successfully'); } catch (error) { // AbortError is silently ignored (user cancelled) @@ -140,6 +146,7 @@ class GraphLoadSave extends GraphUndoRedo { // eslint-disable-next-line no-alert const fileName = prompt('Filename:'); saveAs(blob, `${fileName || `${this.getName()}-concore`}.graphml`); + this.lastSavedActionIndex = this.curActionIndex; toast.success('File saved Successfully'); } } @@ -172,6 +179,7 @@ class GraphLoadSave extends GraphUndoRedo { const stream = await handle.createWritable(); await stream.write(blob); await stream.close(); + this.lastSavedActionIndex = this.curActionIndex; toast.success('File saved Successfully'); } catch (error) { // AbortError is silently ignored (user cancelled) @@ -213,6 +221,7 @@ class GraphLoadSave extends GraphUndoRedo { graphMLParser(graphML).then((graphObject) => { localStorageManager.save(this.id, graphObject); this.loadGraphFromLocalStorage(); + this.lastSavedActionIndex = this.curActionIndex; }); } @@ -225,6 +234,10 @@ class GraphLoadSave extends GraphUndoRedo { const graphContent = localStorageManager.get(this.id); if (!graphContent) return false; this.loadJson(graphContent); + // If loaded from localStorage, assume UNSAVED unless actions are 0. + // Actually, loadJson sets curActionIndex based on history. + // If we want to support recovering unsaved work, we should leave lastSavedActionIndex as 0 (unless graph is empty and curActionIndex is 0). + if (this.curActionIndex === 0) this.lastSavedActionIndex = 0; return true; } diff --git a/src/reducer/actionType.js b/src/reducer/actionType.js index f211767..09f7981 100644 --- a/src/reducer/actionType.js +++ b/src/reducer/actionType.js @@ -44,6 +44,7 @@ const actionType = { SET_FUNCTIONS: 'SET_FUNCTIONS', SET_LOGS: 'SET_LOGS', SET_LOGS_MESSAGE: 'SET_LOGS_MESSAGE', + SET_GRAPH_INSTANCE: 'SET_GRAPH_INSTANCE', }; export default zealit(actionType); diff --git a/src/reducer/reducer.js b/src/reducer/reducer.js index 7654b1f..dd3139f 100644 --- a/src/reducer/reducer.js +++ b/src/reducer/reducer.js @@ -243,6 +243,14 @@ const reducer = (state, action) => { return { ...newState }; } + case T.SET_GRAPH_INSTANCE: { + const newState = { ...state }; + newState.graphs = newState.graphs.map((g) => ( + g.graphID === action.payload.graphID ? { ...g, instance: action.payload.instance } : g + )); + return { ...newState }; + } + default: return state; } From 841905b7d94926a9f3f4017d946af028b00fd3dc Mon Sep 17 00:00:00 2001 From: GREENRAT-K405 Date: Sat, 17 Jan 2026 04:26:44 +0530 Subject: [PATCH 4/5] update upload directory pop-up --- src/component/fileBrowser.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/component/fileBrowser.jsx b/src/component/fileBrowser.jsx index 61fd757..9acd661 100644 --- a/src/component/fileBrowser.jsx +++ b/src/component/fileBrowser.jsx @@ -223,7 +223,7 @@ const LocalFileBrowser = ({ superState, dispatcher }) => { { setConfirmOpen(false); const files = tempFilesRef.current; From 04a77111a39179192c88438ea57ebd7248068491 Mon Sep 17 00:00:00 2001 From: GREENRAT-K405 Date: Sat, 17 Jan 2026 04:30:49 +0530 Subject: [PATCH 5/5] update upload directory pop-up --- src/component/fileBrowser.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/component/fileBrowser.jsx b/src/component/fileBrowser.jsx index 9acd661..23be3b2 100644 --- a/src/component/fileBrowser.jsx +++ b/src/component/fileBrowser.jsx @@ -223,7 +223,7 @@ const LocalFileBrowser = ({ superState, dispatcher }) => { { setConfirmOpen(false); const files = tempFilesRef.current;