From b57b32b55cb6c5816039f2fab307dc7465f8760b Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Fri, 5 May 2023 13:40:32 -0700 Subject: [PATCH] implemented optimized thead loading based on message creation time --- app/mobile/src/context/useAppContext.hook.js | 3 +- app/mobile/src/context/useCardContext.hook.js | 8 ++ .../src/context/useChannelContext.hook.js | 8 ++ .../context/useConversationContext.hook.js | 65 +++++++++++++++-- .../src/context/useStoreContext.hook.js | 73 +++++++++++++++++-- 5 files changed, 142 insertions(+), 15 deletions(-) diff --git a/app/mobile/src/context/useAppContext.hook.js b/app/mobile/src/context/useAppContext.hook.js index d0ad2a60..2abddb38 100644 --- a/app/mobile/src/context/useAppContext.hook.js +++ b/app/mobile/src/context/useAppContext.hook.js @@ -67,8 +67,9 @@ export function useAppContext() { }, []); const setSession = async () => { - const { loginTimestamp } = access.current; + const { loginTimestamp, guid } = access.current; updateState({ session: true, loginTimestamp, status: 'connecting' }); + await store.actions.updateDb(guid); await account.actions.setSession(access.current); await profile.actions.setSession(access.current); await card.actions.setSession(access.current); diff --git a/app/mobile/src/context/useCardContext.hook.js b/app/mobile/src/context/useCardContext.hook.js index 10c53111..ecf3190e 100644 --- a/app/mobile/src/context/useCardContext.hook.js +++ b/app/mobile/src/context/useCardContext.hook.js @@ -493,6 +493,14 @@ export function useCardContext() { const { guid } = access.current || {}; return await store.actions.getCardChannelTopicItems(guid, cardId, channelId); }, + getTopicItemsId: async (cardId, channelId) => { + const { guid } = access.current || {}; + return await store.actions.getCardChannelTopicItemsId(guid, cardId, channelId); + }, + getTopicItemsById: async (cardId, channelId, topics) => { + const { guid } = access.current || {}; + return await store.actions.getCardChannelTopicItemsById(guid, cardId, channelId, topics); + }, setTopicItem: async (cardId, channelId, topicId, topic) => { const { guid } = access.current || {}; return await store.actions.setCardChannelTopicItem(guid, cardId, channelId, topicId, topic); diff --git a/app/mobile/src/context/useChannelContext.hook.js b/app/mobile/src/context/useChannelContext.hook.js index be785ba4..0df0e830 100644 --- a/app/mobile/src/context/useChannelContext.hook.js +++ b/app/mobile/src/context/useChannelContext.hook.js @@ -263,6 +263,14 @@ export function useChannelContext() { const { guid } = access.current || {}; return await store.actions.getChannelTopicItems(guid, channelId); }, + getTopicItemsId: async (channelId) => { + const { guid } = access.current || {}; + return await store.actions.getChannelTopicItemsId(guid, channelId); + }, + getTopicItemsById: async (channelId, topics) => { + const { guid } = access.current || {}; + return await store.actions.getChannelTopicItemsById(guid, channelId, topics); + }, setTopicItem: async (channelId, topic) => { const { guid } = access.current || {}; return await store.actions.setChannelTopicItem(guid, channelId, topic); diff --git a/app/mobile/src/context/useConversationContext.hook.js b/app/mobile/src/context/useConversationContext.hook.js index 08b277f7..8c1f8e5b 100644 --- a/app/mobile/src/context/useConversationContext.hook.js +++ b/app/mobile/src/context/useConversationContext.hook.js @@ -7,7 +7,7 @@ import { ProfileContext } from 'context/ProfileContext'; import CryptoJS from 'crypto-js'; export function useConversationContext() { - const COUNT = 48; + const COUNT = 32; const [state, setState] = useState({ loaded: false, @@ -25,6 +25,7 @@ export function useConversationContext() { const syncing = useRef(false); const update = useRef(false); const loaded = useRef(false); + const stored = useRef([]); const conversationId = useRef(null); const topics = useRef(new Map()); @@ -63,7 +64,26 @@ export function useConversationContext() { if (channelValue) { if (!loaded.current) { - const topicItems = await getTopicItems(cardId, channelId); + + stored.current = await getTopicItemsId(cardId, channelId); + stored.current.sort((a,b) => { + if (a.created > b.created) { + return -1; + } + if (a.created < b.created) { + return 1; + } + return 0; + }); + + const ids = []; + for (let i = 0; i < COUNT; i++) { + if (stored.current.length > 0) { + ids.push(stored.current.shift().topicId); + } + } + + const topicItems = await getTopicItemsById(cardId, channelId, ids); for (let topic of topicItems) { topics.current.set(topic.topicId, topic); } @@ -94,12 +114,27 @@ export function useConversationContext() { updateState({ loaded: true, offsync: false, topics: topics.current, card: cardValue, channel: channelValue }); } else if (loadMore) { - const delta = await getTopicDelta(cardId, channelId, null, COUNT, null, curTopicMarker.current); - const marker = delta.marker ? delta.marker : 1; - await setTopicDelta(cardId, channelId, delta.topics); - await setTopicMarker(cardId, channelId, marker); - curTopicMarker.current = marker; - updateState({ loaded: true, offsync: false, topics: topics.current, card: cardValue, channel: channelValue }); + if (stored.current.length > 0) { + const ids = []; + for (let i = 0; i < COUNT; i++) { + if (stored.current.length > 0) { + ids.push(stored.current.shift().topicId); + } + } + const topicItems = await getTopicItemsById(cardId, channelId, ids); + for (let topic of topicItems) { + topics.current.set(topic.topicId, topic); + } + updateState({ loaded: true, topics: topics.current, card: cardValue, channel: channelValue }); + } + else { + const delta = await getTopicDelta(cardId, channelId, null, COUNT, null, curTopicMarker.current); + const marker = delta.marker ? delta.marker : 1; + await setTopicDelta(cardId, channelId, delta.topics); + await setTopicMarker(cardId, channelId, marker); + curTopicMarker.current = marker; + updateState({ loaded: true, offsync: false, topics: topics.current, card: cardValue, channel: channelValue }); + } } else if (ignoreRevision || topicRevision > curSyncRevision.current) { const delta = await getTopicDelta(cardId, channelId, curSyncRevision.current, null, curTopicMarker.current, null); @@ -347,6 +382,20 @@ export function useConversationContext() { }, } + const getTopicItemsId = async (cardId, channelId) => { + if (cardId) { + return await card.actions.getTopicItemsId(cardId, channelId); + } + return await channel.actions.getTopicItemsId(channelId); + } + + const getTopicItemsById = async (cardId, channelId, topics) => { + if (cardId) { + return await card.actions.getTopicItemsById(cardId, channelId, topics); + } + return await channel.actions.getTopicItemsById(channelId, topics); + } + const getTopicItems = async (cardId, channelId) => { if (cardId) { return await card.actions.getTopicItems(cardId, channelId); diff --git a/app/mobile/src/context/useStoreContext.hook.js b/app/mobile/src/context/useStoreContext.hook.js index 737b0ad1..3778ef77 100644 --- a/app/mobile/src/context/useStoreContext.hook.js +++ b/app/mobile/src/context/useStoreContext.hook.js @@ -13,10 +13,23 @@ export function useStoreContext() { const initSession = async (guid) => { await db.current.executeSql(`CREATE TABLE IF NOT EXISTS channel_${guid} (channel_id text, revision integer, detail_revision integer, topic_revision integer, topic_marker integer, blocked integer, sync_revision integer, detail text, unsealed_detail text, summary text, unsealed_summary text, offsync integer, read_revision integer, unique(channel_id))`); - await db.current.executeSql(`CREATE TABLE IF NOT EXISTS channel_topic_${guid} (channel_id text, topic_id text, revision integer, detail_revision integer, blocked integer, detail text, unsealed_detail text, unique(channel_id, topic_id))`); + await db.current.executeSql(`CREATE TABLE IF NOT EXISTS channel_topic_${guid} (channel_id text, topic_id text, revision integer, created integer, detail_revision integer, blocked integer, detail text, unsealed_detail text, unique(channel_id, topic_id))`); await db.current.executeSql(`CREATE TABLE IF NOT EXISTS card_${guid} (card_id text, revision integer, detail_revision integer, profile_revision integer, detail text, profile text, notified_view integer, notified_article integer, notified_profile integer, notified_channel integer, offsync integer, blocked integer, unique(card_id))`); await db.current.executeSql(`CREATE TABLE IF NOT EXISTS card_channel_${guid} (card_id text, channel_id text, revision integer, detail_revision integer, topic_revision integer, topic_marker integer, sync_revision integer, detail text, unsealed_detail text, summary text, unsealed_summary text, offsync integer, blocked integer, read_revision integer, unique(card_id, channel_id))`); - await db.current.executeSql(`CREATE TABLE IF NOT EXISTS card_channel_topic_${guid} (card_id text, channel_id text, topic_id text, revision integer, detail_revision integer, blocked integer, detail text, unsealed_detail text, unique(card_id, channel_id, topic_id))`); + await db.current.executeSql(`CREATE TABLE IF NOT EXISTS card_channel_topic_${guid} (card_id text, channel_id text, topic_id text, revision integer, created integer, detail_revision integer, blocked integer, detail text, unsealed_detail text, unique(card_id, channel_id, topic_id))`); + } + + const hasColumn = async (table, column) => { + const pragma = await db.current.executeSql(`PRAGMA table_info(${table})`); + if (pragma?.length === 1) { + for (let i = 0; i < pragma[0].rows.length; i++) { + const col = pragma[0].rows.item(i); + if (col.name === column) { + return true; + } + } + } + return false } const actions = { @@ -28,6 +41,16 @@ export function useStoreContext() { await db.current.executeSql("INSERT OR IGNORE INTO app (key, value) values ('session', null);"); return await getAppValue(db.current, 'session'); }, + updateDb: async (guid) => { + const hasChannel = await hasColumn(`channel_topic_${guid}`, 'created'); + if (!hasChannel) { + await db.current.executeSql(`ALTER TABLE channel_topic_${guid} ADD COLUMN created integer default 0`); + } + const hasCardChannel = await hasColumn(`card_channel_topic_${guid}`, 'created'); + if (!hasCardChannel) { + await db.current.executeSql(`ALTER TABLE card_channel_topic_${guid} ADD COLUMN created integer default 0`); + } + }, setSession: async (access) => { await initSession(access.guid); await db.current.executeSql("UPDATE app SET value=? WHERE key='session';", [encodeObject(access)]); @@ -240,9 +263,28 @@ export function useStoreContext() { unsealedDetail: decodeObject(topic.unsealed_detail), })); }, + getChannelTopicItemsId: async (guid, channelId) => { + const values = await getAppValues(db.current, `SELECT topic_id, created FROM channel_topic_${guid} WHERE channel_id=?`, [channelId]); + return values.map(topic => ({ + topicId: topic.topic_id, + created: topic.created, + })); + }, + getChannelTopicItemsById: async (guid, channelId, topics) => { + const q = topics.map(() => '?'); + const values = await getAppValues(db.current, `SELECT topic_id, revision, blocked, detail_revision, detail, unsealed_detail FROM channel_topic_${guid} WHERE channel_id=? AND topic_id in (${q.join(',')})`, [channelId, ...topics]); + return values.map(topic => ({ + topicId: topic.topic_id, + revision: topic.revision, + blocked: topic.blocked, + detailRevision: topic.detail_revision, + detail: decodeObject(topic.detail), + unsealedDetail: decodeObject(topic.unsealed_detail), + })); + }, setChannelTopicItem: async (guid, channelId, topic) => { 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)]); + await db.current.executeSql(`INSERT OR REPLACE INTO channel_topic_${guid} (channel_id, topic_id, revision, created, detail_revision, blocked, detail, unsealed_detail) values (?, ?, ?, ?, ?, false, ?, null);`, [channelId, topicId, revision, detail?.created, 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]); @@ -329,11 +371,30 @@ export function useStoreContext() { detailRevision: topic.detail_revision, detail: decodeObject(topic.detail), unsealedDetail: decodeObject(topic.unsealed_detail), - })); - }, + })); + }, + getCardChannelTopicItemsId: async (guid, cardId, channelId) => { + const values = await getAppValues(db.current, `SELECT topic_id, created FROM card_channel_topic_${guid} WHERE card_id=? AND channel_id=?`, [cardId, channelId]); + return values.map(topic => ({ + topicId: topic.topic_id, + created: topic.created, + })); + }, + getCardChannelTopicItemsById: async (guid, cardId, channelId, topics) => { + const q = topics.map(() => '?'); + const values = await getAppValues(db.current, `SELECT topic_id, revision, blocked, detail_revision, detail, unsealed_detail FROM card_channel_topic_${guid} WHERE card_id=? AND channel_id=? AND topic_id in (${q.join(',')})`, [cardId, channelId, ...topics]); + return values.map(topic => ({ + topicId: topic.topic_id, + revision: topic.revision, + blocked: topic.blocked, + detailRevision: topic.detail_revision, + detail: decodeObject(topic.detail), + unsealedDetail: decodeObject(topic.unsealed_detail), + })); + }, setCardChannelTopicItem: async (guid, cardId, channelId, topic) => { const { topicId, revision, detailRevision, detail } = topic; - await db.current.executeSql(`INSERT OR REPLACE INTO card_channel_topic_${guid} (card_id, channel_id, topic_id, revision, detail_revision, detail, unsealed_detail) values (?, ?, ?, ?, ?, ?, null);`, [cardId, channelId, topicId, revision, detailRevision, encodeObject(detail)]); + await db.current.executeSql(`INSERT OR REPLACE INTO card_channel_topic_${guid} (card_id, channel_id, topic_id, revision, created, detail_revision, detail, unsealed_detail) values (?, ?, ?, ?, ?, ?, ?, null);`, [cardId, channelId, topicId, revision, topic?.created, detailRevision, encodeObject(detail)]); }, setCardChannelTopicItemUnsealedDetail: async (guid, cardId, channelId, topicId, revision, unsealed) => { await db.current.executeSql(`UPDATE card_channel_topic_${guid} set unsealed_detail=? where detail_revision=? AND card_id=? AND channel_id=? AND topic_id=?`, [encodeObject(unsealed), revision, cardId, channelId, topicId]);