From 4479ba5dcaaf19bc8a85598dfa3ef0530501e3f0 Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Tue, 28 Feb 2023 15:25:04 -0800 Subject: [PATCH] preparing conversation screen --- app/mobile/src/session/Session.jsx | 61 +++++++------ app/mobile/src/session/Session.styled.js | 3 + app/mobile/src/session/channels/Channels.jsx | 9 +- .../src/session/channels/Channels.styled.js | 13 +++ .../channels/channelItem/ChannelItem.jsx | 6 +- .../channelItem/ChannelItem.styled.js | 12 +++ .../src/session/channels/useChannels.hook.js | 85 +++++-------------- .../src/session/conversation/Conversation.jsx | 59 +++++++++++-- .../conversation/Conversation.styled.js | 2 +- .../conversation/useConversation.hook.js | 30 ++++++- .../blockedTopics/useBlockedTopics.hook.js | 43 +--------- app/mobile/src/session/welcome/Welcome.jsx | 2 +- 12 files changed, 173 insertions(+), 152 deletions(-) diff --git a/app/mobile/src/session/Session.jsx b/app/mobile/src/session/Session.jsx index 57f2cbd7..6d02c994 100644 --- a/app/mobile/src/session/Session.jsx +++ b/app/mobile/src/session/Session.jsx @@ -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 ( - (screenParams)} - screenListeners={{ state: (e) => { if (e?.data?.state?.index === 0) { conversation.actions.clearConversation() }} }}> + (screenParams)} > - {(props) => setConversation(props.navigation, cardId, channelId)} />} + {(props) => openConversation(props.navigation, cardId, channelId)} />} - }}> - {(props) => } + + {(props) => openDetails(props.navigation)} closeConversation={closeConversation} /> } }}> @@ -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 ( - - + + Profile @@ -179,14 +180,16 @@ export function Session() { Contacts - + - + { channel && ( - + + + )} { !channel && ( @@ -262,9 +265,13 @@ export function Session() { }; return ( -
} - > + ( + + +
+ + + )}> {(props) => } diff --git a/app/mobile/src/session/Session.styled.js b/app/mobile/src/session/Session.styled.js index e682bb19..4f22549c 100644 --- a/app/mobile/src/session/Session.styled.js +++ b/app/mobile/src/session/Session.styled.js @@ -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%', diff --git a/app/mobile/src/session/channels/Channels.jsx b/app/mobile/src/session/channels/Channels.jsx index e545d21d..b7d75ce4 100644 --- a/app/mobile/src/session/channels/Channels.jsx +++ b/app/mobile/src/session/channels/Channels.jsx @@ -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 }) { )} { state.channels.length == 0 && ( - + No Topics Found )} @@ -66,7 +67,7 @@ export function Channels({ navigation, openConversation }) { style={styles.content} data={state.channels} initialNumToRender={25} - renderItem={({ item }) => } + renderItem={({ item }) => } keyExtractor={item => (`${item.cardId}:${item.channelId}`)} /> )} diff --git a/app/mobile/src/session/channels/Channels.styled.js b/app/mobile/src/session/channels/Channels.styled.js index daab5c8c..9e3f7b44 100644 --- a/app/mobile/src/session/channels/Channels.styled.js +++ b/app/mobile/src/session/channels/Channels.styled.js @@ -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, }, diff --git a/app/mobile/src/session/channels/channelItem/ChannelItem.jsx b/app/mobile/src/session/channels/channelItem/ChannelItem.jsx index bd7baa22..a0b28d41 100644 --- a/app/mobile/src/session/channels/channelItem/ChannelItem.jsx +++ b/app/mobile/src/session/channels/channelItem/ChannelItem.jsx @@ -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 ( - openConversation(item.cardId, item.channelId, item.revision)}> + openConversation(item.cardId, item.channelId, item.revision)}> diff --git a/app/mobile/src/session/channels/channelItem/ChannelItem.styled.js b/app/mobile/src/session/channels/channelItem/ChannelItem.styled.js index a8cbecde..039aedc9 100644 --- a/app/mobile/src/session/channels/channelItem/ChannelItem.styled.js +++ b/app/mobile/src/session/channels/channelItem/ChannelItem.styled.js @@ -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', diff --git a/app/mobile/src/session/channels/useChannels.hook.js b/app/mobile/src/session/channels/useChannels.hook.js index 12a09f5a..a81286ce 100644 --- a/app/mobile/src/session/channels/useChannels.hook.js +++ b/app/mobile/src/session/channels/useChannels.hook.js @@ -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); diff --git a/app/mobile/src/session/conversation/Conversation.jsx b/app/mobile/src/session/conversation/Conversation.jsx index 75133599..eb595fe5 100644 --- a/app/mobile/src/session/conversation/Conversation.jsx +++ b/app/mobile/src/session/conversation/Conversation.jsx @@ -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 ConversationHeader; -} +export function Conversation({ navigation, cardId, channelId, closeConversation, openDetails }) { -export function ConversationBody({ state, actions }) { - return ConversationBody -} + const conversation = useContext(ConversationContext); + const { state, actions } = useConversation(); -export function Conversation({ closeConversation, openDetails }) { - return Conversation; + useEffect(() => { + if (navigation) { + navigation.setOptions({ + headerTitle: () => ( + + { state.subject } + + ), + headerRight: () => ( + + + + ), + }); + } + }, [navigation, state.subject]); + + useEffect(() => { + conversation.actions.setConversation(cardId, channelId); + return () => { conversation.actions.clearConversation() }; + }, [cardId, channelId]); + + return ( + + { !navigation && ( + + + + { state.subject } + + + + + + + )} + Conversation + + ); } diff --git a/app/mobile/src/session/conversation/Conversation.styled.js b/app/mobile/src/session/conversation/Conversation.styled.js index ea00a616..d00d6b75 100644 --- a/app/mobile/src/session/conversation/Conversation.styled.js +++ b/app/mobile/src/session/conversation/Conversation.styled.js @@ -22,7 +22,7 @@ export const styles = StyleSheet.create({ flexDirection: 'row', alignItems: 'center', paddingLeft: 16, - paddingTop: 16, + paddingTop: 8, paddingBottom: 8, }, titletext: { diff --git a/app/mobile/src/session/conversation/useConversation.hook.js b/app/mobile/src/session/conversation/useConversation.hook.js index 59d99ddd..e9c5a1d3 100644 --- a/app/mobile/src/session/conversation/useConversation.hook.js +++ b/app/mobile/src/session/conversation/useConversation.hook.js @@ -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 = {}; diff --git a/app/mobile/src/session/profile/blockedTopics/useBlockedTopics.hook.js b/app/mobile/src/session/profile/blockedTopics/useBlockedTopics.hook.js index 118a5eea..70a6c63f 100644 --- a/app/mobile/src/session/profile/blockedTopics/useBlockedTopics.hook.js +++ b/app/mobile/src/session/profile/blockedTopics/useBlockedTopics.hook.js @@ -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}`, diff --git a/app/mobile/src/session/welcome/Welcome.jsx b/app/mobile/src/session/welcome/Welcome.jsx index 755d8e4d..00aa32d8 100644 --- a/app/mobile/src/session/welcome/Welcome.jsx +++ b/app/mobile/src/session/welcome/Welcome.jsx @@ -12,7 +12,7 @@ export function Welcome() { return ( Databag - Communication for the decentralized web + Communication for the Decentralized Web Setup your profile