Add on upgrade listener for DB and overlay when upgrading
This commit is contained in:
parent
110a6bdd1f
commit
72f2c74e9d
77
src/App.js
77
src/App.js
@ -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>
|
||||
);
|
||||
}
|
||||
|
80
src/components/UpgradingLoadingOverlay.js
Normal file
80
src/components/UpgradingLoadingOverlay.js
Normal 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 bear’s 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;
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) => {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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");
|
||||
|
@ -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([]);
|
||||
|
@ -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")
|
||||
|
@ -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);
|
||||
|
36
src/contexts/UserIdContext.js
Normal file
36
src/contexts/UserIdContext.js
Normal 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;
|
@ -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);
|
||||
}
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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>
|
||||
|
110
src/upgrade.js
110
src/upgrade.js
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user