Add on upgrade listener for DB and overlay when upgrading

This commit is contained in:
Mitchell McCaffrey 2021-06-24 16:14:20 +10:00
parent 110a6bdd1f
commit 72f2c74e9d
28 changed files with 302 additions and 143 deletions

View File

@ -12,51 +12,54 @@ import HowTo from "./routes/HowTo";
import Donate from "./routes/Donate";
import { AuthProvider } from "./contexts/AuthContext";
import { DatabaseProvider } from "./contexts/DatabaseContext";
import { SettingsProvider } from "./contexts/SettingsContext";
import { KeyboardProvider } from "./contexts/KeyboardContext";
import { DatabaseProvider } from "./contexts/DatabaseContext";
import { UserIdProvider } from "./contexts/UserIdContext";
import { ToastProvider } from "./components/Toast";
function App() {
return (
<ThemeProvider theme={theme}>
<DatabaseProvider>
<SettingsProvider>
<AuthProvider>
<KeyboardProvider>
<ToastProvider>
<Router>
<Switch>
<Route path="/donate">
<Donate />
</Route>
{/* Legacy support camel case routes */}
<Route path={["/howTo", "/how-to"]}>
<HowTo />
</Route>
<Route path={["/releaseNotes", "/release-notes"]}>
<ReleaseNotes />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/faq">
<FAQ />
</Route>
<Route path="/game/:id">
<Game />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
</ToastProvider>
</KeyboardProvider>
</AuthProvider>
</SettingsProvider>
</DatabaseProvider>
<SettingsProvider>
<AuthProvider>
<KeyboardProvider>
<ToastProvider>
<Router>
<Switch>
<Route path="/donate">
<Donate />
</Route>
{/* Legacy support camel case routes */}
<Route path={["/howTo", "/how-to"]}>
<HowTo />
</Route>
<Route path={["/releaseNotes", "/release-notes"]}>
<ReleaseNotes />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/faq">
<FAQ />
</Route>
<Route path="/game/:id">
<DatabaseProvider>
<UserIdProvider>
<Game />
</UserIdProvider>
</DatabaseProvider>
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
</ToastProvider>
</KeyboardProvider>
</AuthProvider>
</SettingsProvider>
</ThemeProvider>
);
}

View File

@ -0,0 +1,80 @@
import React, { useState, useEffect } from "react";
import { Text } from "theme-ui";
import LoadingOverlay from "./LoadingOverlay";
import { shuffle } from "../helpers/shared";
const facts = [
"Owls can rotate their necks 270 degrees",
"Not all owls hoot",
"Owl flight is almost completely silent",
"Owls are used to represent the Goddess Athena in Greek mythology",
"Owls have the best night vision of any animal",
"Bears can run up to 40 mi (~64 km) per hour ",
"A hibernating bears heart beats at 8 bpm",
"Bears can see in colour",
"Koala bears are not bears",
"A polar bear can swim up to 100 mi (~161 km) without resting",
"A group of bears is called a sleuth or sloth",
"Not all bears hibernate",
];
function UpgradingLoadingOverlay() {
const [subText, setSubText] = useState();
useEffect(() => {
let index = 0;
let randomFacts = shuffle(facts);
function updateFact() {
setSubText(randomFacts[index % (randomFacts.length - 1)]);
index += 1;
}
// Show first fact after 10 seconds then every 20 seconds after that
let interval;
let timeout = setTimeout(() => {
updateFact();
interval = setInterval(() => {
updateFact();
}, 20 * 1000);
}, 10 * 1000);
return () => {
clearTimeout(timeout);
if (interval) {
clearInterval(interval);
}
};
}, []);
return (
<LoadingOverlay>
<Text as="p" variant="body2" m={1}>
Database upgrading, please wait...
</Text>
{subText && (
<>
<Text
sx={{ maxWidth: "200px", textAlign: "center" }}
as="p"
variant="caption"
m={1}
>
We're still working on the upgrade. In the meantime, did you know?
</Text>
<Text
sx={{ maxWidth: "200px", textAlign: "center" }}
as="p"
variant="body2"
>
{subText}
</Text>
</>
)}
</LoadingOverlay>
);
}
export default UpgradingLoadingOverlay;

View File

@ -14,7 +14,7 @@ import {
} from "../../helpers/token";
import Vector2 from "../../helpers/Vector2";
import { useAuth } from "../../contexts/AuthContext";
import { useUserId } from "../../contexts/UserIdContext";
import { useMapData } from "../../contexts/MapDataContext";
import { useTokenData } from "../../contexts/TokenDataContext";
import { useAssets } from "../../contexts/AssetsContext";
@ -25,7 +25,7 @@ import useImageDrop from "../../hooks/useImageDrop";
function GlobalImageDrop({ children, onMapChange, onMapTokensStateCreate }) {
const { addToast } = useToasts();
const { userId } = useAuth();
const userId = useUserId();
const { addMap, getMapState } = useMapData();
const { addToken } = useTokenData();
const { addAssets } = useAssets();

View File

@ -4,7 +4,7 @@ import { Group } from "react-konva";
import { useInteractionEmitter } from "../../contexts/MapInteractionContext";
import { useMapStage } from "../../contexts/MapStageContext";
import { useAuth } from "../../contexts/AuthContext";
import { useUserId } from "../../contexts/UserIdContext";
import Vector2 from "../../helpers/Vector2";
import { getRelativePointerPosition } from "../../helpers/konva";
@ -28,7 +28,7 @@ function MapNotes({
fadeOnHover,
}) {
const interactionEmitter = useInteractionEmitter();
const { userId } = useAuth();
const userId = useUserId();
const mapStageRef = useMapStage();
const [isBrushDown, setIsBrushDown] = useState(false);
const [noteData, setNoteData] = useState(null);

View File

@ -6,7 +6,7 @@ import useImage from "use-image";
import usePrevious from "../../hooks/usePrevious";
import useGridSnapping from "../../hooks/useGridSnapping";
import { useAuth } from "../../contexts/AuthContext";
import { useUserId } from "../../contexts/UserIdContext";
import {
useSetPreventMapInteraction,
useMapWidth,
@ -33,7 +33,7 @@ function MapToken({
fadeOnHover,
map,
}) {
const { userId } = useAuth();
const userId = useUserId();
const mapWidth = useMapWidth();
const mapHeight = useMapHeight();

View File

@ -5,7 +5,7 @@ import SelectMapModal from "../../modals/SelectMapModal";
import SelectMapIcon from "../../icons/SelectMapIcon";
import { useMapData } from "../../contexts/MapDataContext";
import { useAuth } from "../../contexts/AuthContext";
import { useUserId } from "../../contexts/UserIdContext";
function SelectMapButton({
onMapChange,
@ -17,7 +17,7 @@ function SelectMapButton({
const [isModalOpen, setIsModalOpen] = useState(false);
const { updateMapState } = useMapData();
const { userId } = useAuth();
const userId = useUserId();
function openModal() {
if (currentMapState && currentMap && currentMap.owner === userId) {
updateMapState(currentMapState.mapId, currentMapState);

View File

@ -2,7 +2,7 @@ import React, { useEffect, useState, useRef } from "react";
import { Rect, Text } from "react-konva";
import { useSpring, animated } from "react-spring/konva";
import { useAuth } from "../../contexts/AuthContext";
import { useUserId } from "../../contexts/UserIdContext";
import {
useSetPreventMapInteraction,
useMapWidth,
@ -27,7 +27,7 @@ function Note({
onNoteDragEnd,
fadeOnHover,
}) {
const { userId } = useAuth();
const userId = useUserId();
const mapWidth = useMapWidth();
const mapHeight = useMapHeight();

View File

@ -17,7 +17,7 @@ import HideIcon from "../../icons/TokenHideIcon";
import NoteIcon from "../../icons/NoteToolIcon";
import TextIcon from "../../icons/NoteTextIcon";
import { useAuth } from "../../contexts/AuthContext";
import { useUserId } from "../../contexts/UserIdContext";
const defaultNoteMaxSize = 6;
@ -29,7 +29,7 @@ function NoteMenu({
onNoteChange,
map,
}) {
const { userId } = useAuth();
const userId = useUserId();
const wasOpen = usePrevious(isOpen);

View File

@ -21,7 +21,7 @@ import useSetting from "../../hooks/useSetting";
import usePreventSelect from "../../hooks/usePreventSelect";
import { useTokenData } from "../../contexts/TokenDataContext";
import { useAuth } from "../../contexts/AuthContext";
import { useUserId } from "../../contexts/UserIdContext";
import { useMapStage } from "../../contexts/MapStageContext";
import DragContext from "../../contexts/DragContext";
@ -33,7 +33,7 @@ import { findGroup } from "../../helpers/group";
import Vector2 from "../../helpers/Vector2";
function TokenBar({ onMapTokensStateCreate }) {
const { userId } = useAuth();
const userId = useUserId();
const { tokensById, tokenGroups } = useTokenData();
const [fullScreen] = useSetting("map.fullScreen");

View File

@ -1,6 +1,6 @@
import React from "react";
import { useAuth } from "../../contexts/AuthContext";
import { useUserId } from "../../contexts/UserIdContext";
import {
useMapWidth,
useMapHeight,
@ -16,7 +16,7 @@ function TokenDragOverlay({
tokenGroup,
dragging,
}) {
const { userId } = useAuth();
const userId = useUserId();
const mapWidth = useMapWidth();
const mapHeight = useMapHeight();

View File

@ -14,7 +14,7 @@ import UnlockIcon from "../../icons/TokenUnlockIcon";
import ShowIcon from "../../icons/TokenShowIcon";
import HideIcon from "../../icons/TokenHideIcon";
import { useAuth } from "../../contexts/AuthContext";
import { useUserId } from "../../contexts/UserIdContext";
const defaultTokenMaxSize = 6;
function TokenMenu({
@ -25,7 +25,7 @@ function TokenMenu({
onTokenStateChange,
map,
}) {
const { userId } = useAuth();
const userId = useUserId();
const wasOpen = usePrevious(isOpen);

View File

@ -50,11 +50,13 @@ const AssetsContext = React.createContext();
const maxCacheSize = 1e8;
export function AssetsProvider({ children }) {
const { worker, database } = useDatabase();
const { worker, database, databaseStatus } = useDatabase();
useEffect(() => {
worker.cleanAssetCache(maxCacheSize);
}, [worker]);
if (databaseStatus === "loaded") {
worker.cleanAssetCache(maxCacheSize);
}
}, [worker, databaseStatus]);
const getAsset = useCallback(
async (assetId) => {

View File

@ -1,7 +1,5 @@
import React, { useState, useEffect, useContext } from "react";
import { useDatabase } from "./DatabaseContext";
import FakeStorage from "../helpers/FakeStorage";
const AuthContext = React.createContext();
@ -17,31 +15,13 @@ try {
}
export function AuthProvider({ children }) {
const { database, databaseStatus } = useDatabase();
const [password, setPassword] = useState(storage.getItem("auth") || "");
useEffect(() => {
storage.setItem("auth", password);
}, [password]);
const [userId, setUserId] = useState();
useEffect(() => {
if (!database || databaseStatus === "loading") {
return;
}
async function loadUserId() {
const storedUserId = await database.table("user").get("userId");
if (storedUserId) {
setUserId(storedUserId.value);
}
}
loadUserId();
}, [database, databaseStatus]);
const value = {
userId,
password,
setPassword,
};

View File

@ -26,6 +26,7 @@ const worker = Comlink.wrap(new DatabaseWorker());
export function DatabaseProvider({ children }) {
const [database, setDatabase] = useState();
// "loading" | "disabled" | "upgrading" | "loaded"
const [databaseStatus, setDatabaseStatus] = useState("loading");
const [databaseError, setDatabaseError] = useState();
@ -34,7 +35,15 @@ export function DatabaseProvider({ children }) {
let testDBRequest = window.indexedDB.open("__test");
testDBRequest.onsuccess = async function () {
testDBRequest.result.close();
let db = getDatabase({ autoOpen: false });
let db = getDatabase(
{ autoOpen: false },
undefined,
undefined,
true,
(v) => {
setDatabaseStatus("upgrading");
}
);
setDatabase(db);
db.on("ready", () => {
setDatabaseStatus("loaded");

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState, useContext, useCallback } from "react";
import { useAuth } from "./AuthContext";
import { useUserId } from "./UserIdContext";
import { useDatabase } from "./DatabaseContext";
import { applyObservableChange } from "../helpers/dexie";
@ -19,7 +19,7 @@ const defaultMapState = {
export function MapDataProvider({ children }) {
const { database, databaseStatus } = useDatabase();
const { userId } = useAuth();
const userId = useUserId();
const [maps, setMaps] = useState([]);
const [mapStates, setMapStates] = useState([]);

View File

@ -1,7 +1,7 @@
import React, { useEffect, useContext } from "react";
import { useDatabase } from "./DatabaseContext";
import { useAuth } from "./AuthContext";
import { useUserId } from "./UserIdContext";
import { getRandomMonster } from "../helpers/monsters";
@ -11,7 +11,7 @@ export const PlayerStateContext = React.createContext();
export const PlayerUpdaterContext = React.createContext(() => {});
export function PlayerProvider({ session, children }) {
const { userId } = useAuth();
const userId = useUserId();
const { database, databaseStatus } = useDatabase();
const [playerState, setPlayerState] = useNetworkedState(
@ -53,7 +53,7 @@ export function PlayerProvider({ session, children }) {
if (
playerState.nickname &&
database !== undefined &&
databaseStatus !== "loading"
(databaseStatus === "loaded" || databaseStatus === "disabled")
) {
database
.table("user")

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState, useContext, useCallback } from "react";
import { useAuth } from "./AuthContext";
import { useUserId } from "./UserIdContext";
import { useDatabase } from "./DatabaseContext";
import { applyObservableChange } from "../helpers/dexie";
@ -10,7 +10,7 @@ const TokenDataContext = React.createContext();
export function TokenDataProvider({ children }) {
const { database, databaseStatus } = useDatabase();
const { userId } = useAuth();
const userId = useUserId();
const [tokens, setTokens] = useState([]);
const [tokensLoading, setTokensLoading] = useState(true);

View File

@ -0,0 +1,36 @@
import React, { useEffect, useState, useContext } from "react";
import { useDatabase } from "./DatabaseContext";
/**
* @type {React.Context<string|undefined>}
*/
const UserIdContext = React.createContext();
export function UserIdProvider({ children }) {
const { database, databaseStatus } = useDatabase();
const [userId, setUserId] = useState();
useEffect(() => {
if (!database || databaseStatus === "loading") {
return;
}
async function loadUserId() {
const storedUserId = await database.table("user").get("userId");
if (storedUserId) {
setUserId(storedUserId.value);
}
}
loadUserId();
}, [database, databaseStatus]);
return (
<UserIdContext.Provider value={userId}>{children}</UserIdContext.Provider>
);
}
export function useUserId() {
return useContext(UserIdContext);
}
export default UserIdContext;

View File

@ -3,7 +3,7 @@ import Dexie, { DexieOptions } from "dexie";
import { v4 as uuid } from "uuid";
import "dexie-observable";
import { loadVersions, latestVersion } from "./upgrade";
import { loadVersions } from "./upgrade";
import { getDefaultMaps } from "./maps";
import { getDefaultTokens } from "./tokens";
@ -36,16 +36,18 @@ function populate(db) {
* @param {string=} name
* @param {number=} versionNumber
* @param {boolean=} populateData
* @param {import("./upgrade").OnUpgrade=} onUpgrade
* @returns {Dexie}
*/
export function getDatabase(
options,
name = "OwlbearRodeoDB",
versionNumber = latestVersion,
populateData = true
versionNumber = undefined,
populateData = true,
onUpgrade = undefined
) {
let db = new Dexie(name, options);
loadVersions(db, versionNumber);
loadVersions(db, versionNumber, onUpgrade);
if (populateData) {
populate(db);
}

View File

@ -19,7 +19,7 @@ import {
useDebouncedStageScale,
} from "../contexts/MapInteractionContext";
import { MapStageProvider, useMapStage } from "../contexts/MapStageContext";
import AuthContext, { useAuth } from "../contexts/AuthContext";
import UserIdContext, { useUserId } from "../contexts/UserIdContext";
import SettingsContext, { useSettings } from "../contexts/SettingsContext";
import KeyboardContext from "../contexts/KeyboardContext";
import AssetsContext, {
@ -50,7 +50,7 @@ import DatabaseContext, { useDatabase } from "../contexts/DatabaseContext";
*/
function KonvaBridge({ stageRender, children }) {
const mapStageRef = useMapStage();
const auth = useAuth();
const userId = useUserId();
const settings = useSettings();
const assets = useAssets();
const assetURLs = useContext(AssetURLsStateContext);
@ -78,7 +78,7 @@ function KonvaBridge({ stageRender, children }) {
return stageRender(
<DatabaseContext.Provider value={database}>
<AuthContext.Provider value={auth}>
<UserIdContext.Provider value={userId}>
<SettingsContext.Provider value={settings}>
<KeyboardContext.Provider value={keyboardValue}>
<MapStageProvider value={mapStageRef}>
@ -140,7 +140,7 @@ function KonvaBridge({ stageRender, children }) {
</MapStageProvider>
</KeyboardContext.Provider>
</SettingsContext.Provider>
</AuthContext.Provider>
</UserIdContext.Provider>
</DatabaseContext.Provider>
);
}

View File

@ -11,7 +11,7 @@ import LoadingOverlay from "../components/LoadingOverlay";
import LoadingBar from "../components/LoadingBar";
import ErrorBanner from "../components/banner/ErrorBanner";
import { useAuth } from "../contexts/AuthContext";
import { useUserId } from "../contexts/UserIdContext";
import { useDatabase } from "../contexts/DatabaseContext";
import SelectDataModal from "./SelectDataModal";
@ -22,7 +22,7 @@ const importDBName = "OwlbearRodeoImportDB";
function ImportExportModal({ isOpen, onRequestClose }) {
const { worker } = useDatabase();
const { userId } = useAuth();
const userId = useUserId();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState();

View File

@ -25,7 +25,7 @@ import { createMapFromFile } from "../helpers/map";
import useResponsiveLayout from "../hooks/useResponsiveLayout";
import { useMapData } from "../contexts/MapDataContext";
import { useAuth } from "../contexts/AuthContext";
import { useUserId } from "../contexts/UserIdContext";
import { useAssets } from "../contexts/AssetsContext";
import { GroupProvider } from "../contexts/GroupContext";
import { TileDragProvider } from "../contexts/TileDragContext";
@ -40,7 +40,7 @@ function SelectMapModal({
}) {
const { addToast } = useToasts();
const { userId } = useAuth();
const userId = useUserId();
const {
maps,
mapStates,

View File

@ -29,7 +29,7 @@ import Vector2 from "../helpers/Vector2";
import useResponsiveLayout from "../hooks/useResponsiveLayout";
import { useTokenData } from "../contexts/TokenDataContext";
import { useAuth } from "../contexts/AuthContext";
import { useUserId } from "../contexts/UserIdContext";
import { useAssets } from "../contexts/AssetsContext";
import { GroupProvider } from "../contexts/GroupContext";
import { TileDragProvider } from "../contexts/TileDragContext";
@ -38,7 +38,7 @@ import { useMapStage } from "../contexts/MapStageContext";
function SelectTokensModal({ isOpen, onRequestClose, onMapTokensStateCreate }) {
const { addToast } = useToasts();
const { userId } = useAuth();
const userId = useUserId();
const {
tokens,
addToken,

View File

@ -14,7 +14,7 @@ import Modal from "../components/Modal";
import Slider from "../components/Slider";
import LoadingOverlay from "../components/LoadingOverlay";
import { useAuth } from "../contexts/AuthContext";
import { useUserId } from "../contexts/UserIdContext";
import { useDatabase } from "../contexts/DatabaseContext";
import useSetting from "../hooks/useSetting";
@ -24,7 +24,7 @@ import ImportExportModal from "./ImportExportModal";
function SettingsModal({ isOpen, onRequestClose }) {
const { database, databaseStatus } = useDatabase();
const { userId } = useAuth();
const userId = useUserId();
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [labelSize, setLabelSize] = useSetting("map.labelSize");
const [gridSnappingSensitivity, setGridSnappingSensitivity] = useSetting(

View File

@ -3,7 +3,7 @@ import { useToasts } from "react-toast-notifications";
import { useMapData } from "../contexts/MapDataContext";
import { useMapLoading } from "../contexts/MapLoadingContext";
import { useAuth } from "../contexts/AuthContext";
import { useUserId } from "../contexts/UserIdContext";
import { useDatabase } from "../contexts/DatabaseContext";
import { useParty } from "../contexts/PartyContext";
import { useAssets } from "../contexts/AssetsContext";
@ -39,7 +39,7 @@ const defaultMapActions = {
*/
function NetworkedMapAndTokens({ session }) {
const { addToast } = useToasts();
const { userId } = useAuth();
const userId = useUserId();
const partyState = useParty();
const { assetLoadStart, assetProgressUpdate, isLoading } = useMapLoading();

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef } from "react";
import { Group } from "react-konva";
import { useAuth } from "../contexts/AuthContext";
import { useUserId } from "../contexts/UserIdContext";
import MapPointer from "../components/map/MapPointer";
import { isEmpty } from "../helpers/shared";
@ -13,7 +13,7 @@ import useSetting from "../hooks/useSetting";
const sendTickRate = 50;
function NetworkedMapPointer({ session, active }) {
const { userId } = useAuth();
const userId = useUserId();
const [localPointerState, setLocalPointerState] = useState({});
const [pointerColor] = useSetting("pointer.color");

View File

@ -8,6 +8,7 @@ import OfflineBanner from "../components/banner/OfflineBanner";
import LoadingOverlay from "../components/LoadingOverlay";
import Link from "../components/Link";
import MapLoadingOverlay from "../components/map/MapLoadingOverlay";
import UpgradingLoadingOverlay from "../components/UpgradingLoadingOverlay";
import AuthModal from "../modals/AuthModal";
import GameExpiredModal from "../modals/GameExpiredModal";
@ -90,13 +91,16 @@ function Game() {
// Join game
useEffect(() => {
if (sessionStatus === "ready" && databaseStatus !== "loading") {
if (
sessionStatus === "ready" &&
(databaseStatus === "loaded" || databaseStatus === "disabled")
) {
session.joinGame(gameId, password);
}
}, [gameId, password, databaseStatus, session, sessionStatus]);
function handleAuthSubmit(newPassword) {
if (databaseStatus !== "loading") {
if (databaseStatus === "loaded" || databaseStatus === "disabled") {
session.joinGame(gameId, newPassword);
}
}
@ -151,6 +155,9 @@ function Game() {
isOpen={sessionStatus === "needs_update"}
/>
{!sessionStatus && <LoadingOverlay />}
{sessionStatus && databaseStatus === "upgrading" && (
<UpgradingLoadingOverlay />
)}
<MapLoadingOverlay />
</MapStageProvider>
</PartyProvider>

View File

@ -17,9 +17,15 @@ import {
import { getDefaultMaps } from "./maps";
import { getDefaultTokens } from "./tokens";
/**
* @callback OnUpgrade
* @param {number} versionNumber
*/
/**
* @callback VersionCallback
* @param {Version} version
* @param {OnUpgrade=} onUpgrade
*/
/**
@ -37,8 +43,9 @@ export const versions = {
});
},
// v1.2.1 - Move from blob files to array buffers
2(v) {
2(v, onUpgrade) {
v.stores({}).upgrade(async (tx) => {
onUpgrade?.(2);
const maps = await Dexie.waitFor(tx.table("maps").toArray());
let mapBuffers = {};
for (let map of maps) {
@ -53,8 +60,9 @@ export const versions = {
});
},
// v1.3.0 - Added new default tokens
3(v) {
3(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(3);
return tx
.table("states")
.toCollection()
@ -116,8 +124,9 @@ export const versions = {
});
},
// v1.3.1 - Added show grid option
4(v) {
4(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(4);
return tx
.table("maps")
.toCollection()
@ -127,8 +136,9 @@ export const versions = {
});
},
// v1.4.0 - Added fog subtraction
5(v) {
5(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(5);
return tx
.table("states")
.toCollection()
@ -144,8 +154,9 @@ export const versions = {
});
},
// v1.4.2 - Added map resolutions
6(v) {
6(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(6);
return tx
.table("maps")
.toCollection()
@ -156,8 +167,9 @@ export const versions = {
});
},
// v1.5.0 - Fixed default token rogue spelling
7(v) {
7(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(7);
return tx
.table("states")
.toCollection()
@ -171,8 +183,9 @@ export const versions = {
});
},
// v1.5.0 - Added map snap to grid option
8(v) {
8(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(8);
return tx
.table("maps")
.toCollection()
@ -182,8 +195,9 @@ export const versions = {
});
},
// v1.5.1 - Added lock, visibility and modified to tokens
9(v) {
9(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(9);
return tx
.table("states")
.toCollection()
@ -199,8 +213,9 @@ export const versions = {
});
},
// v1.5.1 - Added token prop category and remove isVehicle bool
10(v) {
10(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(10);
return tx
.table("tokens")
.toCollection()
@ -211,8 +226,9 @@ export const versions = {
});
},
// v1.5.2 - Added automatic cache invalidation to maps
11(v) {
11(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(11);
return tx
.table("maps")
.toCollection()
@ -222,8 +238,9 @@ export const versions = {
});
},
// v1.5.2 - Added automatic cache invalidation to tokens
12(v) {
12(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(12);
return tx
.table("tokens")
.toCollection()
@ -233,8 +250,9 @@ export const versions = {
});
},
// v1.6.0 - Added map grouping and grid scale and offset
13(v) {
13(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(13);
return tx
.table("maps")
.toCollection()
@ -256,8 +274,9 @@ export const versions = {
});
},
// v1.6.0 - Added token grouping
14(v) {
14(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(14);
return tx
.table("tokens")
.toCollection()
@ -267,8 +286,9 @@ export const versions = {
});
},
// v1.6.1 - Added width and height to tokens
15(v) {
15(v, onUpgrade) {
v.stores({}).upgrade(async (tx) => {
onUpgrade?.(15);
const tokens = await Dexie.waitFor(tx.table("tokens").toArray());
let tokenSizes = {};
for (let token of tokens) {
@ -293,8 +313,9 @@ export const versions = {
});
},
// v1.7.0 - Added note tool
16(v) {
16(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(16);
return tx
.table("states")
.toCollection()
@ -305,8 +326,9 @@ export const versions = {
});
},
// 1.7.0 (hotfix) - Optimized fog shape edits to only include needed data
17(v) {
17(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(17);
return tx
.table("states")
.toCollection()
@ -328,8 +350,9 @@ export const versions = {
});
},
// 1.8.0 - Added note text only mode, converted draw and fog representations
18(v) {
18(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(18);
return tx
.table("states")
.toCollection()
@ -355,8 +378,9 @@ export const versions = {
});
},
// 1.8.0 - Add thumbnail to maps and add measurement to grid
19(v) {
19(v, onUpgrade) {
v.stores({}).upgrade(async (tx) => {
onUpgrade?.(19);
const userId = (await Dexie.waitFor(tx.table("user").get("userId")))
.value;
const maps = await Dexie.waitFor(tx.table("maps").toArray());
@ -378,8 +402,9 @@ export const versions = {
});
},
// 1.8.0 - Add thumbnail to tokens
20(v) {
20(v, onUpgrade) {
v.stores({}).upgrade(async (tx) => {
onUpgrade?.(20);
const userId = (await Dexie.waitFor(tx.table("user").get("userId")))
.value;
const tokens = await Dexie.waitFor(tx.table("tokens").toArray());
@ -404,8 +429,9 @@ export const versions = {
v.stores({});
},
// v1.8.1 - Shorten fog shape ids
22(v) {
22(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(22);
return tx
.table("states")
.toCollection()
@ -420,8 +446,9 @@ export const versions = {
});
},
// v1.9.0 - Add outlines to tokens
23(v) {
23(v, onUpgrade) {
v.stores({}).upgrade(async (tx) => {
onUpgrade?.(23);
const tokens = await Dexie.waitFor(tx.table("tokens").toArray());
const tokenOutlines = await Dexie.waitFor(
Promise.all(tokens.map(createDataOutline))
@ -447,8 +474,9 @@ export const versions = {
});
},
// v1.9.0 - Move map assets into new table
24(v) {
24(v, onUpgrade) {
v.stores({ assets: "id, owner" }).upgrade((tx) => {
onUpgrade?.(24);
tx.table("maps").each((map) => {
let assets = [];
assets.push({
@ -493,8 +521,9 @@ export const versions = {
});
},
// v1.9.0 - Move token assets into new table
25(v) {
25(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(25);
tx.table("tokens").each((token) => {
let assets = [];
assets.push({
@ -522,8 +551,9 @@ export const versions = {
});
},
// v1.9.0 - Create foreign keys for assets
26(v) {
26(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(26);
tx.table("assets").each((asset) => {
if (asset.prevType === "map") {
tx.table("maps").update(asset.prevId, {
@ -547,8 +577,9 @@ export const versions = {
});
},
// v1.9.0 - Remove asset migration helpers
27(v) {
27(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(27);
tx.table("assets")
.toCollection()
.modify((asset) => {
@ -561,8 +592,9 @@ export const versions = {
});
},
// v1.9.0 - Remap map resolution assets
28(v) {
28(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(28);
tx.table("maps")
.toCollection()
.modify((map) => {
@ -579,8 +611,9 @@ export const versions = {
});
},
// v1.9.0 - Move tokens to use more defaults
29(v) {
29(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(29);
tx.table("tokens")
.toCollection()
.modify(async (token) => {
@ -592,8 +625,9 @@ export const versions = {
});
},
// v1.9.0 - Move tokens to use more defaults and add token outline to token states
30(v) {
30(v, onUpgrade) {
v.stores({}).upgrade(async (tx) => {
onUpgrade?.(30);
const tokens = await Dexie.waitFor(tx.table("tokens").toArray());
tx.table("states")
.toCollection()
@ -645,8 +679,9 @@ export const versions = {
});
},
// v1.9.0 - Remove maps not owned by user as cache is now done on the asset level
31(v) {
31(v, onUpgrade) {
v.stores({}).upgrade(async (tx) => {
onUpgrade?.(31);
const userId = (await Dexie.waitFor(tx.table("user").get("userId")))
?.value;
if (userId) {
@ -655,8 +690,9 @@ export const versions = {
});
},
// v1.9.0 - Remove tokens not owned by user as cache is now done on the asset level
32(v) {
32(v, onUpgrade) {
v.stores({}).upgrade(async (tx) => {
onUpgrade?.(32);
const userId = (await Dexie.waitFor(tx.table("user").get("userId")))
?.value;
if (userId) {
@ -665,8 +701,9 @@ export const versions = {
});
},
// v1.9.0 - Store default maps and tokens in db
33(v) {
33(v, onUpgrade) {
v.stores({}).upgrade(async (tx) => {
onUpgrade?.(33);
const userId = (await Dexie.waitFor(tx.table("user").get("userId")))
?.value;
if (!userId) {
@ -679,8 +716,9 @@ export const versions = {
});
},
// v1.9.0 - Add new group table
34(v) {
34(v, onUpgrade) {
v.stores({ groups: "id" }).upgrade(async (tx) => {
onUpgrade?.(34);
function groupItems(items) {
let groups = [];
let subGroups = {};
@ -714,8 +752,9 @@ export const versions = {
});
},
// v1.9.0 - Remove map and token group in respective tables
35(v) {
35(v, onUpgrade) {
v.stores({}).upgrade((tx) => {
onUpgrade?.(35);
tx.table("maps")
.toCollection()
.modify((map) => {
@ -736,10 +775,11 @@ export const latestVersion = 35;
* Load versions onto a database up to a specific version number
* @param {Dexie} db
* @param {number=} upTo version number to load up to, latest version if undefined
* @param {OnUpgrade=} onUpgrade
*/
export function loadVersions(db, upTo = latestVersion) {
export function loadVersions(db, upTo = latestVersion, onUpgrade = undefined) {
for (let versionNumber = 1; versionNumber <= upTo; versionNumber++) {
versions[versionNumber](db.version(versionNumber));
versions[versionNumber](db.version(versionNumber), onUpgrade);
}
}