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}>
{ state.tabbed && (
<View style={styles.topbar}>
<TouchableOpacity style={styles.sort}>
{ 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.text} />
<TextInput style={styles.inputfield} value={state.topic} onChangeText={actions.setTopic}
autoCapitalize="none" placeholderTextColor={Colors.text} placeholder="Contacts" />
<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}>

View File

@ -48,7 +48,7 @@ export const styles = StyleSheet.create({
textAlign: 'center',
padding: 4,
color: Colors.text,
fontSize: 16,
fontSize: 14,
},
icon: {
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 }) {
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({
tabbed: null,
cards: [],
filter: null,
sorting: false,
});
const dimensions = useWindowDimensions();
@ -27,10 +29,74 @@ export function useCards() {
}
}, [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(() => {
}, [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 = {
setFilter: (filter) => {
updateState({ filter });
},
sort: () => {
updateState({ sorting: true });
},
unsort: () => {
updateState({ sorting: false });
},
};
return { state, actions };

View File

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

View File

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

View File

@ -9,7 +9,7 @@ export function ChannelItem({ item }) {
return (
<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}>
<Text style={styles.subject} numberOfLines={1} ellipsizeMode={'tail'}>{ item.subject }</Text>
<Text style={styles.message} numberOfLines={1} ellipsizeMode={'tail'}>{ item.message }</Text>

View File

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

View File

@ -13,6 +13,7 @@ export function useChannels() {
topic: null,
channels: [],
tabbed: null,
filter: null,
});
const items = useRef([]);
@ -133,7 +134,22 @@ export function useChannels() {
});
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 bCreated = b?.summary?.lastTopic?.created;
if (aCreated === bCreated) {
@ -145,13 +161,16 @@ export function useChannels() {
return -1;
});
updateState({ channels: merged.map(item => setChannelEntry(item)) });
}, [channel, card]);
updateState({ channels: sorted });
}, [channel, card, state.filter]);
const actions = {
setTopic: (topic) => {
updateState({ topic });
},
setFilter: (filter) => {
updateState({ filter });
},
};
return { state, actions };

View File

@ -107,7 +107,9 @@ export function Profile() {
</View>
</TouchableOpacity>
<View style={styles.visible}>
<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}/>
</View>
<TouchableOpacity style={styles.logout} onPress={actions.logout}>