invoking dm and call from contact list in mobile

This commit is contained in:
balzack 2023-04-05 22:27:33 -07:00
parent 6a9fd8c133
commit 843a22c17b
9 changed files with 236 additions and 173 deletions

View File

@ -37,7 +37,7 @@ const CardDrawer = createDrawerNavigator();
const RegistryDrawer = createDrawerNavigator();
const Tab = createBottomTabNavigator();
function ConversationStackScreen() {
function ConversationStackScreen({ dmChannel }) {
const stackParams = { headerStyle: { backgroundColor: Colors.titleBackground }, headerBackTitleVisible: false };
const screenParams = { headerShown: true, headerTintColor: Colors.primary };
@ -74,7 +74,7 @@ function ConversationStackScreen() {
<ConversationStack.Navigator initialRouteName="channels" screenOptions={({ route }) => (screenParams)} >
<ConversationStack.Screen name="channels" options={stackParams}>
{(props) => <Channels navigation={props.navigation} openConversation={(cardId, channelId) => openConversation(props.navigation, cardId, channelId)} />}
{(props) => <Channels navigation={props.navigation} dmChannel={dmChannel} openConversation={(cardId, channelId) => openConversation(props.navigation, cardId, channelId)} />}
</ConversationStack.Screen>
<ConversationStack.Screen name="conversation" options={stackParams}>
@ -107,7 +107,7 @@ function ProfileStackScreen() {
);
}
function ContactStackScreen() {
function ContactStackScreen({ addChannel }) {
const stackParams = { headerStyle: { backgroundColor: Colors.titleBackground }, headerBackTitleVisible: false };
const screenParams = { headerShown: true, headerTintColor: Colors.primary };
@ -140,7 +140,7 @@ function ContactStackScreen() {
<ContactStack.Screen name="cards" options={{ ...stackParams, headerTitle: (props) => (
<CardsHeader filter={filter} setFilter={setFilter} sort={sort} setSort={setSort} openRegistry={openRegistry} />
)}}>
{(props) => <CardsBody filter={filter} sort={sort} openContact={(contact) => openContact(props.navigation, contact)} />}
{(props) => <CardsBody filter={filter} sort={sort} openContact={(contact) => openContact(props.navigation, contact)} addChannel={addChannel} />}
</ContactStack.Screen>
<ContactStack.Screen name="contact" options={{ ...stackParams, headerTitle: (props) => (
@ -160,160 +160,172 @@ function ContactStackScreen() {
);
}
function HomeScreen({ navParams }) {
const drawerParams = { drawerPosition: 'right', headerShown: false, swipeEnabled: false, drawerType: 'front' };
const conversation = useContext(ConversationContext);
const [channel, setChannel] = useState(false);
const [cardId, setCardId] = useState();
const [channelId, setChannelId] = useState();
const setConversation = (card, channel) => {
(async () => {
conversation.actions.setConversation(card, channel);
setCardId(card);
setChannelId(channel);
setChannel(true);
navParams.cardNav.closeDrawer();
})();
};
const closeConversation = () => {
conversation.actions.clearConversation();
setCardId(null);
setChannelId(null);
setChannel(false);
};
const openDetails = () => {
navParams.detailNav.openDrawer();
};
const openProfile = () => {
navParams.profileNav.openDrawer();
}
const openCards = () => {
navParams.cardNav.openDrawer();
}
useEffect(() => {
navParams.detailNav.closeDrawer();
setChannelId(null);
setCardId(null);
setChannel(false);
}, [navParams.closeCount]);
return (
<View style={styles.home}>
<SafeAreaView edges={['top', 'bottom', 'left']} style={styles.sidebar}>
<View edges={['left']} style={styles.options}>
<TouchableOpacity style={styles.option} onPress={openProfile}>
<ProfileIcon color={Colors.text} size={20} />
<Text style={styles.profileLabel}>Profile</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.option} onPress={openCards}>
<CardsIcon color={Colors.text} size={20} />
<Text style={styles.profileLabel}>Contacts</Text>
</TouchableOpacity>
</View>
<View style={styles.channels}>
<Channels dmChannel={navParams.dmChannel} cardId={cardId} channelId={channelId} openConversation={setConversation} />
</View>
</SafeAreaView>
<View style={styles.conversation}>
{ channel && (
<SafeAreaView edges={['top', 'bottom', 'right']}>
<Conversation closeConversation={closeConversation} openDetails={openDetails} />
</SafeAreaView>
)}
{ !channel && (
<Welcome />
)}
</View>
</View>
)
}
function CardDrawerScreen({ navParams }) {
const drawerParams = { drawerPosition: 'right', headerShown: false, swipeEnabled: false, drawerType: 'front' };
const [dmChannel, setDmChannel] = useState(null);
const openContact = (contact) => {
navParams.setContact(contact);
navParams.contactNav.openDrawer();
};
const openRegistry = () => {
navParams.registryNav.openDrawer();
};
return (
<CardDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '50%' } }} drawerContent={(props) => (
<SafeAreaView edges={['top', 'bottom', 'right']} style={styles.drawer}>
<Cards openContact={openContact} openRegistry={openRegistry} addChannel={navParams.addChannel} />
</SafeAreaView>
)}>
<CardDrawer.Screen name="home">
{(props) => <HomeScreen navParams={{...navParams, cardNav: props.navigation}} />}
</CardDrawer.Screen>
</CardDrawer.Navigator>
);
};
function RegistryDrawerScreen({ navParams }) {
const drawerParams = { drawerPosition: 'right', headerShown: false, swipeEnabled: false, drawerType: 'front' };
const openContact = (contact) => {
navParams.setContact(contact);
navParams.contactNav.openDrawer();
};
return (
<RegistryDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '47%' } }} drawerContent={(props) => (
<SafeAreaView edges={['top', 'bottom', 'right']} style={styles.drawer}>
<Registry openContact={openContact} />
</SafeAreaView>
)}>
<RegistryDrawer.Screen name="card">
{(props) => <CardDrawerScreen navParams={{...navParams, registryNav: props.navigation}} />}
</RegistryDrawer.Screen>
</RegistryDrawer.Navigator>
);
};
function ContactDrawerScreen({ navParams }) {
const drawerParams = { drawerPosition: 'right', headerShown: false, swipeEnabled: false, drawerType: 'front' };
const [contact, setContact] = useState(null);
return (
<ContactDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '44%' } }} drawerContent={(props) => (
<ScrollView style={styles.drawer}>
<SafeAreaView edges={['top', 'bottom', 'right']}>
<Contact contact={contact} />
</SafeAreaView>
</ScrollView>
)}>
<ContactDrawer.Screen name="registry">
{(props) => <RegistryDrawerScreen navParams={{...navParams, setContact, contactNav: props.navigation }} />}
</ContactDrawer.Screen>
</ContactDrawer.Navigator>
);
}
function DetailDrawerScreen({ navParams }) {
const drawerParams = { drawerPosition: 'right', headerShown: false, swipeEnabled: false, drawerType: 'front' };
const [closeCount, setCloseCount] = useState(0);
const clearConversation = (navigation) => {
setCloseCount(closeCount+1);
};
return (
<DetailDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '45%' } }} drawerContent={(props) => (
<SafeAreaView style={styles.drawer} edges={['top', 'bottom', 'right']}>
<Details clearConversation={() => clearConversation(props.navigation)} />
</SafeAreaView>
)}>
<DetailDrawer.Screen name="contact">
{(props) => <ContactDrawerScreen navParams={{...navParams, closeCount: closeCount, detailNav: props.navigation}} />}
</DetailDrawer.Screen>
</DetailDrawer.Navigator>
);
}
export function Session() {
const [ringing, setRinging] = useState([]);
const { state, actions } = useSession();
const drawerParams = { drawerPosition: 'right', headerShown: false, swipeEnabled: false, drawerType: 'front' };
const HomeScreen = ({ navParams }) => {
const conversation = useContext(ConversationContext);
const [channel, setChannel] = useState(false);
const [cardId, setCardId] = useState();
const [channelId, setChannelId] = useState();
const setConversation = (card, channel) => {
(async () => {
conversation.actions.setConversation(card, channel);
setCardId(card);
setChannelId(channel);
setChannel(true);
})();
};
const closeConversation = () => {
conversation.actions.clearConversation();
setCardId(null);
setChannelId(null);
setChannel(false);
};
const openDetails = () => {
navParams.detailNav.openDrawer();
};
const openProfile = () => {
navParams.profileNav.openDrawer();
}
const openCards = () => {
navParams.cardNav.openDrawer();
}
useEffect(() => {
navParams.detailNav.closeDrawer();
setChannelId(null);
setCardId(null);
setChannel(false);
}, [navParams.closeCount]);
return (
<View style={styles.home}>
<SafeAreaView edges={['top', 'bottom', 'left']} style={styles.sidebar}>
<View edges={['left']} style={styles.options}>
<TouchableOpacity style={styles.option} onPress={openProfile}>
<ProfileIcon color={Colors.text} size={20} />
<Text style={styles.profileLabel}>Profile</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.option} onPress={openCards}>
<CardsIcon color={Colors.text} size={20} />
<Text style={styles.profileLabel}>Contacts</Text>
</TouchableOpacity>
</View>
<View style={styles.channels}>
<Channels cardId={cardId} channelId={channelId} openConversation={setConversation} />
</View>
</SafeAreaView>
<View style={styles.conversation}>
{ channel && (
<SafeAreaView edges={['top', 'bottom', 'right']}>
<Conversation closeConversation={closeConversation} openDetails={openDetails} />
</SafeAreaView>
)}
{ !channel && (
<Welcome />
)}
</View>
</View>
)
}
const CardDrawerScreen = ({ navParams }) => {
const openContact = (contact) => {
navParams.setContact(contact);
navParams.contactNav.openDrawer();
};
const openRegistry = () => {
navParams.registryNav.openDrawer();
};
return (
<CardDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '50%' } }} drawerContent={(props) => (
<SafeAreaView edges={['top', 'bottom', 'right']} style={styles.drawer}>
<Cards openContact={openContact} openRegistry={openRegistry} />
</SafeAreaView>
)}>
<CardDrawer.Screen name="home">
{(props) => <HomeScreen navParams={{...navParams, cardNav: props.navigation}} />}
</CardDrawer.Screen>
</CardDrawer.Navigator>
);
const [ dmChannel, setDmChannel ] = useState(null);
const addChannel = async (cardId) => {
const id = await actions.setDmChannel(cardId);
setDmChannel({ id });
};
const RegistryDrawerScreen = ({ navParams }) => {
const openContact = (contact) => {
navParams.setContact(contact);
navParams.contactNav.openDrawer();
};
return (
<RegistryDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '47%' } }} drawerContent={(props) => (
<SafeAreaView edges={['top', 'bottom', 'right']} style={styles.drawer}>
<Registry openContact={openContact} />
</SafeAreaView>
)}>
<RegistryDrawer.Screen name="card">
{(props) => <CardDrawerScreen navParams={{...navParams, registryNav: props.navigation}} />}
</RegistryDrawer.Screen>
</RegistryDrawer.Navigator>
);
};
const ContactDrawerScreen = ({ navParams }) => {
const [contact, setContact] = useState(null);
return (
<ContactDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '44%' } }} drawerContent={(props) => (
<ScrollView style={styles.drawer}>
<SafeAreaView edges={['top', 'bottom', 'right']}>
<Contact contact={contact} />
</SafeAreaView>
</ScrollView>
)}>
<ContactDrawer.Screen name="registry">
{(props) => <RegistryDrawerScreen navParams={{...navParams, setContact, contactNav: props.navigation }} />}
</ContactDrawer.Screen>
</ContactDrawer.Navigator>
);
}
const DetailDrawerScreen = ({ navParams }) => {
const [closeCount, setCloseCount] = useState(0);
const clearConversation = (navigation) => {
setCloseCount(closeCount+1);
};
return (
<DetailDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '45%' } }} drawerContent={(props) => (
<SafeAreaView style={styles.drawer} edges={['top', 'bottom', 'right']}>
<Details clearConversation={() => clearConversation(props.navigation)} />
</SafeAreaView>
)}>
<DetailDrawer.Screen name="contact">
{(props) => <ContactDrawerScreen navParams={{...navParams, closeCount: closeCount, detailNav: props.navigation}} />}
</DetailDrawer.Screen>
</DetailDrawer.Navigator>
);
}
useEffect(() => {
let incoming = [];
for (let i = 0; i < state.ringing.length; i++) {
@ -375,7 +387,7 @@ export function Session() {
<ScrollView style={styles.drawer}><SafeAreaView edges={['top', 'bottom', 'right']}><Profile /></SafeAreaView></ScrollView>
)}>
<ProfileDrawer.Screen name="detail">
{(props) => <DetailDrawerScreen navParams={{ profileNav: props.navigation }} />}
{(props) => <DetailDrawerScreen navParams={{ profileNav: props.navigation, state, actions, addChannel, dmChannel }} />}
</ProfileDrawer.Screen>
</ProfileDrawer.Navigator>
)}
@ -399,9 +411,9 @@ export function Session() {
tabBarActiveTintColor: Colors.white,
tabBarInactiveTintColor: Colors.disabled,
})}>
<Tab.Screen name="Conversation" component={ConversationStackScreen} />
<Tab.Screen name="Conversation" children={()=><ConversationStackScreen dmChannel={dmChannel} />} />
<Tab.Screen name="Profile" component={ProfileStackScreen} />
<Tab.Screen name="Contacts" component={ContactStackScreen} />
<Tab.Screen name="Contacts" children={()=><ContactStackScreen addChannel={addChannel} />} />
</Tab.Navigator>
)}
<StatusBar barStyle="dark-content" backgroundColor={Colors.formBackground} />

View File

@ -1,5 +1,5 @@
import { useState } from 'react';
import { FlatList, ScrollView, View, TextInput, TouchableOpacity, Text } from 'react-native';
import { Alert, FlatList, ScrollView, View, TextInput, TouchableOpacity, Text } from 'react-native';
import { styles } from './Cards.styled';
import { useCards } from './useCards.hook';
import { SafeAreaView } from 'react-native-safe-area-context';
@ -38,9 +38,22 @@ export function CardsHeader({ filter, setFilter, sort, setSort, openRegistry })
);
}
export function CardsBody({ filter, sort, openContact }) {
export function CardsBody({ filter, sort, openContact, addChannel }) {
const { state, actions } = useCards(filter, sort);
const call = async (contact) => {
try {
actions.call(contact);
}
catch (err) {
console.log(err);
Alert.alert(
'Failed to Call Contact',
'Please try again.'
)
}
}
return (
<>
{ state.cards.length == 0 && (
@ -52,7 +65,8 @@ export function CardsBody({ filter, sort, openContact }) {
<FlatList style={styles.cards}
data={state.cards}
initialNumToRender={25}
renderItem={({ item }) => <CardItem item={item} openContact={openContact} />}
renderItem={({ item }) => <CardItem item={item} openContact={openContact}
call={() => call(item)} message={() => addChannel(item.cardId)} />}
keyExtractor={item => item.cardId}
/>
)}
@ -60,14 +74,14 @@ export function CardsBody({ filter, sort, openContact }) {
);
}
export function Cards({ openRegistry, openContact }) {
export function Cards({ openRegistry, openContact, addChannel }) {
const [filter, setFilter] = useState();
const [sort, setSort] = useState(false);
return (
<View>
<CardsHeader filter={filter} setFilter={setFilter} sort={sort} setSort={setSort} openRegistry={openRegistry} />
<CardsBody filter={filter} sort={sort} openContact={openContact} />
<CardsBody filter={filter} sort={sort} openContact={openContact} addChannel={addChannel} />
</View>
);
}

View File

@ -1,8 +1,10 @@
import { Text, TouchableOpacity, View } from 'react-native';
import { Logo } from 'utils/Logo';
import { styles } from './CardItem.styled';
import MatIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import Colors from 'constants/Colors';
export function CardItem({ item, openContact }) {
export function CardItem({ item, openContact, call, message }) {
const select = () => {
const { guid, name, handle, node, location, description, imageSet } = item;
@ -19,6 +21,16 @@ export function CardItem({ item, openContact }) {
<Text style={styles.name} numberOfLines={1} ellipsizeMode={'tail'}>{ item.name }</Text>
<Text style={styles.handle} numberOfLines={1} ellipsizeMode={'tail'}>{ item.handle }</Text>
</View>
{ item.status === 'connected' && (
<View style={styles.options}>
<TouchableOpacity style={styles.option} onPress={message}>
<MatIcons name={'message-outline'} size={20} color={Colors.primary} />
</TouchableOpacity>
<TouchableOpacity style={styles.option} onPress={call}>
<MatIcons name={'phone-outline'} size={20} color={Colors.primary} />
</TouchableOpacity>
</View>
)}
{ item.status === 'connected' && item.offsync === 1 && (
<View style={styles.offsync} />
)}

View File

@ -66,5 +66,12 @@ export const styles = StyleSheet.create({
borderRadius: 4,
backgroundColor: Colors.confirmed,
},
options: {
display: 'flex',
flexDirection: 'row',
},
option: {
marginRight: 24,
},
})

View File

@ -1,5 +1,6 @@
import { useState, useEffect, useRef, useContext } from 'react';
import { CardContext } from 'context/CardContext';
import { RingContext } from 'context/RingContext';
export function useCards(filter, sort) {
@ -8,6 +9,7 @@ export function useCards(filter, sort) {
});
const card = useContext(CardContext);
const ring = useContext(RingContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
@ -26,6 +28,7 @@ export function useCards(filter, sort) {
location: location,
description: description,
status: detail.status,
token: detail.token,
offsync: item.offsync,
blocked: item.blocked,
offsync: item.offsync,
@ -85,6 +88,10 @@ export function useCards(filter, sort) {
}, [card, filter, sort]);
const actions = {
call: async (card) => {
const { cardId, guid, node, token } = card || {};
await ring.actions.call(cardId, node, `${guid}.${token}`);
},
};
return { state, actions };

View File

@ -7,7 +7,7 @@ import { Colors } from 'constants/Colors';
import { ChannelItem } from './channelItem/ChannelItem';
import { AddMember } from './addMember/AddMember';
export function Channels({ cardId, channelId, navigation, openConversation }) {
export function Channels({ cardId, channelId, navigation, openConversation, dmChannel }) {
const { state, actions } = useChannels();
@ -26,6 +26,12 @@ export function Channels({ cardId, channelId, navigation, openConversation }) {
}
};
useEffect(() => {
if (dmChannel?.id) {
openConversation(null, dmChannel.id);
}
}, [dmChannel]);
useEffect(() => {
if (navigation) {
navigation.setOptions({

View File

@ -198,10 +198,6 @@ export function ContactBody({ contact }) {
<TouchableOpacity style={styles.button} onPress={reportContact}>
<Text style={styles.buttonText}>Report Contact</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={actions.ring}>
<Text style={styles.buttonText}>Call Contact</Text>
</TouchableOpacity>
</>
)}
{ state.status === 'connecting' && (

View File

@ -1,6 +1,5 @@
import { useState, useEffect, useRef, useContext } from 'react';
import { CardContext } from 'context/CardContext';
import { RingContext } from 'context/RingContext';
import { getListingMessage } from 'api/getListingMessage';
import { getListingImageUrl } from 'api/getListingImageUrl';
import { addFlag } from 'api/addFlag';
@ -23,7 +22,6 @@ export function useContact(contact) {
});
const card = useContext(CardContext);
const ring = useContext(RingContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
@ -153,13 +151,6 @@ export function useContact(contact) {
resync: () => {
card.actions.resync(contact.card);
},
ring: async () => {
console.log("calling!!");
const contact = card.state.cards.get(state.cardId);
const { node, guid } = contact.card?.profile || {}
const { token } = contact.card?.detail || {}
await ring.actions.call(state.cardId, node, `${guid}.${token}`);
},
};
return { state, actions };

View File

@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom';
import config from 'constants/Config';
import { StoreContext } from 'context/StoreContext';
import { CardContext } from 'context/CardContext';
import { ChannelContext } from 'context/ChannelContext';
import { RingContext } from 'context/RingContext';
export function useSession() {
@ -27,6 +28,7 @@ export function useSession() {
});
const ring = useContext(RingContext);
const channel = useContext(ChannelContext);
const card = useContext(CardContext);
const store = useContext(StoreContext);
const dimensions = useWindowDimensions();
@ -122,6 +124,22 @@ export function useSession() {
disableAudio: async () => {
await ring.actions.disableAudio();
},
setDmChannel: async (cardId) => {
let channelId;
channel.state.channels.forEach((entry, id) => {
const cards = entry?.detail?.contacts?.cards || [];
const subject = entry?.detail?.data || '';
const type = entry?.detail?.dataType || '';
if (cards.length == 1 && cards[0] === cardId && type === 'superbasic' && subject === '{"subject":null}') {
channelId = entry.channelId;
}
});
if (channelId != null) {
return channelId;
}
const conversation = await channel.actions.addChannel('superbasic', { subject: null }, [ cardId ]);
return conversation.id;
},
};
return { state, actions };