diff --git a/app/mobile/src/context/useCardContext.hook.js b/app/mobile/src/context/useCardContext.hook.js index 1d9aa38f..c903d8ed 100644 --- a/app/mobile/src/context/useCardContext.hook.js +++ b/app/mobile/src/context/useCardContext.hook.js @@ -82,12 +82,15 @@ export function useCardContext() { }; }; - const setCardChannelField = (cardId, channelId, field, value) => { + const setCardChannelField = (cardId, channelId, field, value, field2, value2) => { const card = cards.current.get(cardId); if (card) { const channel = card.channels.get(channelId); if (channel) { channel[field] = value; + if(field2) { + channel[field2] = value2; + } card.channels.set(channelId, { ...channel }); cards.current.set(cardId, { ...card }); } @@ -374,7 +377,7 @@ export function useCardContext() { getTopics: async (cardId, channelId, revision, count, begin, end) => { const { detail, profile } = cards.current.get(cardId) || {}; const cardToken = `${profile?.guid}.${detail?.token}`; - return await store.actions.getCardChannelTopicItems(guid, cardId, channelId); + return await getContactChannelTopics(profile?.node, cardToken, channelId); }, getChannelTopic: async (cardId, channelId, topicId) => { const { detail, profile } = cards.current.get(cardId) || {}; @@ -401,6 +404,11 @@ export function useCardContext() { await store.actions.setCardChannelItemTopicMarker(guid, cardId, channelId, marker); setCardField(cardId, 'topicMarker', marker); }, + setChannelMarkerAndSync: async (cardId, channelId, marker, revision) => { + const { guid } = access.current; + await store.actions.setCardChannelItemMarkerAndSync(guid, cardId, channelId, marker, revision); + setCardField(cardId, 'topicMarker', marker, 'syncRevision', revision); + }, setCardFlag: async (cardId) => { const { guid } = acccess.current; await store.actions.setCardItemBlocked(guid, cardId); @@ -451,15 +459,15 @@ export function useCardContext() { const { guid } = access.current; return await store.actions.getCardChannelTopicItems(guid, cardId, channelId); }, - setChannelTopicItem: async (cardId, channelId, topicId, topic) => { + setTopicItem: async (cardId, channelId, topicId, topic) => { const { guid } = access.current; return await store.actions.setCardChannelTopicItem(guid, cardId, channelId, topicId, topic); }, - clearChannelTopicItem: async (cardId, channelId, topicId) => { + clearTopicItem: async (cardId, channelId, topicId) => { const { guid } = access.current; return await store.actions.clearCardChannelTopicItem(guid, cardId, channelId, topicId); }, - clearChannelTopicItems: async (cardId, channelId) => { + clearTopicItems: async (cardId, channelId) => { const { guid } = access.current; return await store.actions.clearCardChannelTopicItems(guid, cardId, channelId); }, diff --git a/app/mobile/src/context/useChannelContext.hook.js b/app/mobile/src/context/useChannelContext.hook.js index a93ddb16..0a6270ff 100644 --- a/app/mobile/src/context/useChannelContext.hook.js +++ b/app/mobile/src/context/useChannelContext.hook.js @@ -47,9 +47,12 @@ export function useChannelContext() { } } - const setChannelField = (channelId, field, value) => { + const setChannelField = (channelId, field, value, field2, value2) => { const channel = channels.get(channelId) || {}; channel[field] = value; + if (field2) { + channel[field2] = value2; + } channels.set(channelId, { ...channel }); updateState({ channels: channels.current }); }; @@ -223,6 +226,11 @@ export function useChannelContext() { await store.actions.setChannelItemTopicMarker(guid, channelId, revision); setChannelField(channelId, 'topicMarker', marker); }, + setMarkerAndSync: async (channelId, marker, revision) => { + const { guid } = access.current; + await store.actions.setChannelItemMarkerAndSync(guid, channelId, revision); + setChannelField(channelId, 'topicMarker', marker, 'syncRevision', revision); + }, setChannelFlag: async (channelId) => { const { guid } = access.current; await store.actions.setChannelItemBlocked(guid, channelId); @@ -249,7 +257,7 @@ export function useChannelContext() { const { server, guid } = access.current; return await addFlag(server, guid, channelId, topicId); }, - getTopicItems: async (channelId, revision, count, begin, end) => { + getTopicItems: async (channelId) => { const { guid } = access.current; return await store.actions.getChannelTopicItems(guid, channelId); }, diff --git a/app/mobile/src/context/useConversationContext.hook.js b/app/mobile/src/context/useConversationContext.hook.js index b67f3ce7..18673cad 100644 --- a/app/mobile/src/context/useConversationContext.hook.js +++ b/app/mobile/src/context/useConversationContext.hook.js @@ -8,670 +8,402 @@ import moment from 'moment'; import CryptoJS from 'crypto-js'; export function useConversationContext() { + const COUNT = 64; + const [state, setState] = useState({ - topic: null, - subject: null, - logo: null, - revision: null, - contacts: [], + offsync: false, topics: new Map(), - created: null, - host: null, - init: false, - progress: null, - cardId: null, - channelId: null, - pushEnabled: null, - locked: false, - unlocked: false, - seals: null, + card: null, + channel: null, }); - const store = useContext(StoreContext); - const upload = useContext(UploadContext); const card = useContext(CardContext); const channel = useContext(ChannelContext); - const profile = useContext(ProfileContext); - const topics = useRef(null); - const revision = useRef(null); - const force = useRef(false); - const more = useRef(false); - const detailRevision = useRef(0); - const syncing = useRef(false); - const conversationId = useRef(null); + const reset = useRef(false); - const setView = useRef(0); - const transfer = useRef(null); + const more = useRef(false); + const force = useRef(false); + const syncing = useRef(false); + const update = useRef(false); + + const loaded = useRef(false); + const conversationId = useRef(null); + const topics = useRef(new Map()); const updateState = (value) => { setState((s) => ({ ...s, ...value })) } - 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); - if (transfer.current == null || error || Math.abs(transfer.current - percent) > 5) { - updateState({ progress: percent, uploadError: error }); - transfer.current = percent; - } - - if (error) { - setTimeout(() => { - upload.actions.clearErrors(cardId, channelId); - updateState({ progress: null, uploadError: false }); - transfer.current = null; - }, 2000); - } - } - else { - updateState({ progress: null }); - transfer.current = null; - } - }, [upload, state.cardId, state.channelId]); - - const getTopicItems = async (cardId, channelId) => { - if (cardId) { - return await card.actions.getChannelTopicItems(cardId, channelId); - } - return await channel.actions.getTopicItems(channelId); - } - const setTopicItem = async (cardId, channelId, topic) => { - if (cardId) { - return await card.actions.setChannelTopicItem(cardId, channelId, topic); - } - return await channel.actions.setTopicItem(channelId, topic); - } - 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) => { - if (cardId) { - return await card.actions.getChannelTopic(cardId, channelId, topicId); - } - return await channel.actions.getTopic(channelId, topicId); - } - const getTopics = async (cardId, channelId, revision, begin, end) => { - if (cardId) { - return await card.actions.getChannelTopics(cardId, channelId, revision, 16, begin, end); - } - return await channel.actions.getTopics(channelId, revision, 16, begin, end) - } - const getTopicAssetUrl = (cardId, channelId, assetId) => { - if (cardId) { - return card.actions.getChannelTopicAssetUrl(cardId, channelId, topicId, assetId); - } - return channel.actions.getTopicAssetUrl(channelId, assetId); - } - const addTopic = async (cardId, channelId, message, asssets) => { - if (cardId) { - return await card.actions.addChannelTopic(cardId, channelId, message, assetId); - } - return await channel.actions.addTopic(channelId, message, assetId); - } - const setTopicSubject = async (cardId, channelId, topicId, dataType, data) => { - if (cardId) { - return await card.actions.setChannelTopicSubject(cardId, channelId, topicId, dataType, data); - } - return await channel.actions.setTopicSubject(channelId, topicId, dataType, data); - } - const remove = async (cardId, channelId) => { - if (cardId) { - return await card.actions.removeChannel(cardId, channelId); - } - return await channel.actions.remove(channelId); - } - const removeTopic = async (cardId, channelId, topicId) => { - if (cardId) { - return await card.actions.removeChannelTopic(cardId, channelId, topicId); - } - 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); - } - 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); - } - - useEffect(() => { - if (conversationId.current) { - const { cardId, channelId } = conversationId.current; - const channelItem = getChannel(cardId, channelId); - if (channelItem) { - setChannel(channelItem); - } - } - }, [card, channel]); - const sync = async () => { - const curView = setView.current; - if (!syncing.current) { + if (!syncing.current && (reset.current || update.current || force.current || more.current)) { + + const loadMore = more.current; + const ignoreRevision = force.current; + const conversation = converstaionId.current; + let curRevision, setRevision, marker; + + syncing.current = true; + update.current = false; + force.current = false; + loadMore.current = false; + if (reset.current) { - revision.current = null; - detailRevision.current = null; - topics.current = null; reset.current = false; + loaded.current = false; + topics.current = new Map(); + updateState({ offsync: false, channel: null, card: null, topics: topics.current }); } - if (conversationId.current) { - const { cardId, channelId } = conversationId.current; - const channelItem = getChannel(cardId, channelId); - if (channelItem && (channelItem.revision !== revision.current || force.current || more.current)) { - syncing.current = true; + if (conversation) { + const { cardId, channelId } = conversation; - try { - - // sync from server - let res; - 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; - } - 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) { - 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) { - for (const topic of res.topics) { - if (!topic.data) { - topics.current.delete(topic.id); - await clearTopicItem(cardId, channelId, topic.id); - } - 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 }); - } - } - } - } - } - - if (res?.marker) { - await setTopicMarker(cardId, channelId, res.marker); - } - if (res?.revision) { - await setSyncRevision(cardId, channelId, res.revision); - } - - if (curView == setView.current) { - updateState({ topics: topics.current, init: true, error: false }); - } - - syncing.current = false; - sync(); + if (loaded.current) { + const cardValue = cardId ? card.state.cards.get(cardId) : null; + const channelValue = cardId ? cardValue?.get(channelId) : channel.state.channels.get(channelId); + if (channelValue) { + const { topicRevision, syncRevision, topicMarker } = channelValue; + curRevision = topicRevision; + setRevision = syncRevision; + marker = topicMarker; + updateState({ card: cardValue, channel: channelValue }); } - catch(err) { - console.log(err); - syncing.current = false; - updateState({ error: true }); + else { + console.log("failed to load conversation"); + sysncing.current = false; + return; } } - } - } - } - 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); - } - - const setChannel = async (item) => { - let contacts = []; - let logo = null; - let topic = null; - let subject = null; - let locked = false; - let unlocked = false; - let seals = null; - - 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'); - } - - if (!item) { - updateState({ contacts, logo, subject, topic }); - return; - } - - if (item.cardId) { - contacts.push(card.state.cards.get(item.cardId)); - } - if (item?.detail?.members) { - const profileGuid = profile.state.identity.guid; - item.detail.members.forEach(guid => { - if (profileGuid !== guid) { - const contact = getCard(guid); - contacts.push(contact); + if (!loaded.current) { + const cardValue = cardId ? card.state.cards.get(cardId) : null; + const channelValue = cardId ? cardValue?.get(channelId) : channel.state.channels.get(channelId); + if (channelValue) { + const { topicRevision, syncRevision, topicMarker } = channelValue; + curRevision = topicRevision; + setRevision = syncRevision; + marker = topicMarker; + const topicItems = await getTopicItems(cardId, channelId); + for (let topic: topicItems) { + topics.current.set(topic.topicId, topic); + } + updateState({ card: cardValue, channel: channelValue, topics: topics.current }); + loaded.current = true; + } + else { + console.log("failed to load conversation"); + syncing.current = false; + return; + } } - }) - } - 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'; - } - - if (item?.detail?.dataType === 'sealed') { - locked = true; - unlocked = item.unsealedDetail != null; - if (item.unsealedDetail?.subject) { - topic = item.unsealedDetail.subject; - subject = topic; - } - try { - seals = JSON.parse(item.detail.data).seals; - } - catch (err) { - console.log(err); - } - } - else { - if (item?.detail?.data) { try { - topic = JSON.parse(item?.detail?.data).subject; - subject = topic; + if (!marker) { + const delta = await getTopicDelta(cardId, channelId, null, COUNT, null, null); + await setTopicDelta(cardId, channelId, delta.topics); + setMarkerAndSync(cardId, channelId, topicMarker, topicRevision); + } + if (loadMore && marker) { + const delta = await getTopicDelta(cardId, channelId, null, COUNT, null, marker); + await setTopicDelta(cardId, channelId, delta.topics); + setTopicMarker(cardId, channelId, delta.topicMarker); + } + if (ignoreRevision || curTopicRevision.current !== setTopicRevision.current) { + const delta = await getTopicDelta(cardId, channelId, setRevision, null, marker, null); + await setTopicDelta(cardId, channelId, delta.topics); + setSyncRevision(cardid, channelId, delta.topicRevision); + } } - catch (err) { + catch(err) { console.log(err); + updateState({ offysnc: true }); + syncing.current = false; + return } + + syncing.current = false; + await sync(); } } - 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); - } + } + + const setTopicDelta(cardId, channelId, entries) => { + for (let entry of entries) { + if (entry.data) { + if (entry.data.detail) { + const item = mapTopicEntry(entry); + topics.current.set(item.topicId, item); + } + else { + const topic = await getTopic(cardId, channelId, entry.id); + const item = mapTopicEntry(entry); + topics.current.set(item.topicId, item); } - subject = names.join(', '); } else { - subject = "Notes"; + clearTopicItem(entry.id); } } - - const pushEnabled = await getNotifications(item.cardId, item.channelId); - - const { enableImage, enableAudio, enableVideo } = item.detail; - updateState({ topic, subject, logo, contacts, host: item.cardId, created: timestamp, - enableImage, enableAudio, enableVideo, pushEnabled, locked, unlocked, seals }); + updateState({ offsync: false, topics: topics.current }); } useEffect(() => { + update.current = true; sync(); - }, [card, channel]); + // eslint-disable-next-line + }, [card.state, channel.state]); const actions = { - setChannel: (selected) => { - if (selected == null) { - setView.current++; - conversationId.current = null; - reset.current = true; - updateState({ subject: null, logo: null, locked: true, unlocked: false, contacts: [], topics: new Map() }); + setConversation: async (cardId, channelId) => { + conversationId.current = { cardId, channelId }; + reset.current = true; + await sync(); + }, + clearConversation: async () + conversationId.current = null; + reset.current = true; + await sync(); + }, + setChannelSubject: async (type, subject) => { + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + throw new Error("can only set hosted channel subjects"); } - else if (selected.cardId !== conversationId.current?.cardId || selected.channelId !== conversationId.current?.channelId) { - setView.current++; - conversationId.current = selected; - reset.current = true; - updateState({ subject: null, logo: null, contacts: [], topics: new Map(), init: false, - cardId: selected.cardId, channelId: selected.channelId }); - sync(); - const { cardId, channelId, revision } = selected; - if (cardId) { - card.actions.setChannelReadRevision(cardId, channelId, revision); - } - else { - channel.actions.setReadRevision(channelId, revision); - } + else if(channelId) { + await channel.actions.setSubject(channelId, type, subject); } }, - 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); - } + removeChannel: async () => { + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + await card.actions.removeChannel(cardId, channelId); } - return null; - }, - 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(); + else if (channelId) { + await channel.actions.removeChannel(channelId); } }, - 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(); + getNotifications: async () => { + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + await card.actions.getChannelNotifications(cardId, channelId); } - }, - 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.unsealedDetail); - } - else { - await channel.actions.setTopicUnsealedDetail(channelId, topic.topicId, topic.detailRevision, topic.unsealedDetail); - } - } - catch(err) { - console.log(err); - } - }, - 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); - } - }, - remove: async () => { - if (conversationId.current) { - const { cardId, channelId } = conversationId.current; - await remove(cardId, channelId); + else if (channelId) { + await channel.actions.getNotifications(channelId); } }, setNotifications: async (notify) => { - if (conversationId.current) { - const { cardId, channelId } = conversationId.current; - await setNotifications(cardId, channelId, notify); - updateState({ pushEnabled: notify }); + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + await card.actions.setChannelNotifications(cardId, channelId); } + else if (channelId) { + await channel.actions.setNotifications(channelId, notify); + } + }, + setChannelCard: async (id) => { + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + throw new Error("can only set members on hosted channel"); + } + else if (channelId) { + await channel.actions.setChannelCard(channelId, id); + } + }, + clearChannelCard: async (id) => { + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + throw new Error("can only clear members on hosted channel"); + } + else if (channelId) { + await channel.actions.clearChannelCard(channelId, id); + } + }, + addChannelAlert: async () => { + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + return await card.actions.addChannelAlert(cardId, channelId); + } + else if (channelId) { + return await channel.actions.addChannelAlert(channelId); + } + } + }, + setChannelFlag: async () => { + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + await card.actions.setChannelFlag(cardId, channelId); + } + else if (channelId) { + await channel.actions.setChannelFlag(channelId); + } + }, + clearChannelFlag: async () => { + const { cardId, channelId } = conversationId.current || {}; + if (cardid) { + await card.actions.clearChannelFlag(cardId, channelId); + } + else if (channelId) { + await channel.actions.clearChannelFlag(channelId); + } + }, + addTopic: async (type, message, files) => { + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + await card.actions.addTopic(cardId, channelId, type, message, files); + } + else if (channelId) { + await channel.actions.addTopic(channelId, type, message, files); + } + force.current = true; + await sync(); }, 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(); + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + await card.actions.removeTopic(cardId, channelId, topicId); + } + else { + await channel.actions.removeTopic(channelId, topicId); + } + force.current = true; + await sync(); + }, + unsealTopic: async (topicId, revision, unsealed) => { + const { cardId, channelId } = conversationId.current || {} + if (cardId) { + await card.actions.setUnsealedTopicSubject(cardId, channelId, topicId, revision, unsealed); + } + else {channelId) { + await channel.actions.setUnsealedTopicSubject(channelId, topicId, revision, unsealed); + } + setTopicField(topicId, 'unsaledDetail', unsealed); + }, + setTopicSubject: async (topicId, type, subject) => { + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + await card.actions.setTopicSubject(cardId, channelId, topicId, type, subject); + } + else if (channelId) { + await channel.actions.setTopicSubject(channelId, topicId, type, subject); + } + force.current = true; + await sync(); + }, + addTopicAlert: async (topicId) => { + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + return await card.actions.addTopicAlert(cardId, channelId, topicId); + } + else if (channelId) { + return await channel.actions.addTopicAlert(channelId, topicId); } }, - setTopicSubject: async (topicId, data) => { - if (conversationId.current) { - const { cardId, channelId } = conversationId.current; - if (cardId) { - return await card.actions.setChannelTopicSubject(cardId, channelId, topicId, 'superbasictopic', data); - } - else { - return await channel.actions.setTopicSubject(channelId, topicId, 'superbasictopic', data); - } + setTopicFlag: async (topicId) => { + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + card.actions.setTopicFlag(cardId, channelId, topicId); } + else if (channelId) { + channel.actions.setTopicFlag(channelId, topicId); + } + setTopicField(topicId, 'blocked', true); + }, + clearTopicFlag: async (topicId) => { + const { cardId, channelId } = conversationId.current || {}; + if (cardId) { + card.actions.clearTopicFlag(cardId, channelId, topicId); + } + else if (channelId) { + channel.actions.clearTopicFlag(channelId, topicId); + } + setTopicField(topicId, 'blocked', false); + }, + getTopicAssetUrl: (topicId, assetId) => { + const { cardId, channelId } = conversationId.current || {}; + return getTopicAssetUrl(cardId, channelId, topicId, assetId); + }, + loadMore: () => { + more.current = true; + sync(); + }, + resync: () => { 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 }); - } - - } - }, - 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); - } - } - }, - setBlocked: async () => { - if (conversationId.current) { - const { cardId, channelId } = conversationId.current; - if (cardId) { - await card.actions.setChannelBlocked(cardId, channelId); - } - else { - await channel.actions.setBlocked(channelId); - } - } - }, - 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); - if (topic) { - topic.blocked = 1; - force.current = true; - sync(); - } - } - }, - 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(); - } - } - } - }, - loadMore: () => { - if (conversationId.current) { - more.current = true; - sync(); - } - }, - resync: () => { - if (conversationId.current) { - force.current = true; - sync(); - } - }, } + const getTopicItems = async (cardId, channelId) => { + if (cardId) { + return await card.actions.getTopicItems(cardId, channelId); + } + return await channel.actions.getTopicItems(channelId); + } + + const setTopicItem = async (cardId, channelId, topic) => { + if (cardId) { + return await card.actions.setTopicItem(cardId, channelId, topic); + } + return await channel.actions.setTopicItem(channelId, topic); + } + + const clearTopicItem = async (cardId, channelId, topicId) => { + if (cardId) { + return await card.actions.clearTopicItem(cardId, channelId, topicId); + } + return await channel.actions.clearTopicItem(channelId, topicId); + } + + const setTopicMarker = async (cardId, channelId, marker) => { + if (cardId) { + return await card.actions.setChannelTopicMarker(cardId, channelId, marker); + } + return await channel.actions.setTopicMarker(channelId, marker); + } + + const setSyncRevision = async (cardId, channelId, revision) => { + if (cardId) { + return await card.actions.setChannelSyncRevision(cardId, channelId, revision); + } + return await channel.actions.setSyncRevision(channelid, revision); + } + + const setMarkerAndSync = async (cardId, channelId, marker, revision) => { + if (cardId) { + return await card.actions.setChannelMarkerAndSync(cardId, channelId, marker, revision); + } + return await channel.actions.setMarkerAndSync(channelId, marker, revision); + } + + const getTopicDelta = async (cardId, channelId, revision, count, begin, end) => { + if (cardId) { + return await card.actions.getTopics(cardId, channelId, revision, count, begin, end); + } + return await channel.actions.getTopics(channelId, revision, count, begin, end); + } + + const getTopic = async (cardId, channelId, topicId) => { + if (cardId) { + return await card.actions.getTopic(cardId, channelId, topicId); + } + return await channel.actions.getTopic(channelId, topicId); + } + + const mapTopicItem = (entry) => { + return { + topicId: entry.id, + revision: entry.revision, + detailRevision = entry.data?.detailRevision, + detail = entry.data?.topicDetail, + }; + }; + + const setTopicField = (topicId, field, value) => { + const topic = topics.current.get(topicId); + if (topic) { + topic[field] = value; + } + topics.current.set(topicId, topic); + updateState({ topics: topics.current }); + }; + return { state, actions } } - diff --git a/app/mobile/src/context/useStoreContext.hook.js b/app/mobile/src/context/useStoreContext.hook.js index ff1b8698..040a0ab6 100644 --- a/app/mobile/src/context/useStoreContext.hook.js +++ b/app/mobile/src/context/useStoreContext.hook.js @@ -159,17 +159,6 @@ export function useStoreContext() { blocked: values[0].blocked, }; }, - getCardItemView: async (guid, cardId) => { - const values = await getAppValues(db.current, `SELECT revision, detail_revision, profile_revision FROM card_${guid} WHERE card_id=?`, [cardId]); - if (!values.length) { - return null; - } - return { - revision: values[0].revision, - detailRevision: values[0].detail_revision, - profileRevision: values[0].profile_revision, - }; - }, getCardItems: async (guid) => { const values = await getAppValues(db.current, `SELECT card_id, revision, detail_revision, profile_revision, detail, profile, offsync, blocked, notified_view, notified_profile, notified_article, notified_channel FROM card_${guid}`, []); return values.map(card => ({ @@ -215,6 +204,9 @@ export function useStoreContext() { setChannelItemTopicMarker: async (guid, channelId, marker) => { await db.current.executeSql(`UPDATE channel_${guid} set topic_marker=? where channel_id=?`, [marker, channelId]); }, + setChannelItemMarkerAndSync: async (guid, channelId, marker, revision) => { + await db.current.executeSql(`UPDATE channel_${guid} set sync_revision=?, topic_marker=? where channel_id=?`, [revision, maker, channelId]); + }, setChannelItemBlocked: async (guid, channelId) => { await db.current.executeSql(`UPDATE channel_${guid} set blocked=? where channel_id=?`, [1, channelId]); }, @@ -233,17 +225,6 @@ export function useStoreContext() { setChannelItemUnsealedSummary: async (guid, channelId, revision, unsealed) => { await db.current.executeSql(`UPDATE channel_${guid} set unsealed_summary=? where topic_revision=? AND channel_id=?`, [encodeObject(unsealed), revision, channelId]); }, - getChannelItemView: async (guid, channelId) => { - const values = await getAppValues(db.current, `SELECT revision, detail_revision, topic_revision FROM channel_${guid} WHERE channel_id=?`, [channelId]); - if (!values.length) { - return null; - } - return { - revision: values[0].revision, - detailRevision: values[0].detail_revision, - topicRevision: values[0].topic_revision, - }; - }, getChannelItems: async (guid) => { const values = await getAppValues(db.current, `SELECT channel_id, read_revision, revision, sync_revision, blocked, detail_revision, topic_revision, topic_marker, detail, unsealed_detail, summary, unsealed_summary FROM channel_${guid}`, []); return values.map(channel => ({ @@ -318,6 +299,9 @@ export function useStoreContext() { setCardChannelItemTopicMarker: async (guid, cardId, channelId, marker) => { await db.current.executeSql(`UPDATE card_channel_${guid} set topic_marker=? where card_id=? and channel_id=?`, [marker, cardId, channelId]); }, + setCardChannelItemMakerAndSync: async (guid, cardId, channelId, marker, revision) => { + await db.current.executeSql(`UPDATE card_channel_${guid} set topic_marker=?, sync_revision=? where card_id=? and channel_id=?`, [marker, revision, cardId, channelId]); + }, setCardChannelItemDetail: async (guid, cardId, channelId, revision, detail) => { await db.current.executeSql(`UPDATE card_channel_${guid} set detail_revision=?, detail=?, unsealed_detail=null where card_id=? and channel_id=?`, [revision, encodeObject(detail), cardId, channelId]); }, @@ -330,17 +314,6 @@ export function useStoreContext() { setCardChannelItemUnsealedSummary: async (guid, cardId, channelId, revision, unsealed) => { await db.current.executeSql(`UPDATE card_channel_${guid} set unsealed_summary=? where topic_revision=? AND card_id=? AND channel_id=?`, [encodeObject(unsealed), revision, cardId, channelId]); }, - getCardChannelItemView: async (guid, cardId, channelId) => { - const values = await getAppValues(db.current, `SELECT revision, detail_revision, topic_revision FROM card_channel_${guid} WHERE card_id=? and channel_id=?`, [cardId, channelId]); - if (!values.length) { - return null; - } - return { - revision: values[0].revision, - detailRevision: values[0].detail_revision, - topicRevision: values[0].topic_revision, - }; - }, getCardChannelItems: async (guid) => { const values = await getAppValues(db.current, `SELECT card_id, channel_id, read_revision, sync_revision, revision, blocked, detail_revision, topic_revision, topic_marker, detail, unsealed_detail, summary, unsealed_summary FROM card_channel_${guid}`, []); return values.map(channel => ({