Buffer startup drops until renderer readiness
Keep a bounded FIFO of drop payloads while the startup or recovery document is not validated. Flush each payload exactly once only after the generation-checked renderer Ready signal, retain immediate delivery for a ready renderer, and discard stale payloads when the main window is destroyed or replaced. Wire renderer block, Ready, and window-close transitions through main and cover recovery, safe failure visibility, stale Ready signals, ordered multi-drop delivery, queue bounds, lifecycle cleanup, and duplicate prevention.
This commit is contained in:
+74
-5
@@ -75,29 +75,95 @@ function createStartupRevealGate(window, { onBlock } = {}) {
|
||||
return { block, navigate, request, reveal };
|
||||
}
|
||||
|
||||
function createStartupExternalRevealBindings({ getWindow, getRevealGate, sendDroppedFiles }) {
|
||||
function createStartupExternalRevealBindings({
|
||||
getWindow,
|
||||
getRevealGate,
|
||||
sendDroppedFiles,
|
||||
maxPendingDropPayloads = 32
|
||||
}) {
|
||||
const pendingDropPayloads = [];
|
||||
const pendingDropLimit = Number.isSafeInteger(maxPendingDropPayloads) && maxPendingDropPayloads > 0
|
||||
? maxPendingDropPayloads
|
||||
: 32;
|
||||
let pendingWindow = null;
|
||||
let readyWindow = null;
|
||||
|
||||
function clearPendingDropPayloads(window) {
|
||||
if (window && pendingWindow !== window) return false;
|
||||
pendingDropPayloads.length = 0;
|
||||
pendingWindow = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
function clearWindowState(window) {
|
||||
clearPendingDropPayloads(window);
|
||||
if (!window || readyWindow === window) readyWindow = null;
|
||||
}
|
||||
|
||||
function getActiveWindow() {
|
||||
const window = getWindow();
|
||||
if (!window || (typeof window.isDestroyed === 'function' && window.isDestroyed())) return null;
|
||||
if (!window || (typeof window.isDestroyed === 'function' && window.isDestroyed())) {
|
||||
clearWindowState();
|
||||
return null;
|
||||
}
|
||||
if (pendingWindow && pendingWindow !== window) clearPendingDropPayloads();
|
||||
if (readyWindow && readyWindow !== window) readyWindow = null;
|
||||
return window;
|
||||
}
|
||||
|
||||
function requestReveal() {
|
||||
if (!getActiveWindow()) return false;
|
||||
const activeWindow = getActiveWindow();
|
||||
if (!activeWindow) return false;
|
||||
const revealGate = getRevealGate();
|
||||
if (!revealGate || typeof revealGate.request !== 'function') return false;
|
||||
revealGate.request();
|
||||
return true;
|
||||
}
|
||||
|
||||
function queueDropPayload(window, paths) {
|
||||
if (pendingWindow && pendingWindow !== window) clearPendingDropPayloads();
|
||||
pendingWindow = window;
|
||||
if (pendingDropPayloads.length >= pendingDropLimit) pendingDropPayloads.shift();
|
||||
pendingDropPayloads.push(paths);
|
||||
}
|
||||
|
||||
function handleDropTargetFiles(_event, paths) {
|
||||
const window = getActiveWindow();
|
||||
if (!window) return false;
|
||||
if (!window.isVisible() || window.isMinimized()) requestReveal();
|
||||
sendDroppedFiles(paths);
|
||||
if (readyWindow === window) sendDroppedFiles(paths);
|
||||
else queueDropPayload(window, paths);
|
||||
return true;
|
||||
}
|
||||
|
||||
function rendererBlocked(window) {
|
||||
const activeWindow = getActiveWindow();
|
||||
if (!activeWindow || activeWindow !== window) {
|
||||
clearWindowState(window);
|
||||
return false;
|
||||
}
|
||||
readyWindow = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
function rendererReady(window) {
|
||||
const activeWindow = getActiveWindow();
|
||||
if (!activeWindow || activeWindow !== window) {
|
||||
clearWindowState(window);
|
||||
return false;
|
||||
}
|
||||
readyWindow = window;
|
||||
if (pendingWindow !== window) return true;
|
||||
const payloads = pendingDropPayloads.splice(0);
|
||||
pendingWindow = null;
|
||||
for (const paths of payloads) sendDroppedFiles(paths);
|
||||
return true;
|
||||
}
|
||||
|
||||
function windowClosed(window) {
|
||||
clearWindowState(window);
|
||||
}
|
||||
|
||||
return {
|
||||
bindSecondInstance(app) {
|
||||
app.on('second-instance', requestReveal);
|
||||
@@ -110,7 +176,10 @@ function createStartupExternalRevealBindings({ getWindow, getRevealGate, sendDro
|
||||
},
|
||||
bindDropTargetFiles(ipcMain) {
|
||||
ipcMain.on('drop-target:files', handleDropTargetFiles);
|
||||
}
|
||||
},
|
||||
rendererBlocked,
|
||||
rendererReady,
|
||||
windowClosed
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user