mirror of
https://github.com/balzack/databag.git
synced 2025-03-13 00:50:03 +00:00
refactoring channel context in mobile app
This commit is contained in:
parent
e8902492d4
commit
69b659409a
@ -60,7 +60,7 @@ export function useAppContext() {
|
||||
await profile.actions.setSession(access);
|
||||
await card.actions.setSession(access);
|
||||
await channel.actions.setSession(access);
|
||||
updateState({ session: true, server: access.server, appToken: access.appToken,
|
||||
updateState({ session: true, server: access.server, token: access.appToken,
|
||||
loginTimestamp: access.created });
|
||||
setWebsocket(access.server, access.appToken);
|
||||
}
|
||||
|
@ -18,151 +18,83 @@ import { clearChannelCard } from 'api/clearChannelCard';
|
||||
import { addFlag } from 'api/addFlag';
|
||||
import { setChannelNotifications } from 'api/setChannelNotifications';
|
||||
import { getChannelNotifications } from 'api/getChannelNotifications';
|
||||
import CryptoJS from "crypto-js";
|
||||
import { RSA } from 'react-native-rsa-native';
|
||||
|
||||
export function useChannelContext() {
|
||||
const [state, setState] = useState({
|
||||
offsync: false,
|
||||
channels: new Map(),
|
||||
});
|
||||
const store = useContext(StoreContext);
|
||||
const upload = useContext(UploadContext);
|
||||
|
||||
const session = useRef(null);
|
||||
const curRevision = useRef(null);
|
||||
const access = useRef(null);
|
||||
const setRevision = useRef(null);
|
||||
const syncing = useRef(false);
|
||||
const curRevision = useRef(null);
|
||||
const channels = useRef(new Map());
|
||||
const syncing = useRef(false);
|
||||
const force = useRef(false);
|
||||
const store = useContext(StoreContext);
|
||||
|
||||
const updateState = (value) => {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
|
||||
const unsealKey = async (seals, sealKey) => {
|
||||
let seal;
|
||||
if (seals?.length) {
|
||||
seals.forEach(s => {
|
||||
if (s.publicKey === sealKey.public) {
|
||||
seal = s;
|
||||
}
|
||||
});
|
||||
const setChannelItem = (channel) => {
|
||||
return {
|
||||
cardId: channel.id,
|
||||
revision: channel.revision,
|
||||
detailRevision: channel.data?.detailRevision,
|
||||
topicRevision: channel.data?.topicRevision,
|
||||
detail: channel.data?.channelDetail,
|
||||
summary: channel.data?.channelSummary,
|
||||
}
|
||||
if (seal) {
|
||||
const key = '-----BEGIN RSA PRIVATE KEY-----\n' + sealKey.private + '\n-----END RSA PRIVATE KEY-----'
|
||||
return await RSA.decrypt(seal.sealedKey, key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const setChannelField = (channelId, field, value) => {
|
||||
const channel = channels.get(channelId) || {};
|
||||
channel[field] = value;
|
||||
channels.set(channelId, { ...channel });
|
||||
updateState({ channels: channels.current });
|
||||
};
|
||||
|
||||
const setChannel = (channelId, channel) => {
|
||||
let update = channels.current.get(channelId);
|
||||
if (!update) {
|
||||
update = { readRevision: 0 };
|
||||
const resync = async () => {
|
||||
try {
|
||||
force.current = true;
|
||||
await sync();
|
||||
}
|
||||
update.channelId = channel?.id;
|
||||
update.revision = channel?.revision;
|
||||
update.detail = channel?.data?.channelDetail;
|
||||
update.unsealedDetail = null;
|
||||
update.summary = channel?.data?.channelSummary;
|
||||
update.unsealedSummary = null;
|
||||
update.detailRevision = channel?.data?.detailRevision;
|
||||
update.topicRevision = channel?.data?.topicRevision;
|
||||
channels.current.set(channelId, update);
|
||||
}
|
||||
const setChannelDetail = (channelId, detail, revision) => {
|
||||
let channel = channels.current.get(channelId);
|
||||
if (channel) {
|
||||
channel.detail = detail;
|
||||
channel.unsealedDetail = null;
|
||||
channel.detailRevision = revision;
|
||||
channels.current.set(channelId, channel);
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
const setChannelSummary = (channelId, summary, revision) => {
|
||||
let channel = channels.current.get(channelId);
|
||||
if (channel) {
|
||||
channel.summary = summary;
|
||||
channel.unsealedSummary = null;
|
||||
channel.topicRevision = revision;
|
||||
channels.current.set(channelId, channel);
|
||||
}
|
||||
}
|
||||
const setChannelRevision = (channelId, revision) => {
|
||||
let channel = channels.current.get(channelId);
|
||||
if (channel) {
|
||||
channel.revision = revision;
|
||||
channels.current.set(channelId, channel);
|
||||
}
|
||||
}
|
||||
const setChannelReadRevision = (channelId, revision) => {
|
||||
let channel = channels.current.get(channelId);
|
||||
if (channel) {
|
||||
channel.readRevision = revision;
|
||||
channels.current.set(channelId, channel);
|
||||
}
|
||||
}
|
||||
const setChannelSyncRevision = (channelId, revision) => {
|
||||
let channel = channels.current.get(channelId);
|
||||
if (channel) {
|
||||
channel.syncRevision = revision;
|
||||
channels.current.set(channelId, channel);
|
||||
}
|
||||
}
|
||||
const setChannelTopicMarker = (channelId, marker) => {
|
||||
let channel = channels.current.get(channelId);
|
||||
if (channel) {
|
||||
channel.topicMarker = marker;
|
||||
channels.current.set(channelId, channel);
|
||||
}
|
||||
}
|
||||
const setChannelBlocked = (channelId, blocked) => {
|
||||
let channel = channels.current.get(channelId);
|
||||
if (channel) {
|
||||
channel.blocked = blocked;
|
||||
channels.current.set(channelId, channel);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const sync = async () => {
|
||||
|
||||
if (!syncing.current && setRevision.current !== curRevision.current) {
|
||||
if (!syncing.current && (setRevision.current !== curRevision.current || force.current)) {
|
||||
syncing.current = true;
|
||||
force.current = false;
|
||||
|
||||
try {
|
||||
const revision = curRevision.current;
|
||||
const { server, appToken, guid } = session.current;
|
||||
|
||||
const delta = await getChannels(server, appToken, setRevision.current);
|
||||
const { server, token, guid } = session.current;
|
||||
const delta = await getChannels(server, token, setRevision.current);
|
||||
for (let channel of delta) {
|
||||
if (channel.data) {
|
||||
if (channel.data.channelDetail && channel.data.channelSummary) {
|
||||
await store.actions.setChannelItem(guid, channel);
|
||||
setChannel(channel.id, channel);
|
||||
const item = setChannelItem(channel);
|
||||
if (item.detail && item.summary) {
|
||||
await store.actions.setChannelItem(guid, item);
|
||||
channels.set(item.channelId, item);
|
||||
}
|
||||
else {
|
||||
const { detailRevision, topicRevision, channelDetail, channelSummary } = channel.data;
|
||||
const view = await store.actions.getChannelItemView(guid, channel.id);
|
||||
if (view == null) {
|
||||
let assembled = JSON.parse(JSON.stringify(channel));
|
||||
assembled.data.channelDetail = await getChannelDetail(server, appToken, channel.id);
|
||||
assembled.data.channelSummary = await getChannelSummary(server, appToken, channel.id);
|
||||
await store.actions.setChannelItem(guid, assembled);
|
||||
setChannel(assembled.id, assembled);
|
||||
const { channelId, detailRevision, topicRevision, detail, summary } = channel;
|
||||
const view = await store.actions.getChannelItemView(guid, channelId);
|
||||
if (view?.detailRevision !== detailRevision) {
|
||||
item.detail = await getChannelDetail(server, token, channelId);
|
||||
await store.actions.setChannelItemDetail(guid, channelId, detailRevision, item.detail);
|
||||
}
|
||||
else {
|
||||
if (view.detailRevision != detailRevision) {
|
||||
const detail = await getChannelDetail(server, appToken, channel.id);
|
||||
await store.actions.setChannelItemDetail(guid, channel.id, detailRevision, detail);
|
||||
setChannelDetail(channel.id, detail, detailRevision);
|
||||
}
|
||||
if (view.topicRevision != topicRevision) {
|
||||
const summary = await getChannelSummary(server, appToken, channel.id);
|
||||
await store.actions.setChannelItemSummary(guid, channel.id, topicRevision, summary);
|
||||
setChannelSummary(channel.id, summary, topicRevision);
|
||||
}
|
||||
await store.actions.setChannelItemRevision(guid, channel.id, channel.revision);
|
||||
setChannelRevision(channel.id, channel.revision);
|
||||
if (view?.topicRevision !== topicRevision) {
|
||||
item.summary = await getChannelSummary(server, token, item.channelId);
|
||||
await store.actions.setChannelItemSummary(guid, channel.id, topicRevision, item.summary);
|
||||
}
|
||||
await store.actions.setChannelItem(guid, item);
|
||||
channels.set(channelId, item);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -187,107 +119,59 @@ export function useChannelContext() {
|
||||
};
|
||||
|
||||
const actions = {
|
||||
setSession: async (access) => {
|
||||
const { guid, server, appToken } = access;
|
||||
setSession: async (session) => {
|
||||
if (access.current || syncing.current) {
|
||||
throw new Error('invalid channel state');
|
||||
}
|
||||
access.current = session;
|
||||
channels.current = new Map();
|
||||
const items = await store.actions.getChannelItems(guid);
|
||||
const items = await store.actions.getChannelItems(session.guid);
|
||||
for(item of items) {
|
||||
channels.current.set(item.channelId, item);
|
||||
}
|
||||
const revision = await store.actions.getChannelRevision(guid);
|
||||
updateState({ channels: channels.current });
|
||||
setRevision.current = revision;
|
||||
const revision = await store.actions.getChannelRevision(session.guid);
|
||||
curRevision.current = revision;
|
||||
session.current = access;
|
||||
setRevision.current = revision;
|
||||
setState({ offsync: false, channels: channels.current });
|
||||
},
|
||||
clearSession: () => {
|
||||
session.current = {};
|
||||
channels.current = new Map();
|
||||
updateState({ account: null, channels: channels.current });
|
||||
clearToken: () => {
|
||||
access.current = null;
|
||||
},
|
||||
setRevision: (rev) => {
|
||||
setRevision: async (rev) => {
|
||||
curRevision.current = rev;
|
||||
sync();
|
||||
await sync();
|
||||
},
|
||||
setReadRevision: async (channelId, rev) => {
|
||||
await store.actions.setChannelItemReadRevision(session.current.guid, channelId, rev);
|
||||
setChannelReadRevision(channelId, rev);
|
||||
updateState({ channels: channels.current });
|
||||
addChannel: async (type, subject, cards) => {
|
||||
const { server, token } = access.current;
|
||||
return await addChannel(server, token, type, subject, cards);
|
||||
},
|
||||
setSyncRevision: async (channelId, revision) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelItemSyncRevision(guid, channelId, revision);
|
||||
setChannelSyncRevision(channelId, revision);
|
||||
updateState({ channels: channels.current });
|
||||
removeChannel: async (channelId) => {
|
||||
const { server, token } = access.current;
|
||||
return await removeChannel(access.current, channelId);
|
||||
},
|
||||
setTopicMarker: async (channelId, marker) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelItemTopicMarker(guid, channelId, marker);
|
||||
setChannelTopicMarker(channelId, marker);
|
||||
updateState({ channels: channels.current });
|
||||
setChannelSubject: async (channelId, type, subject) => {
|
||||
const { server, token } = access.current;
|
||||
return await setChannelSubject(server, token, channelId, type, subject);
|
||||
},
|
||||
setBlocked: async (channelId) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelItemBlocked(guid, channelId);
|
||||
setChannelBlocked(channelId, 1);
|
||||
updateState({ channels: channels.current });
|
||||
setChannelCard: async (channelId, cardId) => {
|
||||
const { server, token } = access.current;
|
||||
return await setChannelCard(server, token, channelId, cardId);
|
||||
},
|
||||
clearBlocked: async (channelId) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.clearChannelItemBlocked(guid, channelId);
|
||||
setChannelBlocked(channelId, 0);
|
||||
updateState({ channels: channels.current });
|
||||
clearChannelCard: async (channelId, cardId) => {
|
||||
const { server, token } = access.current;
|
||||
return await clearChannelCard(server, token, channelId, cardId);
|
||||
},
|
||||
setTopicBlocked: async (channelId, topicId) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelTopicBlocked(guid, channelId, topicId, true);
|
||||
},
|
||||
clearTopicBlocked: async (channelId, topicId) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelTopicBlocked(guid, channelId, topicId, false);
|
||||
},
|
||||
getTopicBlocked: async () => {
|
||||
const { guid } = session.current;
|
||||
return await store.actions.getChannelTopicBlocked(guid);
|
||||
},
|
||||
getTopicItems: async (channelId) => {
|
||||
const { guid } = session.current;
|
||||
return await store.actions.getChannelTopicItems(guid, channelId);
|
||||
},
|
||||
setTopicItem: async (channelId, topicId, topic) => {
|
||||
const { guid } = session.current;
|
||||
return await store.actions.setChannelTopicItem(guid, channelId, topicId, topic);
|
||||
},
|
||||
clearTopicItem: async (channelId, topicId) => {
|
||||
const { guid } = session.current;
|
||||
return await store.actions.clearChannelTopicItem(guid, channelId, topicId);
|
||||
},
|
||||
clearTopicItems: async (channelId) => {
|
||||
const { guid } = session.current;
|
||||
return await store.actions.clearChannelTopicItems(guid, channelId);
|
||||
},
|
||||
getTopic: async (channelId, topicId) => {
|
||||
const { server, appToken } = session.current;
|
||||
return await getChannelTopic(server, appToken, channelId, topicId);
|
||||
},
|
||||
getTopics: async (channelId, revision, count, begin, end) => {
|
||||
const { server, appToken } = session.current;
|
||||
return await getChannelTopics(server, appToken, channelId, revision, count, begin, end);
|
||||
},
|
||||
getTopicAssetUrl: (channelId, topicId, assetId) => {
|
||||
const { server, appToken } = session.current;
|
||||
return getChannelTopicAssetUrl(server, appToken, channelId, topicId, assetId);
|
||||
},
|
||||
addTopic: async (channelId, message, files) => {
|
||||
const { server, appToken } = session.current;
|
||||
addTopic: async (channelId, type, message, files) => {
|
||||
const { server, token } = session.current;
|
||||
if (files?.length > 0) {
|
||||
const topicId = await addChannelTopic(server, appToken, channelId, null, null, null);
|
||||
upload.actions.addTopic(server, appToken, channelId, topicId, files, async (assets) => {
|
||||
const topicId = await addChannelTopic(server, token, channelId, null, null, null);
|
||||
upload.actions.addTopic(server, token, channelId, topicId, files, async (assets) => {
|
||||
message.assets = assets;
|
||||
await setChannelTopicSubject(server, appToken, channelId, topicId, 'superbasictopic', message);
|
||||
const subject = message(assets);
|
||||
await setChannelTopicSubject(server, token, channelId, topicId, type, sbuject);
|
||||
}, async () => {
|
||||
try {
|
||||
await removeChannelTopic(server, appToken, channelId, topicId);
|
||||
await removeChannelTopic(server, token, channelId, topicId);
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
@ -295,158 +179,102 @@ export function useChannelContext() {
|
||||
});
|
||||
}
|
||||
else {
|
||||
await addChannelTopic(server, appToken, channelId, 'superbasictopic', message, []);
|
||||
const subject = message([]);
|
||||
await addChannelTopic(server, token, channelId, type, subject, []);
|
||||
}
|
||||
},
|
||||
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, dataType, data) => {
|
||||
const { server, appToken } = session.current;
|
||||
return await setChannelTopicSubject(server, appToken, channelId, topicId, dataType, data);
|
||||
},
|
||||
setTopicUnsealedDetail: async (channelId, topicId, revision, unsealed) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelTopicItemUnsealedDetail(guid, channelId, topicId, revision, unsealed);
|
||||
},
|
||||
setTopicUnsealedSummary: async (channelId, topicId, revision, unsealed) => {
|
||||
const { guid } = seassion.current;
|
||||
await store.actions.setChannelTopicItemUnsealedSummary(guid, channelId, topicId, revision, unsealed);
|
||||
},
|
||||
setSubject: async (channelId, subject) => {
|
||||
const { server, appToken } = session.current;
|
||||
return await setChannelSubject(server, appToken, channelId, 'superbasic', { subject });
|
||||
},
|
||||
setSealedSubject: async (channelId, subject, sealKey) => {
|
||||
const { server, appToken } = session.current;
|
||||
const channel = channels.current.get(channelId);
|
||||
|
||||
let { seals, subjectEncrypted, subjectIv } = JSON.parse(channel.detail.data);
|
||||
const unsealedKey = await unsealKey(seals, sealKey);
|
||||
if (!unsealedKey) {
|
||||
throw new Error("cannot reseal subject");
|
||||
}
|
||||
const key = CryptoJS.enc.Hex.parse(unsealedKey);
|
||||
const iv = CryptoJS.lib.WordArray.random(128 / 8);
|
||||
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ subject }), key, { iv: iv });
|
||||
subjectEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
|
||||
subjectIv = iv.toString();
|
||||
const data = { subjectEncrypted, subjectIv, seals };
|
||||
return await setChannelSubject(server, appToken, channelId, 'sealed', data);
|
||||
},
|
||||
remove: async (channelId) => {
|
||||
const { server, appToken } = session.current;
|
||||
return await removeChannel(server, appToken, channelId);
|
||||
},
|
||||
addBasic: async (subject, cards) => {
|
||||
const { server, appToken } = session.current;
|
||||
return await addChannel(server, appToken, 'superbasic', { subject }, cards);
|
||||
},
|
||||
addSealed: async (subject, cards, keys) => {
|
||||
const { server, appToken } = session.current;
|
||||
|
||||
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 = [];
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const publicKey = keys[i];
|
||||
const key = '-----BEGIN PUBLIC KEY-----\n' + publicKey + '\n-----END PUBLIC KEY-----'
|
||||
const sealedKey = await RSA.encrypt(keyHex, key);
|
||||
seals.push({ publicKey, sealedKey });
|
||||
};
|
||||
|
||||
const data = { subjectEncrypted, subjectIv, seals };
|
||||
return await addChannel(server, appToken, 'sealed', data, cards);
|
||||
},
|
||||
removeTopic: async (channelId, topicId) => {
|
||||
const { server, appToken } = session.current;
|
||||
return await removeChannelTopic(server, appToken, channelId, topicId);
|
||||
const { server, token } = access.current;
|
||||
await removeChannelTopic(server, token, channelId, topicId);
|
||||
},
|
||||
addReport: async (channelId) => {
|
||||
setTopicSubject: async (channelId, topicId, type, subject) => {
|
||||
const { server, token } = access.current;
|
||||
await setChannelTopicSubject(server, token, channelId, topicId, type, subject);
|
||||
},
|
||||
getTopicAssetUrl: (channelId, topicId, assetId) => {
|
||||
const { server, token } = access.current;
|
||||
getChannelTopicAssetUrl(server, token, channelId, topicId, assetId);
|
||||
},
|
||||
|
||||
getTopics: async (channelId) => {
|
||||
const { guid } = session.current;
|
||||
return await store.actions.getChannelTopicItems(guid, channelId);
|
||||
},
|
||||
|
||||
resync: async () => {
|
||||
await resync();
|
||||
},
|
||||
|
||||
setReadRevision: async (channelId, revision) => {
|
||||
const { guid } = access.current;
|
||||
await store.actions.setChannelItemReadRevision(guid, channelId, revision);
|
||||
setChannelField(channelId, 'readRevision', revision);
|
||||
},
|
||||
setSyncRevision: async (channelId, revision) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelItemSyncRevision(guid, channelId, revision);
|
||||
setChannelField(channelId, 'syncRevision', revision);
|
||||
},
|
||||
setTopicMarker: async (channelId, marker) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelItemTopicMarker(guid, channelId, revision);
|
||||
setChannelField(channelId, 'topicMarker', marker);
|
||||
},
|
||||
setChannelFlag: async (channelId) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelItemBlocked(guid, channelId);
|
||||
setChannelField(channelId, 'blocked', true);
|
||||
},
|
||||
clearChannelFlag: async (channelId) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.clearChannelItemBlocked(guid, channelId);
|
||||
setChannelField(channelId, 'blocked', false);
|
||||
},
|
||||
setTopicFlag: async (channelId, topicId) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelTopicBlocked(guid, channelId, topicId, true);
|
||||
},
|
||||
clearTopicFlag: async (channelId, topicId) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelTopicBlocked(guid, channelId, topicId, false);
|
||||
},
|
||||
addChannelAlert: async (channelId) => {
|
||||
const { server, guid } = session.current;
|
||||
return await addFlag(server, guid, channelId);
|
||||
},
|
||||
addTopicReport: async (channelId, topicId) => {
|
||||
addTopicAlert: async (channelId, topicId) => {
|
||||
const { server, guid } = session.current;
|
||||
return await addFlag(server, guid, channelId, topicId);
|
||||
},
|
||||
setCard: async (channelId, cardId) => {
|
||||
const { server, appToken } = session.current;
|
||||
return await setChannelCard(server, appToken, channelId, cardId);
|
||||
getTopicItems: async (channelId, revision, count, begin, end) => {
|
||||
const { server, token } = session.current;
|
||||
return await getChannelTopics(server, token, channelId, revision, count, begin, end);
|
||||
},
|
||||
clearCard: async (channelId, cardId) => {
|
||||
const { server, appToken } = session.current;
|
||||
return await clearChannelCard(server, appToken, channelId, cardId);
|
||||
getTopicItem: async (channelId, topicId) => {
|
||||
const { server, token } = session.current;
|
||||
return await getChannelTopic(server, token, channelId, topicId);
|
||||
},
|
||||
getNotifications: async (channelId) => {
|
||||
const { server, appToken } = session.current;
|
||||
return await getChannelNotifications(server, appToken, channelId);
|
||||
setTopicItem: async (channelId, topic) => {
|
||||
const { guid } = session.current;
|
||||
return await store.actions.setChannelTopicItem(guid, channelId, topic);
|
||||
},
|
||||
setNotifications: async (channelId, notify) => {
|
||||
const { server, appToken } = session.current;
|
||||
return await setChannelNotifications(server, appToken, channelId, notify);
|
||||
clearTopicItem: async (channelId, topicId) => {
|
||||
const { guid } = session.current;
|
||||
return await store.actions.clearChannelTopicItem(guid, channelId, topicId);
|
||||
},
|
||||
unsealChannelSubject: async (channelId, revision, sealKey) => {
|
||||
try {
|
||||
const { guid } = session.current;
|
||||
const channel = channels.current.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.setChannelItemUnsealedDetail(guid, channelId, revision, channel.unsealedDetail);
|
||||
channels.current.set(channelId, { ...channel });
|
||||
updateState({ channels: channels.current });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
console.log(err);
|
||||
}
|
||||
setUnsealedChannelSubject: async (channelId, revision, unsealed) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelItemUnsealedDetail(guid, channelId, revision, unsealed);
|
||||
},
|
||||
unsealChannelSummary: async (channelId, revision, sealKey) => {
|
||||
try {
|
||||
const { guid } = session.current;
|
||||
const channel = channels.current.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.setChannelItemUnsealedSummary(guid, channelId, revision, channel.unsealedSummary);
|
||||
channels.current.set(channelId, { ...channel });
|
||||
updateState({ channels: channels.current });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
console.log(err);
|
||||
}
|
||||
setUnsealedChannelSummary: async (channelId, revision, unsealed) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelItemUnsealedSummary(guid, channelId, revision, unsealed);
|
||||
},
|
||||
}
|
||||
setUnsealedTopicSubject: async (channelId, topicId, revision, unsealed) => {
|
||||
const { guid } = session.current;
|
||||
await store.actions.setChannelTopicItemUnsealedDetail(guid, channelId, topicId, revision, unsealed);
|
||||
},
|
||||
};
|
||||
|
||||
return { state, actions }
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ export function useStoreContext() {
|
||||
},
|
||||
setChannelItem: async (guid, channel) => {
|
||||
const { id, revision, data } = channel;
|
||||
await db.current.executeSql(`INSERT OR REPLACE INTO channel_${guid} (channel_id, revision, detail_revision, topic_revision, detail, summary, unsealed_detail, unsealed_summary) values (?, ?, ?, ?, ?, ?, null, null);`, [id, revision, data.detailRevision, data.topicRevision, encodeObject(data.channelDetail), encodeObject(data.channelSummary)]);
|
||||
await db.current.executeSql(`INSERT OR REPLACE INTO channel_${guid} (channel_id, revision, detail_revision, topic_revision, detail, summary, unsealed_detail, unsealed_summary) values (?, ?, ?, ?, ?, ?, null, null);`, [id, revision, detailRevision, topicRevision, encodeObject(channelDetail), encodeObject(channelSummary)]);
|
||||
},
|
||||
clearChannelItem: async (guid, channelId) => {
|
||||
await db.current.executeSql(`DELETE FROM channel_${guid} WHERE channel_id=?`, [channelId]);
|
||||
@ -275,8 +275,8 @@ export function useStoreContext() {
|
||||
}));
|
||||
},
|
||||
setChannelTopicItem: async (guid, channelId, topic) => {
|
||||
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)]);
|
||||
const { topicId, revision, detailRevision, detail } = topic;
|
||||
await db.current.executeSql(`INSERT OR REPLACE INTO channel_topic_${guid} (channel_id, topic_id, revision, detail_revision, blocked detail, unsealed_detail) values (?, ?, ?, ?, false, ?, null);`, [channelId, topicId, revision, detailRevision, encodeObject(detail)]);
|
||||
},
|
||||
setChannelTopicItemUnsealedDetail: async (guid, channelId, topicId, 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]);
|
||||
|
69
app/mobile/test/Channel.test.js
Normal file
69
app/mobile/test/Channel.test.js
Normal file
@ -0,0 +1,69 @@
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import { View, Text } from 'react-native';
|
||||
import { useTestStoreContext } from './useTestStoreContext.hook';
|
||||
import {render, act, screen, waitFor, fireEvent} from '@testing-library/react-native'
|
||||
import { ChannelContextProvider, ChannelContext } from 'context/ChannelContext';
|
||||
import * as fetchUtil from 'api/fetchUtil';
|
||||
|
||||
function ChannelView() {
|
||||
const [renderCount, setRenderCount] = useState(0);
|
||||
const channel = useContext(ChannelContext);
|
||||
|
||||
useEffect(() => {
|
||||
setRenderCount(renderCount + 1);
|
||||
}, [channel.state]);
|
||||
|
||||
return (
|
||||
<View testID="channel" channel={channel} renderCount={renderCount}>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function ChannelTestApp() {
|
||||
return (
|
||||
<ChannelContextProvider>
|
||||
<ChannelView />
|
||||
</ChannelContextProvider>
|
||||
)
|
||||
}
|
||||
|
||||
const realUseContext = React.useContext;
|
||||
const realFetchWithTimeout = fetchUtil.fetchWithTimeout;
|
||||
const realFetchWithCustomTimeout = fetchUtil.fetchWithCustomTimeout;
|
||||
|
||||
beforeEach(() => {
|
||||
const mockUseContext = jest.fn().mockImplementation((ctx) => {
|
||||
return useTestStoreContext();
|
||||
});
|
||||
React.useContext = mockUseContext;
|
||||
|
||||
const mockFetch = jest.fn().mockImplementation((url, options) => {
|
||||
console.log(url);
|
||||
return Promise.resolve({
|
||||
json: () => Promise.resolve([])
|
||||
});
|
||||
});
|
||||
fetchUtil.fetchWithTimeout = mockFetch;
|
||||
fetchUtil.fetchWithCustomTimeout = mockFetch;
|
||||
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
React.useContext = realUseContext;
|
||||
fetchUtil.fetchWithTimeout = realFetchWithTimeout;
|
||||
fetchUtil.fetchWithCustomTimeout = realFetchWithCustomTimeout;
|
||||
});
|
||||
|
||||
test('testing', async () => {
|
||||
render(<ChannelTestApp />)
|
||||
|
||||
await act(async () => {
|
||||
const channel = screen.getByTestId('channel').props.channel;
|
||||
await channel.actions.setSession({ guid: 'abc', server: 'test.org', token: '123' });
|
||||
await channel.actions.setRevision(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
@ -161,7 +161,6 @@ test('testing', async () => {
|
||||
});
|
||||
|
||||
const renderCount = screen.getByTestId('profile').props.renderCount;
|
||||
console.log("RENDER COUNT:", renderCount);
|
||||
|
||||
await act(async () => {
|
||||
identity = { name: 'renderer' };
|
||||
@ -169,8 +168,6 @@ console.log("RENDER COUNT:", renderCount);
|
||||
await profile.actions.setRevision(2048);
|
||||
});
|
||||
|
||||
console.log(renderCount);
|
||||
|
||||
await act(async () => {
|
||||
expect(screen.getByTestId('profile').props.renderCount).toBe(renderCount + 1);
|
||||
});
|
||||
|
@ -93,6 +93,7 @@ export function useTestStoreContext() {
|
||||
},
|
||||
|
||||
getChannelRevision: async (guid) => {
|
||||
return 1;
|
||||
},
|
||||
setChannelRevision: async (guid, revision) => {
|
||||
},
|
||||
@ -123,6 +124,7 @@ export function useTestStoreContext() {
|
||||
getChannelItemView: async (guid, channelId) => {
|
||||
},
|
||||
getChannelItems: async (guid) => {
|
||||
return [];
|
||||
},
|
||||
|
||||
getChannelTopicItems: async (guid, channelId) => {
|
||||
|
Loading…
Reference in New Issue
Block a user