diff --git a/app/mobile/App.js b/app/mobile/App.js index 3f6e4b6d..bd69a45e 100644 --- a/app/mobile/App.js +++ b/app/mobile/App.js @@ -14,6 +14,7 @@ import { CardContextProvider } from 'context/CardContext'; import { ChannelContextProvider } from 'context/ChannelContext'; import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; import { NavigationContainer } from '@react-navigation/native'; +import { ConversationContextProvider } from 'context/ConversationContext'; export default function App() { @@ -23,20 +24,22 @@ export default function App() { - - - - - } /> - } /> - } /> - } /> - } /> - } /> - - - - + + + + + + } /> + } /> + } /> + } /> + } /> + } /> + + + + + diff --git a/app/mobile/src/context/ConversationContext.js b/app/mobile/src/context/ConversationContext.js new file mode 100644 index 00000000..08cde892 --- /dev/null +++ b/app/mobile/src/context/ConversationContext.js @@ -0,0 +1,14 @@ +import { createContext } from 'react'; +import { useConversationContext } from './useConversationContext.hook'; + +export const ConversationContext = createContext({}); + +export function ConversationContextProvider({ children }) { + const { state, actions } = useConversationContext(); + return ( + + {children} + + ); +} + diff --git a/app/mobile/src/context/useConversationContext.hook.js b/app/mobile/src/context/useConversationContext.hook.js new file mode 100644 index 00000000..22b678b7 --- /dev/null +++ b/app/mobile/src/context/useConversationContext.hook.js @@ -0,0 +1,151 @@ +import { useState, useEffect, useRef, useContext } from 'react'; +import { StoreContext } from 'context/StoreContext'; +import { CardContext } from 'context/CardContext'; +import { ChannelContext } from 'context/ChannelContext'; +import { ProfileContext } from 'context/ProfileContext'; + +export function useConversationContext() { + const [state, setState] = useState({ + subject: null, + logo: null, + contacts: [], + topics: [], + }); + const store = useContext(StoreContext); + const card = useContext(CardContext); + const channel = useContext(ChannelContext); + const profile = useContext(ProfileContext); + const topics = useRef(new Map()); + const revision = useRef(0); + const syncing = useRef(false); + const cardId = useRef(null); + const channelId = useRef(null); + const setView = useRef(0); + + const updateState = (value) => { + setState((s) => ({ ...s, ...value })) + } + + const sync = async () => { + const curView = setView.current; + const item = getChannel(cardId.current, channelId.current); + if (!syncing.current && item?.revision !== revision.current) { + syncing.current = true; + + // stuff + setChannel(item); + + if (curView === setView.current) { + revision.current = item?.revision; + } + syncing.current = false; + sync(); + } + } + + const getCard = (guid) => { + let contact = null + card.state.cards.forEach((card, cardId, map) => { + if (card?.profile?.guid === guid) { + contact = card; + } + }); + return contact; + } + + const getChannel = (cardId, channelId) => { + if (cardId) { + const entry = card.state.cards.get(cardId); + return entry?.channels.get(channelId); + } + return channel.state.channels.get(channelId); + } + + const setChannel = (item) => { + let contacts = []; + let logo = null; + let subject = null; + + if (!item) { + updateState({ contacts, logo, subject }); + return; + } + + if (item.cardId) { + contacts.push(card.state.cards.get(item.cardId)); + } + if (item?.detail?.members) { + const profileGuid = profile.state.profile.guid; + item.detail.members.forEach(guid => { + if (profileGuid !== guid) { + const contact = getCard(guid); + contacts.push(contact); + } + }) + } + + if (contacts.length === 0) { + logo = 'solution'; + } + else if (contacts.length === 1) { + if (contacts[0]?.profile?.imageSet) { + logo = card.actions.getCardLogo(contacts[0].cardId, contacts[0].profileRevision); + } + else { + logo = 'avatar'; + } + } + else { + logo = 'appstore'; + } + + if (item?.detail?.data) { + try { + subject = JSON.parse(item?.detail?.data).subject; + } + catch (err) { + console.log(err); + } + } + if (!subject) { + if (contacts.length) { + let names = []; + for (let contact of contacts) { + if (contact?.profile?.name) { + names.push(contact.profile.name); + } + else if (contact?.profile?.handle) { + names.push(contact?.profile?.handle); + } + } + subject = names.join(', '); + } + else { + subject = "Notes"; + } + } + + updateState({ subject, logo, contacts }); + } + + useEffect(() => { + sync(); + }, [card, channel]); + + const actions = { + setChannel: (channel) => { + if (channel.cardId !== cardId.current || channel.channelId !== channelId.current) { + setView.current++; + revision.current = 0; + topics.current = new Map(); + channelId.current = channel.channelId; + cardId.current = channel.cardId; + sync(); + } + }, + } + + return { state, actions } +} + + diff --git a/app/mobile/src/session/conversation/Conversation.jsx b/app/mobile/src/session/conversation/Conversation.jsx index d01b4103..647ec4c7 100644 --- a/app/mobile/src/session/conversation/Conversation.jsx +++ b/app/mobile/src/session/conversation/Conversation.jsx @@ -1,5 +1,5 @@ -import { View, TouchableOpacity, Text } from 'react-native'; -import { useLayoutEffect } from 'react'; +import { View, TouchableOpacity, Text, FlatList } from 'react-native'; +import { useState, useRef } from 'react'; import { useConversation } from './useConversation.hook'; import { styles } from './Conversation.styled'; import { useNavigation } from '@react-navigation/native'; @@ -33,15 +33,12 @@ export function ConversationBody({ channel }) { const { state, actions } = useConversation(channel?.cardId, channel?.channelId); return ( - - CHANNEL - { channel && ( - <> - { channel?.cardId } - { channel?.channelId } - - )} - + ITEM { item.id }} + keyExtractor={item => item.id} + /> ); } diff --git a/app/mobile/src/session/conversation/Conversation.styled.js b/app/mobile/src/session/conversation/Conversation.styled.js index 5d399ee7..b7d539dc 100644 --- a/app/mobile/src/session/conversation/Conversation.styled.js +++ b/app/mobile/src/session/conversation/Conversation.styled.js @@ -41,5 +41,8 @@ export const styles = StyleSheet.create({ action: { paddingLeft: 8, }, + topics: { + height: '100%', + }, }) diff --git a/app/mobile/src/session/conversation/useConversation.hook.js b/app/mobile/src/session/conversation/useConversation.hook.js index a2f337f6..e6e42244 100644 --- a/app/mobile/src/session/conversation/useConversation.hook.js +++ b/app/mobile/src/session/conversation/useConversation.hook.js @@ -1,105 +1,28 @@ import { useState, useEffect, useContext } from 'react'; -import { CardContext } from 'context/CardContext'; -import { ChannelContext } from 'context/ChannelContext'; -import { ProfileContext } from 'context/ProfileContext'; +import { ConversationContext } from 'context/ConversationContext'; export function useConversation(cardId, channelId) { const [state, setState] = useState({ + topics: [], subject: null, logo: null, }); - const card = useContext(CardContext); - const channel = useContext(ChannelContext); - const profile = useContext(ProfileContext); + const conversation = useContext(ConversationContext); 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; - } + useEffect(() => { + conversation.actions.setChannel({ cardId, channelId }); + }, [cardId, channelId]); useEffect(() => { -console.log(cardId, channelId); - let item; - if (cardId) { - const entry = card.state.cards.get(cardId); - if (entry) { - item = entry.channels.get(channelId); - } - } - else { - item = channel.state.channels.get(channelId); - } - - let contacts = []; - if (item.cardId) { - contacts.push(card.state.cards.get(item.cardId)); - } - if (item?.detail?.members) { - const profileGuid = profile.state.profile.guid; - item.detail.members.forEach(guid => { - if (profileGuid !== guid) { - const contact = getCard(guid); - contacts.push(contact); - } - }) - } - - let logo = null; - if (contacts.length === 0) { - logo = 'solution'; - } - else if (contacts.length === 1) { - if (contacts[0]?.profile?.imageSet) { - logo = card.actions.getCardLogo(contacts[0].cardId, contacts[0].profileRevision); - } - else { - logo = 'avatar'; - } - } - else { - logo = 'appstore'; - } - - let subject = null; - if (item?.detail?.data) { - try { - subject = JSON.parse(item?.detail?.data).subject; - } - catch (err) { - console.log(err); - } - } - if (!subject) { - if (contacts.length) { - let names = []; - for (let contact of contacts) { - if (contact?.profile?.name) { - names.push(contact.profile.name); - } - else if (contact?.profile?.handle) { - names.push(contact?.profile?.handle); - } - } - subject = names.join(', '); - } - else { - subject = "Notes"; - } - } - - updateState({ subject, logo }); - }, [cardId, channelId, profile, card, channel]); + const { topics, subject, logo } = conversation.state; + updateState({ topics, subject, logo }); + }, [conversation]); const actions = { }; diff --git a/app/mobile/src/session/profile/Profile.jsx b/app/mobile/src/session/profile/Profile.jsx index e4584b5b..c192aa4a 100644 --- a/app/mobile/src/session/profile/Profile.jsx +++ b/app/mobile/src/session/profile/Profile.jsx @@ -23,6 +23,8 @@ export function Profile() { const { state, actions } = useProfile(); +console.log(state.imageSource); + const setVisible = async (visible) => { try { await actions.setVisible(visible); diff --git a/app/mobile/src/session/profile/useProfile.hook.js b/app/mobile/src/session/profile/useProfile.hook.js index d129b9da..32521b26 100644 --- a/app/mobile/src/session/profile/useProfile.hook.js +++ b/app/mobile/src/session/profile/useProfile.hook.js @@ -70,6 +70,7 @@ export function useProfile() { navigate('/'); }, setVisible: async (visible) => { + updateState({ visible }); await account.actions.setSearchable(visible); }, setProfileImage: async (data) => {