From aaa793ad7137f010221fd2f73871faf63a6f7907 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Thu, 26 Nov 2020 16:29:10 +1100 Subject: [PATCH] Added multi-threaded initial map and token loading --- package.json | 4 ++++ src/contexts/MapDataContext.js | 15 ++++++++++++--- src/contexts/TokenDataContext.js | 12 +++++++++--- src/modals/SelectMapModal.js | 3 ++- src/modals/SelectTokensModal.js | 12 +++++++++--- src/workers/DatabaseWorker.js | 16 ++++++++++++++++ yarn.lock | 13 +++++++++++++ 7 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 src/workers/DatabaseWorker.js diff --git a/package.json b/package.json index 12a68ac..b038cdb 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@testing-library/user-event": "^12.2.2", "ammo.js": "kripken/ammo.js#aab297a4164779c3a9d8dc8d9da26958de3cb778", "case": "^1.6.3", + "comlink": "^4.3.0", "dexie": "^3.0.3", "err-code": "^2.0.3", "fake-indexeddb": "^3.1.2", @@ -71,5 +72,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "worker-loader": "^3.0.5" } } diff --git a/src/contexts/MapDataContext.js b/src/contexts/MapDataContext.js index 9d7e4b4..2119e23 100644 --- a/src/contexts/MapDataContext.js +++ b/src/contexts/MapDataContext.js @@ -1,8 +1,11 @@ import React, { useEffect, useState, useContext } from "react"; +import * as Comlink from "comlink"; import AuthContext from "./AuthContext"; import DatabaseContext from "./DatabaseContext"; +import DatabaseWorker from "worker-loader!../workers/DatabaseWorker"; // eslint-disable-line import/no-webpack-loader-syntax + import { maps as defaultMaps } from "../maps"; const MapDataContext = React.createContext(); @@ -29,6 +32,8 @@ export function MapDataProvider({ children }) { const [maps, setMaps] = useState([]); const [mapStates, setMapStates] = useState([]); + const [mapsLoading, setMapsLoading] = useState(true); + // Load maps from the database and ensure state is properly setup useEffect(() => { if (!userId || !database || databaseStatus === "loading") { @@ -60,15 +65,16 @@ export function MapDataProvider({ children }) { } async function loadMaps() { - let storedMaps = []; - // Use a cursor instead of toArray to prevent IPC max size error - await database.table("maps").each((map) => storedMaps.push(map)); + const worker = Comlink.wrap(new DatabaseWorker()); + await worker.loadData("maps"); + const storedMaps = await worker.data; const sortedMaps = storedMaps.sort((a, b) => b.created - a.created); const defaultMapsWithIds = await getDefaultMaps(); const allMaps = [...sortedMaps, ...defaultMapsWithIds]; setMaps(allMaps); const storedStates = await database.table("states").toArray(); setMapStates(storedStates); + setMapsLoading(false); } loadMaps(); @@ -137,8 +143,10 @@ export function MapDataProvider({ children }) { try { await database.table("maps").update(id, update); } catch (error) { + // if (error.name !== "QuotaExceededError") { const map = (await getMapFromDB(id)) || {}; await database.table("maps").put({ ...map, id, ...update }); + // } } setMaps((prevMaps) => { const newMaps = [...prevMaps]; @@ -247,6 +255,7 @@ export function MapDataProvider({ children }) { putMap, getMap, getMapFromDB, + mapsLoading, }; return ( {children} diff --git a/src/contexts/TokenDataContext.js b/src/contexts/TokenDataContext.js index ed576de..f94026d 100644 --- a/src/contexts/TokenDataContext.js +++ b/src/contexts/TokenDataContext.js @@ -1,8 +1,11 @@ import React, { useEffect, useState, useContext } from "react"; +import * as Comlink from "comlink"; import AuthContext from "./AuthContext"; import DatabaseContext from "./DatabaseContext"; +import DatabaseWorker from "worker-loader!../workers/DatabaseWorker"; // eslint-disable-line import/no-webpack-loader-syntax + import { tokens as defaultTokens } from "../tokens"; const TokenDataContext = React.createContext(); @@ -14,6 +17,7 @@ export function TokenDataProvider({ children }) { const { userId } = useContext(AuthContext); const [tokens, setTokens] = useState([]); + const [tokensLoading, setTokensLoading] = useState(true); useEffect(() => { if (!userId || !database || databaseStatus === "loading") { @@ -33,13 +37,14 @@ export function TokenDataProvider({ children }) { } async function loadTokens() { - let storedTokens = []; - // Use a cursor instead of toArray to prevent IPC max size error - await database.table("tokens").each((token) => storedTokens.push(token)); + const worker = Comlink.wrap(new DatabaseWorker()); + await worker.loadData("tokens"); + const storedTokens = await worker.data; const sortedTokens = storedTokens.sort((a, b) => b.created - a.created); const defaultTokensWithIds = getDefaultTokes(); const allTokens = [...sortedTokens, ...defaultTokensWithIds]; setTokens(allTokens); + setTokensLoading(false); } loadTokens(); @@ -160,6 +165,7 @@ export function TokenDataProvider({ children }) { putToken, getToken, tokensById, + tokensLoading, }; return ( diff --git a/src/modals/SelectMapModal.js b/src/modals/SelectMapModal.js index 8f745f1..96e7184 100644 --- a/src/modals/SelectMapModal.js +++ b/src/modals/SelectMapModal.js @@ -53,6 +53,7 @@ function SelectMapModal({ resetMap, updateMap, updateMaps, + mapsLoading, } = useContext(MapDataContext); /** @@ -388,7 +389,7 @@ function SelectMapModal({ - {imageLoading && } + {(imageLoading || mapsLoading) && } setIsEditModalOpen(false)} diff --git a/src/modals/SelectTokensModal.js b/src/modals/SelectTokensModal.js index 8351654..f13c3e5 100644 --- a/src/modals/SelectTokensModal.js +++ b/src/modals/SelectTokensModal.js @@ -10,6 +10,7 @@ import ConfirmModal from "./ConfirmModal"; import Modal from "../components/Modal"; import ImageDrop from "../components/ImageDrop"; import TokenTiles from "../components/token/TokenTiles"; +import LoadingOverlay from "../components/LoadingOverlay"; import blobToBuffer from "../helpers/blobToBuffer"; import useKeyboard from "../helpers/useKeyboard"; @@ -20,9 +21,13 @@ import AuthContext from "../contexts/AuthContext"; function SelectTokensModal({ isOpen, onRequestClose }) { const { userId } = useContext(AuthContext); - const { ownedTokens, addToken, removeTokens, updateTokens } = useContext( - TokenDataContext - ); + const { + ownedTokens, + addToken, + removeTokens, + updateTokens, + tokensLoading, + } = useContext(TokenDataContext); /** * Search @@ -256,6 +261,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) { + {tokensLoading && } setIsEditModalOpen(false)} diff --git a/src/workers/DatabaseWorker.js b/src/workers/DatabaseWorker.js new file mode 100644 index 0000000..2987547 --- /dev/null +++ b/src/workers/DatabaseWorker.js @@ -0,0 +1,16 @@ +import * as Comlink from "comlink"; + +import { getDatabase } from "../database"; + +// Worker to load large amounts of database data on a separate thread +let obj = { + data: [], + async loadData(table) { + let db = getDatabase({}); + this.data = []; + // Use a cursor instead of toArray to prevent IPC max size error + await db.table(table).each((map) => this.data.push(map)); + }, +}; + +Comlink.expose(obj); diff --git a/yarn.lock b/yarn.lock index a9321ba..ffd0ea8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3808,6 +3808,11 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +comlink@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.3.0.tgz#80b3366baccd87897dab3638ebfcfae28b2f87c7" + integrity sha512-mu4KKKNuW8TvkfpW/H88HBPeILubBS6T94BdD1VWBXNXfiyqVtwUCVNO1GeNOBTsIswzsMjWlycYr+77F5b84g== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -12720,6 +12725,14 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" +worker-loader@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-3.0.5.tgz#6e13a583c4120ba419eece8e4f2e098b014311bf" + integrity sha512-cOh4UqTtvT8eHpyuuTK2C66Fg/G5Pb7g11bwtKm7uyD0vj2hCGY1APlSzVD75V9ciYZt44VPbFPiSFTSLxkQ+w== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + worker-rpc@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5"