From 5ce589654af2ab080bd0989fb25efd94b9f1e395 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 12 Jun 2025 09:50:10 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20maillist=20=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actions/EmailActions.js | 4 +- src/hooks/useEmail.js | 10 ++- src/stores/EmailSlice.js | 5 +- src/utils/commons.js | 124 +++++++++++++++++++++++++----------- 4 files changed, 98 insertions(+), 45 deletions(-) diff --git a/src/actions/EmailActions.js b/src/actions/EmailActions.js index af05f1b..c32e40e 100644 --- a/src/actions/EmailActions.js +++ b/src/actions/EmailActions.js @@ -312,7 +312,9 @@ export const queryEmailListAction = async ({ opi_sn = '', pagesize = 10, last_id const { errcode, result } = await fetchJSON(`${API_HOST}/v3/mail_list`, _params) const ret = errcode === 0 ? result : [] if (!isEmpty(ret)) { - writeIndexDB([{key: cacheKey, data: ret}], 'maillist', 'mailbox') + const listids = [...new Set(ret.map(ele => ele.MAI_SN))]; + writeIndexDB([{key: cacheKey, data: listids}], 'maillist', 'mailbox') + writeIndexDB(ret.map(ele => ({ data: ele, key: ele.MAI_SN})), 'listrow', 'mailbox') } return ret; } diff --git a/src/hooks/useEmail.js b/src/hooks/useEmail.js index b77018e..face4af 100644 --- a/src/hooks/useEmail.js +++ b/src/hooks/useEmail.js @@ -150,12 +150,10 @@ export const useEmailList = (mailboxDirNode) => { setIsFreshData(false) const cacheKey = isEmpty(COLI_SN) ? `dir-${VKey}` : `order-${VKey}` - const readCache = await readIndexDB(cacheKey, 'maillist', 'mailbox') - if (!isEmpty(readCache)) { - const _x = readCache.data.map((ele) => ({ - ...ele, - key: ele.MAI_SN, - })) + const readCacheIDList = await readIndexDB(cacheKey, 'maillist', 'mailbox') + const readCacheListRowsMap = await readIndexDB(readCacheIDList.data, 'listrow', 'mailbox') + if (!isEmpty(readCacheIDList)) { + const _x = readCacheIDList.data.map((ele) => readCacheListRowsMap.get(ele).data || {}) setMailList(_x) setLoading(false) } diff --git a/src/stores/EmailSlice.js b/src/stores/EmailSlice.js index 0722403..5fa5ec8 100644 --- a/src/stores/EmailSlice.js +++ b/src/stores/EmailSlice.js @@ -107,12 +107,13 @@ const emailSlice = (set, get) => ({ let isNeedRefresh = false if (!isEmpty(readCache)) { setMailboxNestedDirsActive(readCache?.tree || []) - isNeedRefresh = Date.now() - readCache.timestamp > 4 * 60 * 60 * 1000 + isNeedRefresh = Date.now() - readCache.timestamp > 1 * 60 * 60 * 1000 // isNeedRefresh = true; // test: 0 } if (isEmpty(readCache) || isNeedRefresh) { // > {4} 更新 const rootTree = await getRootMailboxDirAction({ opi_sn, userIdStr }) + console.log('empty', opi_sn, userIdStr, isEmpty(readCache), isNeedRefresh, rootTree); setMailboxNestedDirsActive(rootTree) } return false @@ -120,7 +121,7 @@ const emailSlice = (set, get) => ({ async initMailbox({ opi_sn, dei_sn, userId, userIdStr }) { const { setCurrentMailboxOPI, setCurrentMailboxDEI, getOPIEmailDir } = get() - createIndexedDBStore(['dirs', 'maillist', 'mailinfo', 'draft'], 'mailbox') + createIndexedDBStore(['dirs', 'maillist', 'listrow', 'mailinfo', 'draft'], 'mailbox') setCurrentMailboxOPI(opi_sn) setCurrentMailboxDEI(dei_sn) getOPIEmailDir(opi_sn, userIdStr) diff --git a/src/utils/commons.js b/src/utils/commons.js index 80ceed4..7c587cd 100644 --- a/src/utils/commons.js +++ b/src/utils/commons.js @@ -827,7 +827,23 @@ export const writeIndexDB = (rows, table, database) => { } }; -export const readIndexDB = (key=null, table, database) => { +/** + * Reads data from an IndexedDB object store. + * It can read a single record by key, multiple records by an array of keys, or all records. + * + * @param {string|string[]|null} keys - The key(s) to read. + * - If `string`: Reads a single record and returns the data object directly. + * - If `string[]`: Reads multiple records and returns a Map of `rowkey` to `data` objects. + * - If `null` or `undefined` or `empty string/array`: Reads all records and returns a Map of `rowkey` to `data` objects. + * @param {string} table - The name of the IndexedDB object store (table). + * @param {string} database - The name of the IndexedDB database. + * @returns {Promise>} A promise that resolves with the data. + * - Single key: Resolves with the data object or `undefined` if not found. + * - Array of keys or All records: Resolves with a `Map` where keys are rowkeys and values are data objects. + * The Map will be empty if no records are found. + * - Rejects if there's an error opening the database or during the transaction. + */ +export const readIndexDB = (keys=null, table, database) => { return new Promise((resolve, reject) => { let openRequest = indexedDB.open(database) openRequest.onupgradeneeded = function () { @@ -858,44 +874,80 @@ export const readIndexDB = (key=null, table, database) => { let transaction = db.transaction(table, 'readonly') let store = transaction.objectStore(table) // read by key - const getRequest = isEmpty(key) ? store.all() : store.get(key); - getRequest.onsuccess = (event) => { - const result = event.target.result - if (result) { - console.log(`💾Found record with key ${key}:`, result) - resolve(result) - } else { - console.log(`No record found with key ${key}.`) - resolve(); - } - } + // Handle array of keys + if (Array.isArray(keys) && keys.length > 0) { + const promises = keys.map(key => { + return new Promise((innerResolve) => { + const getRequest = store.get(key); + getRequest.onsuccess = (event) => { + const result = event.target.result; + if (result) { + // console.log(`💾Found record with key ${key}:`, result); + innerResolve([key, result]); // Resolve with [key, data] tuple + } else { + console.log(`No record found with key ${key}.`); + innerResolve(void 0); // Resolve with undefined for non-existent keys + } + }; + getRequest.onerror = (event) => { + console.error(`Error getting record with key ${key}:`, event.target.error); + innerResolve(undefined); // Resolve with undefined on error, or innerReject if you want to fail fast + }; + }); + }); - getRequest.onerror = (event) => { - console.error(`Error getting record with key ${key}:`, event.target.error) + Promise.all(promises) + .then(results => { + const resultMap = new Map(); + results.forEach(item => { + if (item !== undefined) { + resultMap.set(item[0], item[1]); // item[0] is key, item[1] is data + } + }); + resolve(resultMap); + }) + .catch(error => { + console.error('Error during batch read:', error); + reject(error); // Reject the main promise if Promise.all encounters an error + }); + } else if (!isEmpty(keys)) { // Handle single key + const getRequest = store.get(keys); + getRequest.onsuccess = (event) => { + const result = event.target.result; + if (result) { + console.log(`💾Found record with key ${keys}:`, result); + resolve(result); + } else { + console.log(`No record found with key ${keys}.`); + resolve(); + } + }; + getRequest.onerror = (event) => { + console.error(`Error getting record with key ${keys}:`, event.target.error); + reject(event.target.error); + }; + } else { // Handle read all + const getAllRequest = store.getAll(); + getAllRequest.onsuccess = (event) => { + const allData = event.target.result; + const resultMap = new Map(); + if (allData && allData.length > 0) { + allData.forEach(item => { + resultMap.set(item.key, item); + }); + console.log(`💾Found all records:`, resultMap); + resolve(resultMap); + } else { + console.log(`No records found.`); + resolve(resultMap); // Resolve with an empty Map if no records + } + }; + getAllRequest.onerror = (event) => { + console.error(`Error getting all records:`, event.target.error); + reject(event.target.error); + }; } - // const request = store.openCursor(null, 'prev'); // 从后往前 - // const results = []; - // let count = 0; - // request.onerror = function (e) { - // reject('Error getting records.') - // } - // request.onsuccess = function (e) { - // const cursor = e.target.result - // if (cursor) { - // if (count < limit) { - // results.unshift(cursor.value) - // count++ - // cursor.continue() - // } else { - // console.log(JSON.stringify(results)) - // resolve(results) - // } - // } else { - // console.log(JSON.stringify(results)) - // resolve(results) - // } - // } } }) };