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.
This commit is contained in:
jolavillette 2026-08-29 12:53:47 +02:00
parent 62a3ced094
commit 74155dafbc
2 changed files with 71 additions and 3 deletions

View File

@ -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'),
])),
];
},

View File

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