From 42c40266795276b61fd31d91a1bdd9f0f281e5f8 Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Thu, 23 Jan 2025 14:14:25 -0800 Subject: [PATCH] preparing call component --- app/client/mobile/src/calling/Calling.tsx | 10 ++++-- .../mobile/src/calling/useCalling.hook.ts | 5 +-- app/client/mobile/src/session/Session.tsx | 32 ++++++++++--------- app/sdk/src/entities.ts | 9 ++++++ app/sdk/src/link.ts | 11 +++++++ app/sdk/src/net/addCall.ts | 10 ++++++ app/sdk/src/net/addContactRing.ts | 9 ++++++ app/sdk/src/net/keepCall.ts | 8 +++++ app/sdk/src/net/removeCall.ts | 8 +++++ app/sdk/src/net/removeContactCall.ts | 7 ++++ app/sdk/src/ring.ts | 7 ++-- 11 files changed, 94 insertions(+), 22 deletions(-) create mode 100644 app/sdk/src/net/addCall.ts create mode 100644 app/sdk/src/net/addContactRing.ts create mode 100644 app/sdk/src/net/keepCall.ts create mode 100644 app/sdk/src/net/removeCall.ts create mode 100644 app/sdk/src/net/removeContactCall.ts diff --git a/app/client/mobile/src/calling/Calling.tsx b/app/client/mobile/src/calling/Calling.tsx index 8981ec9f..39417e3a 100644 --- a/app/client/mobile/src/calling/Calling.tsx +++ b/app/client/mobile/src/calling/Calling.tsx @@ -1,13 +1,19 @@ -import React, { useState } from 'react'; +import React, { useEffect } from 'react'; import { SafeAreaView, Modal, ScrollView, View } from 'react-native'; import { Surface, Icon, Divider, Button, IconButton, Text, TextInput} from 'react-native-paper'; import {styles} from './Calling.styled'; import {useCalling} from './useCalling.hook'; import {BlurView} from '@react-native-community/blur'; -export function Calling() { +export function Calling({ callCard }: { callCard: string }) { const { state, actions } = useCalling(); + useEffect(() => { + if (callCard.cardId) { + actions.call(callCard.cardId); + } + }, [callCard]); + return ( 0) ? styles.active : styles.inactive}> diff --git a/app/client/mobile/src/calling/useCalling.hook.ts b/app/client/mobile/src/calling/useCalling.hook.ts index 478fc857..cda63ebe 100644 --- a/app/client/mobile/src/calling/useCalling.hook.ts +++ b/app/client/mobile/src/calling/useCalling.hook.ts @@ -16,8 +16,6 @@ export function useCalling() { useEffect(() => { if (app.state.session) { const setRinging = (ringing: { cardId: string, callId: string }[]) => { -console.log(">>>> ", ringing); - updateState({ ringing }); } const ring = app.state.session.getRing(); @@ -29,6 +27,9 @@ console.log(">>>> ", ringing); }, [app.state.session]); const actions = { + call: (cardId: string) => { + console.log('calling: ', cardId); + }, } return { state, actions } diff --git a/app/client/mobile/src/session/Session.tsx b/app/client/mobile/src/session/Session.tsx index 8062137a..16cf4a29 100644 --- a/app/client/mobile/src/session/Session.tsx +++ b/app/client/mobile/src/session/Session.tsx @@ -33,19 +33,24 @@ export function Session() { const scheme = useColorScheme(); const [tab, setTab] = useState('content'); const [textCard, setTextCard] = useState({ cardId: null} as {cardId: null|string}); + const [callCard, setCallCard] = useState({ cardId: null} as {cardId: null|string}); const [dismissed, setDismissed] = useState(false); const [disconnected, setDisconnected] = useState(false); const [showDisconnected, setShowDisconnected] = useState(false); - const sessionNav = {strings: state.strings}; - const showContent = {display: tab === 'content' ? 'flex' : 'none'}; - const showContact = {display: tab === 'contacts' ? 'flex' : 'none'}; - const showSettings = {display: tab === 'settings' ? 'flex' : 'none'}; - const textContact = (cardId: null|string) => { setTextCard({ cardId }); } + const callContact = (cardId: null|string) => { + setCallCard({ cardId }); + } + + const sessionNav = {strings: state.strings, callContact, callCard, textContact, textCard}; + const showContent = {display: tab === 'content' ? 'flex' : 'none'}; + const showContact = {display: tab === 'contacts' ? 'flex' : 'none'}; + const showSettings = {display: tab === 'settings' ? 'flex' : 'none'}; + const dismiss = () => { setDismissed(true); setTimeout(() => { @@ -89,7 +94,7 @@ export function Session() { ...styles.body, ...showContact, }}> - + )} - + ); } @@ -226,7 +231,7 @@ function ContentTab({scheme, textCard, contentTab}: {scheme: string, textCard: { ); } -function ContactTab({scheme, textContact}: {scheme: string, textContact: (cardId: null|string)=>void}) { +function ContactTab({scheme, textContact, callContact}: {scheme: string, textContact: (cardId: string)=>void, callContact: (cardId: string)=>void}) { const [contactParams, setContactParams] = useState({ guid: '', } as ContactParams); @@ -244,7 +249,7 @@ function ContactTab({scheme, textContact}: {scheme: string, textContact: (cardId setContactParams(params); props.navigation.navigate('profile'); }} - callContact={(cardId: string)=>console.log("CALL: ", cardId)} + callContact={callContact} textContact={textContact} /> )} @@ -364,7 +369,6 @@ function RegistryScreen({nav}) { } function ContactsScreen({nav}) { - const [textCard, setTextCard] = useState({ cardId: null} as {cardId: null|string}); const ContactsComponent = useCallback( () => ( @@ -373,8 +377,8 @@ function ContactsScreen({nav}) { openContact={(params: ContactParams) => { nav.openContact(params, nav.profile.openDrawer); }} - callContact={(cardId: string)=>console.log('CALL: ', cardId)} - textContact={(cardId: null|string)=>setTextCard({ cardId })} + callContact={nav.callContact} + textContact={nav.textContact} /> ), @@ -393,7 +397,7 @@ function ContactsScreen({nav}) { overlayColor: 'rgba(8,8,8,.9)', }}> {({navigation}) => ( - + )} ); @@ -428,8 +432,6 @@ function SettingsScreen({nav}) { } function HomeScreen({nav}) { - const [textCard, setTextCard] = useState({ cardId: null} as {cardId: null|string}); - return ( diff --git a/app/sdk/src/entities.ts b/app/sdk/src/entities.ts index 67b55807..c3f4ffb6 100644 --- a/app/sdk/src/entities.ts +++ b/app/sdk/src/entities.ts @@ -249,6 +249,15 @@ export const defaultProfileEntity = { node: '', }; +export type Calling = { + id: string; + cardId: string; + callerToken: string; + calleeToken: string; + keepAlive: number; + ice: { urls: string; username: string; credential: string }[]; +} + export type Ringing = { cardId: string; callId: string; diff --git a/app/sdk/src/link.ts b/app/sdk/src/link.ts index be6c2b52..100fe855 100644 --- a/app/sdk/src/link.ts +++ b/app/sdk/src/link.ts @@ -21,6 +21,7 @@ export class LinkModule implements Link { private secure: boolean; private token: string; private ice: { urls: string; username: string; credential: string }[]; + private cleanup: null | (()=>void); constructor(log: Logging) { this.log = log; @@ -36,6 +37,7 @@ export class LinkModule implements Link { this.aliveInterval = null; this.ringInterval = null; this.ice = []; + this.cleanup = null; } public getIce(): { urls: string; username: string; credential: string }[] { @@ -44,6 +46,7 @@ export class LinkModule implements Link { public async call(node: string, secure: boolean, token: string, cardId: string, contactNode: string, contactToken: string) { const call = await addCall(node, secure, token, cardId); + this.cleanup = () => { removeCall(node, secure, token, call.id }; const { id, keepAlive, calleeToken, callerToken, ice } = call; const insecure = /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|:\d+$|$)){4}$/.test(contactNode); @@ -74,6 +77,7 @@ export class LinkModule implements Link { public async join(server: string, access: string, ice: { urls: string; username: string; credential: string }[]) { this.ice = ice; const insecure = /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|:\d+$|$)){4}$/.test(server); + this.cleanup = () => { removeContactCall(server, !insecure, access); } connect(access, server, !insecure); } @@ -103,6 +107,13 @@ export class LinkModule implements Link { if (this.websocket) { this.websocket.close(); } + if (this.cleanup) { + try { + this.cleanup(); + } catch (err) { + this.log.error(err); + } + } } public setStatusListener(listener: (status: string) => void) { diff --git a/app/sdk/src/net/addCall.ts b/app/sdk/src/net/addCall.ts new file mode 100644 index 00000000..4c1ca316 --- /dev/null +++ b/app/sdk/src/net/addCall.ts @@ -0,0 +1,10 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; +import { Calling } from '../entities'; + +export async function addCall(node: string, secure: boolean, token: string, cardId: string): Promise { + const endpoint = `http${secure ? 's' : ''}://${node}/talk/calls?agent=${token}`; + const call = await fetchWithTimeout(endpoint, { method: 'POST', body: JSON.stringify(cardId) }); + checkResponse(call.status); + return await call.json(); +} + diff --git a/app/sdk/src/net/addContactRing.ts b/app/sdk/src/net/addContactRing.ts new file mode 100644 index 00000000..bf5eef60 --- /dev/null +++ b/app/sdk/src/net/addContactRing.ts @@ -0,0 +1,9 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; +import { Ringing } from '../entities'; + +export async function addContactRing(server: string, secure: boolean, token: string, ringing: Ringing): { + const endpoint = `http${secure ? 's' : '' }://${server}/talk/rings/?contact=${token}'; + const { status } = await fetchWithTimeout(endpoint, { method: 'POST', body: JSON.stringify(ringing) }); + checkResponse(status); +} + diff --git a/app/sdk/src/net/keepCall.ts b/app/sdk/src/net/keepCall.ts new file mode 100644 index 00000000..939147b5 --- /dev/null +++ b/app/sdk/src/net/keepCall.ts @@ -0,0 +1,8 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function keepCall(node: string, secure: boolean, token: string, callId: string): Promise { + const endpoint = `http${secure ? 's' : ''}://${node}/talk/calls/${callId}?agent=${token}`; + const { status } = await fetchWithTimeout(endpoint, { method: 'PUT' }); + checkResponse(status); +} + diff --git a/app/sdk/src/net/removeCall.ts b/app/sdk/src/net/removeCall.ts new file mode 100644 index 00000000..fedc68fa --- /dev/null +++ b/app/sdk/src/net/removeCall.ts @@ -0,0 +1,8 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function removeCall(node: string, secure: boolean, token: string, callId: string): Promise { + const endpoint = `http${secure ? 's' : ''}://${node}/talk/calls/${callId}?agent=${token}`; + const { status } = await fetchWithTimeout(endpoint, { method: 'DELETE' }); + checkResponse(status); +} + diff --git a/app/sdk/src/net/removeContactCall.ts b/app/sdk/src/net/removeContactCall.ts new file mode 100644 index 00000000..eb41e3b5 --- /dev/null +++ b/app/sdk/src/net/removeContactCall.ts @@ -0,0 +1,7 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function removeContactCall(server: string, secure: boolean, token: string, callId: string): Promise { + const endpoint = `http${secure ? 's' : ''}://${server}/talk/calls/${callId}?contact=${token}`; + const { status } = await fetchWithTimeout(endpoint, { method: 'DELETE' }); + checkResponse(status); +} diff --git a/app/sdk/src/ring.ts b/app/sdk/src/ring.ts index d70ac37f..f543eeb1 100644 --- a/app/sdk/src/ring.ts +++ b/app/sdk/src/ring.ts @@ -84,14 +84,15 @@ export class RingModule implements Ring { if (!entry || entry.expires < now || entry.status !== 'ringing') { throw new Error('invalid ringing entry'); } + entry.status = 'declined'; + this.emitRinging(); try { - await removeContactCall(contactNode, entry.call.calleeToken, entry.call.callId); + const insecure = /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|:\d+$|$)){4}$/.test(contactNode); + await removeContactCall(contactNode, !insecure, entry.call.calleeToken, entry.call.callId); } catch (err) { console.log(err); } - entry.status = 'declined'; - this.emitRinging(); } public close(): void {