diff --git a/meshchatx/src/frontend/js/GlobalState.js b/meshchatx/src/frontend/js/GlobalState.js index f40e67e..1f0f8bd 100644 --- a/meshchatx/src/frontend/js/GlobalState.js +++ b/meshchatx/src/frontend/js/GlobalState.js @@ -5,6 +5,7 @@ const globalState = reactive({ authSessionResolved: true, authEnabled: false, authenticated: false, + detailedOutboundSendStatus: false, unreadConversationsCount: 0, activeCallTab: "phone", blockedDestinations: [], @@ -21,4 +22,12 @@ const globalState = reactive({ }, }); +export function mergeGlobalConfig(next) { + if (!next || typeof next !== "object") { + return; + } + const prev = globalState.config && typeof globalState.config === "object" ? globalState.config : {}; + globalState.config = { ...prev, ...next }; +} + export default globalState; diff --git a/meshchatx/src/frontend/js/outboundSendQueue.js b/meshchatx/src/frontend/js/outboundSendQueue.js new file mode 100644 index 0000000..3aef512 --- /dev/null +++ b/meshchatx/src/frontend/js/outboundSendQueue.js @@ -0,0 +1,36 @@ +/** + * Serial outbound job queue: one job runs at a time so later messages reuse + * the route established while sending earlier ones to the same peer. + */ +export function createOutboundQueue(processJob) { + const queue = []; + let running = false; + + async function run() { + if (running) { + return; + } + running = true; + try { + while (queue.length) { + const job = queue.shift(); + await processJob(job); + } + } finally { + running = false; + } + } + + return { + enqueue(job) { + queue.push(job); + void run(); + }, + get length() { + return queue.length; + }, + get isRunning() { + return running; + }, + }; +}