diff --git a/app/mobile/ios/Databag.xcodeproj/project.pbxproj b/app/mobile/ios/Databag.xcodeproj/project.pbxproj index c36d2958..4400d142 100644 --- a/app/mobile/ios/Databag.xcodeproj/project.pbxproj +++ b/app/mobile/ios/Databag.xcodeproj/project.pbxproj @@ -571,7 +571,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -643,7 +643,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; diff --git a/app/mobile/src/api/removeContactCall.js b/app/mobile/src/api/removeContactCall.js index 2d2dd315..8e2a553f 100644 --- a/app/mobile/src/api/removeContactCall.js +++ b/app/mobile/src/api/removeContactCall.js @@ -1,6 +1,6 @@ import { checkResponse, fetchWithTimeout } from './fetchUtil'; -export async function removeContactCall(server, token, calllId) { +export async function removeContactCall(server, token, callId) { const call = await fetchWithTimeout(`https://${server}/talk/calls/${callId}?contact=${token}`, { method: 'DELETE' }); checkResponse(call); } diff --git a/app/mobile/src/context/useRingContext.hook.js b/app/mobile/src/context/useRingContext.hook.js index 2bbf3e57..7f53f7d3 100644 --- a/app/mobile/src/context/useRingContext.hook.js +++ b/app/mobile/src/context/useRingContext.hook.js @@ -71,7 +71,7 @@ export function useRingContext() { updateState({ ringing: ringing.current }); }, EXPIRE); }, - ignore: (cardId, callId) => { + ignore: async (cardId, callId) => { const key = `${cardId}:${callId}` const call = ringing.current.get(key); if (call) { diff --git a/app/mobile/src/session/Session.jsx b/app/mobile/src/session/Session.jsx index 997d37e7..b2c6db44 100644 --- a/app/mobile/src/session/Session.jsx +++ b/app/mobile/src/session/Session.jsx @@ -1,4 +1,4 @@ -import { View, ScrollView, TouchableOpacity, StatusBar, Text, Image } from 'react-native'; +import { View, ScrollView, TouchableOpacity, StatusBar, Text, Image, Modal } from 'react-native'; import { useState, useEffect, useContext } from 'react'; import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; @@ -6,6 +6,7 @@ import { createDrawerNavigator } from '@react-navigation/drawer'; import { createStackNavigator } from '@react-navigation/stack'; import { NavigationContainer } from '@react-navigation/native'; import Ionicons from 'react-native-vector-icons/AntDesign'; +import MatIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import { useSession } from './useSession.hook'; import { styles } from './Session.styled'; import Colors from 'constants/Colors'; @@ -22,6 +23,7 @@ import { ConversationContext } from 'context/ConversationContext'; import { ProfileContext } from 'context/ProfileContext'; import { ProfileIcon } from './profileIcon/ProfileIcon'; import { CardsIcon } from './cardsIcon/CardsIcon'; +import { Logo } from 'utils/Logo'; import splash from 'images/session.png'; const ConversationStack = createStackNavigator(); @@ -159,6 +161,7 @@ function ContactStackScreen() { export function Session() { + const [ringing, setRinging] = useState([]); const { state, actions } = useSession(); const drawerParams = { drawerPosition: 'right', headerShown: false, swipeEnabled: false, drawerType: 'front' }; @@ -310,6 +313,31 @@ export function Session() { ); } + useEffect(() => { + let incoming = []; + for (let i = 0; i < state.ringing.length; i++) { + const { img, name, handle, callId, cardId, contactNode, contactToken, calleeToken } = state.ringing[i]; + const label = name ? name : `${handle}@${contactNode}`; + const key = `${cardId}:${callId}` + incoming.push( + + + { label } + actions.ignore(cardId, callId)}> + + + actions.decline(cardId, contactNode, contactToken, callId)}> + + + actions.accept(cardId, callId, contactNode, contactToken, calleeToken)}> + + + + ); + } + setRinging(incoming); + }, [state.ringing]); + return ( @@ -379,6 +407,18 @@ export function Session() { )} + 0} + supportedOrientations={['portrait', 'landscape']} + > + + + { ringing } + + + ); } diff --git a/app/mobile/src/session/Session.styled.js b/app/mobile/src/session/Session.styled.js index d13a93c9..2f36f45e 100644 --- a/app/mobile/src/session/Session.styled.js +++ b/app/mobile/src/session/Session.styled.js @@ -138,5 +138,53 @@ export const styles = StyleSheet.create({ fontSize: 18, color: Colors.tetx, }, + ringBase: { + display: 'flex', + width: '100%', + height: '100%', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: 'rgba(52, 52, 52, 0.8)' + }, + ringFrame: { + backgroundColor: Colors.formBackground, + padding: 16, + width: '90%', + maxWidth: 400, + borderRadius: 4, + }, + ringEntry: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + }, + ringName: { + flexGrow: 1, + paddingLeft: 8, + paddingRight: 8, + }, + ringIgnore: { + borderWidth: 1, + borderRadius: 20, + borderColor: Colors.text, + padding: 6, + marginRight: 4, + }, + ringDecline: { + borderWidth: 1, + borderRadius: 20, + borderColor: Colors.alert, + padding: 6, + marginLeft: 8, + marginRight: 8, + }, + ringAccept: { + borderWidth: 1, + borderRadius: 20, + borderColor: Colors.primary, + padding: 6, + marginLeft: 4, + }, }); diff --git a/app/mobile/src/session/useSession.hook.js b/app/mobile/src/session/useSession.hook.js index 06ee8333..a6dcceb4 100644 --- a/app/mobile/src/session/useSession.hook.js +++ b/app/mobile/src/session/useSession.hook.js @@ -3,10 +3,13 @@ import { useWindowDimensions } from 'react-native'; import { useNavigate } from 'react-router-dom'; import config from 'constants/Config'; import { StoreContext } from 'context/StoreContext'; +import { CardContext } from 'context/CardContext'; +import { RingContext } from 'context/RingContext'; export function useSession() { const [state, setState] = useState({ + ringing: [], tabbled: null, subWidth: '50%', baseWidth: '50%', @@ -15,6 +18,8 @@ export function useSession() { firstRun: null, }); + const ring = useContext(RingContext); + const card = useContext(CardContext); const store = useContext(StoreContext); const dimensions = useWindowDimensions(); const navigate = useNavigate(); @@ -24,6 +29,27 @@ export function useSession() { setState((s) => ({ ...s, ...value })); } + useEffect(() => { + const ringing = []; + const expired = Date.now(); + ring.state.ringing.forEach(call => { + if (call.expires > expired && !call.status) { + const { callId, cardId, calleeToken } = call; + const contact = card.state.cards.get(cardId); + if (contact) { + const { imageSet, name, handle, node, guid } = contact.card?.profile || {}; + const { token } = contact.card?.detail || {}; + const contactToken = `${guid}.${token}`; + const img = imageSet ? card.actions.getCardImageUrl(cardId) : null; + ringing.push({ cardId, img, name, handle, contactNode: node, callId, contactToken, calleeToken }); + } + } + }); + + const { callStatus, localStream, localVideo, localAudio, remoteStream, remoteVideo, remoteAudio } = ring.state; + updateState({ ringing, callStatus, localStream, localVideo, localAudio, remoteStream, remoteVideo, remoteAudio }); + }, [ring.state]); + useEffect(() => { checkFirstRun(); }, []); @@ -55,6 +81,16 @@ export function useSession() { updateState({ firstRun: false }); store.actions.setFirstRun(); }, + ignore: async (cardId, callId) => { + await ring.actions.ignore(cardId, callId); + }, + decline: async (cardId, contactNode, contactToken, callId) => { + await ring.actions.decline(cardId, contactNode, contactToken, callId); + }, + accept: async (cardId, callId, contactNode, contactToken, calleeToken) => { + await ring.actions.accept(cardId, callId, contactNode, contactToken, calleeToken); + }, + }; return { state, actions };