From 74155dafbce51adee4767a723d9ce0b476186578 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Sat, 29 Aug 2026 12:53:47 +0200 Subject: [PATCH] webui v163: adopt a distant chat tunnel that already exists A tunnel to a contact can exist without the web UI knowing: opened from the desktop window, or by the peer, and possibly under another of our identities. The page always asked the core for a tunnel from its own default identity (the first one), so for any other identity the core dug a second tunnel and the phone sat on "Connecting" while the desktop showed the first one green. The tunnel id is sha1(sorted(own || peer)), computable here, so before opening one the page now asks getDistantChatStatus for the id of every own identity and adopts the live tunnel (status 2), else any the core still holds, switching "Chatting as" to that identity. An explicit identity switch keeps opening what the user asked for. Late answers for a contact the user has left are dropped. --- webui-src/app/main.js | 4 +- webui-src/app/people/people_state.js | 70 +++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/webui-src/app/main.js b/webui-src/app/main.js index 1adfefb..b7ecafc 100644 --- a/webui-src/app/main.js +++ b/webui-src/app/main.js @@ -137,7 +137,7 @@ const navbar = () => { ? 'Connected to RetroShare Core' : 'Connection Lost', }), - m('span.webui-version', { style: { fontSize: '0.7em' } }, 'v162'), + m('span.webui-version', { style: { fontSize: '0.7em' } }, 'v163'), m('i.fas.fa-sync-alt.refresh-icon', { style: { cursor: 'pointer', fontSize: '0.8em' }, onclick: () => window.location.reload(true), @@ -277,7 +277,7 @@ const MobileStatus = () => { s.slowest.length > 0 && m('div', 'slowest: ' + s.slowest.map((e) => `${short(e.path)} ${e.ms}ms`).join(', ')), ]); })(), - m('.mobile-status-sheet__version', 'WebUI v162'), + m('.mobile-status-sheet__version', 'WebUI v163'), ])), ]; }, diff --git a/webui-src/app/people/people_state.js b/webui-src/app/people/people_state.js index efc4c15..0171b99 100644 --- a/webui-src/app/people/people_state.js +++ b/webui-src/app/people/people_state.js @@ -458,7 +458,75 @@ function initializeDistantChat(force = false) { return; } - // Otherwise, start a new tunnel for this peer + // A tunnel to this peer may already exist without this page knowing: + // opened from the desktop window, or by the peer, possibly under another + // of our identities. Its id is sha1(sorted(own || peer)), so every + // candidate can be asked for by id before digging a new one -- which the + // core would do for any own identity other than the tunnel's, and the + // page then sat on "Connecting" beside a green tunnel in the desktop UI. + // Explicit identity switches (force) skip this: the user chose. + if (!force) { + const askedFor = State.selectedId; + adoptExistingTunnel(askedFor, (ownId, pid, info) => { + // The answers come back later; the user may have moved on. + if (State.selectedId !== askedFor) return; + if (!ownId) { + openDistantChat(session); + return; + } + State.selectedOwnGxsIdForChat = ownId; + session.pid = pid; + session.status = info; + session.disconnected = false; + State.chatPid = pid; + State.chatMessages = session.messages; + State.distantChatStatus = info; + State.chatDisconnected = false; + State.chatCloseFoundNothing = false; + State.statusPollFailures = 0; + State.chatInputMsg = session.inputMsg || ''; + drainBufferedChatMessages(session); + loadChatMessages(); + startStatusPolling(); + m.redraw(); + }); + return; + } + + openDistantChat(session); +} + +// Ask the core about every tunnel id we could share with this peer, one per +// own identity. Answers with the live one (status 2, "can talk") first, else +// any the core still holds, else nothing. +function adoptExistingTunnel(peerGxsId, done) { + const candidates = (State.ownGxsIds || []) + .map((ownId) => ({ ownId, pid: peopleUtil.distantChatPid(ownId, peerGxsId) })) + .filter((c) => c.pid); + if (candidates.length === 0) { + done(null); + return; + } + + const found = []; + let left = candidates.length; + candidates.forEach((c) => { + rs.rsJsonApiRequest('/rsChats/getDistantChatStatus', { pid: c.pid }, (detail, success) => { + if (success && detail && detail.retval && detail.info) { + found.push({ ...c, info: detail.info }); + } + left -= 1; + if (left > 0) return; + const live = found.find((f) => f.info.status === 2) + || found.find((f) => f.ownId === State.selectedOwnGxsIdForChat) + || found[0]; + if (live) done(live.ownId, live.pid, live.info); + else done(null); + }); + }); +} + +function openDistantChat(session) { session.pid = null; session.status = null; resetSessionMessages(session, [