unsealing topic messages in mobile app

This commit is contained in:
balzack 2022-12-15 23:41:51 -08:00
parent faca16f748
commit b43a26b240
15 changed files with 249 additions and 87 deletions

View File

@ -1,6 +1,6 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function addChannelTopic(server, token, channelId, message, assets ): string {
export async function addChannelTopic(server, token, channelId, messageType, message, assets ): string {
if (message == null && (assets == null || assets.length === 0)) {
let topic = await fetchWithTimeout(`https://${server}/content/channels/${channelId}/topics?agent=${token}`,
@ -12,7 +12,7 @@ export async function addChannelTopic(server, token, channelId, message, assets
else if (assets == null || assets.length === 0) {
let subject = { data: JSON.stringify(message, (key, value) => {
if (value !== null) return value
}), datatype: 'superbasictopic' };
}), datatype: messageType };
let topic = await fetchWithTimeout(`https://${server}/content/channels/${channelId}/topics?agent=${token}&confirm=true`,
{ method: 'POST', body: JSON.stringify(subject) });
@ -78,7 +78,7 @@ export async function addChannelTopic(server, token, channelId, message, assets
let subject = { data: JSON.stringify(message, (key, value) => {
if (value !== null) return value
}), datatype: 'superbasictopic' };
}), datatype: messageType };
let unconfirmed = await fetchWithTimeout(`https://${server}/content/channels/${channelId}/topics/${slot.id}/subject?agent=${token}`,
{ method: 'PUT', body: JSON.stringify(subject) });

View File

@ -1,6 +1,6 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function addContactChannelTopic(server, token, channelId, message, assets ) {
export async function addContactChannelTopic(server, token, channelId, messageType, message, assets ) {
if (message == null && (assets == null || assets.length === 0)) {
let topic = await fetchWithTimeout(`https://${server}/content/channels/${channelId}/topics?contact=${token}`,
{ method: 'POST', body: JSON.stringify({}) });
@ -11,7 +11,7 @@ export async function addContactChannelTopic(server, token, channelId, message,
else if (assets == null || assets.length === 0) {
let subject = { data: JSON.stringify(message, (key, value) => {
if (value !== null) return value
}), datatype: 'superbasictopic' };
}), datatype: messageType };
let topic = await fetchWithTimeout(`https://${server}/content/channels/${channelId}/topics?contact=${token}&confirm=true`,
{ method: 'POST', body: JSON.stringify(subject) });
@ -76,7 +76,7 @@ export async function addContactChannelTopic(server, token, channelId, message,
let subject = { data: JSON.stringify(message, (key, value) => {
if (value !== null) return value
}), datatype: 'superbasictopic' };
}), datatype: messageType };
let unconfirmed = await fetchWithTimeout(`https://${server}/content/channels/${channelId}/topics/${slot.id}/subject?contact=${token}`,
{ method: 'PUT', body: JSON.stringify(subject) });

View File

@ -1,9 +1,9 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function setChannelTopicSubject(server, token, channelId, topicId, data) {
export async function setChannelTopicSubject(server, token, channelId, topicId, dataType, data) {
let subject = { data: JSON.stringify(data, (key, value) => {
if (value !== null) return value
}), datatype: 'superbasictopic' };
}), datatype: dataType };
let channel = await fetchWithTimeout(`https://${server}/content/channels/${channelId}/topics/${topicId}/subject?agent=${token}&confirm=true`,
{ method: 'PUT', body: JSON.stringify(subject) });

View File

@ -1,9 +1,9 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function setContactChannelTopicSubject(server, token, channelId, topicId, data) {
export async function setContactChannelTopicSubject(server, token, channelId, topicId, dataType, data) {
let subject = { data: JSON.stringify(data, (key, value) => {
if (value !== null) return value
}), datatype: 'superbasictopic' };
}), datatype: dataType };
let channel = await fetchWithTimeout(`https://${server}/content/channels/${channelId}/topics/${topicId}/subject?contact=${token}&confirm=true`,
{ method: 'PUT', body: JSON.stringify(subject) });

View File

@ -553,10 +553,10 @@ export function useCardContext() {
const node = profile.node;
const token = `${profile.guid}.${detail.token}`;
if (files?.length > 0) {
const topicId = await addContactChannelTopic(node, token, channelId, null, null);
const topicId = await addContactChannelTopic(node, token, channelId, null, null, null);
upload.actions.addContactTopic(node, token, cardId, channelId, topicId, files, async (assets) => {
message.assets = assets;
await setContactChannelTopicSubject(node, token, channelId, topicId, message);
await setContactChannelTopicSubject(node, token, channelId, topicId, 'superbasictopic', message);
}, async () => {
try {
await removeContactChannelTopic(node, token, channelId, topicId);
@ -567,13 +567,28 @@ export function useCardContext() {
});
}
else {
await addContactChannelTopic(node, token, channelId, message, []);
await addContactChannelTopic(node, token, channelId, 'superbasictopic', message, []);
}
},
addSealedChannelTopic: async (cardId, channelId, message, sealKey) => {
const { detail, profile } = getCardEntry(cardId);
const node = profile.node;
const token = `${profile.guid}.${detail.token}`;
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const key = CryptoJS.enc.Hex.parse(sealKey);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ message }), key, { iv: iv });
const messageEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
const messageIv = iv.toString();
await addContactChannelTopic(node, token, channelId, 'sealedtopic', { messageEncrypted, messageIv });
},
setChannelTopicSubject: async (cardId, channelId, topicId, data) => {
const { detail, profile } = getCardEntry(cardId);
return await setContactChannelTopicSubject(profile.node, `${profile.guid}.${detail.token}`, channelId, topicId, data);
},
setChannelTopicUnsealedDetail: async (cardId, channelId, topicId, revision, unsealed) => {
const { guid } = session.current;
await store.actions.setCardChannelTopicItemUnsealedDetail(guid, cardId, channelId, revision, unsealed);
},
removeChannel: async (cardId, channelId) => {
const { detail, profile } = getCardEntry(cardId);
return await removeContactChannel(profile.node, `${profile.guid}.${detail.token}`, channelId);
@ -604,25 +619,31 @@ export function useCardContext() {
const card = cards.current.get(cardId);
const channel = card.channels.get(channelId);
const { subjectEncrypted, subjectIv, seals } = JSON.parse(channel.detail.data);
seals.forEach(seal => {
if (seal.publicKey === sealKey.public) {
let crypto = new JSEncrypt();
crypto.setPrivateKey(sealKey.private);
const unsealedKey = crypto.decrypt(seal.sealedKey);
const iv = CryptoJS.enc.Hex.parse(subjectIv);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
const enc = CryptoJS.enc.Base64.parse(subjectEncrypted);
let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
if (revision === channel.detailRevision) {
channel.unsealedDetail = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
let unsealed = false;
if (seals?.length) {
seals.forEach(seal => {
if (seal.publicKey === sealKey.public) {
let crypto = new JSEncrypt();
crypto.setPrivateKey(sealKey.private);
const unsealedKey = crypto.decrypt(seal.sealedKey);
const iv = CryptoJS.enc.Hex.parse(subjectIv);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
const enc = CryptoJS.enc.Base64.parse(subjectEncrypted);
let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
if (revision === channel.detailRevision) {
channel.unsealedDetail = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
unsealed = true;
}
}
});
if (unsealed) {
await store.actions.setCardChannelItemUnsealedDetail(guid, cardId, channelId, revision, channel.unsealedDetail);
card.channels.set(channelId, { ...channel });
cards.current.set(cardId, { ...card });
updateState({ cards: cards.current });
}
});
await store.actions.setCardChannelItemUnsealedDetail(guid, cardId, channelId, revision, channel.unsealedDetail);
card.channels.set(channelId, { ...channel });
cards.current.set(cardId, { ...card });
updateState({ cards: cards.current });
}
}
catch(err) {
console.log(err);

View File

@ -252,10 +252,10 @@ export function useChannelContext() {
addTopic: async (channelId, message, files) => {
const { server, appToken } = session.current;
if (files?.length > 0) {
const topicId = await addChannelTopic(server, appToken, channelId, null, null);
const topicId = await addChannelTopic(server, appToken, channelId, null, null, null);
upload.actions.addTopic(server, appToken, channelId, topicId, files, async (assets) => {
message.assets = assets;
await setChannelTopicSubject(server, appToken, channelId, topicId, message);
await setChannelTopicSubject(server, appToken, channelId, topicId, 'superbasictopic', message);
}, async () => {
try {
await removeChannelTopic(server, appToken, channelId, topicId);
@ -266,13 +266,26 @@ export function useChannelContext() {
});
}
else {
await addChannelTopic(server, appToken, channelId, message, []);
await addChannelTopic(server, appToken, channelId, 'superbasictopic', message, []);
}
},
addSealedTopic: async (channelId, message, sealKey) => {
const { server, appToken } = session.current;
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const key = CryptoJS.enc.Hex.parse(sealKey);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ message }), key, { iv: iv });
const messageEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
const messageIv = iv.toString();
await addChannelTopic(server, appToken, channelId, 'sealedtopic', { messageEncrypted, messageIv });
},
setTopicSubject: async (channelId, topicId, data) => {
const { server, appToken } = session.current;
return await setChannelTopicSubject(server, appToken, channelId, topicId, data);
},
setTopicUnsealedDetail: async (channelId, topicId, revision, unsealed) => {
const { guid } = session.current;
await store.actions.setChannelTopicItemUnsealedDetail(guid, channelId, topicId, revision, unsealed);
},
setSubject: async (channelId, subject) => {
const { server, appToken } = session.current;
return await setChannelSubject(server, appToken, channelId, 'superbasic', { subject });
@ -360,24 +373,30 @@ export function useChannelContext() {
const { guid } = session.current;
const channel = channels.current.get(channelId);
const { subjectEncrypted, subjectIv, seals } = JSON.parse(channel.detail.data);
seals.forEach(seal => {
if (seal.publicKey === sealKey.public) {
let crypto = new JSEncrypt();
crypto.setPrivateKey(sealKey.private);
const unsealedKey = crypto.decrypt(seal.sealedKey);
const iv = CryptoJS.enc.Hex.parse(subjectIv);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
const enc = CryptoJS.enc.Base64.parse(subjectEncrypted);
let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
if (revision === channel.detailRevision) {
channel.unsealedDetail = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
if (seals?.length) {
let unsealed = false;
seals.forEach(seal => {
if (seal.publicKey === sealKey.public) {
let crypto = new JSEncrypt();
crypto.setPrivateKey(sealKey.private);
const unsealedKey = crypto.decrypt(seal.sealedKey);
const iv = CryptoJS.enc.Hex.parse(subjectIv);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
const enc = CryptoJS.enc.Base64.parse(subjectEncrypted);
let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
if (revision === channel.detailRevision) {
channel.unsealedDetail = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
unsealed = true;
}
}
});
if (unsealed) {
await store.actions.setChannelItemUnsealedDetail(guid, channelId, revision, channel.unsealedDetail);
channels.current.set(channelId, { ...channel });
updateState({ channels: channels.current });
}
});
await store.actions.setChannelItemUnsealedDetail(guid, channelId, revision, channel.unsealedDetail);
channels.current.set(channelId, { ...channel });
updateState({ channels: channels.current });
}
}
catch(err) {
console.log(err);

View File

@ -5,6 +5,8 @@ import { CardContext } from 'context/CardContext';
import { ChannelContext } from 'context/ChannelContext';
import { ProfileContext } from 'context/ProfileContext';
import moment from 'moment';
import CryptoJS from 'crypto-js';
import { JSEncrypt } from 'jsencrypt'
export function useConversationContext() {
const [state, setState] = useState({
@ -432,6 +434,46 @@ export function useConversationContext() {
sync();
}
},
addSealedTopic: async (message, sealKey) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
await card.actions.addSealedChannelTopic(cardId, channelId, message, sealKey);
}
else {
await channel.actions.addSealedTopic(channelId, message, sealKey);
}
force.current = true;
sync();
}
},
unsealTopic: async (topicId, sealKey) => {
console.log("UNSEAL TOPIC");
try {
const topic = topics.current.get(topicId);
const { messageEncrypted, messageIv } = JSON.parse(topic.detail.data);
const iv = CryptoJS.enc.Hex.parse(messageIv);
const key = CryptoJS.enc.Hex.parse(sealKey);
const enc = CryptoJS.enc.Base64.parse(messageEncrypted);
let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
topic.unsealedDetail = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
topics.current.set(topicId, { ...topic });
updateState({ topics: topics.current });
const { cardId, channelId } = conversationId.current;
if (cardId) {
await card.actions.setChannelTopicUnsealedDetail(cardId, channelId, topic.topicId, topic.detailRevision, topic.unsealedDetial);
}
else {
console.log("channel topic", topic);
await channel.actions.setTopicUnsealedDetail(channelId, topic.topicId, topic.detailRevision, topic.unsealedDetail);
}
}
catch(err) {
console.log(err);
}
},
setSubject: async (subject) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;

View File

@ -271,6 +271,10 @@ export function useStoreContext() {
const { id, revision, data } = topic;
await db.current.executeSql(`INSERT OR REPLACE INTO channel_topic_${guid} (channel_id, topic_id, revision, detail_revision, detail, unsealed_detail) values (?, ?, ?, ?, ?, null);`, [channelId, id, revision, data.detailRevision, encodeObject(data.topicDetail)]);
},
setChannelTopicItemUnsealedDetail: async (guid, channelId, topicId, revision, unsealed) => {
console.log("SAVING:", channelId, revision, unsealed);
await db.current.executeSql(`UPDATE channel_topic_${guid} set unsealed_detail=? where detail_revision=? AND channel_id=? AND topic_id=?`, [encodeObject(unsealed), revision, channelId, topicId]);
},
clearChannelTopicItem: async (guid, channelId, topicId) => {
await db.current.executeSql(`DELETE FROM channel_topic_${guid} WHERE channel_id=? and topic_id=?`, [channelId, topicId]);
},
@ -309,6 +313,7 @@ export function useStoreContext() {
await db.current.executeSql(`UPDATE card_channel_${guid} set detail_revision=?, detail=?, unsealed_detail=null where card_id=? and channel_id=?`, [revision, encodeObject(detail), cardId, channelId]);
},
setCardChannelItemUnsealedDetail: async (guid, cardId, channelId, revision, unsealed) => {
console.log("SAVING:", cardId, channelId, revision, unsealed);
await db.current.executeSql(`UPDATE card_channel_${guid} set unsealed_detail=? where detail_revision=? AND card_id=? AND channel_id=?`, [encodeObject(unsealed), revision, cardId, channelId]);
},
setCardChannelItemSummary: async (guid, cardId, channelId, revision, summary) => {
@ -361,6 +366,9 @@ export function useStoreContext() {
const { id, revision, data } = topic;
await db.current.executeSql(`INSERT OR REPLACE INTO card_channel_topic_${guid} (card_id, channel_id, topic_id, revision, detail_revision, detail, unsealed_detail) values (?, ?, ?, ?, ?, ?, null);`, [cardId, channelId, id, revision, data.detailRevision, encodeObject(data.topicDetail)]);
},
setCardChannelTopicItemUnsealedDetail: async (guid, cardId, channelId, topicId, revision, unsealed) => {
await db.current.executeSql(`UPDATE card_channel_topic_${guid} set unsealed_detail=? where detail_revision=? AND card_id=? AND channel_id=? AND topic_id=?`, [encodeObject(unsealed), revision, cardId, channelId, topicId]);
},
clearCardChannelTopicItem: async (guid, cardId, channelId, topicId) => {
await db.current.executeSql(`DELETE FROM card_channel_topic_${guid} WHERE card_id=? and channel_id=? and topic_id=?`, [cardId, channelId, topicId]);
},

View File

@ -100,7 +100,8 @@ export function ConversationBody() {
maintainVisibleContentPosition={ state.latched ? null : { minIndexForVisibile: 2, } }
inverted={true}
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}
report={actions.reportTopic} />}
keyExtractor={item => item.topicId}
@ -112,7 +113,9 @@ export function ConversationBody() {
</View>
)}
<View>
<AddTopic />
{ (!state.locked || state.sealKey) && (
<AddTopic sealed={state.locked} sealKey={state.sealKey} />
)}
<View style={styles.latchbar}>
{ !state.latched && (
<TouchableOpacity style={styles.latch} onPress={latch}>

View File

@ -13,9 +13,9 @@ import { ImageFile } from './imageFile/ImageFile';
import DocumentPicker from 'react-native-document-picker'
import ColorPicker from 'react-native-wheel-color-picker'
export function AddTopic() {
export function AddTopic({ sealed, sealKey }) {
const { state, actions } = useAddTopic();
const { state, actions } = useAddTopic(sealed, sealKey);
const message = useRef();
const addImage = async () => {
@ -132,22 +132,24 @@ export function AddTopic() {
onSubmitEditing={sendMessage} returnKeyType="send"
autoCapitalize="sentences" placeholder="New Message" multiline={true} />
<View style={styles.addButtons}>
{ state.enableImage && (
{ !sealed && state.enableImage && (
<TouchableOpacity style={styles.addButton} onPress={addImage}>
<AntIcons name="picture" size={20} color={Colors.text} />
</TouchableOpacity>
)}
{ state.enableVideo && (
{ !sealed && state.enableVideo && (
<TouchableOpacity style={styles.addButton} onPress={addVideo}>
<MaterialIcons name="video-outline" size={24} color={Colors.text} />
</TouchableOpacity>
)}
{ state.enableAudio && (
{ !sealed && state.enableAudio && (
<TouchableOpacity style={styles.addButton} onPress={addAudio}>
<MaterialIcons name="music-box-outline" size={20} color={Colors.text} />
</TouchableOpacity>
)}
<View style={styles.divider} />
{ !sealed && (
<View style={styles.divider} />
)}
<TouchableOpacity style={styles.addButton} onPress={actions.showFontSize}>
<MaterialIcons name="format-size" size={20} color={Colors.text} />
</TouchableOpacity>

View File

@ -3,7 +3,7 @@ import { ConversationContext } from 'context/ConversationContext';
import { Image } from 'react-native';
import Colors from 'constants/Colors';
export function useAddTopic(cardId, channelId) {
export function useAddTopic(sealed, sealKey) {
const [state, setState] = useState({
message: null,
@ -112,7 +112,12 @@ export function useAddTopic(cardId, channelId) {
textColor: state.colorSet ? state.color : null,
textSize: state.sizeSet ? state.size : null,
};
await conversation.actions.addTopic(message, state.assets);
if (sealed) {
await conversation.actions.addSealedTopic(message, sealKey);
}
else {
await conversation.actions.addTopic(message, state.assets);
}
updateState({ busy: false, assets: [], message: null,
size: 'medium', sizeSet: false, textSize: 14,
color: Colors.text, colorSet: false,

View File

@ -15,9 +15,9 @@ import Carousel from 'react-native-snap-carousel';
import GestureRecognizer from 'react-native-swipe-gestures';
import avatar from 'images/avatar.png';
export function TopicItem({ item, focused, focus, hosting, remove, update, block, report }) {
export function TopicItem({ item, focused, focus, hosting, sealed, sealKey, remove, update, block, report }) {
const { state, actions } = useTopicItem(item, hosting);
const { state, actions } = useTopicItem(item, hosting, remove, sealed, sealKey);
const erase = () => {
Alert.alert(
@ -163,9 +163,12 @@ export function TopicItem({ item, focused, focus, hosting, remove, update, block
<MatIcons name="weather-cloudy-alert" size={32} color={Colors.alert} />
</View>
)}
{ state.message && (
{ state.message && !state.sealed && (
<Text style={{ ...styles.message, fontSize: state.fontSize, color: state.fontColor }}>{ state.message }</Text>
)}
{ state.sealed && (
<Text style={ styles.sealed }>sealed message</Text>
)}
</>
)}
{ state.status !== 'confirmed' && (

View File

@ -47,6 +47,12 @@ export const styles = StyleSheet.create({
status: {
paddingLeft: 52,
},
sealed: {
paddingRight: 16,
paddingLeft: 52,
color: Colors.grey,
fontStyle: 'italic',
},
focused: {
position: 'absolute',
top: 0,

View File

@ -1,11 +1,12 @@
import { useState, useEffect, useContext } from 'react';
import { ConversationContext } from 'context/ConversationContext';
import { CardContext } from 'context/CardContext';
import { ProfileContext } from 'context/ProfileContext';
import moment from 'moment';
import { useWindowDimensions } from 'react-native';
import Colors from 'constants/Colors';
export function useTopicItem(item, hosting, remove) {
export function useTopicItem(item, hosting, remove, sealed, sealKey) {
const [state, setState] = useState({
name: null,
@ -25,6 +26,7 @@ export function useTopicItem(item, hosting, remove) {
deletable: false,
});
const conversation = useContext(ConversationContext);
const profile = useContext(ProfileContext);
const card = useContext(CardContext);
const dimensions = useWindowDimensions();
@ -38,8 +40,8 @@ export function useTopicItem(item, hosting, remove) {
}, [dimensions]);
useEffect(() => {
const { topicId, detail } = item;
const { guid, data, status, transform } = detail;
const { topicId, detail, unsealedDetail } = item;
const { guid, dataType, data, status, transform } = detail;
let name, nameSet, known, logo;
const identity = profile.state?.profile;
@ -87,29 +89,59 @@ export function useTopicItem(item, hosting, remove) {
}
}
let parsed, message, assets, fontSize, fontColor;
try {
parsed = JSON.parse(data);
message = parsed.text;
assets = parsed.assets;
if (parsed.textSize === 'small') {
fontSize = 10;
let parsed, sealed, message, assets, fontSize, fontColor;
if (dataType === 'superbasictopic') {
try {
sealed = false;
parsed = JSON.parse(data);
message = parsed.text;
assets = parsed.assets;
if (parsed.textSize === 'small') {
fontSize = 10;
}
else if (parsed.textSize === 'large') {
fontSize = 20;
}
else {
fontSize = 14;
}
if (parsed.textColor) {
fontColor = parsed.textColor;
}
else {
fontColor = Colors.text;
}
}
else if (parsed.textSize === 'large') {
fontSize = 20;
}
else {
fontSize = 14;
}
if (parsed.textColor) {
fontColor = parsed.textColor;
}
else {
fontColor = Colors.text;
catch (err) {
console.log(err);
}
}
else if (dataType === 'sealedtopic') {
if (unsealedDetail) {
sealed = false;
parsed = unsealedDetail.message;
message = parsed?.text;
if (parsed?.textSize === 'small') {
fontSize = 10;
}
else if (parsed?.textSize === 'large') {
fontSize = 20;
}
else {
fontSize = 14;
}
if (parsed?.textColor) {
fontColor = parsed?.textColor;
}
else {
fontColor = Colors.text;
}
}
else {
conversation.actions.unsealTopic(topicId, sealKey);
sealed = true;
}
}
catch (err) { }
let timestamp;
const date = new Date(item.detail.created * 1000);
@ -128,7 +160,7 @@ export function useTopicItem(item, hosting, remove) {
const editable = detail.guid === identity.guid && parsed;
const deletable = editable || hosting;
updateState({ logo, name, nameSet, known, 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 });
}, [card, item]);
const actions = {

View File

@ -1,5 +1,8 @@
import { useRef, useState, useEffect, useContext } from 'react';
import { ConversationContext } from 'context/ConversationContext';
import { AccountContext } from 'context/AccountContext';
import CryptoJS from 'crypto-js';
import { JSEncrypt } from 'jsencrypt'
export function useConversation() {
@ -18,15 +21,33 @@ export function useConversation() {
init: false,
error: false,
keyboard: false,
locked: false,
sealKey: null,
});
const delay = useRef(null);
const conversation = useContext(ConversationContext);
const account = useContext(AccountContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
useEffect(() => {
let sealKey;
const { locked, seals } = conversation.state;
if (seals?.length) {
seals.forEach(seal => {
if (seal.publicKey === account.state.sealKey?.public) {
let crypto = new JSEncrypt();
crypto.setPrivateKey(account.state.sealKey.private);
sealKey = crypto.decrypt(seal.sealedKey);
}
});
}
updateState({ locked, sealKey });
}, [conversation.state.locked, conversation.state.seals, account.state.sealKey])
useEffect(() => {
const { error, subject, logo, topics, host, init } = conversation.state;
const items = Array.from(topics.values());
@ -42,7 +63,7 @@ export function useConversation() {
return -1;
});
const filtered = sorted.filter(item => !(item.blocked === 1));
updateState({ topics, subject, logo, host, error, topics: filtered });
updateState({ subject, logo, host, error, topics: filtered });
if (init) {
clearTimeout(delay.current);
updateState({ init: true });