more card context refactor for mobile app

This commit is contained in:
balzack 2023-02-06 10:15:41 -08:00
parent 1d419c05fa
commit 56b181d798
3 changed files with 447 additions and 339 deletions

View File

@ -0,0 +1,197 @@
import CryptoJS from 'crypto-js';
import { RSA } from 'react-native-rsa-native';
import { JSEncrypt } from 'jsencrypt'
export function getChannelSeals(subject) {
const { seals } = JSON.parse(subject);
return seals;
}
export function isUnsealed(seals, sealKey) {
for (let i = 0; i < seals?.length; i++) {
if (seals[i].publicKey === sealKey?.public) {
return sealKey?.private != null;
}
}
return false;
}
export function getContentKey(seals, sealKey) {
for (let i = 0; i < seals?.length; i++) {
if (seals[i].publicKey === sealKey.public) {
const seal = seals[i];
const begin = '-----BEGIN RSA PRIVATE KEY-----\n';
const end = '\n-----END RSA PRIVATE KEY-----';
const key = `${begin}${sealKey.private}${end}`;
return await RSA.decrypt(seal.sealedKey, key);
}
}
throw new Error("unsealKey not available");
}
export function encryptChannelSubject(subject, publicKeys) {
const key = CryptoJS.lib.WordArray.random(256 / 8);
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ subject }), key, { iv: iv });
const subjectEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
const subjectIv = iv.toString();
const keyHex = key.toString();
let seals = [];
let crypto = new JSEncrypt();
publicKeys.forEach(publicKey => {
crypto.setPublicKey(publicKey);
const sealedKey = crypto.encrypt(keyHex);
seals.push({ publicKey, sealedKey });
});
return { subjectEncrypted, subjectIv, seals };
}
export function updateChannelSubject(subject, contentKey) {
const key = CryptoJS.enc.Hex.parse(contentKey);
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ subject }), key, { iv: iv });
const subjectEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
const subjectIv = iv.toString();
return { subjectEncrypted, subjectIv };
}
export function decryptChannelSubject(subject, contentKey) {
const { subjectEncrypted, subjectIv } = JSON.parse(subject);
const iv = CryptoJS.enc.Hex.parse(subjectIv);
const key = CryptoJS.enc.Hex.parse(contentKey);
const enc = CryptoJS.enc.Base64.parse(subjectEncrypted);
const cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
const str = dec.toString(CryptoJS.enc.Utf8);
if (!str) {
return null;
}
return JSON.parse(dec.toString(CryptoJS.enc.Utf8));
}
export function encryptTopicSubject(subject, contentKey) {
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const key = CryptoJS.enc.Hex.parse(contentKey);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify(subject), key, { iv: iv });
const messageEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
const messageIv = iv.toString();
return { messageEncrypted, messageIv };
}
export function decryptTopicSubject(subject, contentKey) {
const { messageEncrypted, messageIv } = JSON.parse(subject);
const iv = CryptoJS.enc.Hex.parse(messageIv);
const key = CryptoJS.enc.Hex.parse(contentKey);
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 });
return JSON.parse(dec.toString(CryptoJS.enc.Utf8));
}
function convertPem(pem) {
var lines = pem.split('\n');
var encoded = '';
for(var i = 0;i < lines.length;i++){
if (lines[i].trim().length > 0 &&
lines[i].indexOf('-BEGIN RSA PRIVATE KEY-') < 0 &&
lines[i].indexOf('-BEGIN RSA PUBLIC KEY-') < 0 &&
lines[i].indexOf('-BEGIN PUBLIC KEY-') < 0 &&
lines[i].indexOf('-END PUBLIC KEY-') < 0 &&
lines[i].indexOf('-END RSA PRIVATE KEY-') < 0 &&
lines[i].indexOf('-END RSA PUBLIC KEY-') < 0) {
encoded += lines[i].trim();
}
}
return encoded
};
export async function generateSeal(password) {
// generate key to encrypt private key
const salt = CryptoJS.lib.WordArray.random(128 / 8);
const aes = CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 1024,
});
// generate rsa key for sealing channel, delay for activity indicators
await new Promise(r => setTimeout(r, 1000));
const crypto = new JSEncrypt({ default_key_size: 2048 });
crypto.getKey();
// encrypt private key
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const privateKey = convertPem(crypto.getPrivateKey());
const enc = CryptoJS.AES.encrypt(privateKey, aes, { iv: iv });
const publicKey = convertPem(crypto.getPublicKey());
// update account
const seal = {
passwordSalt: salt.toString(),
privateKeyIv: iv.toString(),
privateKeyEncrypted: enc.ciphertext.toString(CryptoJS.enc.Base64),
publicKey: publicKey,
}
const sealKey = {
public: publicKey,
private: privateKey,
}
return { seal, sealKey };
}
export function unlockSeal(seal, password) {
// generate key to encrypt private key
const salt = CryptoJS.enc.Hex.parse(seal.passwordSalt);
const aes = CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 1024,
});
// decrypt private key
const iv = CryptoJS.enc.Hex.parse(seal.privateKeyIv);
const enc = CryptoJS.enc.Base64.parse(seal.privateKeyEncrypted)
let cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: enc,
iv: iv
});
const dec = CryptoJS.AES.decrypt(cipherParams, aes, { iv: iv });
const privateKey = dec.toString(CryptoJS.enc.Utf8)
// store ke
const sealKey = {
public: seal.publicKey,
private: privateKey,
}
return sealKey;
}
export function updateSeal(seal, sealKey, password) {
// generate key to encrypt private key
const salt = CryptoJS.lib.WordArray.random(128 / 8);
const aes = CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 1024,
});
// encrypt private key
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const enc = CryptoJS.AES.encrypt(sealKey.private, aes, { iv: iv });
// update account
const updated = {
passwordSalt: salt.toString(),
privateKeyIv: iv.toString(),
privateKeyEncrypted: enc.ciphertext.toString(CryptoJS.enc.Base64),
publicKey: seal.publicKey,
}
return { seal: updated, sealKey };
}

View File

@ -33,6 +33,7 @@ export function useCardContext() {
const [state, setState] = useState({ const [state, setState] = useState({
offsync: false, offsync: false,
cards: new Map(), cards: new Map(),
viewRevision: null,
}); });
const upload = useContext(UploadContext); const upload = useContext(UploadContext);
const access = useRef(null); const access = useRef(null);
@ -47,6 +48,75 @@ export function useCardContext() {
setState((s) => ({ ...s, ...value })) setState((s) => ({ ...s, ...value }))
} }
const setCardItem = (card) => {
return {
cardId: card.id,
revision: card.revision,
detailRevision: card.data?.detailRevision,
profileRevision: card.data?.profileRevision,
detail: card.data?.cardDetail,
profile: card.data?.cardProfile,
notifiedView: card.data?.notifiedView,
notifiedProfile: card.data?.notifiedProfile,
notifiedArticle: card.data?.notifiedArticle,
notifiedChannel: card.data?.notifiedChannel,
}
}
const setCardField = (cardId, field, value) => {
const card = cards.current.get(cardId);
if (card) {
card[field] = value;
cards.set(cardId, { ...card });
updateState({ cards: cards.current });
}
};
const setCardChannelField = (cardId, channelId, field, value) => {
const card = cards.current.get(cardId);
if (card) {
const channel = card.channels.get(channelId);
if (channel) {
channel[field] = value;
card.channels.set(channelId, { ...channel });
cards.current.set(cardId, { ...card });
}
}
};
const resync = async () => {
try {
force.current = true;
await sync();
}
catch (err) {
console.log(err);
}
};
const resyncCard = async (cardId) => {
if (!syncing.current) {
syncing.current = true;
try {
const { token } = access.current;
const card = cards.current.get(cardId) || {};
if (card.detail?.status === 'connected') {
await syncCard(token, card);
}
card.offsync = false;
cards.current.set(cardId, card);
updateState({ cards: cards.current });
}
catch(err) {
console.log(err);
}
syncing.current = false;
await sync();
}
}
@ -249,17 +319,54 @@ export function useCardContext() {
} }
const sync = async () => { const sync = async () => {
if (!syncing.current && (setRevision.current !== curRevision.current || resync.current.length > 0)) { if (!syncing.current && (setRevision.current !== curRevision.current || force.current)) {
syncing.current = true; syncing.current = true;
const { server, appToken, guid } = session.current; force.current = false;
try { try {
const { server, token, guid } = session.current;
const revision = curRevision.current; const revision = curRevision.current;
const delta = await getCards(server, token, setRevision.current);
// get and store
const delta = await getCards(server, appToken, setRevision.current);
for (let card of delta) { for (let card of delta) {
if (card.data) { if (card.data) {
const item = setCardItem(card);
const { profileRevision, profile, detailRevision, detail } = cards.current.get(card.id) || {};
if (item.profileRevision !== profileRevision) {
}
else {
item.profileRevision = profile;
}
if (item.detailRevision !== detailRevision) {
}
else {
item.detailRevision = detail;
]
await store.actions.setCardItem(guid, card);
cards.current.set(card.cardId,
}
else {
await store.actions.clearCardChannelItems(guid, card.id);
await store.actions.clearCardItem(guid, card.id);
cards.current.delete(card.id);
}
}
}
catch (err) {
console.log(err);
syncing.current = false;
updateState({ offsync: true });
return;
}
syncing.current = false;
await sync();
}
};
if (card.data.cardDetail && card.data.cardProfile) { if (card.data.cardDetail && card.data.cardProfile) {
await store.actions.setCardItem(guid, card); await store.actions.setCardItem(guid, card);
setCard(card.id, card); setCard(card.id, card);
@ -293,9 +400,6 @@ export function useCardContext() {
await syncCard(card); await syncCard(card);
} }
else { else {
await store.actions.clearCardChannelItems(guid, card.id);
await store.actions.clearCardItem(guid, card.id);
cards.current.delete(card.id);
} }
} }
@ -434,10 +538,12 @@ export function useCardContext() {
card.channels.set(card.channelId, cardChannel); card.channels.set(card.channelId, cardChannel);
} }
} }
const status = await store.actions.getCardRequestStatus(guid);
const revision = await store.actions.getCardRevision(session.guid); const revision = await store.actions.getCardRevision(session.guid);
curRevision.current = revision; curRevision.current = revision;
setRevision.current = revision; setRevision.current = revision;
setState({ offsync: false, channels: channels.current }); setState({ offsync: false, viewRevision: status?.revision, channels: channels.current });
}, },
clearSession: () => { clearSession: () => {
access.current = null; access.current = null;
@ -482,245 +588,24 @@ export function useCardContext() {
setCardCloseMessage: async (server, message) => { setCardCloseMessage: async (server, message) => {
return await setCardCloseMessage(server, message); return await setCardCloseMessage(server, message);
}, },
getCardImageUrl: (cardId) => { getCardImageUrl: (cardId) => {
const { server, token } = access.current;
return getCardImageUrl(server, token, cardId, revision);
}, },
removeChannel: async (cardId, channelId) => { removeChannel: async (cardId, channelId) => {
const { detail, profile } = cards.current.get(cardId) || {};
const cardToken = `${profile?.guid}.${detail?.token}`;
return await removeContactChannel(profile?.node, cardToken, channelId);
}, },
addTopic: async (cardId, channelId, type, message, files) => { addTopic: async (cardId, channelId, type, type, message, files) => {
}, const { detail, profile } = cards.current.get(cardId) || {};
removeTopic: async (cardId, channelId, topicId) => { const cardToken = `${profile?.guid}.${detail?.token}`;
}, const node = profile?.node;
setTopicSubject: async (cardId, channelId, topicId, type, subject) => {
},
getTopicAssetUrl: (cardId, channelId, topicId, assetId) => {
},
getTopics: async (cardId, channelId, revision, count, begin, end) => {
},
getTopic: async (cardId, channelId, topicId) => {
},
resync: async () => {
},
resyncCard: async (cardId) => {
},
setContactRevision: async (cardId, revision) => {
},
setCardFlag: async (cardId) => {
},
clearCardFlag: async (cardId) => {
},
setChannelReadRevision: async (cardId, channelId, revision) => {
},
setChannelSyncRevision: async (cardId, channelId, revision) => {
},
setChannelTopicMarker: async (cardId, channelId, marker) => {
},
addChannelAlert: async (cardId, channelId) => {
},
setChannelFlag: async (cardId, channelId) => {
},
clearChannelFlag: async (cardId, channelId) => {
},
getChannelNotifications: async (cardId, channelId) => {
},
setChannelNotifications: async (cardId, channelId, notify) => {
},
addTopicAlert: async (cardId, channelId, topicId) => {
},
setTopicFlag: async (cardId, channelId, topicId) => {
},
clearTopicFlag: async (cardId, channelId, topicId) => {
},
getTopicItems: async (cardId, channelId, revision, count, begin, end) => {
},
getTopicItem: async (cardId, channelId, topicId) => {
},
setTopicItem: async (cardId, channelId, topic) => {
},
clearTopicItem: async (cardId, channelId) => {
},
setUnsealedChannelSubject: async (cardId, channelId, revision, unsealed) => {
},
setUnsealedChannelSummary: async (cardId, channelId, revision, unsealed) => {
},
setUnsealedTopicSubject: async (cardId, channelId, topicId, revision, unsealed) => {
},
//setSession: async (access) => {
const { guid, server, appToken } = access;
cards.current = new Map();
const status = await store.actions.getCardRequestStatus(guid);
updateState({ requestRevision: status.revision });
const cardItems = await store.actions.getCardItems(guid);
for (item of cardItems) {
cards.current.set(item.cardId, { ...item, channels: new Map() });
}
const cardChannelItems = await store.actions.getCardChannelItems(guid);
for (item of cardChannelItems) {
setCardChannelItem(item.cardId, item);
}
const revision = await store.actions.getCardRevision(guid);
updateState({ cards: cards.current });
setRevision.current = revision;
curRevision.current = revision;
session.current = access;
},
setRequestRevision: async (revision) => {
const { guid } = session.current
await store.actions.setCardRequestStatus(guid, { revision });
updateState({ requestRevision: revision });
},
//clearSession: () => {
session.current = {};
updateState({ account: null });
},
setRevision: (rev) => {
curRevision.current = rev;
sync();
},
setChannelReadRevision: async (cardId, channelId, rev) => {
await store.actions.setCardChannelItemReadRevision(session.current.guid, cardId, channelId, rev);
setCardChannelReadRevision(cardId, channelId, rev);
updateState({ cards: cards.current });
},
getCardLogo: (cardId, revision) => {
const { server, appToken } = session.current;
return getCardImageUrl(server, appToken, cardId, revision);
},
getByGuid: (guid) => {
let card;
cards.current.forEach((value, key, map) => {
if (value?.profile?.guid === guid) {
card = value;
}
});
return card;
},
//addCard: async (message) => {
const { server, appToken } = session.current;
return await addCard(server, appToken, message);
},
//removeCard: async (cardId) => {
const { server, appToken } = session.current;
return await removeCard(server, appToken, cardId);
},
//setCardConnecting: async (cardId) => {
const { server, appToken } = session.current;
return await setCardConnecting(server, appToken, cardId);
},
//setCardConnected: async (cardId, token, rev) => {
const { server, appToken } = session.current;
return await setCardConnected(server, appToken, cardId, token,
rev.viewRevision, rev.articleRevision, rev.channelRevision, rev.profileRevision);
},
//setCardConfirmed: async (cardId) => {
const { server, appToken } = session.current;
return await setCardConfirmed(server, appToken, cardId);
},
//getCardOpenMessage: async (cardId) => {
const { server, appToken } = session.current;
return await getCardOpenMessage(server, appToken, cardId);
},
//setCardOpenMessage: async (server, message) => {
return await setCardOpenMessage(server, message);
},
//getCardCloseMessage: async (cardId) => {
const { server, appToken } = session.current;
return await getCardCloseMessage(server, appToken, cardId);
},
//setCardCloseMessage: async (server, message) => {
return await setCardCloseMessage(server, message);
},
setCardBlocked: async (cardId) => {
const { guid } = session.current;
setCardBlocked(cardId, true);
await store.actions.setCardItemBlocked(guid, cardId);
updateState({ cards: cards.current });
},
clearCardBlocked: async (cardId) => {
const { guid } = session.current;
setCardBlocked(cardId, false);
await store.actions.clearCardItemBlocked(guid, cardId);
updateState({ cards: cards.current });
},
setSyncRevision: async (cardId, channelId, revision) => {
const { guid } = session.current;
await store.actions.setCardChannelItemSyncRevision(guid, cardId, channelId, revision);
setCardChannelSyncRevision(cardId, channelId, revision);
updateState({ cards: cards.current });
},
setTopicMarker: async (cardId, channelId, marker) => {
const { guid } = session.current;
await store.actions.setCardChannelItemTopicMarker(guid, cardId, channelId, marker);
setCardChannelTopicMarker(cardId, channelId, marker);
updateState({ cards: cards.current });
},
setChannelBlocked: async (cardId, channelId) => {
const { guid } = session.current;
await store.actions.setCardChannelItemBlocked(guid, cardId, channelId);
setCardChannelBlocked(cardId, channelId, true);
updateState({ cards: cards.current });
},
clearChannelBlocked: async (cardId, channelId) => {
const { guid } = session.current;
await store.actions.clearCardChannelItemBlocked(guid, cardId, channelId);
setCardChannelBlocked(cardId, channelId, false);
updateState({ cards: cards.current });
},
setChannelTopicBlocked: async (cardId, channelId, topicId) => {
const { guid } = session.current;
await store.actions.setCardChannelTopicBlocked(guid, cardId, channelId, topicId, true);
},
clearChannelTopicBlocked: async (cardId, channelId, topicId) => {
const { guid } = session.current;
await store.actions.setCardChannelTopicBlocked(guid, cardId, channelId, topicId, false);
},
getChannelTopicBlocked: async () => {
const { guid } = session.current;
return await store.actions.getCardChannelTopicBlocked(guid);
},
getChannelTopicItems: async (cardId, channelId) => {
const { guid } = session.current;
return await store.actions.getCardChannelTopicItems(guid, cardId, channelId);
},
setChannelTopicItem: async (cardId, channelId, topicId, topic) => {
const { guid } = session.current;
return await store.actions.setCardChannelTopicItem(guid, cardId, channelId, topicId, topic);
},
clearChannelTopicItem: async (cardId, channelId, topicId) => {
const { guid } = session.current;
return await store.actions.clearCardChannelTopicItem(guid, cardId, channelId, topicId);
},
clearChannelTopicItems: async (cardId, channelId) => {
const { guid } = session.current;
return await store.actions.clearCardChannelTopicItems(guid, cardId, channelId);
},
getChannelTopic: async (cardId, channelId, topicId) => {
const { detail, profile } = getCardEntry(cardId);
return await getContactChannelTopic(profile.node, `${profile.guid}.${detail.token}`, channelId, topicId);
},
getChannelTopics: async (cardId, channelId, revision, count, begin, end) => {
const { detail, profile } = getCardEntry(cardId);
return await getContactChannelTopics(profile.node, `${profile.guid}.${detail.token}`, channelId, revision, count, begin, end);
},
getChannelTopicAssetUrl: (cardId, channelId, topicId, assetId) => {
const { detail, profile } = getCardEntry(cardId);
return getContactChannelTopicAssetUrl(profile.node, `${profile.guid}.${detail.token}`, channelId, topicId, assetId);
},
addChannelTopic: async (cardId, channelId, message, files) => {
const { detail, profile } = getCardEntry(cardId);
const node = profile.node;
const token = `${profile.guid}.${detail.token}`;
if (files?.length > 0) { if (files?.length > 0) {
const topicId = await addContactChannelTopic(node, token, channelId, null, null, null); const topicId = await addContactChannelTopic(node, token, channelId, null, null, null);
upload.actions.addContactTopic(node, token, cardId, channelId, topicId, files, async (assets) => { upload.actions.addContactTopic(node, token, cardId, channelId, topicId, files, async (assets) => {
message.assets = assets; const subject = message(assets);
await setContactChannelTopicSubject(node, token, channelId, topicId, 'superbasictopic', message); await setContactChannelTopicSubject(node, token, channelId, topicId, type, subject);
}, async () => { }, async () => {
try { try {
await removeContactChannelTopic(node, token, channelId, topicId); await removeContactChannelTopic(node, token, channelId, topicId);
@ -731,108 +616,137 @@ export function useCardContext() {
}); });
} }
else { else {
await addContactChannelTopic(node, token, channelId, 'superbasictopic', message, []); const subject = message([]);
await addContactChannelTopic(node, token, channelId, type, subject, []);
} }
}, },
addSealedChannelTopic: async (cardId, channelId, message, sealKey) => { removeTopic: async (cardId, channelId, topicId) => {
const { detail, profile } = getCardEntry(cardId); const { detail, profile } = cards.current.get(cardId) || {};
const node = profile.node; const cardToken = `${profile?.guid}.${detail?.token}`;
const token = `${profile.guid}.${detail.token}`; return await removeContactChannelTopic(profile?.node, cardToken, channelId, topicId);
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, dataType, data) => { setTopicSubject: async (cardId, channelId, topicId, type, subject) => {
const { detail, profile } = getCardEntry(cardId); const { detail, profile } = cards.current.get(cardId) || {};
return await setContactChannelTopicSubject(profile.node, `${profile.guid}.${detail.token}`, channelId, topicId, dataType, data); const cardToken = `${profile?.guid}.${detail?.token}`;
return await setContactChannelTopicSubject(profile?.node, cardToken, channelId, topicId, type, subject);
}, },
setChannelTopicUnsealedDetail: async (cardId, channelId, topicId, revision, unsealed) => { getTopicAssetUrl: (cardId, channelId, topicId, assetId) => {
const { guid } = session.current; const { detail, profile } = cards.current.get(cardId) || {};
await store.actions.setCardChannelTopicItemUnsealedDetail(guid, cardId, channelId, topicId, revision, unsealed); const cardToken = `${profile?.guid}.${detail?.token}`;
return getContactChannelTopicAssetUrl(profile?.node, cardToken, channelId, topicId, assetId);
}, },
removeChannel: async (cardId, channelId) => { getTopics: async (cardId, channelId, revision, count, begin, end) => {
const { detail, profile } = getCardEntry(cardId); const { detail, profile } = cards.current.get(cardId) || {};
return await removeContactChannel(profile.node, `${profile.guid}.${detail.token}`, channelId); const cardToken = `${profile?.guid}.${detail?.token}`;
return await store.actions.getCardChannelTopicItems(guid, cardId, channelId);
}, },
removeChannelTopic: async (cardId, channelId, topicId) => { getChannelTopic: async (cardId, channelId, topicId) => {
const { detail, profile } = getCardEntry(cardId); const { detail, profile } = cards.current.get(cardId) || {};
return await removeContactChannelTopic(profile.node, `${profile.guid}.${detail.token}`, channelId, topicId); const cardToken = `${profile?.guid}.${detail?.token}`;
return await getContactChannelTopic(profile?.node, cardToken, channelId, topicId);
}, },
addChannelReport: async (cardId, channelId) => { setContactRevision: async (cardId, revision) => {
const { detail, profile } = getCardEntry(cardId); const { guid } = acccess.current
return await addFlag(profile.node, profile.guid, channelId); await store.actions.setCardRequestStatus(guid, { revision });
updateState({ viewRevision: revision });
}, },
addChannelTopicReport: async (cardId, channelId, topicId) => { setChannelReadRevision: async (cardId, channelId, revision) => {
const { detail, profile } = getCardEntry(cardId); const { guid } = access.current;
return await addFlag(profile.node, profile.guid, channelId, topicId); await store.actions.setCardChannelItemReadRevision(guid, cardId, channelId, revision);
setCardField(cardId, 'readRevision', revision);
}, },
setChannelSyncRevision: async (cardId, channelId, revision) => {
const { guid } = access.current;
await store.actions.setCardChannelItemSyncRevision(guid, cardId, channelId, revision);
setCardField(cardId, 'syncRevision', revision);
},
setChannelTopicMarker: async (cardId, channelId, marker) => {
const { guid } = access.current;
await store.actions.setCardChannelItemTopicMarker(guid, cardId, channelId, marker);
setCardField(cardId, 'topicMarker', marker);
},
setCardFlag: async (cardId) => {
const { guid } = acccess.current;
await store.actions.setCardItemBlocked(guid, cardId);
setCardField(cardId, 'blocked', true);
},
clearCardFlag: async (cardId) => {
const { guid } = acccess.current;
await store.actions.clearCardItemBlocked(guid, cardId);
setCardField(cardId, 'blocked', false);
},
setChannelFlag: async (cardId, channelId) => {
const { guid } = access.current;
await store.actions.setCardChannelItemBlocked(guid, cardId, channelId);
setCardChannelField(cardId, channelId, 'blocked', true);
},
clearChannelFlag: async (cardId, channelId) => {
const { guid } = access.current;
await store.actions.setCardChannelItemBlocked(guid, cardId, channelId);
setCardChannelField(cardId, channelId, 'blocked', false);
},
setTopicFlag: async (cardId, channelId, topicId) => {
const { guid } = access.current;
await store.actions.setCardChannelTopicBlocked(guid, cardId, channelId, topicId, true);
},
clearTopicFlag: async (cardId, channelId, topicId) => {
const { guid } = access.current;
await store.actions.setCardChannelTopicBlocked(guid, cardId, channelId, topicId, false);
},
addChannelAlert: async (cardId, channelId) => {
const { profile } = cards.current.get(cardId) || {};
return await addFlag(profile?.node, profile?.guid, channelId);
},
addTopicAlert: async (cardId, channelId, topicId) => {
const { detail, profile } = cards.current.get(cardId) || {};
return await addFlag(profile?.node, profile?.guid, channelId, topicId);
},
getChannelNotifications: async (cardId, channelId) => { getChannelNotifications: async (cardId, channelId) => {
const { detail, profile } = getCardEntry(cardId); const { detail, profile } = cards.current.get(cardId) || {};
return await getContactChannelNotifications(profile.node, `${profile.guid}.${detail.token}`, channelId); const token = `${profile?.guid}.${detail?.token}`;
return await getContactChannelNotifications(profile?.node, token, channelId);
}, },
setChannelNotifications: async (cardId, channelId, notify) => { setChannelNotifications: async (cardId, channelId, notify) => {
const { detail, profile } = getCardEntry(cardId); const { detail, profile } = cards.current.get(cardId) || {};
return await setContactChannelNotifications(profile.node, `${profile.guid}.${detail.token}`, channelId, notify); const token = `${profile?.guid}.${detail?.token}`;
return await setContactChannelNotifications(profile?.node, token, channelId, notify);
}, },
unsealChannelSubject: async (cardId, channelId, revision, sealKey) => {
try { getTopicItems: async (cardId, channelId) => {
const { guid } = session.current; const { guid } = access.current;
const card = cards.current.get(cardId); return await store.actions.getCardChannelTopicItems(guid, cardId, channelId);
const channel = card.channels.get(channelId);
const { subjectEncrypted, subjectIv, seals } = JSON.parse(channel.detail.data);
const unsealedKey = await unsealKey(seals, sealKey);
if (unsealedKey) {
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));
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);
}
}, },
unsealChannelSummary: async (cardId, channelId, revision, sealKey) => { setChannelTopicItem: async (cardId, channelId, topicId, topic) => {
try { const { guid } = access.current;
const { guid } = session.current; return await store.actions.setCardChannelTopicItem(guid, cardId, channelId, topicId, topic);
const card = cards.current.get(cardId);
const channel = card.channels.get(channelId);
const { seals } = JSON.parse(channel.detail.data);
const { messageEncrypted, messageIv } = JSON.parse(channel.summary.lastTopic.data);
const unsealedKey = await unsealKey(seals, sealKey);
if (unsealedKey) {
const iv = CryptoJS.enc.Hex.parse(messageIv);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
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 });
if (revision === channel.topicRevision) {
channel.unsealedSummary = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
await store.actions.setCardChannelItemUnsealedSummary(guid, cardId, channelId, revision, channel.unsealedSummary);
card.channels.set(channelId, { ...channel });
cards.current.set(cardId, { ...card });
updateState({ cards: cards.current });
}
}
}
catch(err) {
console.log(err);
}
}, },
resync: (cardId) => { clearChannelTopicItem: async (cardId, channelId, topicId) => {
resync.current.push(cardId); const { guid } = access.current;
sync(); return await store.actions.clearCardChannelTopicItem(guid, cardId, channelId, topicId);
},
clearChannelTopicItems: async (cardId, channelId) => {
const { guid } = access.current;
return await store.actions.clearCardChannelTopicItems(guid, cardId, channelId);
},
setUnsealedChannelSubject: async (cardId, channelId, revision, unsealed) => {
const { guid } = access.current;
await store.actions.setCardChannelItemUnsealedDetail(guid, cardId, channelId, revision, unsealed);
},
setUnsealedChannelSummary: async (cardId, channelId, revision, unsealed) => {
const { guid } = session.current;
await store.actions.setCardChannelItemUnsealedSummary(guid, cardId, channelId, revision, unsealed);
},
setUnsealedTopicSubject: async (cardId, channelId, topicId, revision, unsealed) => {
const { guid } = access.current;
await store.actions.setCardChannelTopicItemUnsealedDetail(guid, cardId, channelId, topicId, revision, unsealed);
},
resync: async () => {
await resync();
},
resyncCard: async (cardId) => {
await resyncCard(cardId);
}, },
} }

View File

@ -172,7 +172,6 @@ export function useChannelContext() {
if (files?.length > 0) { if (files?.length > 0) {
const topicId = await addChannelTopic(server, token, channelId, null, null, null); const topicId = await addChannelTopic(server, token, channelId, null, null, null);
upload.actions.addTopic(server, token, channelId, topicId, files, async (assets) => { upload.actions.addTopic(server, token, channelId, topicId, files, async (assets) => {
message.assets = assets;
const subject = message(assets); const subject = message(assets);
await setChannelTopicSubject(server, token, channelId, topicId, type, sbuject); await setChannelTopicSubject(server, token, channelId, topicId, type, sbuject);
}, async () => { }, async () => {
@ -201,12 +200,14 @@ export function useChannelContext() {
const { server, token } = access.current; const { server, token } = access.current;
getChannelTopicAssetUrl(server, token, channelId, topicId, assetId); getChannelTopicAssetUrl(server, token, channelId, topicId, assetId);
}, },
getTopics: async (channelId) => { getTopics: async (channelId) => {
const { guid } = session.current; const { server, token } = access.current;
return await store.actions.getChannelTopicItems(guid, channelId); return await getChannelTopics(server, token, channelId, revision, count, begin, end);
},
getTopic: async (channelId, topicId) => {
const { server, token } = access.current;
return await getChannelTopic(server, token, channelId, topicId);
}, },
resync: async () => { resync: async () => {
await resync(); await resync();
}, },
@ -262,12 +263,8 @@ export function useChannelContext() {
return await addFlag(server, guid, channelId, topicId); return await addFlag(server, guid, channelId, topicId);
}, },
getTopicItems: async (channelId, revision, count, begin, end) => { getTopicItems: async (channelId, revision, count, begin, end) => {
const { server, token } = session.current; const { guid } = session.current;
return await getChannelTopics(server, token, channelId, revision, count, begin, end); return await store.actions.getChannelTopicItems(guid, channelId);
},
getTopicItem: async (channelId, topicId) => {
const { server, token } = session.current;
return await getChannelTopic(server, token, channelId, topicId);
}, },
setTopicItem: async (channelId, topic) => { setTopicItem: async (channelId, topic) => {
const { guid } = session.current; const { guid } = session.current;