diff --git a/js/conversation_controller.js b/js/conversation_controller.js index 7a15d689a..6d3bd2bf9 100644 --- a/js/conversation_controller.js +++ b/js/conversation_controller.js @@ -185,7 +185,6 @@ await window.Signal.Data.saveConversation(conversation.attributes, { Conversation: Whisper.Conversation, }); - window.LokiSnodeAPI.refreshSwarmNodesForPubKey(id); } catch (error) { window.log.error( 'Conversation save failed! ', @@ -200,9 +199,11 @@ return conversation; }; - conversation.initialPromise = create().then(() => - conversation.updateProfileAvatar() - ); + conversation.initialPromise = create(); + conversation.initialPromise.then(async () => { + await window.lokiSnodeAPI.refreshSwarmNodesForPubKey(id); + await conversation.updateProfileAvatar(); + }); return conversation; }, diff --git a/js/models/conversations.js b/js/models/conversations.js index 91c3616c1..d8709a37c 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -78,7 +78,7 @@ friendRequestStatus: FriendRequestStatusEnum.none, unlockTimestamp: null, // Timestamp used for expiring friend requests. sessionResetStatus: SessionResetEnum.none, - swarmNodes: new Set([]), + swarmNodes: [], isOnline: false, }; }, @@ -1406,6 +1406,12 @@ }, }; }, + async updateSwarmNodes(swarmNodes) { + this.set({ swarmNodes }); + await window.Signal.Data.updateConversation(this.id, this.attributes, { + Conversation: Whisper.Conversation, + }); + }, async updateLastMessage() { if (!this.id) { return; diff --git a/js/modules/data.js b/js/modules/data.js index e850c723d..c9c3dddbb 100644 --- a/js/modules/data.js +++ b/js/modules/data.js @@ -111,7 +111,6 @@ module.exports = { removeAllSessions, getSwarmNodesByPubkey, - saveSwarmNodesForPubKey, getConversationCount, saveConversation, @@ -676,14 +675,6 @@ async function getSwarmNodesByPubkey(pubkey) { return channels.getSwarmNodesByPubkey(pubkey); } -async function saveSwarmNodesForPubKey(pubKey, swarmNodes, { Conversation }) { - const conversation = await getConversationById(pubKey, { Conversation }); - conversation.set({ swarmNodes }); - await updateConversation(conversation.id, conversation.attributes, { - Conversation, - }); -} - async function getConversationCount() { return channels.getConversationCount(); } diff --git a/js/modules/loki_message_api.js b/js/modules/loki_message_api.js index e802dab13..5dcd82927 100644 --- a/js/modules/loki_message_api.js +++ b/js/modules/loki_message_api.js @@ -1,6 +1,6 @@ /* eslint-disable no-await-in-loop */ /* eslint-disable no-loop-func */ -/* global log, dcodeIO, window, callWorker, Whisper, lokiP2pAPI */ +/* global log, dcodeIO, window, callWorker, lokiP2pAPI, lokiSnodeAPI */ const nodeFetch = require('node-fetch'); const _ = require('lodash'); @@ -106,10 +106,11 @@ class LokiMessageAPI { throw err; } const completedNodes = []; + const failedNodes = []; let successfulRequests = 0; let canResolve = true; - let swarmNodes = await window.Signal.Data.getSwarmNodesByPubkey(pubKey); + let swarmNodes = await lokiSnodeAPI.getSwarmNodesForPubKey(pubKey); const nodeComplete = nodeUrl => { completedNodes.push(nodeUrl); @@ -148,8 +149,9 @@ class LokiMessageAPI { nodeComplete(nodeUrl); } else { log.error('Loki SendMessages:', e); - if (window.LokiSnodeAPI.unreachableNode(pubKey, nodeUrl)) { + if (lokiSnodeAPI.unreachableNode(pubKey, nodeUrl)) { nodeComplete(nodeUrl); + failedNodes.push(nodeUrl); } } } @@ -160,7 +162,9 @@ class LokiMessageAPI { throw new window.textsecure.DNSResolutionError('Sending messages'); } if (swarmNodes.length === 0) { - const freshNodes = await window.LokiSnodeAPI.getFreshSwarmNodes(pubKey); + const freshNodes = await lokiSnodeAPI.getFreshSwarmNodes(pubKey); + const goodNodes = _.difference(freshNodes, failedNodes); + await lokiSnodeAPI.updateSwarmNodes(pubKey, goodNodes); swarmNodes = _.difference(freshNodes, completedNodes); if (swarmNodes.length === 0) { if (successfulRequests !== 0) { @@ -172,13 +176,10 @@ class LokiMessageAPI { new Error('Ran out of swarm nodes to query') ); } - await window.Signal.Data.saveSwarmNodesForPubKey(pubKey, swarmNodes, { - Conversation: Whisper.Conversation, - }); } const remainingRequests = - MINIMUM_SUCCESSFUL_REQUESTS - completedNodes.length; + MINIMUM_SUCCESSFUL_REQUESTS - successfulRequests; await Promise.all( swarmNodes @@ -196,7 +197,7 @@ class LokiMessageAPI { let ourSwarmNodes; try { - ourSwarmNodes = await window.LokiSnodeAPI.getOurSwarmNodes(); + ourSwarmNodes = await lokiSnodeAPI.getOurSwarmNodes(); } catch (e) { throw new window.textsecure.EmptySwarmError(ourKey, e); } @@ -224,7 +225,7 @@ class LokiMessageAPI { nodeComplete(nodeUrl); if (result.lastHash) { - window.LokiSnodeAPI.updateLastHash(nodeUrl, result.lastHash); + lokiSnodeAPI.updateLastHash(nodeUrl, result.lastHash); callback(result.messages); } successfulRequests += 1; @@ -242,7 +243,7 @@ class LokiMessageAPI { nodeComplete(nodeUrl); } else { log.error('Loki RetrieveMessages:', e); - if (window.LokiSnodeAPI.unreachableNode(ourKey, nodeUrl)) { + if (lokiSnodeAPI.unreachableNode(ourKey, nodeUrl)) { nodeComplete(nodeUrl); } } @@ -255,7 +256,7 @@ class LokiMessageAPI { } if (Object.keys(ourSwarmNodes).length === 0) { try { - ourSwarmNodes = await window.LokiSnodeAPI.getOurSwarmNodes(); + ourSwarmNodes = await lokiSnodeAPI.getOurSwarmNodes(); // Filter out the nodes we have already got responses from completedNodes.forEach(nodeUrl => delete ourSwarmNodes[nodeUrl]); } catch (e) { @@ -277,7 +278,7 @@ class LokiMessageAPI { } const remainingRequests = - MINIMUM_SUCCESSFUL_REQUESTS - completedNodes.length; + MINIMUM_SUCCESSFUL_REQUESTS - successfulRequests; await Promise.all( Object.entries(ourSwarmNodes) diff --git a/js/modules/loki_snode_api.js b/js/modules/loki_snode_api.js index 2fe719633..a93fb02ca 100644 --- a/js/modules/loki_snode_api.js +++ b/js/modules/loki_snode_api.js @@ -1,5 +1,5 @@ /* eslint-disable class-methods-use-this */ -/* global log, window, Whisper */ +/* global log, window, ConversationController */ const fetch = require('node-fetch'); const is = require('@sindresorhus/is'); @@ -58,17 +58,11 @@ class LokiSnodeAPI { if (this.contactSwarmNodes[nodeUrl].failureCount < FAILURE_THRESHOLD) { return false; } - const conversation = window.ConversationController.get(pubKey); - const swarmNodes = conversation.get('swarmNodes'); - if (swarmNodes.delete(nodeUrl)) { - conversation.set({ swarmNodes }); - await window.Signal.Data.updateConversation( - conversation.id, - conversation.attributes, - { - Conversation: Whisper.Conversation, - } - ); + const conversation = ConversationController.get(pubKey); + const swarmNodes = [...conversation.get('swarmNodes')]; + if (nodeUrl in swarmNodes) { + const filteredNodes = swarmNodes.filter(node => node !== nodeUrl); + await conversation.updateSwarmNodes(filteredNodes); delete this.contactSwarmNodes[nodeUrl]; } return true; @@ -84,6 +78,29 @@ class LokiSnodeAPI { } } + async getSwarmNodesForPubKey(pubKey) { + try { + const conversation = ConversationController.get(pubKey); + const swarmNodes = [...conversation.get('swarmNodes')]; + return swarmNodes; + } catch (e) { + throw new window.textsecure.ReplayableError({ + message: 'Could not get conversation', + }); + } + } + + async updateSwarmNodes(pubKey, newNodes) { + try { + const conversation = ConversationController.get(pubKey); + await conversation.updateSwarmNodes(newNodes); + } catch (e) { + throw new window.textsecure.ReplayableError({ + message: 'Could not get conversation', + }); + } + } + async getOurSwarmNodes() { if ( !this.ourSwarmNodes || @@ -108,9 +125,7 @@ class LokiSnodeAPI { async refreshSwarmNodesForPubKey(pubKey) { const newNodes = await this.getFreshSwarmNodes(pubKey); - await window.Signal.Data.saveSwarmNodesForPubKey(pubKey, newNodes, { - Conversation: Whisper.Conversation, - }); + this.updateSwarmNodes(pubKey, newNodes); } async getFreshSwarmNodes(pubKey) { @@ -121,6 +136,7 @@ class LokiSnodeAPI { newSwarmNodes = await this.getSwarmNodes(pubKey); } catch (e) { // TODO: Handle these errors sensibly + log.error('Failed to get new swarm nodes'); newSwarmNodes = []; } resolve(newSwarmNodes); diff --git a/preload.js b/preload.js index 65a788d89..2b3e673b7 100644 --- a/preload.js +++ b/preload.js @@ -288,7 +288,7 @@ window.WebAPI = initializeWebAPI({ const LokiSnodeAPI = require('./js/modules/loki_snode_api'); -window.LokiSnodeAPI = new LokiSnodeAPI({ +window.lokiSnodeAPI = new LokiSnodeAPI({ url: config.serverUrl, swarmServerPort: config.swarmServerPort, });