adding conversation context

This commit is contained in:
Roland Osborne 2022-09-28 15:09:10 -07:00
parent 0f60c385ad
commit 0f8a90fb85
8 changed files with 205 additions and 111 deletions

View File

@ -14,6 +14,7 @@ import { CardContextProvider } from 'context/CardContext';
import { ChannelContextProvider } from 'context/ChannelContext'; import { ChannelContextProvider } from 'context/ChannelContext';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
import { NavigationContainer } from '@react-navigation/native'; import { NavigationContainer } from '@react-navigation/native';
import { ConversationContextProvider } from 'context/ConversationContext';
export default function App() { export default function App() {
@ -23,20 +24,22 @@ export default function App() {
<ChannelContextProvider> <ChannelContextProvider>
<AccountContextProvider> <AccountContextProvider>
<ProfileContextProvider> <ProfileContextProvider>
<AppContextProvider> <ConversationContextProvider>
<SafeAreaProvider> <AppContextProvider>
<NativeRouter> <SafeAreaProvider>
<Routes> <NativeRouter>
<Route path="/" element={ <Root /> } /> <Routes>
<Route path="/admin" element={ <Admin /> } /> <Route path="/" element={ <Root /> } />
<Route path="/login" element={ <Access mode="login" /> } /> <Route path="/admin" element={ <Admin /> } />
<Route path="/reset" element={ <Access mode="reset" /> } /> <Route path="/login" element={ <Access mode="login" /> } />
<Route path="/create" element={ <Access mode="create" /> } /> <Route path="/reset" element={ <Access mode="reset" /> } />
<Route path="/session" element={ <NavigationContainer><Session/></NavigationContainer> } /> <Route path="/create" element={ <Access mode="create" /> } />
</Routes> <Route path="/session" element={ <NavigationContainer><Session/></NavigationContainer> } />
</NativeRouter> </Routes>
</SafeAreaProvider> </NativeRouter>
</AppContextProvider> </SafeAreaProvider>
</AppContextProvider>
</ConversationContextProvider>
</ProfileContextProvider> </ProfileContextProvider>
</AccountContextProvider> </AccountContextProvider>
</ChannelContextProvider> </ChannelContextProvider>

View File

@ -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 (
<ConversationContext.Provider value={{ state, actions }}>
{children}
</ConversationContext.Provider>
);
}

View File

@ -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 }
}

View File

@ -1,5 +1,5 @@
import { View, TouchableOpacity, Text } from 'react-native'; import { View, TouchableOpacity, Text, FlatList } from 'react-native';
import { useLayoutEffect } from 'react'; import { useState, useRef } from 'react';
import { useConversation } from './useConversation.hook'; import { useConversation } from './useConversation.hook';
import { styles } from './Conversation.styled'; import { styles } from './Conversation.styled';
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
@ -33,15 +33,12 @@ export function ConversationBody({ channel }) {
const { state, actions } = useConversation(channel?.cardId, channel?.channelId); const { state, actions } = useConversation(channel?.cardId, channel?.channelId);
return ( return (
<View> <FlatList style={styles.topics}
<Text>CHANNEL</Text> data={state.topics}
{ channel && ( inverted={true}
<> renderItem={({item}) => <View style={{ height: item.id }}><Text>ITEM { item.id }</Text></View>}
<Text>{ channel?.cardId }</Text> keyExtractor={item => item.id}
<Text>{ channel?.channelId }</Text> />
</>
)}
</View>
); );
} }

View File

@ -41,5 +41,8 @@ export const styles = StyleSheet.create({
action: { action: {
paddingLeft: 8, paddingLeft: 8,
}, },
topics: {
height: '100%',
},
}) })

View File

@ -1,105 +1,28 @@
import { useState, useEffect, useContext } from 'react'; import { useState, useEffect, useContext } from 'react';
import { CardContext } from 'context/CardContext'; import { ConversationContext } from 'context/ConversationContext';
import { ChannelContext } from 'context/ChannelContext';
import { ProfileContext } from 'context/ProfileContext';
export function useConversation(cardId, channelId) { export function useConversation(cardId, channelId) {
const [state, setState] = useState({ const [state, setState] = useState({
topics: [],
subject: null, subject: null,
logo: null, logo: null,
}); });
const card = useContext(CardContext); const conversation = useContext(ConversationContext);
const channel = useContext(ChannelContext);
const profile = useContext(ProfileContext);
const updateState = (value) => { const updateState = (value) => {
setState((s) => ({ ...s, ...value })); setState((s) => ({ ...s, ...value }));
} }
const getCard = (guid) => { useEffect(() => {
let contact = null conversation.actions.setChannel({ cardId, channelId });
card.state.cards.forEach((card, cardId, map) => { }, [cardId, channelId]);
if (card?.profile?.guid === guid) {
contact = card;
}
});
return contact;
}
useEffect(() => { useEffect(() => {
console.log(cardId, channelId); const { topics, subject, logo } = conversation.state;
let item; updateState({ topics, subject, logo });
if (cardId) { }, [conversation]);
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 actions = { const actions = {
}; };

View File

@ -23,6 +23,8 @@ export function Profile() {
const { state, actions } = useProfile(); const { state, actions } = useProfile();
console.log(state.imageSource);
const setVisible = async (visible) => { const setVisible = async (visible) => {
try { try {
await actions.setVisible(visible); await actions.setVisible(visible);

View File

@ -70,6 +70,7 @@ export function useProfile() {
navigate('/'); navigate('/');
}, },
setVisible: async (visible) => { setVisible: async (visible) => {
updateState({ visible });
await account.actions.setSearchable(visible); await account.actions.setSearchable(visible);
}, },
setProfileImage: async (data) => { setProfileImage: async (data) => {