rendering card items

This commit is contained in:
Roland Osborne 2022-09-23 11:09:51 -07:00
parent 911744daa4
commit 55debaba7c
12 changed files with 223 additions and 18 deletions

View File

@ -13,13 +13,20 @@ export function Cards() {
<View style={styles.container}> <View style={styles.container}>
{ state.tabbed && ( { state.tabbed && (
<View style={styles.topbar}> <View style={styles.topbar}>
<TouchableOpacity style={styles.sort}> { state.sorting && (
<Ionicons style={styles.icon} name="menufold" size={18} color={Colors.text} /> <TouchableOpacity style={styles.sort} onPress={actions.unsort}>
</TouchableOpacity> <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}> <View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="search1" size={16} color={Colors.text} /> <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.text} placeholder="Contacts" /> autoCapitalize="none" placeholderTextColor={Colors.disabled} placeholder="Contacts" />
<View style={styles.space} /> <View style={styles.space} />
</View> </View>
<TouchableOpacity style={styles.add}> <TouchableOpacity style={styles.add}>

View File

@ -48,7 +48,7 @@ export const styles = StyleSheet.create({
textAlign: 'center', textAlign: 'center',
padding: 4, padding: 4,
color: Colors.text, color: Colors.text,
fontSize: 16, fontSize: 14,
}, },
icon: { icon: {
paddingLeft: 8, paddingLeft: 8,

View File

@ -1,4 +1,35 @@
import { Text, TouchableOpacity, View } from 'react-native';
import { Logo } from 'utils/Logo';
import { styles } from './CardItem.styled';
import { useCardItem } from './useCardItem.hook';
export function CardItem({ item }) { export function CardItem({ item }) {
return <></>;
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} />
)}
{ 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>
);
} }

View File

@ -0,0 +1,61 @@
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,
},
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,
},
})

View File

@ -0,0 +1,17 @@
import { useState, useEffect, useRef, useContext } from 'react';
import { useWindowDimensions } from 'react-native';
export function useCardItem(item) {
const [state, setState] = useState({});
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
const actions = {
};
return { state, actions };
}

View File

@ -9,6 +9,8 @@ export function useCards() {
const [state, setState] = useState({ const [state, setState] = useState({
tabbed: null, tabbed: null,
cards: [], cards: [],
filter: null,
sorting: false,
}); });
const dimensions = useWindowDimensions(); const dimensions = useWindowDimensions();
@ -27,10 +29,74 @@ export function useCards() {
} }
}, [dimensions]); }, [dimensions]);
const setCardItem = (item) => {
const { profile, detail } = item;
return {
cardId: item.cardId,
name: profile.name,
handle: `${profile.handle}@${profile.node}`,
status: detail.status,
updated: detail.statusUpdated,
logo: profile.imageSet ? card.actions.getCardLogo(item.cardId, profile.revision) : 'avatar',
}
};
useEffect(() => { useEffect(() => {
}, [card]); const cards = Array.from(card.state.cards.values());
const items = cards.map(setCardItem);
const filtered = items.filter(item => {
if (!state.filter) {
return true;
}
const lower = state.filter.toLowerCase();
if (item.name) {
if (item.name.toLowerCase().includes(lower)) {
return true;
}
}
if (item.handle) {
if (item.handle.toLowerCase().includes(lower)) {
return true;
}
}
return false;
})
if (state.sorting) {
filtered.sort((a, b) => {
if (a.name === b.name) {
return 0;
}
if (!a.name || (a.name < b.name)) {
return -1;
}
return 1;
});
}
else {
filtered.sort((a, b) => {
if (a.updated === b.updated) {
return 0;
}
if (!a.updated || (a.updated < b.updated)) {
return 1;
}
return -1;
});
}
updateState({ cards: filtered });
}, [card, state.filter, state.sorting]);
const actions = { const actions = {
setFilter: (filter) => {
updateState({ filter });
},
sort: () => {
updateState({ sorting: true });
},
unsort: () => {
updateState({ sorting: false });
},
}; };
return { state, actions }; return { state, actions };

View File

@ -15,9 +15,9 @@ export function Channels() {
<> <>
<View style={styles.topbar}> <View style={styles.topbar}>
<View style={styles.inputwrapper}> <View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="search1" size={16} color={Colors.text} /> <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.text} placeholder="Topic" /> autoCapitalize="none" placeholderTextColor={Colors.disabled} placeholder="Topic" />
<View style={styles.space} /> <View style={styles.space} />
</View> </View>
<TouchableOpacity style={styles.add}> <TouchableOpacity style={styles.add}>

View File

@ -38,7 +38,7 @@ export const styles = StyleSheet.create({
textAlign: 'center', textAlign: 'center',
padding: 4, padding: 4,
color: Colors.text, color: Colors.text,
fontSize: 16, fontSize: 14,
}, },
icon: { icon: {
paddingLeft: 8, paddingLeft: 8,

View File

@ -9,7 +9,7 @@ export function ChannelItem({ item }) {
return ( return (
<TouchableOpacity style={styles.container} activeOpacity={1} onPress={actions.setRead}> <TouchableOpacity style={styles.container} activeOpacity={1} onPress={actions.setRead}>
<Logo src={item.logo} width={40} height={40} radius={6} /> <Logo src={item.logo} width={32} height={32} radius={6} />
<View style={styles.detail}> <View style={styles.detail}>
<Text style={styles.subject} numberOfLines={1} ellipsizeMode={'tail'}>{ item.subject }</Text> <Text style={styles.subject} numberOfLines={1} ellipsizeMode={'tail'}>{ item.subject }</Text>
<Text style={styles.message} numberOfLines={1} ellipsizeMode={'tail'}>{ item.message }</Text> <Text style={styles.message} numberOfLines={1} ellipsizeMode={'tail'}>{ item.message }</Text>

View File

@ -21,9 +21,11 @@ export const styles = StyleSheet.create({
}, },
subject: { subject: {
color: Colors.text, color: Colors.text,
fontSize: 14,
}, },
message: { message: {
color: Colors.disabled, color: Colors.disabled,
fontSize: 12,
}, },
dot: { dot: {
width: 8, width: 8,

View File

@ -13,6 +13,7 @@ export function useChannels() {
topic: null, topic: null,
channels: [], channels: [],
tabbed: null, tabbed: null,
filter: null,
}); });
const items = useRef([]); const items = useRef([]);
@ -133,7 +134,22 @@ export function useChannels() {
}); });
merged.push(...Array.from(channel.state.channels.values())); merged.push(...Array.from(channel.state.channels.values()));
merged.sort((a, b) => { const items = merged.map(setChannelEntry);
const filtered = items.filter(item => {
if (!state.filter) {
return true;
}
const lower = state.filter.toLowerCase();
if (item.subject) {
if (item.subject.toLowerCase().includes(lower)) {
return true;
}
}
return false;
});
const sorted = filtered.sort((a, b) => {
const aCreated = a?.summary?.lastTopic?.created; const aCreated = a?.summary?.lastTopic?.created;
const bCreated = b?.summary?.lastTopic?.created; const bCreated = b?.summary?.lastTopic?.created;
if (aCreated === bCreated) { if (aCreated === bCreated) {
@ -145,13 +161,16 @@ export function useChannels() {
return -1; return -1;
}); });
updateState({ channels: merged.map(item => setChannelEntry(item)) }); updateState({ channels: sorted });
}, [channel, card]); }, [channel, card, state.filter]);
const actions = { const actions = {
setTopic: (topic) => { setTopic: (topic) => {
updateState({ topic }); updateState({ topic });
}, },
setFilter: (filter) => {
updateState({ filter });
},
}; };
return { state, actions }; return { state, actions };

View File

@ -107,7 +107,9 @@ export function Profile() {
</View> </View>
</TouchableOpacity> </TouchableOpacity>
<View style={styles.visible}> <View style={styles.visible}>
<Text style={styles.visibleText}>Visible in Registry</Text> <TouchableOpacity onPress={() => setVisible(!state.searchable)} activeOpacity={1}>
<Text style={styles.visibleText}>Visible in Registry</Text>
</TouchableOpacity>
<Switch style={styles.visibleSwitch} value={state.searchable} onValueChange={setVisible} trackColor={styles.switch}/> <Switch style={styles.visibleSwitch} value={state.searchable} onValueChange={setVisible} trackColor={styles.switch}/>
</View> </View>
<TouchableOpacity style={styles.logout} onPress={actions.logout}> <TouchableOpacity style={styles.logout} onPress={actions.logout}>