databag/app/mobile/src/context/useConversationContext.hook.js

678 lines
22 KiB
JavaScript
Raw Normal View History

2022-09-28 22:09:10 +00:00
import { useState, useEffect, useRef, useContext } from 'react';
import { StoreContext } from 'context/StoreContext';
2022-10-22 06:01:28 +00:00
import { UploadContext } from 'context/UploadContext';
2022-09-28 22:09:10 +00:00
import { CardContext } from 'context/CardContext';
import { ChannelContext } from 'context/ChannelContext';
import { ProfileContext } from 'context/ProfileContext';
2022-10-09 04:55:56 +00:00
import moment from 'moment';
2022-12-16 07:41:51 +00:00
import CryptoJS from 'crypto-js';
2022-09-28 22:09:10 +00:00
export function useConversationContext() {
const [state, setState] = useState({
2022-10-10 06:26:32 +00:00
topic: null,
2022-09-28 22:09:10 +00:00
subject: null,
logo: null,
2022-10-04 21:00:49 +00:00
revision: null,
2022-09-28 22:09:10 +00:00
contacts: [],
2022-09-30 05:34:31 +00:00
topics: new Map(),
2022-10-18 20:40:14 +00:00
created: null,
2022-10-09 04:55:56 +00:00
host: null,
2022-10-20 22:09:10 +00:00
init: false,
2022-10-22 06:01:28 +00:00
progress: null,
cardId: null,
channelId: null,
2022-11-14 21:30:58 +00:00
pushEnabled: null,
2022-12-14 23:07:45 +00:00
locked: false,
unlocked: false,
seals: null,
2022-09-28 22:09:10 +00:00
});
const store = useContext(StoreContext);
2022-10-22 06:01:28 +00:00
const upload = useContext(UploadContext);
2022-09-28 22:09:10 +00:00
const card = useContext(CardContext);
const channel = useContext(ChannelContext);
const profile = useContext(ProfileContext);
2022-09-30 05:34:31 +00:00
const topics = useRef(null);
const revision = useRef(null);
2022-10-06 22:34:29 +00:00
const force = useRef(false);
const more = useRef(false);
2022-09-29 18:31:55 +00:00
const detailRevision = useRef(0);
2022-09-28 22:09:10 +00:00
const syncing = useRef(false);
2022-09-29 18:31:55 +00:00
const conversationId = useRef(null);
2022-09-30 05:34:31 +00:00
const reset = useRef(false);
2022-09-28 22:09:10 +00:00
const setView = useRef(0);
2022-10-22 17:57:59 +00:00
const transfer = useRef(null);
2022-09-28 22:09:10 +00:00
const updateState = (value) => {
setState((s) => ({ ...s, ...value }))
}
2022-10-22 06:01:28 +00:00
useEffect(() => {
const { cardId, channelId } = state;
const key = cardId ? `${cardId}:${channelId}` : `:${channelId}`
const progress = upload.state.progress.get(key);
if (progress) {
let count = 0;
let complete = 0;
let active = 0;
let loaded = 0;
let total = 0;
let error = false;
progress.forEach(post => {
count += post.count;
complete += (post.index - 1);
if (post.active) {
active += 1;
loaded += post.active.loaded;
total += post.active.total;
}
if (post.error) {
error = true;
}
});
percent = Math.floor(((((loaded / total) * active) + complete) / count) * 100);
2022-10-22 17:57:59 +00:00
if (transfer.current == null || error || Math.abs(transfer.current - percent) > 5) {
updateState({ progress: percent, uploadError: error });
transfer.current = percent;
}
2022-10-22 06:01:28 +00:00
if (error) {
setTimeout(() => {
upload.actions.clearErrors(cardId, channelId);
updateState({ progress: null, uploadError: false });
2022-10-22 17:57:59 +00:00
transfer.current = null;
2022-10-22 06:01:28 +00:00
}, 2000);
}
}
else {
updateState({ progress: null });
2022-10-22 17:57:59 +00:00
transfer.current = null;
2022-10-22 06:01:28 +00:00
}
}, [upload, state.cardId, state.channelId]);
2022-09-29 18:31:55 +00:00
const getTopicItems = async (cardId, channelId) => {
if (cardId) {
return await card.actions.getChannelTopicItems(cardId, channelId);
}
return await channel.actions.getTopicItems(channelId);
}
2022-09-30 05:34:31 +00:00
const setTopicItem = async (cardId, channelId, topic) => {
2022-09-29 18:31:55 +00:00
if (cardId) {
2022-09-30 05:34:31 +00:00
return await card.actions.setChannelTopicItem(cardId, channelId, topic);
2022-09-29 18:31:55 +00:00
}
2022-09-30 05:34:31 +00:00
return await channel.actions.setTopicItem(channelId, topic);
2022-09-29 18:31:55 +00:00
}
const clearTopicItem = async (cardId, channelId, topicId) => {
if (cardId) {
return await card.actions.clearChannelTopicItem(cardId, channelId, topicId);
}
return await channel.actions.clearTopicItem(channelId, topicId);
}
const getTopic = async (cardId, channelId, topicId) => {
2022-09-29 22:21:18 +00:00
if (cardId) {
return await card.actions.getChannelTopic(cardId, channelId, topicId);
}
return await channel.actions.getTopic(channelId, topicId);
2022-09-29 18:31:55 +00:00
}
const getTopics = async (cardId, channelId, revision, begin, end) => {
2022-09-29 22:21:18 +00:00
if (cardId) {
return await card.actions.getChannelTopics(cardId, channelId, revision, 16, begin, end);
2022-09-29 22:21:18 +00:00
}
return await channel.actions.getTopics(channelId, revision, 16, begin, end)
2022-09-29 18:31:55 +00:00
}
2022-09-29 22:21:18 +00:00
const getTopicAssetUrl = (cardId, channelId, assetId) => {
if (cardId) {
return card.actions.getChannelTopicAssetUrl(cardId, channelId, topicId, assetId);
}
return channel.actions.getTopicAssetUrl(channelId, assetId);
2022-09-29 18:31:55 +00:00
}
const addTopic = async (cardId, channelId, message, asssets) => {
2022-09-29 22:21:18 +00:00
if (cardId) {
return await card.actions.addChannelTopic(cardId, channelId, message, assetId);
}
return await channel.actions.addTopic(channelId, message, assetId);
2022-09-29 18:31:55 +00:00
}
const setTopicSubject = async (cardId, channelId, topicId, dataType, data) => {
2022-09-29 22:21:18 +00:00
if (cardId) {
return await card.actions.setChannelTopicSubject(cardId, channelId, topicId, dataType, data);
2022-09-29 22:21:18 +00:00
}
return await channel.actions.setTopicSubject(channelId, topicId, dataType, data);
2022-09-29 18:31:55 +00:00
}
2022-09-29 22:21:18 +00:00
const remove = async (cardId, channelId) => {
if (cardId) {
return await card.actions.removeChannel(cardId, channelId);
}
return await channel.actions.remove(channelId);
2022-09-29 18:31:55 +00:00
}
2022-09-29 22:21:18 +00:00
const removeTopic = async (cardId, channelId, topicId) => {
if (cardId) {
return await card.actions.removeChannelTopic(cardId, channelId, topicId);
}
2022-11-14 21:30:58 +00:00
return await channel.actions.removeTopic(channelId, topicId);
}
const setNotifications = async (cardId, channelId, notify) => {
if (cardId) {
return await card.actions.setChannelNotifications(cardId, channelId, notify);
}
return await channel.actions.setNotifications(channelId, notify);
}
const getNotifications = async (cardId, channelId) => {
if (cardId) {
return await card.actions.getChannelNotifications(cardId, channelId);
}
return await channel.actions.getNotifications(channelId);
2022-09-29 18:31:55 +00:00
}
2022-09-30 05:34:31 +00:00
const setSyncRevision = async (cardId, channelId, revision) => {
if (cardId) {
return await card.actions.setSyncRevision(cardId, channelId, revision);
}
return await channel.actions.setSyncRevision(channelId, revision);
}
const setTopicMarker = async (cardId, channelId, marker) => {
if (cardId) {
return await card.actions.setTopicMarker(cardId, channelId, marker);
}
return await channel.actions.setTopicMarker(channelId, marker);
}
2022-09-29 18:31:55 +00:00
useEffect(() => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
const channelItem = getChannel(cardId, channelId);
if (channelItem) {
setChannel(channelItem);
}
}
}, [card, channel]);
2022-09-28 22:09:10 +00:00
const sync = async () => {
const curView = setView.current;
2022-09-30 05:34:31 +00:00
if (!syncing.current) {
if (reset.current) {
revision.current = null;
detailRevision.current = null;
topics.current = null;
reset.current = false;
}
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
const channelItem = getChannel(cardId, channelId);
if (channelItem && (channelItem.revision !== revision.current || force.current || more.current)) {
2022-09-30 05:34:31 +00:00
syncing.current = true;
2022-09-29 18:31:55 +00:00
2022-09-30 05:34:31 +00:00
try {
2022-09-28 22:09:10 +00:00
// sync from server
let res;
2022-09-30 05:34:31 +00:00
if (!topics.current) {
topics.current = new Map();
const items = await getTopicItems(cardId, channelId);
items.forEach(item => {
topics.current.set(item.topicId, item);
});
await setChannel(channelItem);
detailRevision.current = channelItem.detailRevision;
2022-09-29 23:00:48 +00:00
}
else if (detailRevision.current != channelItem.detailRevision) {
await setChannel(channelItem);
detailRevision.current = channelItem.detailRevision;
}
else if (more.current) {
more.current = false;
res = await getTopics(cardId, channelId, null, null, channelItem.topicMarker)
}
else if (channelItem.topicRevision !== channelItem.syncRevision || force.current) {
2022-10-06 22:34:29 +00:00
force.current = false;
res = await getTopics(cardId, channelId, channelItem.syncRevision, channelItem.topicMarker)
}
else {
if (cardId) {
card.actions.setChannelReadRevision(cardId, channelId, revision.current);
}
else {
channel.actions.setReadRevision(channelId, channelItem.revision);
}
revision.current = channelItem.revision;
}
if (res?.topics) {
2022-09-30 05:34:31 +00:00
for (const topic of res.topics) {
if (!topic.data) {
topics.current.delete(topic.id);
await clearTopicItem(cardId, channelId, topic.id);
}
2022-10-07 05:52:28 +00:00
else {
const cached = topics.current.get(topic.id);
if (!cached || cached.detailRevision != topic.data.detailRevision) {
if (!topic.data.topicDetail) {
const updated = await getTopic(cardId, channelId, topic.id);
topic.data = updated.data;
}
if (!topic.data) {
topics.current.delete(topic.id);
await clearTopicItem(cardId, channelId, topic.id);
}
else {
await setTopicItem(cardId, channelId, topic);
const { id, revision, data } = topic;
topics.current.set(id, { topicId: id, revision: revision, detailRevision: topic.data.detailRevision, detail: topic.data.topicDetail });
}
2022-09-30 05:34:31 +00:00
}
}
}
}
if (res?.marker) {
await setTopicMarker(cardId, channelId, res.marker);
}
if (res?.revision) {
2022-10-06 22:34:29 +00:00
await setSyncRevision(cardId, channelId, res.revision);
2022-09-29 23:00:48 +00:00
}
2022-09-30 05:34:31 +00:00
if (curView == setView.current) {
2022-10-24 22:01:11 +00:00
updateState({ topics: topics.current, init: true, error: false });
2022-09-29 23:00:48 +00:00
}
2022-09-30 05:34:31 +00:00
syncing.current = false;
sync();
2022-09-29 18:31:55 +00:00
}
2022-09-30 05:34:31 +00:00
catch(err) {
console.log(err);
syncing.current = false;
2022-10-24 22:01:11 +00:00
updateState({ error: true });
2022-09-29 18:31:55 +00:00
}
}
2022-09-28 22:09:10 +00:00
}
}
}
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);
}
2022-11-14 21:30:58 +00:00
const setChannel = async (item) => {
2022-09-28 22:09:10 +00:00
let contacts = [];
let logo = null;
2022-10-10 06:26:32 +00:00
let topic = null;
2022-09-28 22:09:10 +00:00
let subject = null;
2022-12-14 23:07:45 +00:00
let locked = false;
let unlocked = false;
let seals = null;
2022-09-28 22:09:10 +00:00
2022-10-09 04:55:56 +00:00
let timestamp;
const date = new Date(item.detail.created * 1000);
const now = new Date();
const offset = now.getTime() - date.getTime();
if(offset < 86400000) {
timestamp = moment(date).format('h:mma');
}
else if (offset < 31449600000) {
timestamp = moment(date).format('M/DD');
}
else {
timestamp = moment(date).format('M/DD/YYYY');
}
2022-09-28 22:09:10 +00:00
if (!item) {
2022-10-10 06:26:32 +00:00
updateState({ contacts, logo, subject, topic });
2022-09-28 22:09:10 +00:00
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';
}
2022-12-14 23:07:45 +00:00
if (item?.detail?.dataType === 'sealed') {
locked = true;
unlocked = item.unsealedDetail != null;
if (item.unsealedDetail?.subject) {
topic = item.unsealedDetail.subject;
subject = topic;
2022-12-14 23:07:45 +00:00
}
2022-09-28 22:09:10 +00:00
try {
2022-12-14 23:07:45 +00:00
seals = JSON.parse(item.detail.data).seals;
2022-09-28 22:09:10 +00:00
}
catch (err) {
console.log(err);
}
}
2022-12-14 23:07:45 +00:00
else {
if (item?.detail?.data) {
try {
topic = JSON.parse(item?.detail?.data).subject;
subject = topic;
}
catch (err) {
console.log(err);
}
}
}
2022-09-28 22:09:10 +00:00
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";
}
}
2022-11-14 21:30:58 +00:00
const pushEnabled = await getNotifications(item.cardId, item.channelId);
2022-10-27 16:36:21 +00:00
const { enableImage, enableAudio, enableVideo } = item.detail;
updateState({ topic, subject, logo, contacts, host: item.cardId, created: timestamp,
2022-12-14 23:07:45 +00:00
enableImage, enableAudio, enableVideo, pushEnabled, locked, unlocked, seals });
2022-09-28 22:09:10 +00:00
}
useEffect(() => {
sync();
}, [card, channel]);
const actions = {
2022-10-04 18:52:22 +00:00
setChannel: (selected) => {
if (selected == null) {
2022-09-29 18:31:55 +00:00
setView.current++;
conversationId.current = null;
2022-09-30 05:34:31 +00:00
reset.current = true;
updateState({ subject: null, logo: null, locked: true, unlocked: false, contacts: [], topics: new Map() });
2022-09-29 18:31:55 +00:00
}
2022-10-04 18:52:22 +00:00
else if (selected.cardId !== conversationId.current?.cardId || selected.channelId !== conversationId.current?.channelId) {
2022-09-28 22:09:10 +00:00
setView.current++;
2022-10-04 18:52:22 +00:00
conversationId.current = selected;
2022-09-30 05:34:31 +00:00
reset.current = true;
2022-10-22 06:01:28 +00:00
updateState({ subject: null, logo: null, contacts: [], topics: new Map(), init: false,
cardId: selected.cardId, channelId: selected.channelId });
2022-09-28 22:09:10 +00:00
sync();
2022-10-04 18:52:22 +00:00
const { cardId, channelId, revision } = selected;
if (cardId) {
card.actions.setChannelReadRevision(cardId, channelId, revision);
}
else {
channel.actions.setReadRevision(channelId, revision);
}
2022-09-28 22:09:10 +00:00
}
},
2022-10-05 07:09:57 +00:00
getTopicAssetUrl: (topicId, assetId) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
return card.actions.getChannelTopicAssetUrl(cardId, channelId, topicId, assetId);
}
else {
return channel.actions.getTopicAssetUrl(channelId, topicId, assetId);
}
}
return null;
},
2022-10-06 22:34:29 +00:00
addTopic: async (message, files) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
await card.actions.addChannelTopic(cardId, channelId, message, files);
}
else {
await channel.actions.addTopic(channelId, message, files);
}
force.current = true;
sync();
}
},
2022-12-16 07:41:51 +00:00
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) => {
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 {
await channel.actions.setTopicUnsealedDetail(channelId, topic.topicId, topic.detailRevision, topic.unsealedDetail);
}
}
catch(err) {
console.log(err);
}
},
2022-10-10 06:26:32 +00:00
setSubject: async (subject) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
throw new Error("can only set hosted channel subjects");
}
await channel.actions.setSubject(channelId, subject);
}
},
setSealedSubject: async (subject, sealKey) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
throw new Error("can only set hosted channel subjects");
}
await channel.actions.setSealedSubject(channelId, subject, sealKey);
}
},
2022-10-10 06:55:08 +00:00
remove: async () => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
await remove(cardId, channelId);
}
},
2022-11-14 21:30:58 +00:00
setNotifications: async (notify) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
await setNotifications(cardId, channelId, notify);
updateState({ pushEnabled: notify });
}
},
2022-10-18 06:22:06 +00:00
removeTopic: async (topicId) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
await card.actions.removeChannelTopic(cardId, channelId, topicId);
}
else {
await channel.actions.removeTopic(channelId, topicId);
}
force.current = true;
sync();
}
},
2022-10-18 18:13:45 +00:00
setTopicSubject: async (topicId, data) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
return await card.actions.setChannelTopicSubject(cardId, channelId, topicId, 'superbasictopic', data);
2022-10-18 18:13:45 +00:00
}
else {
return await channel.actions.setTopicSubject(channelId, topicId, 'superbasictopic', data);
2022-10-18 18:13:45 +00:00
}
}
force.current = true;
sync();
},
setSealedTopicSubject: async (topicId, data, sealKey) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const key = CryptoJS.enc.Hex.parse(sealKey);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ message: data }), key, { iv: iv });
const messageEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
const messageIv = iv.toString();
if (cardId) {
return await card.actions.setChannelTopicSubject(cardId, channelId, topicId, 'sealedtopic', { messageEncrypted, messageIv });
}
else {
return await channel.actions.setTopicSubject(channelId, topicId, 'sealedtopic', { messageEncrypted, messageIv });
}
}
},
2022-10-11 05:05:15 +00:00
setCard: async (id) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
throw new Error("can only set members on hosted channel");
}
await channel.actions.setCard(channelId, id);
}
},
clearCard: async (id) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
throw new Error("can only clear members on hosted channel");
}
await channel.actions.clearCard(channelId, id);
}
},
addReport: async () => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
return await card.actions.addChannelReport(cardId, channelId);
}
else {
return await channel.actions.addReport(channelId);
}
}
},
addTopicReport: async(topicId) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
return await card.actions.addChannelTopicReport(cardId, channelId, topicId);
}
else {
return await channel.actions.addTopicReport(channelId, topicId);
}
}
},
2022-10-11 19:18:08 +00:00
setBlocked: async () => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
await card.actions.setChannelBlocked(cardId, channelId);
}
else {
await channel.actions.setBlocked(channelId);
}
}
},
2022-10-18 20:40:14 +00:00
blockTopic: async (topicId) => {
if (conversationId.current) {
const { cardId, channelId } = conversationId.current;
if (cardId) {
await card.actions.setChannelTopicBlocked(cardId, channelId, topicId);
}
else {
await channel.actions.setTopicBlocked(channelId, topicId);
}
const topic = topics.current.get(topicId);
2022-10-18 21:52:13 +00:00
if (topic) {
topic.blocked = 1;
force.current = true;
sync();
}
2022-10-18 20:40:14 +00:00
}
},
2022-10-18 21:52:13 +00:00
unblockTopic: async (cardId, channelId, topicId) => {
if (conversationId.current) {
if (conversationId.current.cardId == cardId && conversationId.current.channelId == channelId) {
const topic = topics.current.get(topicId);
if (topic) {
topic.blocked = 0;
force.current = true;
sync();
}
}
}
2022-10-24 22:01:11 +00:00
},
loadMore: () => {
if (conversationId.current) {
more.current = true;
sync();
}
},
2022-10-24 22:01:11 +00:00
resync: () => {
if (conversationId.current) {
force.current = true;
sync();
}
},
2022-09-28 22:09:10 +00:00
}
return { state, actions }
}