diff --git a/public/index.html b/public/index.html index ab0d892..25e0260 100644 --- a/public/index.html +++ b/public/index.html @@ -229,6 +229,37 @@
Audio Call Address
{{ config.audio_call_address_hash }}
+
+
+
Status
+
+
+ {{ activeInboundAudioCalls.length }} Incoming {{ activeInboundAudioCalls.length === 1 ? 'Call' : 'Calls' }} + {{ activeOutboundAudioCalls.length }} Outgoing {{ activeOutboundAudioCalls.length === 1 ? 'Call' : 'Calls' }} +
+
Hung up, waiting for call...
+
+
+
+ + + + + + + + + + + + + +
+
@@ -574,6 +605,7 @@ displayName: "Anonymous Peer", config: null, + audioCalls: [], lxmfDeliveryAnnounces: [], tab: "peers", @@ -606,12 +638,22 @@ }; }, mounted: function() { + this.connectWebsocket(); this.getLxmfDeliveryAnnounces(); this.getNomadnetworkNodeAnnounces(); window.onNodePageUrlClick = (url) => { this.onNodePageUrlClick(url); }; + + // update calls list + this.updateCallsList(); + + // update calls list every 5 seconds + setInterval(() => { + this.updateCallsList(); + }, 3000); + }, methods: { connectWebsocket: function() { @@ -1515,6 +1557,39 @@ onDestinationPathClick: function(path) { alert(`${path.hops} ${ path.hops === 1 ? 'hop' : 'hops' } away via ${path.next_hop_interface}`); }, + async updateCallsList() { + try { + + // fetch calls + const response = await axios.get("/api/v1/calls"); + + // update ui + this.audioCalls = response.data.audio_calls; + + } catch(e) { + // do nothing on error + } + }, + async hangupAllCalls() { + + // confirm user wants to hang up calls + if(!confirm("Are you sure you want to hang up all incoming and outgoing calls?")){ + return; + } + + try { + + // hangup all calls + await axios.get(`/api/v1/calls/hangup-all`); + + // reload calls list + await this.updateCallsList(); + + } catch(e) { + // ignore error hanging up call + } + + }, }, computed: { isMobile() { @@ -1593,6 +1668,21 @@ return true; }, + activeAudioCalls() { + return this.audioCalls.filter(function(audioCall) { + return audioCall.is_active; + }); + }, + activeInboundAudioCalls() { + return this.activeAudioCalls.filter(function(audioCall) { + return !audioCall.is_outbound; + }); + }, + activeOutboundAudioCalls() { + return this.activeAudioCalls.filter(function(audioCall) { + return audioCall.is_outbound; + }); + }, }, }).mount('#app'); diff --git a/src/audio_call_manager.py b/src/audio_call_manager.py index d616d43..63f654d 100644 --- a/src/audio_call_manager.py +++ b/src/audio_call_manager.py @@ -134,6 +134,12 @@ class AudioCallManager: if audio_call is not None: self.audio_calls.remove(audio_call) + # hangup all calls + def hangup_all(self): + for audio_call in self.audio_calls: + audio_call.hangup() + return None + # attempts to initiate a call to the provided destination and returns the link hash on success async def initiate(self, destination_hash: bytes, timeout_seconds: int = 15) -> bytes: diff --git a/web.py b/web.py index 048b775..676e729 100644 --- a/web.py +++ b/web.py @@ -175,6 +175,14 @@ class ReticulumWebChat: "audio_calls": audio_calls, }) + # hangup all calls + @routes.get("/api/v1/calls/hangup-all") + async def index(request): + self.audio_call_manager.hangup_all() + return web.json_response({ + "message": "All calls have been hungup", + }) + # get call @routes.get("/api/v1/calls/{audio_call_link_hash}") async def index(request): @@ -325,7 +333,7 @@ class ReticulumWebChat: audio_call.hangup() return web.json_response({ - "message": "call has been hungup", + "message": "Call has been hungup", }) # announce