adding profile settings drawer for tablet

This commit is contained in:
Roland Osborne 2023-09-14 15:48:31 -07:00
parent 4e380a8e05
commit 10925079bf
18 changed files with 705 additions and 1160 deletions

View File

@ -17,6 +17,7 @@ const LightColors = {
descriptionText: '#888888', descriptionText: '#888888',
text: '#444444', text: '#444444',
screenBase: '#dddddd', screenBase: '#dddddd',
drawerBase: '#eeeeee',
areaBase: '#ffffff', areaBase: '#ffffff',
modalBase: '#ffffff', modalBase: '#ffffff',
modalOverlay: 'rgba(52,52,52,0.8)', modalOverlay: 'rgba(52,52,52,0.8)',
@ -69,6 +70,7 @@ const DarkColors = {
descriptionText: '#bbbbbb', descriptionText: '#bbbbbb',
text: '#ffffff', text: '#ffffff',
screenBase: '#222222', screenBase: '#222222',
drawerBase: '#333333',
areaBase: '#444444', areaBase: '#444444',
modalBase: '#111111', modalBase: '#111111',
modalOverlay: 'rgba(88,88,88,0.8)', modalOverlay: 'rgba(88,88,88,0.8)',
@ -124,6 +126,7 @@ export const Colors = {
descriptionText: getColor('descriptionText'), descriptionText: getColor('descriptionText'),
text: getColor('text'), text: getColor('text'),
screenBase: getColor('screenBase'), screenBase: getColor('screenBase'),
drawerBase: getColor('drawerBase'),
areaBase: getColor('areaBase'), areaBase: getColor('areaBase'),
modalOverlay: getColor('modalOverlay'), modalOverlay: getColor('modalOverlay'),
modalBase: getColor('modalBase'), modalBase: getColor('modalBase'),

View File

@ -11,6 +11,7 @@ import { useSession } from './useSession.hook';
import { styles } from './Session.styled'; import { styles } from './Session.styled';
import Colors from 'constants/Colors'; import Colors from 'constants/Colors';
import { Profile } from './profile/Profile'; import { Profile } from './profile/Profile';
import { ProfileSettings } from './profileSettings/ProfileSettings';
import { CardsHeader, CardsBody, Cards } from './cards/Cards'; import { CardsHeader, CardsBody, Cards } from './cards/Cards';
import { RegistryHeader, RegistryBody, Registry } from './registry/Registry'; import { RegistryHeader, RegistryBody, Registry } from './registry/Registry';
import { ContactHeader, ContactBody, Contact } from './contact/Contact'; import { ContactHeader, ContactBody, Contact } from './contact/Contact';
@ -433,7 +434,7 @@ export function Session({ sharing, clearSharing }) {
<View style={styles.container}> <View style={styles.container}>
{ state.tabbed === false && ( { state.tabbed === false && (
<ProfileDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '45%' } }} drawerContent={(props) => ( <ProfileDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '45%' } }} drawerContent={(props) => (
<ScrollView style={styles.drawer}><SafeAreaView edges={['top', 'bottom', 'right']}><Profile /></SafeAreaView></ScrollView> <ProfileSettings />
)}> )}>
<ProfileDrawer.Screen name="detail"> <ProfileDrawer.Screen name="detail">
{(props) => <DetailDrawerScreen navParams={{ profileNav: props.navigation, state, actions, addChannel, dmChannel, shareChannel, shareIntent, setShareIntent }} />} {(props) => <DetailDrawerScreen navParams={{ profileNav: props.navigation, state, actions, addChannel, dmChannel, shareChannel, shareIntent, setShareIntent }} />}

View File

@ -95,7 +95,7 @@ export const styles = StyleSheet.create({
paddingTop: 8, paddingTop: 8,
paddingLeft: 8, paddingLeft: 8,
paddingRight: 8, paddingRight: 8,
backgroundColor: Colors.formBackground, backgroundColor: Colors.drawerBase,
}, },
options: { options: {
display: 'flex', display: 'flex',

View File

@ -13,7 +13,7 @@ import { useProfile } from './useProfile.hook';
import { styles } from './Profile.styled'; import { styles } from './Profile.styled';
import avatar from 'images/avatar.png'; import avatar from 'images/avatar.png';
export function Profile() { export function Profile({ drawer }) {
const [busyDetail, setBusyDetail] = useState(false); const [busyDetail, setBusyDetail] = useState(false);
const { state, actions } = useProfile(); const { state, actions } = useProfile();
@ -71,79 +71,87 @@ export function Profile() {
} }
return ( return (
<View style={styles.container}> <>
<Image source={state.imageSource} style={{ ...styles.logo, width: state.imageWidth, height: state.imageHeight }} resizeMode={'contain'} /> { drawer && (
<View style={styles.drawerContainer}>
<View style={styles.content}> <Text style={styles.drawerHeader} adjustsFontSizeToFit={true}>{ state.username }</Text>
<View style={{ width: state.imageWidth, height: state.imageHeight - OVERLAP }} /> <View style={styles.drawerFrame}>
<Image source={state.imageSource} style={styles.drawerLogo} resizeMode={'contain'} />
<View style={styles.control}> <View style={styles.drawerLogoEdit}>
<Menu> <Text style={styles.editLabel}>{ state.strings.edit }</Text>
<MenuTrigger customStyles={styles.trigger}> <MatIcons name="square-edit-outline" size={14} color={Colors.linkText} />
<View style={styles.edit}>
<Text style={styles.editLabel}>{ state.strings.edit }</Text>
<MatIcons name="square-edit-outline" size={14} color={Colors.linkText} />
</View>
</MenuTrigger>
<MenuOptions optionsContainerStyle={{ width: 'auto' }} style={styles.options}>
<MenuOption onSelect={onGallery}>
<Text style={styles.option}>{ state.strings.editImage }</Text>
</MenuOption>
<MenuOption onSelect={actions.showDetails}>
<Text style={styles.option}>{ state.strings.editDetails }</Text>
</MenuOption>
</MenuOptions>
</Menu>
</View>
<View style={{ ...styles.details, width: state.detailWidth }}>
{ state.name && (
<Text style={styles.nameSet} numberOfLines={1} adjustsFontSizeToFit={true}>{ state.name }</Text>
)}
{ !state.name && (
<Text style={styles.nameUnset}>{ state.strings.name }</Text>
)}
<Text style={styles.username} numberOfLines={1}>{ state.username }</Text>
<View style={styles.attributes}>
<View style={styles.entry}>
<AntIcons name="enviromento" style={styles.icon} size={20} color={Colors.text} />
{ state.location && (
<Text style={styles.locationSet}>{ state.location }</Text>
)}
{ !state.location && (
<Text style={styles.locationUnset}>Location</Text>
)}
</View>
<View style={styles.divider} />
<ScrollView style={styles.description}>
<View style={styles.entry}>
<MatIcons name="book-open-outline" style={styles.descriptionIcon} size={20} color={Colors.text} />
{ state.description && (
<Text style={styles.descriptionSet}>{ state.description }</Text>
)}
{ !state.description && (
<Text style={styles.descriptionUnset}>Description</Text>
)}
</View> </View>
</ScrollView> </View>
</View> </View>
)}
<View style={styles.group}> { !drawer && (
<TouchableOpacity style={styles.entry} activeOpacity={1}> <View style={styles.container}>
<MatIcons name="eye-outline" style={styles.icon} size={20} color={Colors.text} /> <Image source={state.imageSource} style={{ ...styles.logo, width: state.imageWidth, height: state.imageHeight }} resizeMode={'contain'} />
<TouchableOpacity activeOpacity={1} onPress={() => setVisible(!state.searchable)}> <View style={styles.content}>
<Text style={styles.visibleLabel}>{ state.strings.visibleRegistry }</Text> <View style={{ width: state.imageWidth, height: state.imageHeight - OVERLAP }} />
</TouchableOpacity> <View style={styles.control}>
<Switch value={state.searchable} style={Platform.OS==='ios' ? styles.visibleSwitch : {}} thumbColor={Colors.sliderGrip} ios_backgroundColor={Colors.disabledIndicator} <Menu>
trackColor={styles.track} onValueChange={setVisible} /> <MenuTrigger customStyles={styles.trigger}>
</TouchableOpacity> <View style={styles.edit}>
<Text style={styles.editLabel}>{ state.strings.edit }</Text>
<MatIcons name="square-edit-outline" size={14} color={Colors.linkText} />
</View>
</MenuTrigger>
<MenuOptions optionsContainerStyle={{ width: 'auto' }} style={styles.options}>
<MenuOption onSelect={onGallery}>
<Text style={styles.option}>{ state.strings.editImage }</Text>
</MenuOption>
<MenuOption onSelect={actions.showDetails}>
<Text style={styles.option}>{ state.strings.editDetails }</Text>
</MenuOption>
</MenuOptions>
</Menu>
</View>
<View style={{ ...styles.details, width: state.detailWidth }}>
{ state.name && (
<Text style={styles.nameSet} numberOfLines={1} adjustsFontSizeToFit={true}>{ state.name }</Text>
)}
{ !state.name && (
<Text style={styles.nameUnset}>{ state.strings.name }</Text>
)}
<Text style={styles.username} numberOfLines={1}>{ state.username }</Text>
<View style={styles.attributes}>
<View style={styles.entry}>
<AntIcons name="enviromento" style={styles.icon} size={20} color={Colors.text} />
{ state.location && (
<Text style={styles.locationSet}>{ state.location }</Text>
)}
{ !state.location && (
<Text style={styles.locationUnset}>Location</Text>
)}
</View>
<View style={styles.divider} />
<ScrollView style={styles.description}>
<View style={styles.entry}>
<MatIcons name="book-open-outline" style={styles.descriptionIcon} size={20} color={Colors.text} />
{ state.description && (
<Text style={styles.descriptionSet}>{ state.description }</Text>
)}
{ !state.description && (
<Text style={styles.descriptionUnset}>Description</Text>
)}
</View>
</ScrollView>
</View>
<View style={styles.group}>
<TouchableOpacity style={styles.entry} activeOpacity={1}>
<MatIcons name="eye-outline" style={styles.icon} size={20} color={Colors.text} />
<TouchableOpacity activeOpacity={1} onPress={() => setVisible(!state.searchable)}>
<Text style={styles.visibleLabel}>{ state.strings.visibleRegistry }</Text>
</TouchableOpacity>
<Switch value={state.searchable} style={Platform.OS==='ios' ? styles.visibleSwitch : {}} thumbColor={Colors.sliderGrip} ios_backgroundColor={Colors.disabledIndicator}
trackColor={styles.track} onValueChange={setVisible} />
</TouchableOpacity>
</View>
</View>
</View>
</View> </View>
)}
</View>
<Modal <Modal
animationType="fade" animationType="fade"
@ -212,8 +220,7 @@ export function Profile() {
</View> </View>
</Modal> </Modal>
</View> </>
</View>
); );
} }

View File

@ -2,6 +2,43 @@ import { StyleSheet } from 'react-native';
import { Colors } from 'constants/Colors'; import { Colors } from 'constants/Colors';
export const styles = StyleSheet.create({ export const styles = StyleSheet.create({
drawerContainer: {
width: '100%',
display: 'flex',
alignItems: 'center',
},
drawerHeader: {
fontFamily: 'roboto',
color: Colors.text,
fontSize: 20,
padding: 16,
},
drawerFrame: {
width: '50%',
maxWidth: 400,
paddingBottom: 16,
},
drawerLogo: {
aspectRatio: 1,
resizeMode: 'contain',
borderRadius: 8,
width: null,
height: null,
},
drawerLogoEdit: {
position: 'absolute',
top: 0,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
alignSelf: 'center',
backgroundColor: 'rgba(255,255,255,0.6)',
paddingLeft: 8,
paddingRight: 8,
borderBottomLeftRadius: 6,
borderBottomRightRadius: 6,
},
container: { container: {
width: '100%', width: '100%',
height: '100%', height: '100%',

View File

@ -1,48 +0,0 @@
import { FlatList, View, Alert, TouchableOpacity, Text } from 'react-native';
import { styles } from './BlockedContacts.styled';
import { useBlockedContacts } from './useBlockedContacts.hook';
import { Logo } from 'utils/Logo';
export function BlockedContacts() {
const { state, actions } = useBlockedContacts();
const unblock = (cardId) => {
Alert.alert(
'Unblocking Contact',
'Confirm?',
[
{ text: "Cancel", onPress: () => {}, },
{ text: "Unblock", onPress: () => actions.unblock(cardId) },
],
);
};
const BlockedItem = ({ item }) => {
return (
<TouchableOpacity style={styles.item} onPress={() => unblock(item.cardId)}>
<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>
)
}
return (
<View style={styles.container}>
{ state.cards.length === 0 && (
<Text style={styles.default}>No Blocked Contacts</Text>
)}
{ state.cards.length !== 0 && (
<FlatList
data={state.cards}
renderItem={({item}) => <BlockedItem item={item} />}
keyExtractor={item => item.cardId}
/>
)}
</View>
);
}

View File

@ -1,43 +0,0 @@
import { StyleSheet } from 'react-native';
import { Colors } from 'constants/Colors';
export const styles = StyleSheet.create({
container: {
backgroundColor: Colors.white,
display: 'flex',
width: '100%',
justifyContent: 'center',
fontSize: 14,
height: 200,
},
default: {
textAlign: 'center',
color: Colors.grey,
},
item: {
width: '100%',
display: 'flex',
flexDirection: 'row',
height: 48,
paddingLeft: 16,
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,
},
});

View File

@ -1,53 +0,0 @@
import { useState, useEffect, useContext } from 'react';
import { CardContext } from 'context/CardContext';
export function useBlockedContacts() {
const [state, setState] = useState({
cards: [],
});
const card = useContext(CardContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
const setCardItem = (item) => {
const { profile } = item;
return {
cardId: item.cardId,
name: profile?.name,
handle: `${profile?.handle}@${profile?.node}`,
blocked: item.blocked,
logo: profile?.imageSet ? card.actions.getCardImageUrl(item.cardId) : 'avatar',
}
};
useEffect(() => {
const cards = Array.from(card.state.cards.values());
const items = cards.map(setCardItem);
const filtered = items.filter(item => {
return item.blocked;
});
filtered.sort((a, b) => {
if (a.name === b.name) {
return 0;
}
if (!a.name || (a.name < b.name)) {
return -1;
}
return 1;
});
updateState({ cards: filtered });
}, [card.state]);
const actions = {
unblock: async (cardId) => {
await card.actions.clearCardFlag(cardId);
}
};
return { state, actions };
}

View File

@ -1,47 +0,0 @@
import { FlatList, View, Alert, TouchableOpacity, Text } from 'react-native';
import { styles } from './BlockedMessages.styled';
import { useBlockedMessages } from './useBlockedMessages.hook';
import { Logo } from 'utils/Logo';
export function BlockedMessages() {
const { state, actions } = useBlockedMessages();
const unblock = (cardId, channelId, topicId) => {
Alert.alert(
'Unblocking Message',
'Confirm?',
[
{ text: "Cancel", onPress: () => {}, },
{ text: "Unblock", onPress: () => actions.unblock(cardId, channelId, topicId) },
],
);
};
const BlockedItem = ({ item }) => {
return (
<TouchableOpacity style={styles.item} onPress={() => unblock(item.cardId, item.channelId, item.topicId)}>
<View style={styles.detail}>
<Text style={styles.name} numberOfLines={1} ellipsizeMode={'tail'}>{ item.name }</Text>
<Text style={styles.created} numberOfLines={1} ellipsizeMode={'tail'}>{ item.timestamp }</Text>
</View>
</TouchableOpacity>
)
}
return (
<View style={styles.container}>
{ state.messages.length === 0 && (
<Text style={styles.default}>No Blocked Messages</Text>
)}
{ state.messages.length !== 0 && (
<FlatList
data={state.messages}
renderItem={({item}) => <BlockedItem item={item} />}
keyExtractor={item => item.id}
/>
)}
</View>
);
}

View File

@ -1,46 +0,0 @@
import { StyleSheet } from 'react-native';
import { Colors } from 'constants/Colors';
export const styles = StyleSheet.create({
container: {
backgroundColor: Colors.white,
display: 'flex',
width: '100%',
justifyContent: 'center',
fontSize: 14,
height: 200,
},
default: {
textAlign: 'center',
color: Colors.grey,
},
item: {
width: '100%',
display: 'flex',
flexDirection: 'row',
height: 32,
paddingLeft: 16,
alignItems: 'center',
borderBottomWidth: 1,
borderColor: Colors.itemDivider,
},
detail: {
paddingLeft: 12,
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
width: '100%',
},
name: {
color: Colors.text,
fontSize: 14,
flexGrow: 1,
flexShrink: 1,
minWidth: 0,
},
created: {
color: Colors.text,
fontSize: 12,
paddingRight: 16,
},
});

View File

@ -1,121 +0,0 @@
import { useState, useEffect, useContext } from 'react';
import { StoreContext } from 'context/StoreContext';
import { ChannelContext } from 'context/ChannelContext';
import { CardContext } from 'context/CardContext';
import { ProfileContext } from 'context/ProfileContext';
import { getCardByGuid } from 'context/cardUtil';
import moment from 'moment';
export function useBlockedMessages() {
const [state, setState] = useState({
messages: []
});
const store = useContext(StoreContext);
const card = useContext(CardContext);
const channel = useContext(ChannelContext);
const profile = useContext(ProfileContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
const setTopicItem = (cardId, channelId, topic) => {
let name, nameSet
if (topic.detail.guid === profile.state.identity.guid) {
const identity = profile.state.identity;
if (identity.name) {
name = identity.name;
}
else {
name = `${identity.handle}@${identity.node}`;
}
nameSet = true;
}
else {
const contact = getCardByGuid(card.state.cards, topic.detail.guid);
if (contact) {
if (contact?.card?.profile?.name) {
name = contact.card.profile.name;
}
else {
name = `${contact.card.profile.handle}@${contact.card.profile.node}`;
}
nameSet = true;
}
else {
name = 'unknown';
nameSet = false;
}
}
let timestamp;
const date = new Date(topic.detail.created * 1000);
const now = new Date();
const offset = now.getTime() - date.getTime();
if(offset < 86400000) {
timestamp = moment(date).format('h:mma');
}
else if (offset < 31449600000) {
timestamp = moment(date).format('M/DD');
}
else {
timestamp = moment(date).format('M/DD/YYYY');
}
const { topicId } = topic;
return { name, nameSet, timestamp, cardId, channelId, topicId, id: `${cardId}:${channelId}:${topicId}` };
};
const loadBlocked = async () => {
const channels = [];
channel.state.channels.forEach((channelItem, channelId, map) => {
channels.push({ channelId });
});
card.state.cards.forEach((cardItem, cardId, map) => {
cardItem.channels.forEach((channelItem, channelId, map) => {
channels.push({ cardId, channelId });
});
});
const merged = [];
for(let i = 0; i < channels.length; i++) {
let topics;
const { cardId, channelId } = channels[i];
if (cardId) {
topics = await card.actions.getTopicItems(cardId, channelId);
}
else {
topics = await channel.actions.getTopicItems(channelId);
}
topics.forEach((topic) => {
if (topic.blocked) {
merged.push(setTopicItem(cardId, channelId, topic));
}
});
}
updateState({ messages: merged });
};
useEffect(() => {
loadBlocked();
}, []);
const actions = {
unblock: async (cardId, channelId, topicId) => {
const id = `${cardId}:${channelId}:${topicId}`;
if (cardId) {
card.actions.clearTopicFlag(cardId, channelId, topicId);
}
else {
channel.actions.clearTopicFlag(channelId, topicId);
}
updateState({ messages: state.messages.filter(item => item.id !== id) });
}
};
return { state, actions };
}

View File

@ -1,47 +0,0 @@
import { FlatList, View, Alert, TouchableOpacity, Text } from 'react-native';
import { styles } from './BlockedTopics.styled';
import { useBlockedTopics } from './useBlockedTopics.hook';
import { Logo } from 'utils/Logo';
export function BlockedTopics() {
const { state, actions } = useBlockedTopics();
const unblock = (cardId, channelId) => {
Alert.alert(
'Unblocking Contact',
'Confirm?',
[
{ text: "Cancel", onPress: () => {}, },
{ text: "Unblock", onPress: () => actions.unblock(cardId, channelId) },
],
);
};
const BlockedItem = ({ item }) => {
return (
<TouchableOpacity style={styles.item} onPress={() => unblock(item.cardId, item.channelId)}>
<View style={styles.detail}>
<Text style={styles.name} numberOfLines={1} ellipsizeMode={'tail'}>{ item.name }</Text>
<Text style={styles.created} numberOfLines={1} ellipsizeMode={'tail'}>{ item.created }</Text>
</View>
</TouchableOpacity>
)
}
return (
<View style={styles.container}>
{ state.channels.length === 0 && (
<Text style={styles.default}>No Blocked Topics</Text>
)}
{ state.channels.length !== 0 && (
<FlatList
data={state.channels}
renderItem={({item}) => <BlockedItem item={item} />}
keyExtractor={item => item.id}
/>
)}
</View>
);
}

View File

@ -1,46 +0,0 @@
import { StyleSheet } from 'react-native';
import { Colors } from 'constants/Colors';
export const styles = StyleSheet.create({
container: {
backgroundColor: Colors.white,
display: 'flex',
width: '100%',
justifyContent: 'center',
fontSize: 14,
height: 200,
},
default: {
textAlign: 'center',
color: Colors.grey,
},
item: {
width: '100%',
display: 'flex',
flexDirection: 'row',
height: 32,
paddingLeft: 16,
alignItems: 'center',
borderBottomWidth: 1,
borderColor: Colors.itemDivider,
},
detail: {
paddingLeft: 12,
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
width: '100%',
},
name: {
color: Colors.text,
fontSize: 14,
flexGrow: 1,
flexShrink: 1,
minWidth: 0,
},
created: {
color: Colors.text,
fontSize: 12,
paddingRight: 16,
},
});

View File

@ -1,92 +0,0 @@
import { useState, useEffect, useContext } from 'react';
import { CardContext } from 'context/CardContext';
import { ChannelContext } from 'context/ChannelContext';
import { ProfileContext } from 'context/ProfileContext';
import { getCardByGuid } from 'context/cardUtil';
import { getChannelSubjectLogo } from 'context/channelUtil';
import moment from 'moment';
export function useBlockedTopics() {
const [state, setState] = useState({
channels: []
});
const profile = useContext(ProfileContext);
const card = useContext(CardContext);
const channel = useContext(ChannelContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
const getCard = (guid) => {
let contact = null
card.state.cards.forEach((card, cardId, map) => {
if (card?.profile?.guid === guid) {
contact = card;
}
});
return contact;
}
const setChannelItem = (item) => {
let timestamp;
const date = new Date(item.channel.detail.created * 1000);
const now = new Date();
const offset = now.getTime() - date.getTime();
if(offset < 86400000) {
timestamp = moment(date).format('h:mma');
}
else if (offset < 31449600000) {
timestamp = moment(date).format('M/DD');
}
else {
timestamp = moment(date).format('M/DD/YYYY');
}
const profileGuid = profile.state?.identity?.guid;
const { logo, subject } = getChannelSubjectLogo(item.cardId, profileGuid, item.channel, card.state.cards, card.actions.getCardImageUrl);
return {
id: `${item.cardId}:${item.channel.channelId}`,
cardId: item.cardId,
channelId: item.channel.channelId,
name: subject,
created: timestamp,
}
};
useEffect(() => {
let merged = [];
card.state.cards.forEach((cardItem, cardId, map) => {
cardItem.channels.forEach((channelItem) => {
if (channelItem.blocked) {
merged.push({ cardId, channel: channelItem });
}
});
});
channel.state.channels.forEach((channelItem, channelId, map) => {
if (channelItem.blocked) {
merged.push({ channel: channelItem });
}
});
const items = merged.map(setChannelItem);
updateState({ channels: items });
}, [card.state, channel.state]);
const actions = {
unblock: async (cardId, channelId) => {
if (cardId) {
await card.actions.clearChannelFlag(cardId, channelId);
}
else {
await channel.actions.clearChannelFlag(channelId);
}
}
};
return { state, actions };
}

View File

@ -49,7 +49,7 @@ export function useProfile() {
useEffect(() => { useEffect(() => {
const { name, handle, node, location, description, image } = profile.state.identity; const { name, handle, node, location, description, image } = profile.state.identity;
const imageSource = image ? { uri: profile.state.imageUrl } : avatar; const imageSource = image ? { uri: profile.state.imageUrl } : avatar;
const username = `${handle} / ${node}` const username = `${handle}/${node}`
updateState({ name, username, location, description, imageSource }); updateState({ name, username, location, description, imageSource });
}, [profile.state]); }, [profile.state]);

View File

@ -0,0 +1,18 @@
import { ScrollView, View } from 'react-native';
import { Profile } from '../profile/Profile';
import { Settings } from '../settings/Settings';
import { styles } from './ProfileSettings.styled';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
export function ProfileSettings() {
return (
<ScrollView style={styles.drawer}>
<SafeAreaView style={styles.container} edges={['top', 'right']}>
<Profile drawer={true} />
<Settings drawer={true} />
</SafeAreaView>
</ScrollView>
);
}

View File

@ -0,0 +1,15 @@
import { StyleSheet } from 'react-native';
import { Colors } from 'constants/Colors';
export const styles = StyleSheet.create({
drawer: {
width: '100%',
height: '100%',
backgroundColor: Colors.drawerBase,
},
container: {
display: 'flex',
alignItems: 'center',
},
});

File diff suppressed because it is too large Load Diff