From 29c062d7016a738d7cf1b9a672be6aa6b86bb48d Mon Sep 17 00:00:00 2001 From: liamcottle Date: Mon, 20 Jan 2025 16:14:08 +1300 Subject: [PATCH 001/131] stop updating message state if message gets cancelled --- meshchat.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meshchat.py b/meshchat.py index f43201a..d2a4ce3 100644 --- a/meshchat.py +++ b/meshchat.py @@ -2681,9 +2681,10 @@ class ReticulumMeshChat: has_delivered = lxmf_message.state == LXMF.LXMessage.DELIVERED has_propagated = lxmf_message.state == LXMF.LXMessage.SENT and lxmf_message.method == LXMF.LXMessage.PROPAGATED has_failed = lxmf_message.state == LXMF.LXMessage.FAILED + is_cancelled = lxmf_message.state == LXMF.LXMessage.CANCELLED # check if we should stop updating - if has_delivered or has_propagated or has_failed: + if has_delivered or has_propagated or has_failed or is_cancelled: should_update_message = False # handle an announce received from reticulum, for an audio call address From 4ae83ca9800d3426dd85abd824029b16a704ae8e Mon Sep 17 00:00:00 2001 From: liamcottle Date: Mon, 20 Jan 2025 21:14:53 +1300 Subject: [PATCH 002/131] fix formatting --- meshchat.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/meshchat.py b/meshchat.py index d2a4ce3..99714c4 100644 --- a/meshchat.py +++ b/meshchat.py @@ -1863,6 +1863,7 @@ class ReticulumMeshChat: else: print(f"unhandled field: {field}") return data + def convert_nomadnet_field_data_to_map(self, field_data): data = {} if field_data is not None or "{}": @@ -1878,9 +1879,6 @@ class ReticulumMeshChat: return data - - - # handle data received from websocket client async def on_websocket_data_received(self, client, data): From 580c9071384f56b769e70d02338c70695ff09535 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Tue, 21 Jan 2025 23:19:55 +1300 Subject: [PATCH 003/131] add ability to double click an lxmf.delivery node in network visualiser to open the conversation --- meshchat.py | 5 +++ .../components/messages/MessagesPage.vue | 33 ++++++++++++++ .../network-visualiser/NetworkVisualiser.vue | 44 +++++++++++++++++++ src/frontend/main.js | 3 +- 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/meshchat.py b/meshchat.py index 99714c4..3ed2e6a 100644 --- a/meshchat.py +++ b/meshchat.py @@ -1007,6 +1007,7 @@ class ReticulumMeshChat: # get query params aspect = request.query.get("aspect", None) identity_hash = request.query.get("identity_hash", None) + destination_hash = request.query.get("destination_hash", None) limit = request.query.get("limit", None) # build announces database query @@ -1020,6 +1021,10 @@ class ReticulumMeshChat: if identity_hash is not None: query = query.where(database.Announce.identity_hash == identity_hash) + # filter by provided destination hash + if destination_hash is not None: + query = query.where(database.Announce.destination_hash == destination_hash) + # limit results if limit is not None: query = query.limit(limit) diff --git a/src/frontend/components/messages/MessagesPage.vue b/src/frontend/components/messages/MessagesPage.vue index 0310fa4..4cba9f0 100644 --- a/src/frontend/components/messages/MessagesPage.vue +++ b/src/frontend/components/messages/MessagesPage.vue @@ -38,6 +38,9 @@ export default { ConversationViewer, MessagesSidebar, }, + props: { + destinationHash: String, + }, data() { return { @@ -76,6 +79,11 @@ export default { this.getConversations(); }, 5000); + // compose message if a destination hash was provided on page load + if(this.destinationHash){ + this.onComposeNewMessage(this.destinationHash); + } + }, methods: { async onComposeNewMessage(destinationHash) { @@ -93,6 +101,9 @@ export default { destinationHash = destinationHash.replace("lxmf@", ""); } + // fetch updated announce as we might be composing new message before we loaded the announces list + await this.getLxmfDeliveryAnnounce(destinationHash); + // attempt to find existing peer so we can show their name const existingPeer = this.peers[destinationHash]; if(existingPeer){ @@ -165,6 +176,28 @@ export default { console.log(e); } }, + async getLxmfDeliveryAnnounce(destinationHash) { + try { + + // fetch announce for destination hash + const response = await window.axios.get(`/api/v1/announces`, { + params: { + destination_hash: destinationHash, + limit: 1, + }, + }); + + // update ui + const lxmfDeliveryAnnounces = response.data.announces; + for(const lxmfDeliveryAnnounce of lxmfDeliveryAnnounces){ + this.updatePeerFromAnnounce(lxmfDeliveryAnnounce); + } + + } catch(e) { + // do nothing if failed to load announce + console.log(e); + } + }, async getConversations() { try { const response = await window.axios.get(`/api/v1/lxmf/conversations`); diff --git a/src/frontend/components/network-visualiser/NetworkVisualiser.vue b/src/frontend/components/network-visualiser/NetworkVisualiser.vue index 9f3f971..9836536 100644 --- a/src/frontend/components/network-visualiser/NetworkVisualiser.vue +++ b/src/frontend/components/network-visualiser/NetworkVisualiser.vue @@ -165,6 +165,47 @@ export default { }, }); + // handle double click on a node + this.network.on("doubleClick", (params) => { + + // get clicked node id + const clickedNodeId = params.nodes[0]; + if(!clickedNodeId){ + return; + } + + // find node by id + const node = this.network.body.nodes[clickedNodeId]; + if(!node){ + return; + } + + // handle double click on an announce node + if(node.options.group === "announce"){ + + // get announce + const announce = node.options._announce; + if(!announce) { + return; + } + + // handle double click on lxmf.delivery node + if(announce.aspect === "lxmf.delivery"){ + + // go to messages page for this destination hash + this.$router.push({ + name: "messages", + params: { + destinationHash: announce.destination_hash, + }, + }); + + } + + } + + }); + // update network await this.update(); @@ -358,6 +399,9 @@ export default { } + // attach announce to this node + node._announce = announce; + // add node nodes.push(node); diff --git a/src/frontend/main.js b/src/frontend/main.js index 3fdece6..664cf68 100644 --- a/src/frontend/main.js +++ b/src/frontend/main.js @@ -46,7 +46,8 @@ const router = createRouter({ }, { name: "messages", - path: '/messages', + path: '/messages/:destinationHash?', + props: true, component: defineAsyncComponent(() => import("./components/messages/MessagesPage.vue")), }, { From 7d1681fbf19ddf2091d3eab93e2c9bed23567d69 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Tue, 21 Jan 2025 23:28:00 +1300 Subject: [PATCH 004/131] auto update router url when navigating through conversations --- .../components/messages/MessagesPage.vue | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/frontend/components/messages/MessagesPage.vue b/src/frontend/components/messages/MessagesPage.vue index 4cba9f0..9f04790 100644 --- a/src/frontend/components/messages/MessagesPage.vue +++ b/src/frontend/components/messages/MessagesPage.vue @@ -15,7 +15,7 @@ :my-lxmf-address-hash="config?.lxmf_address_hash" :selected-peer="selectedPeer" :conversations="conversations" - @close="selectedPeer = null" + @close="onCloseConversationViewer" @reload-conversations="getConversations"/> @@ -211,7 +211,18 @@ export default { this.peers[announce.destination_hash] = announce; }, onPeerClick: function(peer) { + + // update selected peer this.selectedPeer = peer; + + // update current route + this.$router.replace({ + name: "messages", + params: { + destinationHash: peer.destination_hash, + }, + }); + }, onConversationClick: function(conversation) { @@ -222,6 +233,17 @@ export default { this.$refs["conversation-viewer"].markConversationAsRead(conversation); }, + onCloseConversationViewer: function() { + + // clear selected peer + this.selectedPeer = null; + + // update current route + this.$router.replace({ + name: "messages", + }); + + }, }, watch: { conversations() { From 558e4c8b3df268bec711cb0e05137a856dc72060 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Tue, 21 Jan 2025 23:32:48 +1300 Subject: [PATCH 005/131] use isActive instead of isExactActive to allow url props to still show link as active --- src/frontend/components/SidebarLink.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/components/SidebarLink.vue b/src/frontend/components/SidebarLink.vue index 50e0dc8..c3d97d6 100644 --- a/src/frontend/components/SidebarLink.vue +++ b/src/frontend/components/SidebarLink.vue @@ -1,11 +1,11 @@