databag/net/web/src/context/useConversationContext.hook.js

415 lines
12 KiB
JavaScript
Raw Normal View History

2022-04-28 18:30:54 +00:00
import { useEffect, useState, useRef, useContext } from 'react';
2022-05-23 22:10:33 +00:00
import { ProfileContext } from 'context/ProfileContext';
2022-04-28 18:30:54 +00:00
import { CardContext } from 'context/CardContext';
import { ChannelContext } from 'context/ChannelContext';
import CryptoJS from 'crypto-js';
import { JSEncrypt } from 'jsencrypt'
2022-04-28 18:30:54 +00:00
export function useConversationContext() {
2022-07-10 07:33:50 +00:00
const TOPIC_BATCH = 32;
2022-04-28 18:30:54 +00:00
const [state, setState] = useState({
init: false,
2022-07-14 22:38:20 +00:00
error: false,
2022-08-30 19:48:33 +00:00
loadingInit: true,
loadingMore: false,
2022-05-04 07:50:31 +00:00
cardId: null,
channelId: null,
2022-05-13 07:15:15 +00:00
subject: null,
2022-05-15 18:04:27 +00:00
contacts: null,
2022-05-23 22:10:33 +00:00
members: new Set(),
2022-04-28 18:30:54 +00:00
topics: new Map(),
2022-07-11 07:19:59 +00:00
revision: null,
enableImage: null,
enabelAudio: null,
enableVideo: null,
sealed: false,
seals: null,
image: null,
logoUrl: null,
logoImg: null,
2022-04-28 18:30:54 +00:00
});
2022-07-10 07:33:50 +00:00
const EVENT_OPEN = 1;
const EVENT_MORE = 2;
const EVENT_UPDATE = 3;
2022-07-14 22:38:20 +00:00
const EVENT_RESYNC = 4;
2022-07-10 07:33:50 +00:00
const events = useRef([]);
const channelView = useRef({
cardId: null,
channelId: null,
batch: 1,
revision: null,
begin: null,
init: false,
error: false,
});
2022-04-28 18:30:54 +00:00
const card = useContext(CardContext);
const channel = useContext(ChannelContext);
2022-05-23 22:10:33 +00:00
const profile = useContext(ProfileContext);
2022-04-28 18:30:54 +00:00
const topics = useRef(new Map());
2022-05-04 07:50:31 +00:00
const view = useRef(0);
const more = useRef(true);
2022-07-10 07:33:50 +00:00
const serialize = useRef(0);
2022-04-28 18:30:54 +00:00
const updateState = (value) => {
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;
}
2022-05-13 07:15:15 +00:00
const getSubject = (conversation) => {
2022-07-11 07:19:59 +00:00
if (!conversation) {
return null;
}
if (conversation.data.channelDetail.dataType === 'sealed') {
return conversation.data.unsealedChannel?.subject;
2022-05-13 07:15:15 +00:00
}
else {
try {
let subject = JSON.parse(conversation.data.channelDetail.data).subject;
if (subject) {
return subject;
}
}
catch (err) {
console.log(err);
}
2022-05-13 07:15:15 +00:00
}
return null;
2022-05-15 18:04:27 +00:00
}
const getContacts = (conversation) => {
2022-07-11 07:19:59 +00:00
if (!conversation) {
return null;
}
2022-05-13 07:15:15 +00:00
let members = [];
if (conversation.guid) {
members.push(card.actions.getCardByGuid(conversation.guid)?.data?.cardProfile?.handle);
}
for (let member of conversation.data.channelDetail.members) {
let contact = card.actions.getCardByGuid(member)
if(contact?.data?.cardProfile?.handle) {
members.push(contact?.data?.cardProfile?.handle);
}
}
return members.join(', ');
}
2022-05-23 22:10:33 +00:00
const getMembers = (conversation) => {
2022-07-11 07:19:59 +00:00
if (!conversation) {
return null;
}
2022-05-23 22:10:33 +00:00
let members = new Set();
if (conversation.guid) {
members.add(conversation.guid);
}
for (let member of conversation.data.channelDetail.members) {
2022-08-05 19:36:07 +00:00
if (profile.state.profile.guid !== member) {
2022-05-23 22:10:33 +00:00
members.add(member);
}
}
return members;
}
2022-07-10 07:33:50 +00:00
const getChannel = () => {
const { cardId, channelId } = channelView.current;
if (cardId) {
return card.actions.getChannel(cardId, channelId);
}
return channel.actions.getChannel(channelId);
}
const getTopicDelta = async (revision, count, begin, end) => {
const { cardId, channelId } = channelView.current;
if (cardId) {
return await card.actions.getChannelTopics(cardId, channelId, revision, count, begin, end);
}
return await channel.actions.getChannelTopics(channelId, revision, count, begin, end);
}
2022-05-02 07:49:11 +00:00
2022-07-10 07:33:50 +00:00
const getTopic = async (topicId) => {
const { cardId, channelId } = channelView.current;
2022-04-28 18:30:54 +00:00
if (cardId) {
2022-07-10 07:33:50 +00:00
return await card.actions.getChannelTopic(cardId, channelId, topicId);
}
return await channel.actions.getChannelTopic(channelId, topicId);
}
const getChannelRevision = async () => {
const { cardId, channelId } = channelView.current;
if (cardId) {
return await card.actions.getChannelRevision(cardId, channelId);
}
return await channel.actions.getChannelRevision(channelId);
}
const setTopicDelta = async (delta, curView) => {
for (let topic of delta) {
if (topic.data == null) {
2022-08-05 19:36:07 +00:00
if (curView === view.current) {
2022-07-10 07:33:50 +00:00
topics.current.delete(topic.id);
}
2022-05-12 06:33:00 +00:00
}
2022-07-10 07:33:50 +00:00
else {
let cur = topics.current.get(topic.id);
if (cur == null) {
cur = { id: topic.id, data: {} };
}
2022-08-05 19:36:07 +00:00
if (topic.data.detailRevision !== cur.data.detailRevision) {
2022-07-10 07:33:50 +00:00
if(topic.data.topicDetail) {
cur.data.topicDetail = topic.data.topicDetail;
cur.data.detailRevision = topic.data.detailRevision;
cur.data.unsealedMessage = null;
2022-04-28 18:30:54 +00:00
}
else {
2022-07-10 07:33:50 +00:00
let slot = await getTopic(topic.id);
cur.data.topicDetail = slot.data.topicDetail;
cur.data.detailRevision = slot.data.detailRevision;
cur.data.unsealedMessage = null;
2022-04-28 18:30:54 +00:00
}
}
2022-07-10 07:33:50 +00:00
cur.revision = topic.revision;
2022-08-05 19:36:07 +00:00
if (curView === view.current) {
2022-07-10 07:33:50 +00:00
topics.current.set(topic.id, cur);
2022-05-04 07:50:31 +00:00
}
2022-04-28 18:30:54 +00:00
}
}
2022-07-10 07:33:50 +00:00
}
const setTopics = async (ev) => {
const curView = view.current;
try {
2022-08-05 19:36:07 +00:00
if (ev.type === EVENT_OPEN) {
2022-07-10 07:33:50 +00:00
const { cardId, channelId } = ev.data;
channelView.current.cardId = cardId;
channelView.current.channelId = channelId;
channelView.current.batch = 1;
channelView.current.error = false;
channelView.current.init = true;
let delta = await getTopicDelta(null, TOPIC_BATCH, null, null);
await setTopicDelta(delta.topics, curView);
channelView.current.revision = delta.revision;
channelView.current.begin = delta.marker;
}
2022-08-05 19:36:07 +00:00
else if (ev.type === EVENT_MORE) {
2022-07-10 07:33:50 +00:00
if (channelView.current.init) {
channelView.current.batch += 1;
let delta = await getTopicDelta(null, channelView.current.batch * TOPIC_BATCH, null, channelView.current.begin);
await setTopicDelta(delta.topics, curView);
channelView.current.begin = delta.marker;
2022-05-04 07:50:31 +00:00
}
2022-07-10 07:33:50 +00:00
}
2022-08-05 19:36:07 +00:00
else if (ev.type === EVENT_UPDATE || ev.type === EVENT_RESYNC) {
2022-07-10 07:33:50 +00:00
let deltaRevision = getChannelRevision();
2022-08-05 19:36:07 +00:00
if (channelView.current.init && deltaRevision !== channelView.current.revision) {
2022-07-10 07:33:50 +00:00
let delta = await getTopicDelta(channelView.current.revision, null, channelView.current.begin, null);
await setTopicDelta(delta.topics, curView);
2022-07-11 07:19:59 +00:00
channelView.current.revision = delta.revision;
2022-05-04 07:50:31 +00:00
}
2022-04-28 18:30:54 +00:00
}
2022-07-11 07:19:59 +00:00
2022-08-05 19:36:07 +00:00
if (curView === view.current) {
2022-07-11 07:19:59 +00:00
let chan = getChannel();
2022-07-11 07:19:59 +00:00
let contacts = getContacts(chan);
let subject = getSubject(chan);
2022-07-11 07:19:59 +00:00
let members = getMembers(chan);
let seals = getSeals(chan);
2022-09-01 19:03:42 +00:00
const enableImage = chan?.data?.channelDetail?.enableImage;
const enableAudio = chan?.data?.channelDetail?.enableAudio;
const enableVideo = chan?.data?.channelDetail?.enableVideo;
const sealed = chan?.data?.channelDetail?.dataType === 'sealed';
let logoUrl = null;
let logoImg = null;
if (!members?.size) {
subject = subject ? subject : "Notes";
logoImg = "solution";
}
else if (members.size > 1) {
subject = subject ? subject : "Group";
logoImg = "appstore";
}
else {
const contact = card.actions.getCardByGuid(members.values().next().value);
subject = subject ? subject : card.actions.getName(contact.id);
logoUrl = card.actions.getImageUrl(contact.id);
}
2022-07-11 07:19:59 +00:00
updateState({
init: true,
2022-07-14 22:38:20 +00:00
error: false,
sealed,
seals,
2022-07-11 07:19:59 +00:00
subject,
logoImg,
logoUrl,
2022-07-11 07:19:59 +00:00
contacts,
members,
enableImage,
enableAudio,
enableVideo,
2022-07-11 07:19:59 +00:00
topics: topics.current,
revision: channelView.current.revision,
});
}
2022-07-10 07:33:50 +00:00
}
catch (err) {
2022-07-14 22:38:20 +00:00
console.log(err);
updateState({ error: true });
2022-04-28 18:30:54 +00:00
}
}
const updateConversation = async () => {
if (!card.state.init || !channel.state.init) {
return;
}
2022-08-05 19:36:07 +00:00
if (serialize.current === 0) {
2022-07-10 07:33:50 +00:00
serialize.current++;
2022-04-28 18:30:54 +00:00
2022-07-10 07:33:50 +00:00
while (events.current.length > 0) {
2022-07-11 07:19:59 +00:00
// collapse updates
while (events.current.length > 1) {
2022-08-05 19:36:07 +00:00
if(events.current[0].type === EVENT_UPDATE && events.current[1].type === EVENT_UPDATE) {
2022-07-11 07:19:59 +00:00
events.current.shift();
}
else {
break;
}
}
const ev = events.current.shift();
await setTopics(ev);
2022-05-02 07:49:11 +00:00
}
2022-08-30 19:48:33 +00:00
updateState({ loadingInit: false, loadingMore: false });
2022-07-10 07:33:50 +00:00
serialize.current--;
2022-04-28 18:30:54 +00:00
}
};
useEffect(() => {
2022-07-10 07:33:50 +00:00
events.current.push({ type: EVENT_UPDATE });
updateConversation();
2022-09-02 05:35:28 +00:00
// eslint-disable-next-line
2022-04-28 18:30:54 +00:00
}, [card, channel]);
const actions = {
setConversationId: (cardId, channelId) => {
2022-05-04 07:50:31 +00:00
view.current += 1;
2022-08-30 19:48:33 +00:00
updateState({ init: false, loadingInit: true });
2022-07-10 07:33:50 +00:00
events.current = [{ type: EVENT_OPEN, data: { cardId, channelId }}];
2022-07-11 07:19:59 +00:00
updateState({ subject: null, cardId, channelId, topics: new Map() });
2022-07-10 17:20:05 +00:00
topics.current = new Map();
2022-07-10 07:33:50 +00:00
updateConversation();
},
addHistory: () => {
if (more.current && !state.loadingMore) {
more.current = false;
updateState({ loadingMore: true });
events.current.push({ type: EVENT_MORE });
updateConversation();
setTimeout(() => {
more.current = true;
}, 2000);
}
2022-05-02 07:49:11 +00:00
},
2022-05-15 18:04:27 +00:00
setChannelSubject: async (subject) => {
2022-07-10 07:33:50 +00:00
return await channel.actions.setChannelSubject(channelView.current.channelId, subject);
2022-05-15 18:04:27 +00:00
},
2022-05-24 22:21:12 +00:00
setChannelCard: async (cardId) => {
2022-07-10 07:33:50 +00:00
return await channel.actions.setChannelCard(channelView.current.channelId, cardId);
2022-05-24 22:21:12 +00:00
},
clearChannelCard: async (cardId) => {
2022-07-10 07:33:50 +00:00
return await channel.actions.clearChannelCard(channelView.current.channelId, cardId);
2022-05-24 22:21:12 +00:00
},
2022-05-02 07:49:11 +00:00
getAssetUrl: (topicId, assetId) => {
2022-07-10 07:33:50 +00:00
const { cardId, channelId } = channelView.current;
if (channelView.current.cardId) {
2022-05-02 07:49:11 +00:00
return card.actions.getContactChannelTopicAssetUrl(cardId, channelId, topicId, assetId);
}
else {
return channel.actions.getChannelTopicAssetUrl(channelId, topicId, assetId);
}
2022-05-12 06:33:00 +00:00
},
removeConversation: async () => {
2022-07-10 07:33:50 +00:00
const { cardId, channelId } = channelView.current;
2022-05-12 06:33:00 +00:00
if (cardId) {
return await card.actions.removeChannel(cardId, channelId);
}
else {
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);
}
},
2022-05-26 22:19:58 +00:00
removeTopic: async (topicId) => {
2022-07-10 07:33:50 +00:00
const { cardId, channelId } = channelView.current;
2022-05-26 22:19:58 +00:00
if (cardId) {
return await card.actions.removeChannelTopic(cardId, channelId, topicId);
}
else {
return await channel.actions.removeChannelTopic(channelId, topicId);
}
},
2022-05-27 06:57:16 +00:00
setTopicSubject: async (topicId, data) => {
2022-07-10 07:33:50 +00:00
const { cardId, channelId } = channelView.current;
2022-05-27 06:57:16 +00:00
if (cardId) {
return await card.actions.setChannelTopicSubject(cardId, channelId, topicId, data);
}
else {
return await channel.actions.setChannelTopicSubject(channelId, topicId, data);
}
2022-07-14 22:38:20 +00:00
},
setSealedTopicSubject: async (topicId, data, sealKey) => {
const { cardId, channelId } = channelView.current;
if (cardId) {
return await card.actions.setSealedChannelTopicSubject(cardId, channelId, topicId, data, sealKey);
}
else {
return await channel.actions.setSealedChannelTopicSubject(channelId, topicId, data, sealKey);
}
},
2022-07-14 22:38:20 +00:00
resync: () => {
2022-08-31 04:15:13 +00:00
updateState({ error: false });
2022-07-14 22:38:20 +00:00
events.current.push({ type: EVENT_RESYNC });
updateConversation();
2022-05-27 06:57:16 +00:00
}
2022-04-28 18:30:54 +00:00
}
return { state, actions }
}