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, [