optimizing conversation load

This commit is contained in:
Roland Osborne 2023-03-03 10:30:28 -08:00
parent 74305a0994
commit ef1c81343d
8 changed files with 103 additions and 41 deletions

View File

@ -87,16 +87,18 @@ export function useConversationContext() {
if (!curTopicMarker.current) { if (!curTopicMarker.current) {
const delta = await getTopicDelta(cardId, channelId, null, COUNT, null, null); const delta = await getTopicDelta(cardId, channelId, null, COUNT, null, null);
await setTopicDelta(cardId, channelId, delta.topics); await setTopicDelta(cardId, channelId, delta.topics);
await setMarkerAndSync(cardId, channelId, delta.marker, delta.revision); const marker = delta.marker ? delta.marker : 1;
curTopicMarker.current = delta.marker; await setMarkerAndSync(cardId, channelId, marker, delta.revision);
curTopicMarker.current = marker;
curSyncRevision.current = delta.revision; curSyncRevision.current = delta.revision;
updateState({ loaded: true, offsync: false, topics: topics.current, card: cardValue, channel: channelValue }); updateState({ loaded: true, offsync: false, topics: topics.current, card: cardValue, channel: channelValue });
} }
else if (loadMore && marker) { else if (loadMore && marker) {
const delta = await getTopicDelta(cardId, channelId, null, COUNT, null, curTopicMarker.current); const delta = await getTopicDelta(cardId, channelId, null, COUNT, null, curTopicMarker.current);
const marker = delta.marker ? delta.marker : 1;
await setTopicDelta(cardId, channelId, delta.topics); await setTopicDelta(cardId, channelId, delta.topics);
await setTopicMarker(cardId, channelId, delta.marker); await setTopicMarker(cardId, channelId, marker);
curTopicMarker.current = delta.marker; curTopicMarker.current = marker;
updateState({ loaded: true, offsync: false, topics: topics.current, card: cardValue, channel: channelValue }); updateState({ loaded: true, offsync: false, topics: topics.current, card: cardValue, channel: channelValue });
} }
else if (ignoreRevision || topicRevision > curSyncRevision.current) { else if (ignoreRevision || topicRevision > curSyncRevision.current) {
@ -105,8 +107,6 @@ export function useConversationContext() {
await setSyncRevision(cardId, channelId, delta.revision); await setSyncRevision(cardId, channelId, delta.revision);
curSyncRevision.current = delta.revision; curSyncRevision.current = delta.revision;
updateState({ loaded: true, offsync: false, topics: topics.current, card: cardValue, channel: channelValue }); updateState({ loaded: true, offsync: false, topics: topics.current, card: cardValue, channel: channelValue });
console.log("HEADER", delta);
} }
else { else {
updateState({ loaded: true, offsync: false, topics: topics.current, card: cardValue, channel: channelValue }); updateState({ loaded: true, offsync: false, topics: topics.current, card: cardValue, channel: channelValue });
@ -419,7 +419,6 @@ console.log("HEADER", delta);
topic[field] = value; topic[field] = value;
} }
topics.current.set(topicId, { ...topic }); topics.current.set(topicId, { ...topic });
updateState({ topics: topics.current });
}; };
return { state, actions } return { state, actions }

View File

@ -1,7 +1,7 @@
import { useEffect, useState, useRef, useContext } from 'react'; import { useEffect, useState, useRef, useContext } from 'react';
import SQLite from "react-native-sqlite-storage"; import SQLite from "react-native-sqlite-storage";
const DATABAG_DB = 'db_v_123.db'; const DATABAG_DB = 'db_v_126.db';
export function useStoreContext() { export function useStoreContext() {
const [state, setState] = useState({}); const [state, setState] = useState({});

View File

@ -56,7 +56,7 @@ export function Conversation({ navigation, cardId, channelId, closeConversation,
), ),
}); });
} }
}, [navigation, state.subject]); }, [navigation, state.subject, state.loaded]);
useEffect(() => { useEffect(() => {
return () => { closeConversation(); }; return () => { closeConversation(); };
@ -85,7 +85,7 @@ export function Conversation({ navigation, cardId, channelId, closeConversation,
<ActivityIndicator color={Colors.grey} size="large" /> <ActivityIndicator color={Colors.grey} size="large" />
</View> </View>
)} )}
{ state.loaded && ( { state.loaded && state.topics.length !== 0 && (
<FlatList style={styles.conversation} <FlatList style={styles.conversation}
contentContainerStyle={styles.topics} contentContainerStyle={styles.topics}
data={state.topics} data={state.topics}
@ -93,14 +93,18 @@ export function Conversation({ navigation, cardId, channelId, closeConversation,
initialNumToRender={16} initialNumToRender={16}
renderItem={({item}) => <TopicItem item={item} focused={item.topicId === state.focus} renderItem={({item}) => <TopicItem item={item} focused={item.topicId === state.focus}
focus={() => actions.setFocus(item.topicId)} hosting={state.host == null} focus={() => actions.setFocus(item.topicId)} hosting={state.host == null}
sealed={state.sealed} sealKey={state.sealKey}
remove={actions.removeTopic} update={actions.editTopic} block={actions.blockTopic} remove={actions.removeTopic} update={actions.editTopic} block={actions.blockTopic}
report={actions.reportTopic} />} report={actions.reportTopic} contentKey={state.contentKey} /> }
keyExtractor={item => item.topicId} keyExtractor={item => item.topicId}
/> />
)} )}
{ state.loaded && state.topics.length === 0 && (
<View style={styles.empty}>
<Text style={styles.emptytext}>Empty Topic</Text>
</View> </View>
<AddTopic /> )}
</View>
<AddTopic contentKey={state.contentKey} />
</View> </View>
</View> </View>
); );

View File

@ -67,5 +67,15 @@ export const styles = StyleSheet.create({
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
}, },
empty: {
display: 'flex',
flexGrow: 1,
alignItems: 'center',
justifyContent: 'center',
},
emptytext: {
fontSize: 18,
color: Colors.disabled,
},
}); });

View File

@ -13,9 +13,9 @@ import { VideoFile } from './videoFile/VideoFile';
import { AudioFile } from './audioFile/AudioFile'; import { AudioFile } from './audioFile/AudioFile';
import { ImageFile } from './imageFile/ImageFile'; import { ImageFile } from './imageFile/ImageFile';
export function AddTopic({ sealed, sealKey }) { export function AddTopic({ contentKey }) {
const { state, actions } = useAddTopic(sealed, sealKey); const { state, actions } = useAddTopic(contentKey);
const message = useRef(); const message = useRef();
const addImage = async () => { const addImage = async () => {
@ -132,22 +132,22 @@ export function AddTopic({ sealed, sealKey }) {
onSubmitEditing={sendMessage} returnKeyType="send" onSubmitEditing={sendMessage} returnKeyType="send"
autoCapitalize="sentences" placeholder="New Message" multiline={true} /> autoCapitalize="sentences" placeholder="New Message" multiline={true} />
<View style={styles.addButtons}> <View style={styles.addButtons}>
{ !sealed && state.enableImage && ( { !state.locked && state.enableImage && (
<TouchableOpacity style={styles.addButton} onPress={addImage}> <TouchableOpacity style={styles.addButton} onPress={addImage}>
<AntIcons name="picture" size={20} color={Colors.text} /> <AntIcons name="picture" size={20} color={Colors.text} />
</TouchableOpacity> </TouchableOpacity>
)} )}
{ !sealed && state.enableVideo && ( { !state.locked && state.enableVideo && (
<TouchableOpacity style={styles.addButton} onPress={addVideo}> <TouchableOpacity style={styles.addButton} onPress={addVideo}>
<MatIcons name="video-outline" size={24} color={Colors.text} /> <MatIcons name="video-outline" size={24} color={Colors.text} />
</TouchableOpacity> </TouchableOpacity>
)} )}
{ !sealed && state.enableAudio && ( { !state.locked && state.enableAudio && (
<TouchableOpacity style={styles.addButton} onPress={addAudio}> <TouchableOpacity style={styles.addButton} onPress={addAudio}>
<MatIcons name="music-box-outline" size={20} color={Colors.text} /> <MatIcons name="music-box-outline" size={20} color={Colors.text} />
</TouchableOpacity> </TouchableOpacity>
)} )}
{ !sealed && ( { !state.locked && (
<View style={styles.divider} /> <View style={styles.divider} />
)} )}
<TouchableOpacity style={styles.addButton} onPress={actions.showFontSize}> <TouchableOpacity style={styles.addButton} onPress={actions.showFontSize}>
@ -161,10 +161,13 @@ export function AddTopic({ sealed, sealKey }) {
{ state.busy && ( { state.busy && (
<ActivityIndicator color={Colors.primary} /> <ActivityIndicator color={Colors.primary} />
)} )}
{ !state.busy && (state.message || state.assets.length > 0) && ( { state.locked && !contentKey && (
<MatIcons name="lock" size={20} color={Colors.lightgrey} />
)}
{ !state.busy && (!state.locked || contentKey) && (state.message || state.assets.length > 0) && (
<MatIcons name="send-outline" size={20} color={Colors.text} /> <MatIcons name="send-outline" size={20} color={Colors.text} />
)} )}
{ !state.busy && !(state.message || state.assets.length > 0) && ( { !state.busy && (!state.locked || contentKey) && !(state.message || state.assets.length > 0) && (
<MatIcons name="send-outline" size={20} color={Colors.lightgrey} /> <MatIcons name="send-outline" size={20} color={Colors.lightgrey} />
)} )}
</TouchableOpacity> </TouchableOpacity>

View File

@ -5,7 +5,7 @@ import Colors from 'constants/Colors';
import { getChannelSeals, getContentKey, encryptTopicSubject } from 'context/sealUtil'; import { getChannelSeals, getContentKey, encryptTopicSubject } from 'context/sealUtil';
import { AccountContext } from 'context/AccountContext'; import { AccountContext } from 'context/AccountContext';
export function useAddTopic(sealed, sealKey) { export function useAddTopic(contentKey) {
const [state, setState] = useState({ const [state, setState] = useState({
message: null, message: null,
@ -21,6 +21,7 @@ export function useAddTopic(sealed, sealKey) {
enableImage: false, enableImage: false,
enableAudio: false, enableAudio: false,
enableVideo: false, enableVideo: false,
locked: true,
}); });
const assetId = useRef(0); const assetId = useRef(0);
@ -33,7 +34,8 @@ export function useAddTopic(sealed, sealKey) {
useEffect(() => { useEffect(() => {
const { enableVideo, enableAudio, enableImage } = conversation.state.channel?.detail || {}; const { enableVideo, enableAudio, enableImage } = conversation.state.channel?.detail || {};
updateState({ enableImage, enableAudio, enableVideo }); const locked = conversation.state.channel?.detail?.dataType === 'superbasic' ? false : true;
updateState({ enableImage, enableAudio, enableVideo, locked });
}, [conversation.state]); }, [conversation.state]);
const actions = { const actions = {
@ -107,20 +109,12 @@ export function useAddTopic(sealed, sealKey) {
updateState({ color, colorSet: true }); updateState({ color, colorSet: true });
}, },
addTopic: async () => { addTopic: async () => {
if (!state.busy) { if (!state.busy && (!state.locked || contentKey)) {
try { try {
updateState({ busy: true }); updateState({ busy: true });
let contentKey;
const type = conversation.state.channel?.detail?.dataType === 'superbasic' ? 'superbasictopic' : 'sealedtopic';
if (type === 'sealedtopic') {
const channelDetail = conversation.state.channel?.detail;
const seals = getChannelSeals(channelDetail?.data);
const sealKey = account.state.sealKey;
contentKey = await getContentKey(seals, sealKey);
}
const assemble = (assets) => { const assemble = (assets) => {
if (type === 'superbasictopic') { if (!state.locked) {
if (assets?.length) { if (assets?.length) {
return { return {
assets, assets,
@ -146,6 +140,7 @@ export function useAddTopic(sealed, sealKey) {
return encryptTopicSubject({ message }, contentKey); return encryptTopicSubject({ message }, contentKey);
} }
}; };
const type = state.locked ? "sealedtopic" : "superbasictopic";
await conversation.actions.addTopic(type, assemble, state.assets); await conversation.actions.addTopic(type, assemble, state.assets);
updateState({ busy: false, assets: [], message: null, updateState({ busy: false, assets: [], message: null,
size: 'medium', sizeSet: false, textSize: 14, size: 'medium', sizeSet: false, textSize: 14,

View File

@ -7,7 +7,7 @@ import moment from 'moment';
import { useWindowDimensions } from 'react-native'; import { useWindowDimensions } from 'react-native';
import Colors from 'constants/Colors'; import Colors from 'constants/Colors';
import { getCardByGuid } from 'context/cardUtil'; import { getCardByGuid } from 'context/cardUtil';
import { getChannelSeals, isUnsealed, getContentKey, decryptTopicSubject } from 'context/sealUtil'; import { decryptTopicSubject } from 'context/sealUtil';
export function useTopicItem(item, hosting, remove, contentKey) { export function useTopicItem(item, hosting, remove, contentKey) {
@ -118,9 +118,26 @@ export function useTopicItem(item, hosting, remove, contentKey) {
} }
} }
else if (dataType === 'sealedtopic') { else if (dataType === 'sealedtopic') {
if (unsealedDetail) { let unsealed = unsealedDetail;
if (!unsealed && contentKey) {
try {
unsealed = decryptTopicSubject(detail?.data, contentKey);
(async () => {
try {
await conversation.actions.unsealTopic(topicId, revision, unsealed);
}
catch(err) {
console.log(err);
}
})();
}
catch(err) {
console.log(err);
}
}
if (unsealed) {
sealed = false; sealed = false;
parsed = unsealedDetail.message; parsed = unsealed.message;
message = parsed?.text; message = parsed?.text;
if (parsed?.textSize === 'small') { if (parsed?.textSize === 'small') {
fontSize = 10; fontSize = 10;
@ -140,7 +157,6 @@ export function useTopicItem(item, hosting, remove, contentKey) {
} }
else { else {
sealed = true; sealed = true;
unsealTopic(topicId, revision, detail);
} }
} }
@ -162,18 +178,15 @@ export function useTopicItem(item, hosting, remove, contentKey) {
const deletable = editable || hosting; const deletable = editable || hosting;
updateState({ logo, name, nameSet, known, sealed, message, fontSize, fontColor, timestamp, transform, status, assets, deletable, editable, editData: parsed, editMessage: message }); updateState({ logo, name, nameSet, known, sealed, message, fontSize, fontColor, timestamp, transform, status, assets, deletable, editable, editData: parsed, editMessage: message });
}, [conversation.state, card.state, account.state, item]); }, [conversation.state, card.state, account.state, item, contentKey]);
const unsealTopic = async (topicId, revision, topicDetail) => { const unsealTopic = async (topicId, revision, topicDetail) => {
try { try {
console.log("UNSEAL", topicId);
const channelDetail = conversation.state.channel?.detail; const channelDetail = conversation.state.channel?.detail;
const seals = getChannelSeals(channelDetail?.data); const seals = getChannelSeals(channelDetail?.data);
const sealKey = account.state.sealKey; const sealKey = account.state.sealKey;
if (isUnsealed(seals, sealKey)) { if (isUnsealed(seals, sealKey)) {
const contentKey = await getContentKey(seals, sealKey); const contentKey = await getContentKey(seals, sealKey);
const unsealed = decryptTopicSubject(topicDetail.data, contentKey);
await conversation.actions.unsealTopic(topicId, revision, unsealed);
} }
} }
catch(err) { catch(err) {

View File

@ -4,6 +4,7 @@ import { CardContext } from 'context/CardContext';
import { AccountContext } from 'context/AccountContext'; import { AccountContext } from 'context/AccountContext';
import { ConversationContext } from 'context/ConversationContext'; import { ConversationContext } from 'context/ConversationContext';
import { getChannelSubjectLogo } from 'context/channelUtil'; import { getChannelSubjectLogo } from 'context/channelUtil';
import { getChannelSeals, isUnsealed, getContentKey, decryptTopicSubject } from 'context/sealUtil';
export function useConversation() { export function useConversation() {
const [state, setState] = useState({ const [state, setState] = useState({
@ -11,6 +12,7 @@ export function useConversation() {
logo: null, logo: null,
topic: [], topic: [],
loaded: false, loaded: false,
contentKey: null,
}); });
const updateState = (value) => { const updateState = (value) => {
@ -22,6 +24,42 @@ export function useConversation() {
const conversation = useContext(ConversationContext); const conversation = useContext(ConversationContext);
const account = useContext(AccountContext); const account = useContext(AccountContext);
const contentKey = useRef();
const keyId = useRef();
useEffect(() => {
setContentKey();
}, [conversation.state, account.state]);
const setContentKey = async () => {
const type = conversation.state.channel?.detail?.dataType;
if (type === 'sealed') {
const cardId = conversation.state.card?.card?.cardId;
const channelId = conversation.state.channel?.channelId;
const contentId = `${cardId}:${channelId}`;
if (contentId !== keyId.current) {
const channelDetail = conversation.state.channel?.detail;
const seals = getChannelSeals(channelDetail?.data);
const sealKey = account.state.sealKey;
if (isUnsealed(seals, sealKey)) {
contentKey.current = await getContentKey(seals, sealKey);
keyId.current = contentId;
updateState({ contentKey: contentKey.current });
}
else if (keyId.current != null) {
contentKey.current = null;
keyId.current = null;
updateState({ contentKey: null });
}
}
}
else if (keyId.current != null) {
contentKey.current = null;
keyId.current = null;
updateState({ contentKey: null });
}
};
useEffect(() => { useEffect(() => {
const loaded = conversation.state.loaded; const loaded = conversation.state.loaded;
const cardId = conversation.state.card?.cardId; const cardId = conversation.state.card?.cardId;