rebuilding contact screen

This commit is contained in:
balzack 2023-09-17 23:09:10 -07:00
parent 2711b579ba
commit d5b779a04e
7 changed files with 229 additions and 122 deletions

View File

@ -56,7 +56,7 @@ const LightColors = {
const DarkColors = { const DarkColors = {
overlay: 'dark', overlay: 'dark',
statusBar: 'light-content', statusBar: 'light-content',
tabBar: '#111111', tabBar: '#118811',
activeTabIcon: '#dddddd', activeTabIcon: '#dddddd',
idleTabIcon: '#aaaaaa', idleTabIcon: '#aaaaaa',
activeBorder: '#aaaaaa', activeBorder: '#aaaaaa',
@ -103,7 +103,7 @@ const DarkColors = {
disabledIndicator: '#eeeeee', disabledIndicator: '#eeeeee',
disconnectedIndicator: '#aa0000', disconnectedIndicator: '#aa0000',
sliderGrip: '#eeeeee', sliderGrip: '#eeeeee',
areaBorder: '#aaaaaa', areaBorder: '#777777',
}; };
function getColor(label) { function getColor(label) {

View File

@ -84,6 +84,7 @@ const Strings = [
// contacts page // contacts page
add: 'Add', add: 'Add',
back: 'Back',
}, },
{ {
visibleRegistry: 'Visible dans le Registre', visibleRegistry: 'Visible dans le Registre',
@ -165,6 +166,7 @@ const Strings = [
//constacts page //constacts page
add: 'Ajouter', add: 'Ajouter',
back: 'Arrière',
}, },
{ {
visibleRegistry: 'Visible en el Registro', visibleRegistry: 'Visible en el Registro',
@ -246,6 +248,7 @@ const Strings = [
// contacts page // contacts page
add: 'Agregar', add: 'Agregar',
back: 'Atrás',
}, },
{ {
visibleRegistry: 'Sichtbar in der Registrierung', visibleRegistry: 'Sichtbar in der Registrierung',
@ -327,6 +330,7 @@ const Strings = [
//contacts page //contacts page
add: 'Hinzufügen', add: 'Hinzufügen',
back: 'Rückwärts',
} }
]; ];

View File

@ -14,7 +14,7 @@ import { Profile } from './profile/Profile';
import { ProfileSettings } from './profileSettings/ProfileSettings'; import { ProfileSettings } from './profileSettings/ProfileSettings';
import { CardsHeader, CardsBody, Cards } from './cards/Cards'; import { CardsHeader, CardsBody, Cards } from './cards/Cards';
import { RegistryHeader, RegistryBody, Registry } from './registry/Registry'; import { RegistryHeader, RegistryBody, Registry } from './registry/Registry';
import { ContactHeader, ContactBody, Contact } from './contact/Contact'; import { Contact } from './contact/Contact';
import { Details } from './details/Details'; import { Details } from './details/Details';
import { Conversation, ConversationHeader, ConversationBody } from './conversation/Conversation'; import { Conversation, ConversationHeader, ConversationBody } from './conversation/Conversation';
import { Welcome } from './welcome/Welcome'; import { Welcome } from './welcome/Welcome';
@ -163,10 +163,8 @@ function ContactStackScreen({ addChannel }) {
{(props) => <CardsBody filter={filter} sort={sort} openContact={(contact) => openContact(props.navigation, contact)} addChannel={addChannel} />} {(props) => <CardsBody filter={filter} sort={sort} openContact={(contact) => openContact(props.navigation, contact)} addChannel={addChannel} />}
</ContactStack.Screen> </ContactStack.Screen>
<ContactStack.Screen name="contact" options={{ ...stackParams, headerTitle: (props) => ( <ContactStack.Screen name="contact" options={{ headerShown: false }}>
<ContactHeader contact={contact} /> {(props) => <Contact contact={contact} back={props.navigation.goBack} />}
)}}>
{(props) => <ScrollView><ContactBody contact={contact} /></ScrollView>}
</ContactStack.Screen> </ContactStack.Screen>
<ContactStack.Screen name="registry" options={{ ...stackParams, headerTitle: (props) => ( <ContactStack.Screen name="registry" options={{ ...stackParams, headerTitle: (props) => (

View File

@ -67,8 +67,8 @@ export const styles = StyleSheet.create({
color: Colors.white, color: Colors.white,
}, },
tabBar: { tabBar: {
borderTopWidth: 0, borderColor: Colors.tabBar,
backgroundColor: Colors.primary, backgroundColor: Colors.screenBase,
maxHeight: 72, maxHeight: 72,
}, },
home: { home: {

View File

@ -1,9 +1,11 @@
import { Alert, View, Text, TouchableOpacity } from 'react-native'; import { Alert, View, Text, TouchableOpacity, ScrollView, Image } from 'react-native';
import { styles } from './Contact.styled'; import { styles } from './Contact.styled';
import { useContact } from './useContact.hook'; import { useContact } from './useContact.hook';
import Ionicons from 'react-native-vector-icons/AntDesign'; import Ionicons from 'react-native-vector-icons/AntDesign';
import { Logo } from 'utils/Logo'; import { Logo } from 'utils/Logo';
import { Colors } from 'constants/Colors'; import { Colors } from 'constants/Colors';
import AntIcons from 'react-native-vector-icons/AntDesign';
import MatIcons from 'react-native-vector-icons/MaterialCommunityIcons';
export function ContactHeader({ contact }) { export function ContactHeader({ contact }) {
const handle = contact?.node ? `${contact?.handle}@${contact?.node}` : contact?.handle; const handle = contact?.node ? `${contact?.handle}@${contact?.node}` : contact?.handle;
@ -298,13 +300,62 @@ export function ContactBody({ contact }) {
); );
} }
export function Contact({ contact }) { export function Contact({ contact, drawer, back }) {
const { state, actions } = useContact(contact);
const OVERLAP = 32;
return ( return (
<View> <>
<ContactHeader contact={contact} /> { drawer && (
<ContactBody contact={contact} /> <Text>CONTACT DRAWER</Text>
</View> )}
{ !drawer && (
<View style={styles.container}>
<Image style={{ ...styles.logo, width: state.imageWidth, height: state.imageHeight }}
source={state.imageSource} resizeMode={'contain'} />
<View style={styles.content}>
<View style={{ ...styles.space, width: state.imageWidth, height: state.imageHeight - OVERLAP }}>
<TouchableOpacity style={styles.back} onPress={back}>
<Text style={styles.backLabel}>{ state.strings.back }</Text>
</TouchableOpacity>
</View>
<View style={{ ...styles.details, width: state.detailWidth }}>
{ state.name && (
<Text style={styles.nameSet} numberOfLines={1} adjustsFontSizeToFit={true}>{ state.name }</Text>
)}
{ !state.name && (
<Text style={styles.nameUnset}>{ state.strings.name }</Text>
)}
<Text style={styles.username} numberOfLines={1}>{ state.username }</Text>
<View style={styles.attributes}>
<View style={styles.entry}>
<AntIcons name="enviromento" style={styles.icon} size={20} color={Colors.text} />
{ state.location && (
<Text style={styles.locationSet}>{ state.location }</Text>
)}
{ !state.location && (
<Text style={styles.locationUnset}>Location</Text>
)}
</View>
<View style={styles.divider} />
<ScrollView style={styles.description}>
<View style={styles.entry}>
<MatIcons name="book-open-outline" style={styles.descriptionIcon} size={20} color={Colors.text} />
{ state.description && (
<Text style={styles.descriptionSet}>{ state.description }</Text>
)}
{ !state.description && (
<Text style={styles.descriptionUnset}>Description</Text>
)}
</View>
</ScrollView>
</View>
</View>
</View>
</View>
)}
</>
); );
} }

View File

@ -2,133 +2,162 @@ import { StyleSheet } from 'react-native';
import { Colors } from 'constants/Colors'; import { Colors } from 'constants/Colors';
export const styles = StyleSheet.create({ export const styles = StyleSheet.create({
container: { container: {
width: '100%', width: '100%',
height: '100%', height: '100%',
},
content: {
width: '100%',
height: '100%',
position: 'absolute',
top: 0,
display: 'flex', display: 'flex',
flexDirection: 'column',
paddingBottom: 32,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center',
backgroundColor: Colors.formBackground,
}, },
wrapper: { logo: {
backgroundColor: Colors.formBackground, alignSelf: 'center',
}, },
title: { details: {
minHeight: 32,
borderTopRightRadius: 32,
borderTopLeftRadius: 32,
backgroundColor: Colors.screenBase,
borderTopWidth: 1,
borderColor: Colors.areaBorder,
borderLeftWidth: 0.2,
borderRightWidth: 0.2,
paddingLeft: 1,
paddingRight: 1,
display: 'flex',
flexShrink: 1,
flexGrow: 1,
paddingBottom: 16,
},
space: {
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'flex-start',
},
back: {
backgroundColor: Colors.screenBase,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
marginLeft: 32,
borderColor: Colors.areaBorder,
borderTopWidth: 1,
borderLeftWidth: 1,
borderRightWidth: 1,
},
backLabel: {
color: Colors.linkText,
fontSize: 14,
fontFamily: 'Roboto',
paddingLeft: 16,
paddingRight: 16,
paddingTop: 4,
paddingBottom: 2,
},
nameSet: {
display: 'flex',
color: Colors.text,
fontFamily: 'roboto',
fontSize: 48,
flexShrink: 1,
paddingTop: 8,
paddingLeft: 16,
paddingRight: 16,
},
nameUnset: {
color: Colors.inputPlaceholder,
fontFamily: 'roboto',
fontSize: 48,
fontStyle: 'italic',
paddingTop: 8,
paddingLeft: 16,
paddingRight: 16,
},
username: {
color: Colors.text,
fontFamily: 'roboto',
fontSize: 18, fontSize: 18,
paddingLeft: 16,
paddingRight: 16,
}, },
resync: { group: {
marginLeft: 16,
marginRight: 16,
backgroundColor: Colors.areaBase,
borderRadius: 8,
marginTop: 16,
display: 'flex', display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
}, },
glyph: { attributes: {
paddingTop: 2, marginLeft: 16,
marginRight: 16,
backgroundColor: Colors.areaBase,
borderRadius: 8,
marginTop: 16,
display: 'flex',
flexShrink: 1,
}, },
icon: { divider: {
width: 32, width: '100%',
paddingLeft: 8 height: 2,
backgroundColor: Colors.screenBase,
}, },
drawer: { entry: {
paddingTop: 16,
},
close: {
width: '100%', width: '100%',
display: 'flex', display: 'flex',
alignItems: 'flex-end',
paddingRight: 32,
},
header: {
display: 'flex',
flexDirection: 'row',
alignItems: 'flex-end',
justifyContent: 'center',
},
status: {
color: Colors.grey,
paddingBottom: 20,
paddingTop: 4,
},
headerText: {
fontSize: 18,
overflow: 'hidden',
textAlign: 'center',
color: Colors.text,
},
camera: {
position: 'absolute',
bottom: 0,
left: 0,
padding: 8,
backgroundColor: Colors.lightgrey,
borderBottomLeftRadius: 8,
borderTopRightRadius: 8,
},
gallery: {
position: 'absolute',
bottom: 0,
right: 0,
padding: 8,
backgroundColor: Colors.lightgrey,
borderBottomRightRadius: 8,
borderTopLeftRadius: 8,
},
detail: {
paddingTop: 32,
paddingLeft: 32,
paddingRight: 32,
display: 'flex',
flexDirection: 'column',
alignItems: 'center', alignItems: 'center',
color: Colors.text,
},
attribute: {
display: 'flex',
flexDirection: 'row', flexDirection: 'row',
paddingBottom: 8, padding: 8,
}, },
nametext: { description: {
fontSize: 18, display: 'flex',
flexShrink: 1,
},
descriptionIcon: {
alignSelf: 'flex-start',
paddingLeft: 8,
paddingRight: 16,
},
drawerDescriptionIcon: {
alignSelf: 'flex-start',
paddingLeft: 8,
paddingRight: 8, paddingRight: 8,
fontWeight: 'bold',
color: Colors.text,
}, },
locationtext: { icon: {
fontSize: 16,
paddingLeft: 8, paddingLeft: 8,
color: Colors.text, paddingRight: 16,
}, },
descriptiontext: { drawerIcon: {
fontSize: 16,
paddingLeft: 8, paddingLeft: 8,
paddingRight: 8,
},
locationSet: {
fontSize: 16,
color: Colors.text, color: Colors.text,
fontFamily: 'roboto',
flex: 1,
}, },
button: { locationUnset: {
width: 192, fontSize: 16,
padding: 6, color: Colors.inputPlaceholder,
backgroundColor: Colors.primary, fontFamily: 'roboto',
borderRadius: 4, fontStyle: 'italic',
display: 'flex', flex: 1,
alignItems: 'center',
justifyContent: 'center',
marginTop: 16,
}, },
buttonText: { descriptionSet: {
color: Colors.white, fontSize: 16,
color: Colors.text,
fontFamily: 'roboto',
flex: 1,
}, },
alert: { descriptionUnset: {
width: 192, fontSize: 16,
padding: 6, color: Colors.inputPlaceholder,
backgroundColor: Colors.alert, fontFamily: 'roboto',
borderRadius: 4, fontStyle: 'italic',
display: 'flex', flex: 1,
alignItems: 'center',
justifyContent: 'center',
marginTop: 16,
},
alertText: {
color: Colors.white,
}, },
}) })

View File

@ -1,10 +1,13 @@
import { useState, useEffect, useRef, useContext } from 'react'; import { useState, useEffect, useRef, useContext } from 'react';
import { useWindowDimensions } from 'react-native';
import { CardContext } from 'context/CardContext'; import { CardContext } from 'context/CardContext';
import { ProfileContext } from 'context/ProfileContext'; import { ProfileContext } from 'context/ProfileContext';
import { getListingMessage } from 'api/getListingMessage'; import { getListingMessage } from 'api/getListingMessage';
import { getListingImageUrl } from 'api/getListingImageUrl'; import { getListingImageUrl } from 'api/getListingImageUrl';
import { addFlag } from 'api/addFlag'; import { addFlag } from 'api/addFlag';
import { getCardByGuid } from 'context/cardUtil'; import { getCardByGuid } from 'context/cardUtil';
import { getLanguageStrings } from 'constants/Strings';
import avatar from 'images/avatar.png';
export function useContact(contact) { export function useContact(contact) {
@ -14,14 +17,22 @@ export function useContact(contact) {
node: null, node: null,
location: null, location: null,
description: null, description: null,
logo: null,
status: null, status: null,
cardId: null, cardId: null,
guid: null, guid: null,
busy: false, busy: false,
offsync: false, offsync: false,
strings: getLanguageStrings(),
imageSource: null,
imageWidth: null,
imageHeight: null,
detailWidth: null,
username: null,
}); });
const dimensions = useWindowDimensions();
const card = useContext(CardContext); const card = useContext(CardContext);
const profile = useContext(ProfileContext); const profile = useContext(ProfileContext);
@ -29,6 +40,16 @@ export function useContact(contact) {
setState((s) => ({ ...s, ...value })); setState((s) => ({ ...s, ...value }));
} }
useEffect(() => {
const { width, height } = dimensions;
if (height > width) {
updateState({ imageWidth: width, imageHeight: width, detailWidth: width + 2 });
}
else {
updateState({ imageWidth: height, imageHeight: height, detailWidth: width + 2 });
}
}, [dimensions]);
useEffect(() => { useEffect(() => {
const contactCard = getCardByGuid(card.state.cards, contact?.guid); const contactCard = getCardByGuid(card.state.cards, contact?.guid);
const { server } = profile.state; const { server } = profile.state;
@ -36,14 +57,18 @@ export function useContact(contact) {
const { offsync, profile, detail, cardId } = contactCard.card; const { offsync, profile, detail, cardId } = contactCard.card;
const { name, handle, node, location, description, guid, imageSet, revision } = profile; const { name, handle, node, location, description, guid, imageSet, revision } = profile;
const host = node ? node : server; const host = node ? node : server;
const logo = imageSet ? card.actions.getCardImageUrl(cardId) : 'avatar';
updateState({ offsync, name, handle, node: server, location, description, logo, cardId, guid, status: detail.status }); const username = `${handle}/${node}`
const imageSource = imageSet ? { uri: card.actions.getCardImageUrl(cardId) } : avatar;
updateState({ offsync, name, handle, node: server, location, description, imageSource, username, cardId, guid, status: detail.status });
} }
else { else {
const { guid, handle, node, name, location, description, imageSet } = contact || {}; const { guid, handle, node, name, location, description, imageSet } = contact || {};
const host = node ? node : server; const host = node ? node : server;
const logo = imageSet ? getListingImageUrl(server, guid) : 'avatar';
updateState({ guid, handle, node: host, name, location, description, logo, offsync: false, status: null }); const username = `${handle}/${node}`
const imageSource = imageSet ? { uri: getListingImageUrl(server, guid) } : avatar;
updateState({ guid, handle, node: host, name, location, description, imageSource, username, offsync: false, status: null });
} }
}, [contact, card.state, profile.state]); }, [contact, card.state, profile.state]);