mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
rendering registry
This commit is contained in:
parent
6d316d563e
commit
822d333401
@ -84,15 +84,24 @@ export function Session() {
|
||||
const clearCardStack = (navigation) => {
|
||||
navigation.goBack();
|
||||
}
|
||||
const setRegistryStack = (navigation) => {
|
||||
navigation.navigate('registry');
|
||||
}
|
||||
const clearRegistryStack = (navigation) => {
|
||||
navigation.goBack();
|
||||
}
|
||||
|
||||
return (
|
||||
<ContactStack.Navigator screenOptions={({ route }) => ({ headerShown: false })}>
|
||||
<ContactStack.Screen name="cards">
|
||||
{(props) => <Cards openContact={(cardId) => setCardStack(props.navigation, cardId)} />}
|
||||
{(props) => <Cards openRegistry={() => setRegistryStack(props.navigation)} openContact={(cardId) => setCardStack(props.navigation, cardId)} />}
|
||||
</ContactStack.Screen>
|
||||
<ContactStack.Screen name="card">
|
||||
{(props) => <Contact cardId={cardId} closeContact={() => clearCardStack(props.navigation)} />}
|
||||
</ContactStack.Screen>
|
||||
<ContactStack.Screen name="registry">
|
||||
{(props) => <Registry closeRegistry={() => clearRegistryStack(props.navigation)} />}
|
||||
</ContactStack.Screen>
|
||||
</ContactStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
@ -12,28 +12,35 @@ export function Cards({ openRegistry }) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{ state.tabbed && (
|
||||
<View style={styles.topbar}>
|
||||
{ state.sorting && (
|
||||
<TouchableOpacity style={styles.sort} onPress={actions.unsort}>
|
||||
<Ionicons style={styles.icon} name="menufold" size={18} color={Colors.text} />
|
||||
<>
|
||||
<View style={styles.topbar}>
|
||||
{ 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}>
|
||||
<Ionicons name={'adduser'} size={16} color={Colors.white} style={[styles.box, { transform: [ { rotateY: "180deg" }, ]} ]}/>
|
||||
<Text style={styles.newtext}>New</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}>
|
||||
<Ionicons name={'adduser'} size={16} color={Colors.white} style={[styles.box, { transform: [ { rotateY: "180deg" }, ]} ]}/>
|
||||
<Text style={styles.newtext}>New</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<FlatList style={styles.cards}
|
||||
data={state.cards}
|
||||
renderItem={({ item }) => <CardItem item={item} />}
|
||||
keyExtractor={item => item.cardId}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{ !state.tabbed && (
|
||||
<SafeAreaView edges={['right']} style={styles.searcharea}>
|
||||
@ -50,20 +57,20 @@ export function Cards({ openRegistry }) {
|
||||
)}
|
||||
<View style={styles.inputwrapper}>
|
||||
<Ionicons style={styles.icon} name="search1" size={16} color={Colors.disabled} />
|
||||
<TextInput style={styles.inputfield} value={state.topic} onChangeText={actions.setTopic}
|
||||
<TextInput style={styles.inputfield} value={state.filter} onChangeText={actions.setFilter}
|
||||
autoCapitalize="none" placeholderTextColor={Colors.disabled} placeholder="Contacts" />
|
||||
</View>
|
||||
<TouchableOpacity style={styles.add} onPress={openRegistry}>
|
||||
<Ionicons name={'adduser'} size={16} color={Colors.white} style={[styles.box, { transform: [ { rotateY: "180deg" }, ]} ]}/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<FlatList style={styles.cards}
|
||||
data={state.cards}
|
||||
renderItem={({ item }) => <CardItem item={item} />}
|
||||
keyExtractor={item => item.cardId}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
)}
|
||||
<FlatList style={styles.cards}
|
||||
data={state.cards}
|
||||
renderItem={({ item }) => <CardItem item={item} />}
|
||||
keyExtractor={item => item.cardId}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -8,28 +8,35 @@ export function CardItem({ item }) {
|
||||
const { state, actions } = useCardItem(item);
|
||||
|
||||
return (
|
||||
<TouchableOpacity style={styles.container} activeOpacity={1}>
|
||||
<Logo src={item.logo} width={32} height={32} radius={6} />
|
||||
<View style={styles.detail}>
|
||||
<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.confirmed} />
|
||||
<View>
|
||||
{ item.cardId && (
|
||||
<TouchableOpacity style={styles.container} activeOpacity={1}>
|
||||
<Logo src={item.logo} width={32} height={32} radius={6} />
|
||||
<View style={styles.detail}>
|
||||
<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.confirmed} />
|
||||
)}
|
||||
{ item.status === 'requested' && (
|
||||
<View style={styles.requested} />
|
||||
)}
|
||||
{ item.status === 'connecting' && (
|
||||
<View style={styles.connecting} />
|
||||
)}
|
||||
{ item.status === 'pending' && (
|
||||
<View style={styles.pending} />
|
||||
)}
|
||||
{ item.status === 'confirmed' && (
|
||||
<View style={styles.confirmed} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{ item.status === 'requested' && (
|
||||
<View style={styles.requested} />
|
||||
{ !item.cardId && (
|
||||
<View style={styles.space} />
|
||||
)}
|
||||
{ item.status === 'connecting' && (
|
||||
<View style={styles.connecting} />
|
||||
)}
|
||||
{ item.status === 'pending' && (
|
||||
<View style={styles.pending} />
|
||||
)}
|
||||
{ item.status === 'confirmed' && (
|
||||
<View style={styles.confirmed} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,9 @@ export const styles = StyleSheet.create({
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
},
|
||||
space: {
|
||||
height: 64,
|
||||
},
|
||||
name: {
|
||||
color: Colors.text,
|
||||
fontSize: 14,
|
||||
|
@ -84,6 +84,7 @@ export function useCards() {
|
||||
return -1;
|
||||
});
|
||||
}
|
||||
filtered.push({cardId:''});
|
||||
updateState({ cards: filtered });
|
||||
}, [card, state.filter, state.sorting]);
|
||||
|
||||
|
@ -1,4 +1,73 @@
|
||||
export function Registry() {
|
||||
return <></>
|
||||
import { useContext } from 'react';
|
||||
import { ActivityIndicator, FlatList, ScrollView, View, TextInput, TouchableOpacity, Text } from 'react-native';
|
||||
import { styles } from './Registry.styled';
|
||||
import { useRegistry } from './useRegistry.hook';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import Ionicons from '@expo/vector-icons/AntDesign';
|
||||
import { RegistryItem } from './registryItem/RegistryItem';
|
||||
import Colors from 'constants/Colors';
|
||||
|
||||
export function Registry({ closeRegistry, openContact }) {
|
||||
const { state, actions } = useRegistry();
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{ state.tabbed && (
|
||||
<>
|
||||
<View style={styles.topbar}>
|
||||
{ state.busy && (
|
||||
<View style={styles.search}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
)}
|
||||
{ !state.busy && (
|
||||
<TouchableOpacity style={styles.search} onPress={actions.search}>
|
||||
<Ionicons name={'search1'} size={16} color={Colors.white} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<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>
|
||||
<TouchableOpacity style={styles.close} onPress={closeRegistry}>
|
||||
<Ionicons name={'close'} size={24} color={Colors.text} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<FlatList style={styles.accounts}
|
||||
data={state.accounts}
|
||||
renderItem={({ item }) => <RegistryItem item={item} />}
|
||||
keyExtractor={item => item.guid}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{ !state.tabbed && (
|
||||
<SafeAreaView edges={['right']}>
|
||||
<View style={styles.searcharea}>
|
||||
<View style={styles.searchbar}>
|
||||
{ state.busy && (
|
||||
<View style={styles.search}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
)}
|
||||
{ !state.busy && (
|
||||
<TouchableOpacity style={styles.search} onPress={actions.search}>
|
||||
<Ionicons name={'search1'} size={16} color={Colors.white} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<View style={styles.inputwrapper}>
|
||||
<TextInput style={styles.inputfield} value={state.server} onChangeText={actions.setServer}
|
||||
autoCapitalize="none" placeholderTextColor={Colors.disabled} placeholder="Server" />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<FlatList style={styles.accounts}
|
||||
data={state.accounts}
|
||||
renderItem={({ item }) => <RegistryItem item={item} />}
|
||||
keyExtractor={item => item.guid}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
|
106
app/mobile/src/session/registry/Registry.styled.js
Normal file
106
app/mobile/src/session/registry/Registry.styled.js
Normal file
@ -0,0 +1,106 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { Colors } from 'constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
container: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
topbar: {
|
||||
borderTopWidth: 1,
|
||||
borderBottomWidth: 1,
|
||||
borderColor: Colors.divider,
|
||||
paddingTop: 6,
|
||||
paddingBottom: 6,
|
||||
paddingLeft: 16,
|
||||
paddingRight: 16,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
searcharea: {
|
||||
borderBottomWidth: 1,
|
||||
borderColor: Colors.divider,
|
||||
},
|
||||
searchbar: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
paddingTop: 16,
|
||||
paddingLeft: 8,
|
||||
paddingBottom: 8,
|
||||
alignItems: 'center',
|
||||
},
|
||||
inputwrapper: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
borderRadius: 4,
|
||||
backgroundColor: Colors.white,
|
||||
alignItems: 'center',
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
marginRight: 8,
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
marginLeft: 8,
|
||||
},
|
||||
inputfield: {
|
||||
flex: 1,
|
||||
textAlign: 'center',
|
||||
padding: 4,
|
||||
color: Colors.text,
|
||||
fontSize: 14,
|
||||
},
|
||||
icon: {
|
||||
paddingLeft: 8,
|
||||
},
|
||||
accounts: {
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
width: '100%',
|
||||
paddingLeft: 16,
|
||||
paddingRight: 16,
|
||||
minHeight: 0,
|
||||
},
|
||||
addbottom: {
|
||||
marginRight: 8,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 8,
|
||||
borderRadius: 4,
|
||||
},
|
||||
bottomText: {
|
||||
color: Colors.primary,
|
||||
paddingLeft: 8,
|
||||
},
|
||||
search: {
|
||||
backgroundColor: Colors.primary,
|
||||
marginLeft: 8,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 8,
|
||||
borderRadius: 4,
|
||||
},
|
||||
close: {
|
||||
marginRight: 8,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 8,
|
||||
borderRadius: 4,
|
||||
},
|
||||
newtext: {
|
||||
paddingLeft: 8,
|
||||
color: Colors.white,
|
||||
},
|
||||
findarea: {
|
||||
borderTopWidth: 1,
|
||||
borderColor: Colors.divider,
|
||||
}
|
||||
})
|
||||
|
@ -0,0 +1,27 @@
|
||||
import { Text, TouchableOpacity, View } from 'react-native';
|
||||
import { Logo } from 'utils/Logo';
|
||||
import { styles } from './RegistryItem.styled';
|
||||
import { useRegistryItem } from './useRegistryItem.hook';
|
||||
|
||||
export function RegistryItem({ item }) {
|
||||
|
||||
const { state, actions } = useRegistryItem(item);
|
||||
|
||||
return (
|
||||
<View>
|
||||
{ item.guid && (
|
||||
<TouchableOpacity style={styles.container} activeOpacity={1}>
|
||||
<Logo src={item.logo} width={32} height={32} radius={6} />
|
||||
<View style={styles.detail}>
|
||||
<Text style={styles.name} numberOfLines={1} ellipsizeMode={'tail'}>{ item.name }</Text>
|
||||
<Text style={styles.handle} numberOfLines={1} ellipsizeMode={'tail'}>{ item.handle }</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{ !item.guid && (
|
||||
<View style={styles.space} />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { Colors } from 'constants/Colors';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
container: {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
height: 48,
|
||||
alignItems: 'center',
|
||||
borderBottomWidth: 1,
|
||||
borderColor: Colors.itemDivider,
|
||||
},
|
||||
space: {
|
||||
height: 64
|
||||
},
|
||||
detail: {
|
||||
paddingLeft: 12,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
},
|
||||
name: {
|
||||
color: Colors.text,
|
||||
fontSize: 14,
|
||||
},
|
||||
handle: {
|
||||
color: Colors.text,
|
||||
fontSize: 12,
|
||||
},
|
||||
connected: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
backgroundColor: Colors.connected,
|
||||
},
|
||||
requested: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
backgroundColor: Colors.requested,
|
||||
},
|
||||
connecting: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
backgroundColor: Colors.connecting,
|
||||
},
|
||||
pending: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
backgroundColor: Colors.pending,
|
||||
},
|
||||
confirmed: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
backgroundColor: Colors.confirmed,
|
||||
},
|
||||
})
|
||||
|
@ -0,0 +1,17 @@
|
||||
import { useState, useEffect, useRef, useContext } from 'react';
|
||||
import { useWindowDimensions } from 'react-native';
|
||||
|
||||
export function useRegistryItem(item) {
|
||||
|
||||
const [state, setState] = useState({});
|
||||
|
||||
const updateState = (value) => {
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
}
|
||||
|
||||
const actions = {
|
||||
};
|
||||
|
||||
return { state, actions };
|
||||
}
|
||||
|
82
app/mobile/src/session/registry/useRegistry.hook.js
Normal file
82
app/mobile/src/session/registry/useRegistry.hook.js
Normal file
@ -0,0 +1,82 @@
|
||||
import { useState, useEffect, useRef, useContext } from 'react';
|
||||
import { useWindowDimensions } from 'react-native';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ProfileContext } from 'context/ProfileContext';
|
||||
import { getListing } from 'api/getListing';
|
||||
import { getListingImageUrl } from 'api/getListingImageUrl';
|
||||
import config from 'constants/Config';
|
||||
|
||||
export function useRegistry() {
|
||||
|
||||
const [state, setState] = useState({
|
||||
tabbed: null,
|
||||
accounts: [],
|
||||
server: null,
|
||||
busy: false,
|
||||
});
|
||||
|
||||
const dimensions = useWindowDimensions();
|
||||
const profile = useContext(ProfileContext);
|
||||
|
||||
const updateState = (value) => {
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (dimensions.width > config.tabbedWidth) {
|
||||
updateState({ tabbed: false });
|
||||
}
|
||||
else {
|
||||
updateState({ tabbed: true });
|
||||
}
|
||||
}, [dimensions]);
|
||||
|
||||
useEffect(() => {
|
||||
const server = profile.state.profile.node;
|
||||
updateState({ server });
|
||||
getAccounts(server);
|
||||
}, [profile]);
|
||||
|
||||
const setAccountItem = (item) => {
|
||||
return {
|
||||
guid: item.guid,
|
||||
name: item.name,
|
||||
handle: `${item.handle}@${item.node}`,
|
||||
logo: item.imageSet ? getListingImageUrl(item.node, item.guid) : 'avatar',
|
||||
}
|
||||
};
|
||||
|
||||
const getAccounts = async (server, ignore) => {
|
||||
if (!state.busy) {
|
||||
try {
|
||||
updateState({ busy: true });
|
||||
const accounts = await getListing(server, true);
|
||||
const filtered = accounts.filter(item => {
|
||||
if (item.guid === profile.state.profile.guid) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
const items = filtered.map(setAccountItem);
|
||||
items.push({guid:''});
|
||||
updateState({ busy: false, accounts: items });
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
updateState({ busy: false, accounts: [] });
|
||||
if (!ignore) {
|
||||
throw new Error('failed list accounts');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const actions = {
|
||||
setServer: (filter) => {
|
||||
updateState({ filter });
|
||||
},
|
||||
};
|
||||
|
||||
return { state, actions };
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user