preparing conversation screen

This commit is contained in:
Roland Osborne 2023-02-28 15:25:04 -08:00
parent 8d5209396a
commit 4479ba5dca
12 changed files with 173 additions and 152 deletions

View File

@ -43,34 +43,31 @@ export function Session() {
const screenParams = { headerShown: true, headerTintColor: Colors.primary };
const ConversationStackScreen = () => {
const conversation = useContext(ConversationContext);
const [cardId, setCardId] = useState();
const [channelId, setChannelId] = useState();
const setConversation = async (navigation, cardId, channelId) => {
const openConversation = async (navigation, card, channel) => {
setCardId(card);
setChannelId(channel);
navigation.navigate('conversation');
await conversation.actions.setConversation(cardId, channelId);
}
const clearConversation = (navigation) => {
navigation.dispatch(
CommonActions.reset({ index: 0, routes: [ { name: 'channels' }, ], })
);
conversation.actions.clearConversation();
const closeConversation = (navigation) => {
setCardId(null);
setChannelId(null);
}
const openDetails = (navigation) => {
navigation.navigate('details');
}
return (
<ConversationStack.Navigator
initialRouteName="channels"
screenOptions={({ route }) => (screenParams)}
screenListeners={{ state: (e) => { if (e?.data?.state?.index === 0) { conversation.actions.clearConversation() }} }}>
<ConversationStack.Navigator initialRouteName="channels" screenOptions={({ route }) => (screenParams)} >
<ConversationStack.Screen name="channels" options={stackParams}>
{(props) => <Channels navigation={props.navigation} openConversation={(cardId, channelId) => setConversation(props.navigation, cardId, channelId)} />}
{(props) => <Channels navigation={props.navigation} openConversation={(cardId, channelId) => openConversation(props.navigation, cardId, channelId)} />}
</ConversationStack.Screen>
<ConversationStack.Screen name="conversation" options={{ ...stackParams, headerTitle: (props) => <ConversationHeader closeConversation={clearConversation} openDetails={openDetails} /> }}>
{(props) => <ConversationBody />}
<ConversationStack.Screen name="conversation" options={stackParams}>
{(props) => <Conversation navigation={props.navigation} cardId={cardId} channelId={channelId} openDetails={() => openDetails(props.navigation)} closeConversation={closeConversation} /> }
</ConversationStack.Screen>
<ConversationStack.Screen name="details" options={{ ...stackParams, headerTitle: (props) => <DetailsHeader /> }}>
@ -143,13 +140,17 @@ export function Session() {
const conversation = useContext(ConversationContext);
const [channel, setChannel] = useState(false);
const [cardId, setCardId] = useState();
const [channelId, setChannelId] = useState();
const setConversation = (cardId, channelId) => {
conversation.actions.setConversation(cardId, channelId);
const setConversation = (card, channel) => {
setCardId(card);
setChannelId(channel);
setChannel(true);
};
const clearConversation = () => {
conversation.actions.clearConversation();
const closeConversation = () => {
setCardId(null);
setChannelId(null);
setChannel(false);
};
const openDetails = () => {
@ -169,8 +170,8 @@ export function Session() {
return (
<View style={styles.home}>
<SafeAreaView edges={['top', 'bottom']} style={styles.sidebar}>
<SafeAreaView edges={['left']} style={styles.options}>
<SafeAreaView edges={['top', 'bottom', 'left']} style={styles.sidebar}>
<View edges={['left']} style={styles.options}>
<TouchableOpacity style={styles.option} onPress={openProfile}>
<ProfileIcon color={Colors.text} size={20} />
<Text style={styles.profileLabel}>Profile</Text>
@ -179,14 +180,16 @@ export function Session() {
<CardsIcon color={Colors.text} size={20} />
<Text style={styles.profileLabel}>Contacts</Text>
</TouchableOpacity>
</SafeAreaView>
</View>
<View style={styles.channels}>
<Channels openConversation={setConversation} />
<Channels cardId={cardId} channelId={channelId} openConversation={setConversation} />
</View>
</SafeAreaView>
<View style={styles.conversation}>
{ channel && (
<Conversation closeConversation={clearConversation} openDetails={openDetails} />
<SafeAreaView edges={['top', 'bottom', 'right']}>
<Conversation cardId={cardId} channelId={channelId} closeConversation={closeConversation} openDetails={openDetails} />
</SafeAreaView>
)}
{ !channel && (
<Welcome />
@ -262,9 +265,13 @@ export function Session() {
};
return (
<DetailDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '45%' } }}
drawerContent={(props) => <Details closeConversation={closeConversation} />}
>
<DetailDrawer.Navigator screenOptions={{ ...drawerParams, drawerStyle: { width: '45%' } }} drawerContent={(props) => (
<ScrollView style={styles.drawer}>
<SafeAreaView edges={['top', 'bottom', 'right']}>
<Details closeConversation={closeConversation} />
</SafeAreaView>
</ScrollView>
)}>
<DetailDrawer.Screen name="contact">
{(props) => <ContactDrawerScreen navParams={{...navParams, detailNav: props.navigation}} />}
</DetailDrawer.Screen>

View File

@ -80,9 +80,12 @@ export const styles = StyleSheet.create({
height: '100%',
width: '33%',
maxWidth: 500,
borderRightWidth: 1,
borderColor: Colors.divider,
},
conversation: {
width: '67%',
backgroundColor: Colors.formFocus,
},
drawer: {
width: '100%',

View File

@ -7,14 +7,15 @@ import { Colors } from 'constants/Colors';
import { ChannelItem } from './channelItem/ChannelItem';
import { AddMember } from './addMember/AddMember';
export function Channels({ navigation, openConversation }) {
export function Channels({ cardId, channelId, navigation, openConversation }) {
const { state, actions } = useChannels();
const addChannel = async () => {
try {
await actions.addChannel();
const channelId = await actions.addChannel();
actions.hideAdding();
openConversation(null, channelId);
}
catch (err) {
console.log(err);
@ -57,7 +58,7 @@ export function Channels({ navigation, openConversation }) {
</View>
)}
{ state.channels.length == 0 && (
<View style={styles.content}>
<View style={styles.notfound}>
<Text style={styles.notfoundtext}>No Topics Found</Text>
</View>
)}
@ -66,7 +67,7 @@ export function Channels({ navigation, openConversation }) {
style={styles.content}
data={state.channels}
initialNumToRender={25}
renderItem={({ item }) => <ChannelItem item={item} openConversation={openConversation} />}
renderItem={({ item }) => <ChannelItem cardId={cardId} channelId={channelId} item={item} openConversation={openConversation} />}
keyExtractor={item => (`${item.cardId}:${item.channelId}`)}
/>
)}

View File

@ -78,10 +78,23 @@ export const styles = StyleSheet.create({
flexShrink: 1,
paddingLeft: 4,
},
notfound: {
flexGrow: 1,
flexShrink: 1,
display: 'flex',
alignItems: 'center',
display: 'flex',
justifyContent: 'center',
},
notfoundtext: {
fontSize: 18,
color: Colors.disabled,
},
columnbottom: {
paddingLeft: 24,
paddingRight: 16,
paddingTop: 8,
paddingBottom: 16,
borderTopWidth: 1,
borderColor: Colors.divider,
},

View File

@ -6,10 +6,12 @@ import { useChannelItem } from './useChannelItem.hook';
import Colors from 'constants/Colors';
import Ionicons from 'react-native-vector-icons/MaterialCommunityIcons';
export function ChannelItem({ item, openConversation }) {
export function ChannelItem({ cardId, channelId, item, openConversation }) {
const container = (cardId === item.cardId && channelId === item.channelId) ? styles.active : styles.container;
return (
<TouchableOpacity style={styles.container} activeOpacity={1} onPress={() => openConversation(item.cardId, item.channelId, item.revision)}>
<TouchableOpacity style={container} activeOpacity={1} onPress={() => openConversation(item.cardId, item.channelId, item.revision)}>
<Logo src={item.logo} width={32} height={32} radius={3} />
<View style={styles.detail}>
<View style={styles.subject}>

View File

@ -13,6 +13,18 @@ export const styles = StyleSheet.create({
paddingLeft: 16,
paddingRight: 16,
},
active: {
width: '100%',
display: 'flex',
flexDirection: 'row',
height: 48,
alignItems: 'center',
borderBottomWidth: 1,
borderColor: Colors.itemDivider,
paddingLeft: 16,
paddingRight: 16,
backgroundColor: Colors.formFocus,
},
detail: {
paddingLeft: 12,
display: 'flex',

View File

@ -6,6 +6,7 @@ import { AppContext } from 'context/AppContext';
import { ProfileContext } from 'context/ProfileContext';
import { getChannelSeals, isUnsealed, getContentKey, encryptChannelSubject, decryptChannelSubject, decryptTopicSubject } from 'context/sealUtil';
import { getCardByGuid } from 'context/cardUtil';
import { getChannelSubjectLogo } from 'context/channelUtil';
export function useChannels() {
const [state, setState] = useState({
@ -37,20 +38,15 @@ export function useChannels() {
const timestamp = item.summary.lastTopic.created;
const { readRevision, topicRevision } = item;
// extract or decrypt subject
let locked;
let unlocked;
let message;
let subject;
// decrypt subject and message
let locked = false;
let unlocked = false;
if (item.detail.dataType === 'sealed') {
locked = true;
const seals = getChannelSeals(item.detail.data);
if (isUnsealed(seals, account.state.sealKey)) {
unlocked = true;
if (item.unsealedDetail) {
subject = item.unsealedDetail.subject;
}
else {
if (!item.unsealedDetail) {
try {
const contentKey = await getContentKey(seals, account.state.sealKey);
const unsealed = decryptChannelSubject(item.detail.data, contentKey);
@ -66,10 +62,7 @@ export function useChannels() {
}
}
if (item.summary.lastTopic.dataType === 'sealedtopic') {
if (item.unsealedSummary) {
message = item.unsealedSummary.message.text;
}
else {
if (!item.unsealedSummary) {
try {
const contentKey = await getContentKey(seals, account.state.sealKey);
const unsealed = decryptTopicSubject(item.summary.lastTopic.data, contentKey);
@ -87,18 +80,20 @@ export function useChannels() {
}
}
}
let message;
if (item?.detail?.dataType === 'sealed') {
if (typeof item?.unsealedSummary?.message?.text === 'string') {
message = item.unsealedSummary.message.text;
}
}
if (item.detail.dataType === 'superbasic') {
locked = false;
unlocked = false;
try {
subject = JSON.parse(item.detail.data).subject;
}
catch(err) {
console.log(err);
}
if (item.summary.lastTopic.dataType === 'superbasictopic') {
try {
message = JSON.parse(item.summary.lastTopic.data).text;
const data = JSON.parse(item.summary.lastTopic.data);
if (typeof data.text === 'string') {
message = data.text;
}
}
catch(err) {
console.log(err);
@ -106,50 +101,8 @@ export function useChannels() {
}
}
const contacts = [];
if (cardId) {
contacts.push(cardId);
}
item.detail.members.forEach(guid => {
if (guid !== profile.state.identity.guid) {
contacts.push(getCardByGuid(card.state.cards, guid)?.card?.cardId);
}
})
if (!subject) {
if (contacts.length === 0) {
subject = 'Notes';
}
else {
const names = [];
contacts.forEach(id => {
const contact = card.state.cards.get(id);
if (contact?.card.profile?.name) {
names.push(contact.card.profile.name);
}
else {
names.push(contact?.card.profile?.handle);
}
});
subject = names.join(', ');
}
}
if (contacts.length === 0) {
logo = 'solution';
}
else if (contacts.length === 1) {
const contact = card.state.cards.get(contacts[0]);
if (contact?.card?.profile?.imageSet) {
logo = card.actions.getCardImageUrl(contacts[0])
}
else {
logo = 'avatar';
}
}
else {
logo = 'appstore';
}
const profileGuid = profile.state?.identity?.guid;
const { logo, subject } = getChannelSubjectLogo(cardId, profileGuid, item, card.state.cards, card.actions.getCardImageUrl);
const updated = (loginTimestamp < timestamp) && (readRevision < topicRevision);

View File

@ -1,13 +1,54 @@
import { Text, } from 'react-native';
import { useEffect, useContext } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { ConversationContext } from 'context/ConversationContext';
import { useConversation } from './useConversation.hook';
import { styles } from './Conversation.styled';
import { Colors } from 'constants/Colors';
import Ionicons from 'react-native-vector-icons/AntDesign';
import { Logo } from 'utils/Logo';
export function ConversationHeader({ closeConversation, openDetails, state, actions }) {
return <Text>ConversationHeader</Text>;
}
export function Conversation({ navigation, cardId, channelId, closeConversation, openDetails }) {
export function ConversationBody({ state, actions }) {
return <Text>ConversationBody</Text>
}
const conversation = useContext(ConversationContext);
const { state, actions } = useConversation();
export function Conversation({ closeConversation, openDetails }) {
return <Text>Conversation</Text>;
useEffect(() => {
if (navigation) {
navigation.setOptions({
headerTitle: () => (
<View style={styles.title}>
<Text style={styles.titletext}>{ state.subject }</Text>
</View>
),
headerRight: () => (
<TouchableOpacity onPress={openDetails}>
<Ionicons name={'setting'} size={24} color={Colors.primary} style={styles.titlebutton} />
</TouchableOpacity>
),
});
}
}, [navigation, state.subject]);
useEffect(() => {
conversation.actions.setConversation(cardId, channelId);
return () => { conversation.actions.clearConversation() };
}, [cardId, channelId]);
return (
<View>
{ !navigation && (
<View style={styles.header}>
<TouchableOpacity style={styles.headertitle} onPress={openDetails}>
<Logo src={state.logo} width={32} height={32} radius={2} />
<Text style={styles.titletext}>{ state.subject }</Text>
<Ionicons name={'setting'} size={24} color={Colors.primary} style={styles.titlebutton} />
</TouchableOpacity>
<TouchableOpacity style={styles.headerclose} onPress={closeConversation}>
<Ionicons name={'close'} size={28} color={Colors.grey} style={styles.titlebutton} />
</TouchableOpacity>
</View>
)}
<Text>Conversation</Text>
</View>
);
}

View File

@ -22,7 +22,7 @@ export const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
paddingLeft: 16,
paddingTop: 16,
paddingTop: 8,
paddingBottom: 8,
},
titletext: {

View File

@ -1,7 +1,33 @@
import { useState } from 'react';
import { useEffect, useState, useContext } from 'react';
import { ProfileContext } from 'context/ProfileContext';
import { CardContext } from 'context/CardContext';
import { ConversationContext } from 'context/ConversationContext';
import { getChannelSubjectLogo } from 'context/channelUtil';
export function useConversation() {
const [state, setState] = useState({});
const [state, setState] = useState({
subject: null,
logo: null,
});
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
const profile = useContext(ProfileContext);
const card = useContext(CardContext);
const conversation = useContext(ConversationContext);
useEffect(() => {
const cardId = conversation.state.card?.cardId;
const profileGuid = profile.state.identity?.guid;
const channel = conversation.state.channel;
const cards = card.state.cards;
cardImageUrl = card.actions.getCardImageUrl;
const { logo, subject } = getChannelSubjectLogo(cardId, profileGuid, channel, cards, cardImageUrl);
updateState({ logo, subject });
}, [conversation.state, card.state, profile.state]);
const actions = {};

View File

@ -3,6 +3,7 @@ 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() {
@ -44,47 +45,9 @@ export function useBlockedTopics() {
timestamp = moment(date).format('M/DD/YYYY');
}
let subject;
if (item?.detail?.data) {
try {
topic = JSON.parse(item?.detail?.data).subject;
subject = topic;
}
catch (err) {
console.log(err);
}
}
if (!subject) {
let contacts = [];
if (item.cardId) {
contacts.push(card.state.cards.get(item.cardId));
}
if (item.channel.detail?.members) {
const profileGuid = profile.state.identity.guid;
item.channel.detail.members.forEach(guid => {
if (profileGuid !== guid) {
const contact = getCardByGuid(card.state.cards, guid);
contacts.push(contact);
}
})
}
if (contacts.length) {
let names = [];
for (let contact of contacts) {
if (contact?.card?.profile?.name) {
names.push(contact.card.profile.name);
}
else if (contact?.card?.profile?.handle) {
names.push(contact.card.profile.handle);
}
}
subject = names.join(', ');
}
else {
subject = "Notes";
}
}
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}`,

View File

@ -12,7 +12,7 @@ export function Welcome() {
return (
<SafeAreaView style={styles.container}>
<Text style={styles.header}>Databag</Text>
<Text style={styles.label}>Communication for the decentralized web</Text>
<Text style={styles.label}>Communication for the Decentralized Web</Text>
<Image style={styles.image} source={session} />
<View style={styles.steps}>
<Text style={styles.stepstext}>Setup your profile</Text>