diff --git a/net/server/internal/api_keepCall.go b/net/server/internal/api_keepCall.go index aa13cfdf..5e585bdd 100644 --- a/net/server/internal/api_keepCall.go +++ b/net/server/internal/api_keepCall.go @@ -2,6 +2,7 @@ package databag import ( "net/http" + "github.com/gorilla/mux" ) //KeepCall keeps call and signaling alive @@ -13,11 +14,8 @@ func KeepCall(w http.ResponseWriter, r *http.Request) { return } - var callId string - if err := ParseRequest(r, w, &callId); err != nil { - ErrResponse(w, http.StatusBadRequest, err) - return - } + params := mux.Vars(r) + callId := params["callId"] bridgeRelay.KeepAlive(account.ID, callId); WriteResponse(w, nil); diff --git a/net/server/internal/api_removeCall.go b/net/server/internal/api_removeCall.go index c9830635..33152c74 100644 --- a/net/server/internal/api_removeCall.go +++ b/net/server/internal/api_removeCall.go @@ -3,16 +3,14 @@ package databag import ( "net/http" "errors" + "github.com/gorilla/mux" ) //RemoveCall adds an active call with ice signal and relay func RemoveCall(w http.ResponseWriter, r *http.Request) { - var callId string - if err := ParseRequest(r, w, &callId); err != nil { - ErrResponse(w, http.StatusBadRequest, err) - return - } + params := mux.Vars(r) + callId := params["callId"] tokenType := ParamTokenType(r) if tokenType == APPTokenAgent { diff --git a/net/server/internal/bridge.go b/net/server/internal/bridge.go index 7bae141f..5f6b4308 100644 --- a/net/server/internal/bridge.go +++ b/net/server/internal/bridge.go @@ -8,7 +8,7 @@ import ( ) var bridgeRelay BridgeRelay; -const BridgeKeepAlive = 6 +const BridgeKeepAlive = 15 type BridgeStatus struct { status string diff --git a/net/server/internal/routers.go b/net/server/internal/routers.go index c0902aec..fb8360c0 100644 --- a/net/server/internal/routers.go +++ b/net/server/internal/routers.go @@ -800,28 +800,28 @@ var endpoints = routes{ route{ "AddCall", strings.ToUpper("Post"), - "/talk/call", + "/talk/calls", AddCall, }, route{ "KeepCall", strings.ToUpper("Put"), - "/talk/call/{callId}", + "/talk/calls/{callId}", KeepCall, }, route{ "EndCall", strings.ToUpper("Delete"), - "/talk/call/{callId}", + "/talk/calls/{callId}", EndCall, }, route{ "AddRing", strings.ToUpper("Post"), - "/talk/ring", + "/talk/rings", AddRing, }, diff --git a/net/web/src/api/addCall.js b/net/web/src/api/addCall.js new file mode 100644 index 00000000..5fc03be6 --- /dev/null +++ b/net/web/src/api/addCall.js @@ -0,0 +1,10 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function addCall(token, cardId) { + let param = "?agent=" + token + let call = await fetchWithTimeout('/talk/calls' + param, { method: 'POST', body: JSON.stringify(cardId) }); + checkResponse(call) + let ret = await call.json() + return ret; +} + diff --git a/net/web/src/api/addContactRing.js b/net/web/src/api/addContactRing.js index fd387762..2b8f5ab9 100644 --- a/net/web/src/api/addContactRing.js +++ b/net/web/src/api/addContactRing.js @@ -6,7 +6,7 @@ export async function addContactRing(server, token, call) { host = `https://${server}` } - let ring = await fetchWithTimeout(`${host}/talk/ring?contact=${token}`, { method: 'POST', body: JSON.stringify(call) }); + let ring = await fetchWithTimeout(`${host}/talk/rings?contact=${token}`, { method: 'POST', body: JSON.stringify(call) }); checkResponse(ring); } diff --git a/net/web/src/api/keepCall.js b/net/web/src/api/keepCall.js new file mode 100644 index 00000000..f3f8e6ed --- /dev/null +++ b/net/web/src/api/keepCall.js @@ -0,0 +1,8 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function keepCall(token, callId) { + let param = "?agent=" + token + let call = await fetchWithTimeout(`/talk/calls/${callId}` + param, { method: 'PUT' }); + checkResponse(call) +} + diff --git a/net/web/src/api/removeCall.js b/net/web/src/api/removeCall.js new file mode 100644 index 00000000..9261a1b6 --- /dev/null +++ b/net/web/src/api/removeCall.js @@ -0,0 +1,8 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function removeCall(token, callId) { + let param = "?agent=" + token + let call = await fetchWithTimeout(`/talk/calls/${callId}` + param, { method: 'DELETE' }); + checkResponse(call) +} + diff --git a/net/web/src/api/removeContactCall.js b/net/web/src/api/removeContactCall.js new file mode 100644 index 00000000..d2679ee2 --- /dev/null +++ b/net/web/src/api/removeContactCall.js @@ -0,0 +1,12 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function removeContactCall(server, token, callId) { + let host = ""; + if (server) { + host = `https://${server}` + } + + const call = await fetchWithTimeout(`${host}/talk/calls/${callId}?contact=${token}`, { method: 'DELETE' }); + checkResponse(call); +} + diff --git a/net/web/src/context/useAppContext.hook.js b/net/web/src/context/useAppContext.hook.js index eff405bb..ab395683 100644 --- a/net/web/src/context/useAppContext.hook.js +++ b/net/web/src/context/useAppContext.hook.js @@ -45,12 +45,14 @@ export function useAppContext(websocket) { profileContext.actions.setToken(token); cardContext.actions.setToken(token); channelContext.actions.setToken(token); + ringContext.actions.setToken(token); } catch (err) { accountContext.actions.clearToken(); profileContext.actions.clearToken(); cardContext.actions.clearToken(); channelContext.actions.clearToken(); + ringContext.actions.clearToken(); throw err; } setWebsocket(token); @@ -59,8 +61,8 @@ export function useAppContext(websocket) { const clearSession = () => { uploadContext.actions.clear(); storeContext.actions.clear(); - ringContext.actions.clear(); + ringContext.actions.clearToken(); accountContext.actions.clearToken(); profileContext.actions.clearToken(); cardContext.actions.clearToken(); @@ -176,11 +178,9 @@ export function useAppContext(websocket) { let activity = JSON.parse(ev.data); updateState({ status: 'connected' }); if (activity.revision) { -console.log("GOR REVISION"); setAppRevision(activity.revision); } if (activity.ring) { -console.log("GOT PHONE!"); const { cardId, callId, calleeToken } = activity.ring; ringContext.actions.ring(cardId, callId, calleeToken); } diff --git a/net/web/src/context/useRingContext.hook.js b/net/web/src/context/useRingContext.hook.js index e32ced62..b03c4911 100644 --- a/net/web/src/context/useRingContext.hook.js +++ b/net/web/src/context/useRingContext.hook.js @@ -1,22 +1,39 @@ import { useEffect, useContext, useState, useRef } from 'react'; +import { createWebsocket } from 'api/fetchUtil'; +import { addContactRing } from 'api/addContactRing'; +import { addCall } from 'api/addCall'; +import { keepCall } from 'api/keepCall'; +import { removeCall } from 'api/removeCall'; +import { removeContactCall } from 'api/removeContactCall'; export function useRingContext() { const [state, setState] = useState({ ringing: new Map(), + callStatus: null, }); const access = useRef(null); const EXPIRE = 3000000 const ringing = useRef(new Map()); + const calling = useRef(null); + const ws = useRef(null); const updateState = (value) => { setState((s) => ({ ...s, ...value })) } const actions = { - clear: () => { + setToken: (token) => { + if (access.current) { + throw new Error("invalid ring state"); + } + access.current = token; ringing.current = new Map(); - updateState({ ringing: ringing.current }); + calling.current = null; + updateState({ callStatus: null, ringing: ringing.current }); + }, + clearToken: () => { + access.current = null; }, ring: (cardId, callId, calleeToken) => { const key = `${cardId}:${callId}` @@ -30,14 +47,11 @@ export function useRingContext() { }, ignore: (cardId, callId) => { const key = `${cardId}:${callId}` -console.log("IGNORE", key); const call = ringing.current.get(key); if (call) { call.status = 'ignored' ringing.current.set(key, call); updateState({ ringing: ringing.current }); -console.log(ringing.current); - } }, decline: (cardId, callId) => { @@ -49,13 +63,109 @@ console.log(ringing.current); updateState({ ringing: ringing.current }); } }, - accept: (cardId, callId) => { + accept: async (cardId, callId, contactNode, contactToken, calleeToken) => { + if (calling.current) { + throw new Error("active session"); + } + const key = `${cardId}:${callId}` const call = ringing.current.get(key); if (call) { call.status = 'accepted' ringing.current.set(key, call); updateState({ ringing: ringing.current }); + + // connect signal socket + calling.current = { state: "connecting", callId, contactNode, contactToken, host: false }; + updateState({ callStatus: "connecting" }); + ws.current = createWebsocket(`wss://${contactNode}/signal`); + ws.current.onmessage = (ev) => { + // handle messages [impolite] + console.log(ev); + } + ws.current.onclose = (e) => { + // update state to disconnected + calling.current = null; + updateState({ calling: null }); + } + ws.current.onopen = () => { + calling.current.state = "connected" + updateState({ callStatus: "connected" }); + ws.current.send(JSON.stringify({ AppToken: calleeToken })) + } + ws.current.error = (e) => { + console.log(e) + ws.current.close(); + } + } + }, + end: async () => { + if (!calling.current) { + throw new Error('inactive session'); + } + try { + const { host, callId, contactNode, contactToken } = calling.current; + if (host) { + await removeCall(access.current, callId); + } + else { + await removeContactCall(contactNode, contactToken, callId); + } + } + catch (err) { + console.log(err); + } + ws.current.close(); + }, + call: async (cardId, contactNode, contactToken) => { + if (calling.current) { + throw new Error("active session"); + } + + // create call + const call = await addCall(access.current, cardId); + const { callId, keepAlive, callerToken, calleeToken } = call; + const aliveInterval = setInterval(async () => { + try { + await keepCall(access.current, call.callId); + } + catch (err) { + console.log(err); + } + }, keepAlive * 1000); + let index = 0; + const ringInterval = setInterval(async () => { + try { + await addContactRing(contactNode, contactToken, { index, callId, calleeToken }); + index += 1; + } + catch (err) { + console.log(err); + } + }, 3000); + calling.current = { state: "connecting", callId, host: true }; + updateState({ callStatus: "connecting" }); + const protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://'; + ws.current = createWebsocket(`${protocol}${window.location.host}/signal`); + ws.current.onmessage = (ev) => { + // handle messages [polite] + // on connected stop ringing + console.log(ev); + } + ws.current.onclose = (e) => { + clearInterval(ringInterval); + clearInterval(aliveInterval); + calling.current = null; + updateState({ callStatus: null }); + } + ws.current.onopen = () => { + calling.current.state = "ringing"; + updateState({ callStatus: "ringing" }); + ws.current.send(JSON.stringify({ AppToken: callerToken })) + } + ws.current.error = (e) => { + console.log(e) + ws.current.close(); } }, } diff --git a/net/web/src/session/contact/useContact.hook.js b/net/web/src/session/contact/useContact.hook.js index 6a41e5f9..1354c02f 100644 --- a/net/web/src/session/contact/useContact.hook.js +++ b/net/web/src/session/contact/useContact.hook.js @@ -1,5 +1,6 @@ import { useContext, useState, useEffect } from 'react'; import { CardContext } from 'context/CardContext'; +import { RingContext } from 'context/RingContext'; import { ViewportContext } from 'context/ViewportContext'; import { getListingMessage } from 'api/getListingMessage'; import { getCardByGuid } from 'context/cardUtil'; @@ -21,6 +22,7 @@ export function useContact(guid, listing, close) { }); const card = useContext(CardContext); + const ring = useContext(RingContext); const viewport = useContext(ViewportContext); const updateState = (value) => { @@ -157,12 +159,12 @@ export function useContact(guid, listing, close) { }); }, ring: async () => { - console.log("ringing!!"); + console.log("calling!!"); const contact = card.state.cards.get(state.cardId); const { node, guid } = contact.data.cardProfile; const { token } = contact.data.cardDetail; - await addContactRing(node, `${guid}.${token}`, { index: 0, callId: 'abc', calleeToken: '123' }); - console.log(contact); + await ring.actions.call(state.cardId, node, `${guid}.${token}`); + //await addContactRing(node, `${guid}.${token}`, { index: 0, callId: 'abc', calleeToken: '123' }); }, };