From 6f2b72c3f11347a7927fc198ebb680bcba8d92bc Mon Sep 17 00:00:00 2001 From: jolavillette Date: Tue, 18 Aug 2026 09:03:23 +0200 Subject: [PATCH] webui v149: selecting a contact in People opened a distant chat tunnel The right pane keeps its last tab in State.activeTab, and that state is global rather than per contact. So the list item handler ends on "if the chat tab is the one showing, connect" -- which is never true on a fresh page, and always true afterwards. Once a conversation has been opened, every later click in the contact list requests a GXS tunnel toward whoever was just selected: a network action the other side sees, from a click that only meant "show me this profile". Selecting somebody now shows their profile, and the tunnel waits for the Chat Conversation tab. The three places that do mean it -- the tab itself, the Start Chat button, the Chats list and its context menu -- are untouched. The mirror case was broken the other way round. "Start private chat" from a chat room calls setSelectedId(id, 'chat'), which preselects the chat tab on a page that is not mounted yet; nothing ever opened the tunnel there, so the pane sat on its Connecting spinner for good. That intent is explicit, so it is now remembered and honoured once the own identities are loaded. Co-Authored-By: Claude Opus 5 (1M context) --- webui-src/app/main.js | 4 ++-- webui-src/app/people/people.js | 15 ++++++++++++++- webui-src/app/people/people_sidebar.js | 11 ++++++++--- webui-src/app/people/people_state.js | 1 + 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/webui-src/app/main.js b/webui-src/app/main.js index 0fcf600..22bed62 100644 --- a/webui-src/app/main.js +++ b/webui-src/app/main.js @@ -114,7 +114,7 @@ const navbar = () => { ? 'Connected to RetroShare Core' : 'Connection Lost', }), - m('span.webui-version', { style: { fontSize: '0.7em' } }, 'v148'), + m('span.webui-version', { style: { fontSize: '0.7em' } }, 'v149'), m('i.fas.fa-sync-alt.refresh-icon', { style: { cursor: 'pointer', fontSize: '0.8em' }, onclick: () => window.location.reload(true), @@ -236,7 +236,7 @@ const MobileStatus = () => { m('small', statusbar.formatBytes(state.totalOut)), ]), ]), - m('.mobile-status-sheet__version', 'WebUI v148'), + m('.mobile-status-sheet__version', 'WebUI v149'), ])), ]; }, diff --git a/webui-src/app/people/people.js b/webui-src/app/people/people.js index a6098f1..78af98e 100644 --- a/webui-src/app/people/people.js +++ b/webui-src/app/people/people.js @@ -41,7 +41,19 @@ const PeopleLayout = () => { m.redraw(); }); loadGxsIdentities(); - loadOwnGxsIds().then(() => preloadAllChatHistory()); + loadOwnGxsIds().then(() => { + preloadAllChatHistory(); + // "Start private chat" from a chat room routes here with the chat tab + // preselected, but nothing ever opened the tunnel: the pane sat on its + // Connecting spinner for good. That intent is explicit, so it is + // honoured -- once the own identities needed to open a tunnel are in. + if (State.pendingChatOpen && State.pendingChatOpen === State.selectedId) { + State.pendingChatOpen = null; + initializeDistantChat(); + } else { + State.pendingChatOpen = null; + } + }); stopWatchingOwnIds = peopleUtil.watchOwnIds((ids) => { State.ownGxsIds = ids || []; if (!peopleUtil.isUsableIdentityId(State.selectedId)) { @@ -184,6 +196,7 @@ PeopleLayout.setSelectedId = (id, activeTab = 'details', showCompose = false) => State.activeFilter = filter; State.selectedId = id; State.activeTab = activeTab; + State.pendingChatOpen = activeTab === 'chat' ? id : null; State.mobilePane = 'detail'; if (showCompose) { State.showMailCompose = true; diff --git a/webui-src/app/people/people_sidebar.js b/webui-src/app/people/people_sidebar.js index 24060da..85d4303 100644 --- a/webui-src/app/people/people_sidebar.js +++ b/webui-src/app/people/people_sidebar.js @@ -281,9 +281,14 @@ const PeopleSidebar = () => { State.chatPid = null; State.chatMessages = []; stopStatusPolling(); - if (State.activeTab === 'chat') { - initializeDistantChat(); - } + // Selecting somebody is not asking to talk to them. + // The chat tab is sticky, so inheriting it here meant + // that once a conversation had been opened, every + // later click in the list silently requested a GXS + // tunnel toward the contact -- an action the peer + // sees. Show the profile; the tunnel waits for the + // Chat Conversation tab. + State.activeTab = 'details'; } m.redraw(); }, diff --git a/webui-src/app/people/people_state.js b/webui-src/app/people/people_state.js index 95bb6a0..418960b 100644 --- a/webui-src/app/people/people_state.js +++ b/webui-src/app/people/people_state.js @@ -28,6 +28,7 @@ const State = { historySearchQuery: '', fullHistoryMessages: [], isHistoryLoading: false, + pendingChatOpen: null, // gxsId a chat was explicitly asked for from another page }; function getDistantChatSession(gxsId) {