diff --git a/net/web/src/api/addChannelTopic.js b/net/web/src/api/addChannelTopic.js
index 69f76d2a..e30106c3 100644
--- a/net/web/src/api/addChannelTopic.js
+++ b/net/web/src/api/addChannelTopic.js
@@ -1,6 +1,6 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
-export async function addChannelTopic(token, channelId, message, assets ): string {
+export async function addChannelTopic(token, channelId, datatype, message, assets ): string {
if (message == null && (assets == null || assets.length === 0)) {
let topic = await fetchWithTimeout(`/content/channels/${channelId}/topics?agent=${token}`,
@@ -12,7 +12,7 @@ export async function addChannelTopic(token, channelId, message, assets ): strin
else if (assets == null || assets.length === 0) {
let subject = { data: JSON.stringify(message, (key, value) => {
if (value !== null) return value
- }), datatype: 'superbasictopic' };
+ }), datatype };
let topic = await fetchWithTimeout(`/content/channels/${channelId}/topics?agent=${token}&confirm=true`,
{ method: 'POST', body: JSON.stringify(subject) });
@@ -78,7 +78,7 @@ export async function addChannelTopic(token, channelId, message, assets ): strin
let subject = { data: JSON.stringify(message, (key, value) => {
if (value !== null) return value
- }), datatype: 'superbasictopic' };
+ }), datatype };
let unconfirmed = await fetchWithTimeout(`/content/channels/${channelId}/topics/${slot.id}/subject?agent=${token}`,
{ method: 'PUT', body: JSON.stringify(subject) });
diff --git a/net/web/src/api/addContactChannelTopic.js b/net/web/src/api/addContactChannelTopic.js
index 8e962b88..34c134ff 100644
--- a/net/web/src/api/addContactChannelTopic.js
+++ b/net/web/src/api/addContactChannelTopic.js
@@ -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, datatype, message, assets ) {
let host = "";
if (server) {
host = `https://${server}`
@@ -16,7 +16,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 };
let topic = await fetchWithTimeout(`${host}/content/channels/${channelId}/topics?contact=${token}&confirm=true`,
{ method: 'POST', body: JSON.stringify(subject) });
@@ -81,7 +81,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 };
let unconfirmed = await fetchWithTimeout(`${host}/content/channels/${channelId}/topics/${slot.id}/subject?contact=${token}`,
{ method: 'PUT', body: JSON.stringify(subject) });
diff --git a/net/web/src/context/useCardContext.hook.js b/net/web/src/context/useCardContext.hook.js
index 6dbca521..7b121f24 100644
--- a/net/web/src/context/useCardContext.hook.js
+++ b/net/web/src/context/useCardContext.hook.js
@@ -332,7 +332,7 @@ export function useCardContext() {
let token = cardProfile.guid + '.' + cardDetail.token;
let node = cardProfile.node;
if (files?.length) {
- 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);
@@ -346,7 +346,7 @@ export function useCardContext() {
});
}
else {
- await addContactChannelTopic(node, token, channelId, message, files);
+ await addContactChannelTopic(node, token, channelId, 'superbasictopic', message, files);
}
try {
resync.current.push(cardId);
@@ -356,6 +356,17 @@ export function useCardContext() {
console.log(err);
}
},
+ addSealedChannelTopic: async (cardId, channelId, sealKey, message) => {
+ let { cardProfile, cardDetail } = cards.current.get(cardId).data;
+ let token = cardProfile.guid + '.' + cardDetail.token;
+ let node = cardProfile.node;
+ 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 });
+ },
getChannel: (cardId, channelId) => {
let card = cards.current.get(cardId);
let channel = card.channels.get(channelId);
diff --git a/net/web/src/context/useChannelContext.hook.js b/net/web/src/context/useChannelContext.hook.js
index 5d59e211..47db14f1 100644
--- a/net/web/src/context/useChannelContext.hook.js
+++ b/net/web/src/context/useChannelContext.hook.js
@@ -202,7 +202,7 @@ export function useChannelContext() {
},
addChannelTopic: async (channelId, message, files) => {
if (files?.length) {
- const topicId = await addChannelTopic(access.current, channelId, null, null);
+ const topicId = await addChannelTopic(access.current, channelId, null, null, null);
upload.actions.addTopic(access.current, channelId, topicId, files, async (assets) => {
message.assets = assets;
await setChannelTopicSubject(access.current, channelId, topicId, message);
@@ -216,7 +216,7 @@ export function useChannelContext() {
});
}
else {
- await addChannelTopic(access.current, channelId, message, files);
+ await addChannelTopic(access.current, channelId, 'superbasictopic', message, files);
}
try {
await setChannels(null);
@@ -225,6 +225,14 @@ export function useChannelContext() {
console.log(err);
}
},
+ addSealedChannelTopic: async (channelId, sealKey, message) => {
+ 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(access.current, channelId, 'sealedtopic', { messageEncrypted, messageIv });
+ },
getChannel: (channelId) => {
return channels.current.get(channelId);
},
diff --git a/net/web/src/context/useConversationContext.hook.js b/net/web/src/context/useConversationContext.hook.js
index d6f58a15..efa914ae 100644
--- a/net/web/src/context/useConversationContext.hook.js
+++ b/net/web/src/context/useConversationContext.hook.js
@@ -2,6 +2,8 @@ import { useEffect, useState, useRef, useContext } from 'react';
import { ProfileContext } from 'context/ProfileContext';
import { CardContext } from 'context/CardContext';
import { ChannelContext } from 'context/ChannelContext';
+import CryptoJS from 'crypto-js';
+import { JSEncrypt } from 'jsencrypt'
export function useConversationContext() {
const TOPIC_BATCH = 32;
@@ -22,6 +24,7 @@ export function useConversationContext() {
enabelAudio: null,
enableVideo: null,
sealed: false,
+ seals: null,
image: null,
logoUrl: null,
logoImg: null,
@@ -55,6 +58,18 @@ export function useConversationContext() {
setState((s) => ({ ...s, ...value }));
}
+ const getSeals = (conversation) => {
+ try {
+ if (conversation.data.channelDetail.dataType === 'sealed') {
+ return JSON.parse(conversation.data.channelDetail.data).seals;
+ }
+ }
+ catch (err) {
+ console.log(err);
+ }
+ return null;
+ }
+
const getSubject = (conversation) => {
if (!conversation) {
return null;
@@ -160,11 +175,13 @@ export function useConversationContext() {
if(topic.data.topicDetail) {
cur.data.topicDetail = topic.data.topicDetail;
cur.data.detailRevision = topic.data.detailRevision;
+ cur.data.unsealedMessage = null;
}
else {
let slot = await getTopic(topic.id);
cur.data.topicDetail = slot.data.topicDetail;
cur.data.detailRevision = slot.data.detailRevision;
+ cur.data.unsealedMessage = null;
}
}
cur.revision = topic.revision;
@@ -212,6 +229,7 @@ export function useConversationContext() {
let contacts = getContacts(chan);
let subject = getSubject(chan);
let members = getMembers(chan);
+ const seals = getSeals(chan);
const enableImage = chan?.data?.channelDetail?.enableImage;
const enableAudio = chan?.data?.channelDetail?.enableAudio;
const enableVideo = chan?.data?.channelDetail?.enableVideo;
@@ -237,6 +255,7 @@ export function useConversationContext() {
init: true,
error: false,
sealed,
+ seals,
subject,
logoImg,
logoUrl,
@@ -337,6 +356,23 @@ export function useConversationContext() {
return await channel.actions.removeChannel(channelId);
}
},
+ unsealTopic: async (topicId, sealKey) => {
+ try {
+ const topic = topics.current.get(topicId);
+ const { messageEncrypted, messageIv } = JSON.parse(topic.data.topicDetail.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.data.unsealedMessage = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
+ topics.current.set(topicId, topic);
+ updateState({ topics: topics.current });
+ }
+ catch(err) {
+ console.log(err);
+ }
+ },
removeTopic: async (topicId) => {
const { cardId, channelId } = channelView.current;
if (cardId) {
diff --git a/net/web/src/session/conversation/Conversation.jsx b/net/web/src/session/conversation/Conversation.jsx
index f68c2a59..c44284a6 100644
--- a/net/web/src/session/conversation/Conversation.jsx
+++ b/net/web/src/session/conversation/Conversation.jsx
@@ -14,7 +14,7 @@ export function Conversation({ closeConversation, openDetails, cardId, channelId
const thread = useRef(null);
const topicRenderer = (topic) => {
- return (