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
21 changes: 20 additions & 1 deletion src/LiveDevelopment/BrowserScripts/LiveDevProtocolRemote.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@
const lpFn = registeredPhoenixCommFns[fnName];
if(!lpFn) {
console.error(`PhoenixComm: No such LP function ${fnName}`);
return;
}
try {
const response = lpFn(paramObj);
Expand All @@ -390,6 +391,12 @@
let lpCommReady = false;
const pendingExecPromises = new Map();
let queuedExecRequests = []; // array of { fnName, paramObj, fnExecID }
// A pending execPhoenixFn is only ever settled by a response message, so if
// the editor disconnects mid-call the promise would hang forever (and any
// awaiting caller — e.g. a styles-bar session reset — stalls). Reject after
// this long instead; it's far beyond any real round-trip, so it only fires
// on a genuine hang, never on a slow-but-legitimate operation.
const PHOENIX_FN_TIMEOUT_MS = 30000;

/**
* Sends immediately if ready, else queues for later replay.
Expand Down Expand Up @@ -425,7 +432,13 @@
execPhoenixFn: function (fnName, paramObj) {
return new Promise((resolve, reject) => {
const fnExecID = currentFnExecID++;
pendingExecPromises.set(fnExecID, { resolve, reject });
const timer = setTimeout(function () {
if (pendingExecPromises.has(fnExecID)) {
pendingExecPromises.delete(fnExecID);
reject(new Error(`execPhoenixFn timed out: ${fnName}`));
}
}, PHOENIX_FN_TIMEOUT_MS);
pendingExecPromises.set(fnExecID, { resolve, reject, timer });
_sendOrQueueExec({
execFnName: fnName,
paramObj,
Expand All @@ -445,9 +458,15 @@
function _onPhoenixExecResponse(fnName, fnExecID, resolveWith, rejectWith) {
const pendingPromise = pendingExecPromises.get(fnExecID);
if(!pendingPromise) {
// already settled (e.g. by the timeout) or an unknown id — bail
// rather than dereference undefined and throw in the message handler
console.error(`execPhoenixFn: No response promise found! for ${fnName}: ${fnExecID}`);
return;
}
pendingExecPromises.delete(fnExecID);
if (pendingPromise.timer) {
clearTimeout(pendingPromise.timer);
}
if(rejectWith) {
pendingPromise.reject(rejectWith);
} else {
Expand Down
23 changes: 23 additions & 0 deletions src/LiveDevelopment/BrowserScripts/RemoteFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@
// called when an item is selected from the more options dropdown
"handleDropdownClick",
"updateContent", // in-place content refresh for control box etc. after drag
// a DOM edit changed the selected element's attributes or rebuilt its node —
// refresh shown UI in place; must never resurrect dismissed UI
"onSelectedElementMutated",
"reRegisterEventHandlers",
"handleClick", // handle click on an icon in the tool box.
// when escape key is presses in the editor, we may need to dismiss the live edit boxes.
Expand Down Expand Up @@ -1201,6 +1204,7 @@
targetElement,
childElement,
self = this;
let selectedElementMutated = false;

this.rememberedNodes = {};

Expand Down Expand Up @@ -1235,9 +1239,15 @@
case "attrChange":
case "attrAdd":
targetElement.setAttribute(edit.attribute, self._parseEntities(edit.value));
if (targetElement === previouslySelectedElement) {
selectedElementMutated = true;
}
break;
case "attrDelete":
targetElement.removeAttribute(edit.attribute);
if (targetElement === previouslySelectedElement) {
selectedElementMutated = true;
}
break;
case "elementDelete":
if (targetElement.remove) {
Expand Down Expand Up @@ -1372,9 +1382,22 @@
SHARED_STATE._editorBox.element = freshElement;
}
redrawEverything();
selectedElementMutated = true;
}
}
}

// neither path above refreshes selection-anchored UI CONTENT (the paths
// only reposition / re-point element refs), so content rendered at
// selection time — e.g. the control box's tag/#id/.classes line — would
// stay stale after the selected element's markup changed
if (selectedElementMutated && previouslySelectedElement && previouslySelectedElement.isConnected) {

Check warning on line 1394 in src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=phcode-dev_phoenix&issues=AZ9SM20ym2kYts_tts2b&open=AZ9SM20ym2kYts_tts2b&pullRequest=3020
getAllToolHandlers().forEach(function (handler) {
if (handler.onSelectedElementMutated) {
handler.onSelectedElementMutated(previouslySelectedElement);
}
});
}
};

function applyDOMEdits(edits) {
Expand Down
Loading