mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
rendering card items
This commit is contained in:
parent
911744daa4
commit
55debaba7c
@ -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}>
|
||||||
|
@ -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,
|
||||||
|
@ -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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
61
app/mobile/src/session/cards/cardItem/CardItem.styled.js
Normal file
61
app/mobile/src/session/cards/cardItem/CardItem.styled.js
Normal 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,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
17
app/mobile/src/session/cards/cardItem/useCardItem.hook.js
Normal file
17
app/mobile/src/session/cards/cardItem/useCardItem.hook.js
Normal 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 };
|
||||||
|
}
|
||||||
|
|
@ -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 };
|
||||||
|
@ -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}>
|
||||||
|
@ -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,
|
||||||
|
@ -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>
|
||||||
|
@ -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,
|
||||||
|
@ -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 };
|
||||||
|
@ -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}>
|
||||||
|
Loading…
Reference in New Issue
Block a user