From e4be0738f3e4459de139856d2e55c73cdbcccf43 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Tue, 18 Aug 2026 09:27:05 +0200 Subject: [PATCH] Distant chat: closing a conversation left its contact behind, and always said it worked closeDistantChatConnexion() closes the GXS tunnel and stops there, on a comment wondering whether the contact should go too ("also remove contact. Or do we wait for the notification?"). It should: handleRecvDataItem() drops incoming items only for tunnels it finds no contact for, so the entry left in mDistantChatContacts keeps accepting data for a conversation the user has just left. A peer that keeps writing re-digs the tunnel and the chat comes back. markDistantChatAsClosed(), which runs when the *remote* end closes, has always removed it; closing it ourselves now means the same thing, and that function becomes the same call. The return value was the constant true, so every caller -- the chat window, the web UI, any JSON API client -- was told the conversation had been closed even when there was nothing to close: closeExistingTunnel() answers false and logs "Cannot close distant tunnel connection. No connection openned for tunnel id" when the tunnel had already died on its own, which is the normal state of a conversation left open for a while. It now answers whether anything was actually released, so a client can tell the user which of the two happened. Co-Authored-By: Claude Opus 5 (1M context) --- src/chat/distantchat.cc | 44 ++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/chat/distantchat.cc b/src/chat/distantchat.cc index 27b2dd40e6..141883fa0e 100644 --- a/src/chat/distantchat.cc +++ b/src/chat/distantchat.cc @@ -254,15 +254,8 @@ void DistantChatService::receiveData( const RsGxsTunnelId& tunnel_id, unsigned c void DistantChatService::markDistantChatAsClosed(const DistantChatPeerId& dcpid) { - mGxsTunnels->closeExistingTunnel( - RsGxsTunnelId(dcpid), DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID ); - - RS_STACK_MUTEX(mDistantChatMtx) ; - - std::map::iterator it = mDistantChatContacts.find(dcpid) ; - - if(it != mDistantChatContacts.end()) - mDistantChatContacts.erase(it) ; + // Same work, whether the close comes from the remote end or from us. + closeDistantChatConnexion(dcpid) ; } bool DistantChatService::initiateDistantChatConnexion( @@ -327,11 +320,34 @@ bool DistantChatService::getDistantChatStatus(const DistantChatPeerId& tunnel_id bool DistantChatService::closeDistantChatConnexion(const DistantChatPeerId &tunnel_id) { - mGxsTunnels->closeExistingTunnel(RsGxsTunnelId(tunnel_id), DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID) ; - - // also remove contact. Or do we wait for the notification? - - return true ; + // The contact has to go with the tunnel. Leaving it behind kept the + // conversation alive on our side: handleRecvDataItem() only drops incoming + // items for tunnels it finds no contact for, so a peer that keeps writing + // re-digs the tunnel and the chat we just left comes back. That is what + // markDistantChatAsClosed() does when the *remote* end closes, and closing + // it ourselves has to mean the same thing. + + bool tunnel_closed = mGxsTunnels->closeExistingTunnel( + RsGxsTunnelId(tunnel_id), DISTANT_CHAT_GXS_TUNNEL_SERVICE_ID ); + + bool contact_removed = false; + { + RS_STACK_MUTEX(mDistantChatMtx) ; + + auto it = mDistantChatContacts.find(tunnel_id) ; + + if(it != mDistantChatContacts.end()) + { + mDistantChatContacts.erase(it) ; + contact_removed = true ; + } + } + + // Answering true whatever happened made every client -- the chat window, + // the web UI, any JSON API caller -- report a conversation as closed when + // nothing had been closed at all, the tunnel having died on its own before. + + return tunnel_closed || contact_removed ; } uint32_t DistantChatService::getDistantChatPermissionFlags()