Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/GraphWorkspace.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const GraphComp = (props) => {
isOpen={superState.confirmModal.open}
title="Confirm"
message={superState.confirmModal.message}
actions={superState.confirmModal.actions}
onConfirm={() => {
if (superState.confirmModal.onConfirm) superState.confirmModal.onConfirm();
dispatcher({ type: T.SET_CONFIRM_MODAL, payload: { open: false, message: '', onConfirm: null } });
Expand Down
26 changes: 23 additions & 3 deletions src/component/modals/ConfirmModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,34 @@ import ParentModal from './ParentModal';
import './confirmModal.css';

const ConfirmModal = ({
isOpen, title, message, onConfirm, onCancel,
isOpen, title, message, onConfirm, onCancel, actions,
}) => (
<ParentModal ModelOpen={isOpen} closeModal={onCancel} title={title}>
<div className="confirm-modal-content">
<div className="confirm-modal-message">{message}</div>
<div className="confirm-modal-actions">
<button type="button" className="confirm-btn" onClick={onConfirm}>Yes</button>
<button type="button" className="cancel-btn" onClick={onCancel}>No</button>
{
actions && actions.length
? actions.map((action) => (
<button
key={action.label}
type="button"
className={action.className || 'cancel-btn'}
onClick={() => {
if (action.onClick) action.onClick();
onCancel();
}}
>
{action.label}
</button>
))
: (
<>
<button type="button" className="confirm-btn" onClick={onConfirm}>Yes</button>
<button type="button" className="cancel-btn" onClick={onCancel}>No</button>
</>
)
}
</div>
</div>
</ParentModal>
Expand Down
1 change: 1 addition & 0 deletions src/component/modals/confirmModal.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
.confirm-modal-message {
margin-bottom: 20px;
font-size: 1.1em;
white-space: pre-line;
}
.confirm-modal-actions {
display: flex;
Expand Down
94 changes: 91 additions & 3 deletions src/graph-builder/graph-core/6-server.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { toast } from 'react-toastify';
import Axios from 'axios';
import { actionType as T } from '../../reducer';
import { EXECUTION_ENGINE_URL } from '../../serverCon/config';
import serverConConfig, { EXECUTION_ENGINE_URL } from '../../serverCon/config';
import GraphLoadSave from './5-load-save';
// import {
// postGraph, updateGraph, forceUpdateGraph, getGraph, getGraphWithHashCheck,
Expand All @@ -11,6 +11,86 @@ import {
} from '../../serverCon/crud_http';

class GraphServer extends GraphLoadSave {
static isSyncConflictError(err) {
const msg = (err && err.message ? err.message : '').toLowerCase();
return msg.includes('different history')
|| msg.includes('latest changes')
|| msg.includes('can not update');
}

showSyncConflictModal(reason) {
const localHash = this.actionArr.length ? this.actionArr.at(-1).hash : 'None';
const setModal = (remoteHash) => {
const message = [
reason || 'Sync conflict detected.',
`Local hash: ${localHash}`,
`Remote hash: ${remoteHash || 'Remote history is newer/different'}`,
'Choose how to resolve this conflict.',
].join('\n');
this.dispatcher({
type: T.SET_CONFIRM_MODAL,
payload: {
open: true,
message,
actions: [
{
label: 'Pull remote',
className: 'confirm-btn',
onClick: () => this.forcePullFromServer(),
},
{
label: 'Force push local',
className: 'confirm-btn',
onClick: () => {
if (this.serverID) {
forceUpdateGraph(this.serverID, this.getGraphML()).catch((err) => {
toast.error(err.response?.data?.message || err.message);
});
} else {
postGraph(this.getGraphML()).then((serverID) => {
this.set({ serverID });
}).catch((err) => {
toast.error(err.response?.data?.message || err.message);
});
}
},
},
{
label: 'Open remote in new tab',
className: 'cancel-btn',
onClick: () => {
if (!this.serverID) return;
const remotePath = serverConConfig.getGraph(this.serverID);
const remoteURL = `${serverConConfig.baseURL}${remotePath}`;
window.open(remoteURL, '_blank', 'noopener,noreferrer');
},
},
{
label: 'Cancel',
className: 'cancel-btn',
onClick: null,
},
],
},
});
};
if (!this.serverID) {
setModal('Unknown');
return;
}
getGraph(this.serverID).then((graphXML) => {
try {
const doc = new DOMParser().parseFromString(graphXML, 'application/xml');
const hashes = doc.getElementsByTagName('hash');
setModal(hashes.length ? (hashes[hashes.length - 1].textContent || '') : '');
} catch {
setModal('Unavailable');
}
}).catch(() => {
setModal('Unavailable');
});
}

set(config) {
const { serverID } = config;
super.set(config);
Expand Down Expand Up @@ -73,6 +153,10 @@ class GraphServer extends GraphLoadSave {
updateGraph(this.serverID, this.getGraphML()).then(() => {

}).catch((err) => {
if (GraphServer.isSyncConflictError(err)) {
this.showSyncConflictModal('Cannot push: local and remote histories diverged.');
return;
}
toast.error(err.response?.data?.message || err.message);
});
} else {
Expand Down Expand Up @@ -127,8 +211,12 @@ class GraphServer extends GraphLoadSave {
if (this.serverID) {
getGraphWithHashCheck(this.serverID, this.actionArr.at(-1).hash).then((graphXML) => {
this.setGraphML(graphXML);
}).catch(() => {

}).catch((err) => {
if (GraphServer.isSyncConflictError(err)) {
this.showSyncConflictModal('Cannot pull: local and remote histories diverged.');
return;
}
toast.error(err.response?.data?.message || err.message);
});
} else {
toast.success('Not on server');
Expand Down