using navigation headers

This commit is contained in:
Roland Osborne 2022-09-27 12:21:40 -07:00
parent ec3f6ac303
commit a112ff5bc9
11 changed files with 217 additions and 42 deletions

View File

@ -1,6 +1,7 @@
export const Colors = {
background: '#8fbea7',
primary: '#448866',
titleBackground: '#f6f6f6',
formBackground: '#f2f2f2',
formFocus: '#f8f8f8',
formHover: '#efefef',

View File

@ -9,11 +9,13 @@ import Ionicons from '@expo/vector-icons/AntDesign';
import { useSession } from './useSession.hook';
import { styles } from './Session.styled';
import Colors from 'constants/Colors';
import { Profile } from './profile/Profile';
import { ProfileTitle, Profile } from './profile/Profile';
import { Channels } from './channels/Channels';
import { Cards } from './cards/Cards';
import { Registry } from './registry/Registry';
import { Contact } from './contact/Contact';
import { CardsTitle, CardsBody, Cards } from './cards/Cards';
import { useCards } from './cards/useCards.hook';
import { RegistryTitle, RegistryBody, Registry } from './registry/Registry';
import { useRegistry } from './registry/useRegistry.hook';
import { Contact, ContactTitle } from './contact/Contact';
import { Details } from './details/Details';
import { Conversation } from './conversation/Conversation';
import { Welcome } from './welcome/Welcome';
@ -92,8 +94,8 @@ export function Session() {
}
const ProfileStackScreen = () => {
return (
<ProfileStack.Navigator screenOptions={({ route }) => ({ headerShown: false })}>
<ProfileStack.Screen name="profile" component={Profile} />
<ProfileStack.Navigator screenOptions={({ route }) => ({ headerShown: true })}>
<ProfileStack.Screen name="profile" component={Profile} options={{ headerStyle: { backgroundColor: Colors.titleBackground }, headerTitle: (props) => <ProfileTitle {...props} /> }} />
</ProfileStack.Navigator>
);
}
@ -113,16 +115,31 @@ export function Session() {
navigation.goBack();
}
const registry = useRegistry();
const cards = useCards();
return (
<ContactStack.Navigator screenOptions={({ route }) => ({ headerShown: false })}>
<ContactStack.Screen name="cards">
{(props) => <Cards openRegistry={() => setRegistryStack(props.navigation)} openContact={(contact) => setCardStack(props.navigation, contact)} />}
<ContactStack.Navigator screenOptions={({ route }) => ({ headerShow: true })}>
<ContactStack.Screen name="cards" options={{
headerStyle: { backgroundColor: Colors.titleBackground },
headerBackTitleVisible: false,
headerTitle: (props) => { console.log(props); return <CardsTitle state={cards.state} actions={cards.actions} openRegistry={setRegistryStack} /> }
}}>
{(props) => <CardsBody state={cards.state} actions={cards.actions} openContact={(contact) => setCardStack(props.navigation, contact)} />}
</ContactStack.Screen>
<ContactStack.Screen name="contact">
<ContactStack.Screen name="contact" options={{
headerStyle: { backgroundColor: Colors.titleBackground },
headerBackTitleVisible: false,
headerTitle: (props) => <ContactTitle contact={selected} {...props} />
}}>
{(props) => <Contact contact={selected} closeContact={() => clearCardStack(props.navigation)} />}
</ContactStack.Screen>
<ContactStack.Screen name="registry">
{(props) => <Registry closeRegistry={() => clearRegistryStack(props.navigation)} openContact={(contact) => setCardStack(props.navigation, contact)} />}
<ContactStack.Screen name="registry" options={{
headerStyle: { backgroundColor: Colors.titleBackground },
headerBackTitleVisible: false,
headerTitle: (props) => <RegistryTitle state={registry.state} actions={registry.actions} contact={selected} {...props} />
}}>
{(props) => <RegistryBody state={registry.state} actions={registry.actions} openContact={(contact) => setCardStack(props.navigation, contact)} />}
</ContactStack.Screen>
</ContactStack.Navigator>
);
@ -306,12 +323,8 @@ export function Session() {
<Tab.Screen name="Conversation">
{(props) => (<SafeAreaView style={styles.tabframe} edges={['top']}><ConversationStackScreen /></SafeAreaView>)}
</Tab.Screen>
<Tab.Screen name="Profile">
{(props) => (<SafeAreaView style={styles.tabframe} edges={['top']}><ProfileStackScreen /></SafeAreaView>)}
</Tab.Screen>
<Tab.Screen name="Contacts">
{(props) => (<SafeAreaView style={styles.tabframe} edges={['top']}><ContactStackScreen /></SafeAreaView>)}
</Tab.Screen>
<Tab.Screen name="Profile" component={ProfileStackScreen} />
<Tab.Screen name="Contacts" component={ContactStackScreen} />
</Tab.Navigator>
)}
</View>

View File

@ -6,6 +6,46 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import Ionicons from '@expo/vector-icons/AntDesign';
import { CardItem } from './cardItem/CardItem';
import Colors from 'constants/Colors';
import { useNavigation } from '@react-navigation/native';
export function CardsTitle({ state, actions, openRegistry }) {
const navigation = useNavigation();
return (
<View style={styles.title}>
{ state.sorting && (
<TouchableOpacity style={styles.sort} onPress={actions.unsort}>
<Ionicons style={styles.icon} name="menufold" size={18} color={Colors.text} />
</TouchableOpacity>
)}
{ !state.sorting && (
<TouchableOpacity style={styles.sort} onPress={actions.sort}>
<Ionicons style={styles.icon} name="menufold" size={18} color={Colors.disabled} />
</TouchableOpacity>
)}
<View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="search1" size={16} color={Colors.disabled} />
<TextInput style={styles.inputfield} value={state.filter} onChangeText={actions.setFilter}
autoCapitalize="none" placeholderTextColor={Colors.disabled} placeholder="Contacts" />
<View style={styles.space} />
</View>
<TouchableOpacity style={styles.add} onPress={() => openRegistry(navigation)}>
<Ionicons name={'adduser'} size={16} color={Colors.white} style={[styles.box, { transform: [ { rotateY: "180deg" }, ]} ]}/>
<Text style={styles.newtext}>New</Text>
</TouchableOpacity>
</View>
);
}
export function CardsBody({ state, actions, openContact }) {
return (
<FlatList style={styles.cards}
data={state.cards}
renderItem={({ item }) => <CardItem item={item} openContact={openContact} />}
keyExtractor={item => item.cardId}
/>
);
}
export function Cards({ openRegistry, openContact }) {
const { state, actions } = useCards();

View File

@ -8,11 +8,17 @@ export const styles = StyleSheet.create({
display: 'flex',
flexDirection: 'column',
},
title: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
topbar: {
borderTopWidth: 1,
borderBottomWidth: 1,
borderColor: Colors.divider,
paddingTop: 6,
paddingTop: 32,
paddingBottom: 6,
paddingLeft: 16,
paddingRight: 16,

View File

@ -7,6 +7,11 @@ import { Logo } from 'utils/Logo';
import Ionicons from '@expo/vector-icons/AntDesign';
import Colors from 'constants/Colors';
export function ContactTitle({ contact, closeContact }) {
const { state, actions } = useContact(contact, closeContact);
return (<Text style={styles.title}>{ `${state.handle}@${state.node}` }</Text>);
}
export function Contact({ contact, closeContact }) {
const { state, actions } = useContact(contact, closeContact);
@ -119,19 +124,9 @@ export function Contact({ contact, closeContact }) {
setContact(actions.connectContact);
}
return (
<ScrollView>
<SafeAreaView style={styles.container} edges={['top', 'bottom', 'right']}>
{ state.tabbed && (
<View style={styles.close}>
<TouchableOpacity onPress={closeContact}>
<Ionicons name={'close'} size={24} color={Colors.text} />
</TouchableOpacity>
</View>
)}
<View style={styles.header}>
<Text style={styles.headerText}>{ `${state.handle}@${state.node}` }</Text>
</View>
const Body = () => {
return (
<View style={styles.container}>
<Text style={styles.status}>{ `[${getStatusText(state.status)}]` }</Text>
<View style={{ width: 128 }}>
<Logo src={state.logo} width={128} height={128} radius={8} />
@ -235,7 +230,23 @@ export function Contact({ contact, closeContact }) {
</>
)}
</View>
</SafeAreaView>
</View>
);
}
return (
<ScrollView>
{ state.tabbed && (
<Body />
)}
{ !state.tabbed && (
<SafeAreaView style={styles.drawer} edges={['top', 'bottom', 'right']}>
<View style={styles.header}>
<Text style={styles.headerText}>{ `${state.handle}@${state.node}` }</Text>
</View>
<Body />
</SafeAreaView>
)}
</ScrollView>
)
}

View File

@ -11,6 +11,12 @@ export const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
},
title: {
fontSize: 18,
},
drawer: {
paddingTop: 16,
},
close: {
width: '100%',
display: 'flex',

View File

@ -10,6 +10,13 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import { BlockedTopics } from './blockedTopics/BlockedTopics';
import { BlockedContacts } from './blockedContacts/BlockedContacts';
export function ProfileTitle(props) {
const { state, actions } = useProfile();
return (
<Text style={styles.title}>{ `${state.handle}@${state.node}` }</Text>
)
}
export function Profile() {
const { state, actions } = useProfile();
@ -94,12 +101,9 @@ export function Profile() {
const enabled = (state.checked && state.available && state.editConfirm === state.editPassword && state.editPassword);
return (
<ScrollView>
<SafeAreaView style={styles.container} edges={['top', 'bottom', 'right']}>
<TouchableOpacity style={styles.header} onPress={actions.showLoginEdit}>
<Text style={styles.headerText}>{ `${state.handle}@${state.node}` }</Text>
</TouchableOpacity>
const Body = () => {
return (
<View style={styles.container}>
<View style={{ width: 128 }}>
<Logo src={state.imageSource} width={128} height={128} radius={8} />
<TouchableOpacity style={styles.camera} onPress={onCamera}>
@ -129,6 +133,9 @@ export function Profile() {
</TouchableOpacity>
<Switch style={styles.visibleSwitch} value={state.searchable} onValueChange={setVisible} trackColor={styles.switch}/>
</View>
<TouchableOpacity style={styles.link} onPress={actions.showLoginEdit}>
<Text style={styles.linkText}>Change Login</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.link} onPress={actions.showBlockedCards}>
<Text style={styles.linkText}>Manage Blocked Contacts</Text>
</TouchableOpacity>
@ -139,7 +146,25 @@ export function Profile() {
<Ionicons name="logout" size={14} color={Colors.white} />
<Text style={styles.logoutText}>Logout</Text>
</TouchableOpacity>
</SafeAreaView>
</View>
);
};
return (
<ScrollView>
{ state.tabbed && (
<View style={styles.body}>
<Body />
</View>
)}
{ !state.tabbed && (
<SafeAreaView style={styles.drawer} edges={['top', 'bottom', 'right']}>
<View style={styles.header}>
<Text style={styles.headerText}>{ `${state.handle}@${state.node}` }</Text>
</View>
<Body />
</SafeAreaView>
)}
<Modal
animationType="fade"
transparent={true}

View File

@ -11,9 +11,16 @@ export const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
},
drawer: {
paddingTop: 16,
},
title: {
fontSize: 18,
},
body: {
paddingTop: 16,
},
header: {
paddingBottom: 32,
paddingTop: 24,
display: 'flex',
flexDirection: 'row',
alignItems: 'flex-end',

View File

@ -1,8 +1,10 @@
import { useState, useEffect, useRef, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { useWindowDimensions } from 'react-native';
import { ProfileContext } from 'context/ProfileContext';
import { AccountContext } from 'context/AccountContext';
import { AppContext } from 'context/AppContext';
import config from 'constants/Config';
export function useProfile() {
@ -28,9 +30,11 @@ export function useProfile() {
showConfirm: false,
blockedChannels: false,
blockedCards: false,
tabbed: null,
});
const app = useContext(AppContext);
const dimensions = useWindowDimensions();
const account = useContext(AccountContext);
const profile = useContext(ProfileContext);
const navigate = useNavigate();
@ -40,6 +44,15 @@ export function useProfile() {
setState((s) => ({ ...s, ...value }));
}
useEffect(() => {
if (dimensions.width > config.tabbedWidth) {
updateState({ tabbed: false });
}
else {
updateState({ tabbed: true });
}
}, [dimensions]);
useEffect(() => {
const { name, handle, node, location, description, image } = profile.state.profile;
const imageSource = image ? profile.state.imageUrl : 'avatar';

View File

@ -7,6 +7,53 @@ import Ionicons from '@expo/vector-icons/AntDesign';
import { RegistryItem } from './registryItem/RegistryItem';
import Colors from 'constants/Colors';
export function RegistryTitle({ state, actions }) {
const search = async () => {
try {
await actions.search();
}
catch (err) {
console.log(err);
Alert.alert(
'Server Listing Failed',
'Please try again.'
);
}
}
return (
<View style={styles.title}>
<View style={styles.inputwrapper}>
<TextInput style={styles.inputfield} value={state.server} onChangeText={actions.setServer}
autoCapitalize="none" placeholderTextColor={Colors.disabled} placeholder="Server" />
<View style={styles.space} />
</View>
{ state.busy && (
<View style={styles.search}>
<ActivityIndicator />
</View>
)}
{ !state.busy && (
<TouchableOpacity style={styles.search} onPress={search}>
<Ionicons name={'search1'} size={16} color={Colors.white} />
</TouchableOpacity>
)}
</View>
);
}
export function RegistryBody({ state, actions, openContact }) {
return (
<FlatList style={styles.accounts}
data={state.accounts}
renderItem={({ item }) => <RegistryItem item={item} openContact={openContact} />}
keyExtractor={item => item.guid}
/>
);
}
export function Registry({ closeRegistry, openContact }) {
const search = async () => {

View File

@ -8,6 +8,12 @@ export const styles = StyleSheet.create({
display: 'flex',
flexDirection: 'column',
},
title: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
topbar: {
borderTopWidth: 1,
borderBottomWidth: 1,