add ui to show active calls

This commit is contained in:
liamcottle
2024-05-23 23:24:26 +12:00
parent d4750c992a
commit 4c953ced2b
3 changed files with 105 additions and 1 deletions

View File

@@ -229,6 +229,37 @@
<div>Audio Call Address</div> <div>Audio Call Address</div>
<div class="text-sm text-gray-700">{{ config.audio_call_address_hash }}</div> <div class="text-sm text-gray-700">{{ config.audio_call_address_hash }}</div>
</div> </div>
<div class="p-1 flex">
<div>
<div>Status</div>
<div class="text-sm text-gray-700">
<div v-if="activeAudioCalls.length > 0" class="flex space-x-2">
<span v-if="activeInboundAudioCalls.length > 0">{{ activeInboundAudioCalls.length }} Incoming {{ activeInboundAudioCalls.length === 1 ? 'Call' : 'Calls' }}</span>
<span v-else>{{ activeOutboundAudioCalls.length }} Outgoing {{ activeOutboundAudioCalls.length === 1 ? 'Call' : 'Calls' }}</span>
</div>
<div v-else>Hung up, waiting for call...</div>
</div>
</div>
<div v-if="activeAudioCalls.length > 0" class="ml-auto my-auto mr-1 space-x-2">
<!-- view incoming calls -->
<a href="call.html" target="_blank" title="View Incoming Calls" class="my-auto inline-flex items-center gap-x-1 rounded-full bg-green-500 p-2 text-sm font-semibold text-white shadow-sm hover:bg-green-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-500">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5">
<path fill-rule="evenodd" d="M4.25 5.5a.75.75 0 0 0-.75.75v8.5c0 .414.336.75.75.75h8.5a.75.75 0 0 0 .75-.75v-4a.75.75 0 0 1 1.5 0v4A2.25 2.25 0 0 1 12.75 17h-8.5A2.25 2.25 0 0 1 2 14.75v-8.5A2.25 2.25 0 0 1 4.25 4h5a.75.75 0 0 1 0 1.5h-5Z" clip-rule="evenodd" />
<path fill-rule="evenodd" d="M6.194 12.753a.75.75 0 0 0 1.06.053L16.5 4.44v2.81a.75.75 0 0 0 1.5 0v-4.5a.75.75 0 0 0-.75-.75h-4.5a.75.75 0 0 0 0 1.5h2.553l-9.056 8.194a.75.75 0 0 0-.053 1.06Z" clip-rule="evenodd" />
</svg>
</a>
<!-- hangup all calls -->
<button title="Hangup all Calls" @click="hangupAllCalls" type="button" class="my-auto inline-flex items-center gap-x-1 rounded-full bg-red-500 p-2 text-sm font-semibold text-white shadow-sm hover:bg-red-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5 rotate-[135deg] translate-y-0.5">
<path fill-rule="evenodd" d="M2 3.5A1.5 1.5 0 0 1 3.5 2h1.148a1.5 1.5 0 0 1 1.465 1.175l.716 3.223a1.5 1.5 0 0 1-1.052 1.767l-.933.267c-.41.117-.643.555-.48.95a11.542 11.542 0 0 0 6.254 6.254c.395.163.833-.07.95-.48l.267-.933a1.5 1.5 0 0 1 1.767-1.052l3.223.716A1.5 1.5 0 0 1 18 15.352V16.5a1.5 1.5 0 0 1-1.5 1.5H15c-1.149 0-2.263-.15-3.326-.43A13.022 13.022 0 0 1 2.43 8.326 13.019 13.019 0 0 1 2 5V3.5Z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div>
</div> </div>
</div> </div>
@@ -574,6 +605,7 @@
displayName: "Anonymous Peer", displayName: "Anonymous Peer",
config: null, config: null,
audioCalls: [],
lxmfDeliveryAnnounces: [], lxmfDeliveryAnnounces: [],
tab: "peers", tab: "peers",
@@ -606,12 +638,22 @@
}; };
}, },
mounted: function() { mounted: function() {
this.connectWebsocket(); this.connectWebsocket();
this.getLxmfDeliveryAnnounces(); this.getLxmfDeliveryAnnounces();
this.getNomadnetworkNodeAnnounces(); this.getNomadnetworkNodeAnnounces();
window.onNodePageUrlClick = (url) => { window.onNodePageUrlClick = (url) => {
this.onNodePageUrlClick(url); this.onNodePageUrlClick(url);
}; };
// update calls list
this.updateCallsList();
// update calls list every 5 seconds
setInterval(() => {
this.updateCallsList();
}, 3000);
}, },
methods: { methods: {
connectWebsocket: function() { connectWebsocket: function() {
@@ -1515,6 +1557,39 @@
onDestinationPathClick: function(path) { onDestinationPathClick: function(path) {
alert(`${path.hops} ${ path.hops === 1 ? 'hop' : 'hops' } away via ${path.next_hop_interface}`); 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: { computed: {
isMobile() { isMobile() {
@@ -1593,6 +1668,21 @@
return true; 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'); }).mount('#app');
</script> </script>

View File

@@ -134,6 +134,12 @@ class AudioCallManager:
if audio_call is not None: if audio_call is not None:
self.audio_calls.remove(audio_call) 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 # 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: async def initiate(self, destination_hash: bytes, timeout_seconds: int = 15) -> bytes:

10
web.py
View File

@@ -175,6 +175,14 @@ class ReticulumWebChat:
"audio_calls": audio_calls, "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 # get call
@routes.get("/api/v1/calls/{audio_call_link_hash}") @routes.get("/api/v1/calls/{audio_call_link_hash}")
async def index(request): async def index(request):
@@ -325,7 +333,7 @@ class ReticulumWebChat:
audio_call.hangup() audio_call.hangup()
return web.json_response({ return web.json_response({
"message": "call has been hungup", "message": "Call has been hungup",
}) })
# announce # announce