Added database faker for when indexedb is disabled
Database is now in a context with a status New FAQ for indexdb errors
This commit is contained in:
parent
05d5c76c86
commit
60059ff447
@ -9,6 +9,7 @@
|
|||||||
"@testing-library/react": "^9.3.2",
|
"@testing-library/react": "^9.3.2",
|
||||||
"@testing-library/user-event": "^7.1.2",
|
"@testing-library/user-event": "^7.1.2",
|
||||||
"dexie": "^2.0.4",
|
"dexie": "^2.0.4",
|
||||||
|
"fake-indexeddb": "^3.0.0",
|
||||||
"interactjs": "^1.9.7",
|
"interactjs": "^1.9.7",
|
||||||
"normalize-wheel": "^1.0.1",
|
"normalize-wheel": "^1.0.1",
|
||||||
"react": "^16.13.0",
|
"react": "^16.13.0",
|
||||||
|
39
src/App.js
39
src/App.js
@ -9,28 +9,31 @@ import About from "./routes/About";
|
|||||||
import FAQ from "./routes/FAQ";
|
import FAQ from "./routes/FAQ";
|
||||||
|
|
||||||
import { AuthProvider } from "./contexts/AuthContext";
|
import { AuthProvider } from "./contexts/AuthContext";
|
||||||
|
import { DatabaseProvider } from "./contexts/DatabaseContext";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<AuthProvider>
|
<DatabaseProvider>
|
||||||
<Router>
|
<AuthProvider>
|
||||||
<Switch>
|
<Router>
|
||||||
<Route path="/about">
|
<Switch>
|
||||||
<About />
|
<Route path="/about">
|
||||||
</Route>
|
<About />
|
||||||
<Route path="/faq">
|
</Route>
|
||||||
<FAQ />
|
<Route path="/faq">
|
||||||
</Route>
|
<FAQ />
|
||||||
<Route path="/game/:id">
|
</Route>
|
||||||
<Game />
|
<Route path="/game/:id">
|
||||||
</Route>
|
<Game />
|
||||||
<Route path="/">
|
</Route>
|
||||||
<Home />
|
<Route path="/">
|
||||||
</Route>
|
<Home />
|
||||||
</Switch>
|
</Route>
|
||||||
</Router>
|
</Switch>
|
||||||
</AuthProvider>
|
</Router>
|
||||||
|
</AuthProvider>
|
||||||
|
</DatabaseProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import React from "react";
|
import React, { useContext } from "react";
|
||||||
import { Flex } from "theme-ui";
|
import { Flex, Box, Text } from "theme-ui";
|
||||||
import SimpleBar from "simplebar-react";
|
import SimpleBar from "simplebar-react";
|
||||||
|
|
||||||
import AddIcon from "../../icons/AddIcon";
|
import AddIcon from "../../icons/AddIcon";
|
||||||
|
|
||||||
import MapTile from "./MapTile";
|
import MapTile from "./MapTile";
|
||||||
|
import Link from "../Link";
|
||||||
|
|
||||||
|
import DatabaseContext from "../../contexts/DatabaseContext";
|
||||||
|
|
||||||
function MapTiles({
|
function MapTiles({
|
||||||
maps,
|
maps,
|
||||||
@ -16,59 +19,80 @@ function MapTiles({
|
|||||||
onMapReset,
|
onMapReset,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
}) {
|
}) {
|
||||||
|
const { databaseStatus } = useContext(DatabaseContext);
|
||||||
return (
|
return (
|
||||||
<SimpleBar style={{ maxHeight: "300px", width: "500px" }}>
|
<Box sx={{ position: "relative" }}>
|
||||||
<Flex
|
<SimpleBar style={{ maxHeight: "300px", width: "500px" }}>
|
||||||
py={2}
|
|
||||||
bg="muted"
|
|
||||||
sx={{
|
|
||||||
flexWrap: "wrap",
|
|
||||||
width: "500px",
|
|
||||||
borderRadius: "4px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex
|
<Flex
|
||||||
onClick={onMapAdd}
|
py={2}
|
||||||
sx={{
|
|
||||||
":hover": {
|
|
||||||
color: "primary",
|
|
||||||
},
|
|
||||||
":focus": {
|
|
||||||
outline: "none",
|
|
||||||
},
|
|
||||||
":active": {
|
|
||||||
color: "secondary",
|
|
||||||
},
|
|
||||||
width: "150px",
|
|
||||||
height: "150px",
|
|
||||||
borderRadius: "4px",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
cursor: "pointer",
|
|
||||||
}}
|
|
||||||
m={2}
|
|
||||||
bg="muted"
|
bg="muted"
|
||||||
aria-label="Add Map"
|
sx={{
|
||||||
title="Add Map"
|
flexWrap: "wrap",
|
||||||
|
width: "500px",
|
||||||
|
borderRadius: "4px",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<AddIcon large />
|
<Flex
|
||||||
|
onClick={onMapAdd}
|
||||||
|
sx={{
|
||||||
|
":hover": {
|
||||||
|
color: "primary",
|
||||||
|
},
|
||||||
|
":focus": {
|
||||||
|
outline: "none",
|
||||||
|
},
|
||||||
|
":active": {
|
||||||
|
color: "secondary",
|
||||||
|
},
|
||||||
|
width: "150px",
|
||||||
|
height: "150px",
|
||||||
|
borderRadius: "4px",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
m={2}
|
||||||
|
bg="muted"
|
||||||
|
aria-label="Add Map"
|
||||||
|
title="Add Map"
|
||||||
|
>
|
||||||
|
<AddIcon large />
|
||||||
|
</Flex>
|
||||||
|
{maps.map((map) => (
|
||||||
|
<MapTile
|
||||||
|
key={map.id}
|
||||||
|
map={map}
|
||||||
|
mapState={
|
||||||
|
selectedMap && map.id === selectedMap.id && selectedMapState
|
||||||
|
}
|
||||||
|
isSelected={selectedMap && map.id === selectedMap.id}
|
||||||
|
onMapSelect={onMapSelect}
|
||||||
|
onMapRemove={onMapRemove}
|
||||||
|
onMapReset={onMapReset}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
{maps.map((map) => (
|
</SimpleBar>
|
||||||
<MapTile
|
{databaseStatus === "disabled" && (
|
||||||
key={map.id}
|
<Box
|
||||||
map={map}
|
sx={{
|
||||||
mapState={
|
position: "absolute",
|
||||||
selectedMap && map.id === selectedMap.id && selectedMapState
|
top: 0,
|
||||||
}
|
left: 0,
|
||||||
isSelected={selectedMap && map.id === selectedMap.id}
|
right: 0,
|
||||||
onMapSelect={onMapSelect}
|
textAlign: "center",
|
||||||
onMapRemove={onMapRemove}
|
}}
|
||||||
onMapReset={onMapReset}
|
bg="highlight"
|
||||||
onSubmit={onSubmit}
|
p={1}
|
||||||
/>
|
>
|
||||||
))}
|
<Text as="p" variant="body2">
|
||||||
</Flex>
|
Map saving is unavailable. See <Link to="/faq">FAQ</Link> for more
|
||||||
</SimpleBar>
|
information.
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, useContext } from "react";
|
||||||
import shortid from "shortid";
|
import shortid from "shortid";
|
||||||
|
|
||||||
import { getRandomMonster } from "../helpers/monsters";
|
import DatabaseContext from "./DatabaseContext";
|
||||||
|
|
||||||
import db from "../database";
|
import { getRandomMonster } from "../helpers/monsters";
|
||||||
|
|
||||||
const AuthContext = React.createContext();
|
const AuthContext = React.createContext();
|
||||||
|
|
||||||
export function AuthProvider({ children }) {
|
export function AuthProvider({ children }) {
|
||||||
|
const { database } = useContext(DatabaseContext);
|
||||||
|
|
||||||
const [password, setPassword] = useState(
|
const [password, setPassword] = useState(
|
||||||
sessionStorage.getItem("auth") || ""
|
sessionStorage.getItem("auth") || ""
|
||||||
);
|
);
|
||||||
@ -20,41 +22,47 @@ export function AuthProvider({ children }) {
|
|||||||
|
|
||||||
const [userId, setUserId] = useState();
|
const [userId, setUserId] = useState();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!database) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
async function loadUserId() {
|
async function loadUserId() {
|
||||||
const storedUserId = await db.table("user").get("userId");
|
const storedUserId = await database.table("user").get("userId");
|
||||||
if (storedUserId) {
|
if (storedUserId) {
|
||||||
setUserId(storedUserId.value);
|
setUserId(storedUserId.value);
|
||||||
} else {
|
} else {
|
||||||
const id = shortid.generate();
|
const id = shortid.generate();
|
||||||
setUserId(id);
|
setUserId(id);
|
||||||
db.table("user").add({ key: "userId", value: id });
|
database.table("user").add({ key: "userId", value: id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadUserId();
|
loadUserId();
|
||||||
}, []);
|
}, [database]);
|
||||||
|
|
||||||
const [nickname, setNickname] = useState("");
|
const [nickname, setNickname] = useState("");
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!database) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
async function loadNickname() {
|
async function loadNickname() {
|
||||||
const storedNickname = await db.table("user").get("nickname");
|
const storedNickname = await database.table("user").get("nickname");
|
||||||
if (storedNickname) {
|
if (storedNickname) {
|
||||||
setNickname(storedNickname.value);
|
setNickname(storedNickname.value);
|
||||||
} else {
|
} else {
|
||||||
const name = getRandomMonster();
|
const name = getRandomMonster();
|
||||||
setNickname(name);
|
setNickname(name);
|
||||||
db.table("user").add({ key: "nickname", value: name });
|
database.table("user").add({ key: "nickname", value: name });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadNickname();
|
loadNickname();
|
||||||
}, []);
|
}, [database]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (nickname !== undefined) {
|
if (nickname !== undefined && database !== undefined) {
|
||||||
db.table("user").update("nickname", { value: nickname });
|
database.table("user").update("nickname", { value: nickname });
|
||||||
}
|
}
|
||||||
}, [nickname]);
|
}, [nickname, database]);
|
||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
userId,
|
userId,
|
||||||
|
54
src/contexts/DatabaseContext.js
Normal file
54
src/contexts/DatabaseContext.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import Dexie from "dexie";
|
||||||
|
|
||||||
|
const DatabaseContext = React.createContext();
|
||||||
|
|
||||||
|
export function DatabaseProvider({ children }) {
|
||||||
|
const [database, setDatabase] = useState();
|
||||||
|
const [databaseStatus, setDatabaseStatus] = useState("loading");
|
||||||
|
|
||||||
|
function loadVersions(db) {
|
||||||
|
db.version(1).stores({
|
||||||
|
maps: "id, owner",
|
||||||
|
states: "mapId",
|
||||||
|
tokens: "id, owner",
|
||||||
|
user: "key",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Create a test database and open it to see if indexedDB is enabled
|
||||||
|
let testDBRequest = window.indexedDB.open("__test");
|
||||||
|
testDBRequest.onsuccess = function () {
|
||||||
|
testDBRequest.result.close();
|
||||||
|
let db = new Dexie("OwlbearRodeoDB");
|
||||||
|
loadVersions(db);
|
||||||
|
setDatabase(db);
|
||||||
|
setDatabaseStatus("loaded");
|
||||||
|
window.indexedDB.deleteDatabase("__test");
|
||||||
|
};
|
||||||
|
// If indexedb disabled create an in memory database
|
||||||
|
testDBRequest.onerror = async function () {
|
||||||
|
console.warn("Database is disabled, no state will be saved");
|
||||||
|
const indexedDB = await import("fake-indexeddb");
|
||||||
|
const IDBKeyRange = await import("fake-indexeddb/lib/FDBKeyRange");
|
||||||
|
let db = new Dexie("OwlbearRodeoDB", { indexedDB, IDBKeyRange });
|
||||||
|
loadVersions(db);
|
||||||
|
setDatabase(db);
|
||||||
|
setDatabaseStatus("disabled");
|
||||||
|
window.indexedDB.deleteDatabase("__test");
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const value = {
|
||||||
|
database,
|
||||||
|
databaseStatus,
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<DatabaseContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</DatabaseContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DatabaseContext;
|
@ -1,11 +0,0 @@
|
|||||||
import Dexie from "dexie";
|
|
||||||
|
|
||||||
const db = new Dexie("OwlbearRodeoDB");
|
|
||||||
db.version(1).stores({
|
|
||||||
maps: "id, owner",
|
|
||||||
states: "mapId",
|
|
||||||
tokens: "id, owner",
|
|
||||||
user: "key",
|
|
||||||
});
|
|
||||||
|
|
||||||
export default db;
|
|
@ -2,13 +2,12 @@ import React, { useRef, useState, useEffect, useContext } from "react";
|
|||||||
import { Box, Button, Flex, Label, Text } from "theme-ui";
|
import { Box, Button, Flex, Label, Text } from "theme-ui";
|
||||||
import shortid from "shortid";
|
import shortid from "shortid";
|
||||||
|
|
||||||
import db from "../database";
|
|
||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
import MapTiles from "../components/map/MapTiles";
|
import MapTiles from "../components/map/MapTiles";
|
||||||
import MapSettings from "../components/map/MapSettings";
|
import MapSettings from "../components/map/MapSettings";
|
||||||
|
|
||||||
import AuthContext from "../contexts/AuthContext";
|
import AuthContext from "../contexts/AuthContext";
|
||||||
|
import DatabaseContext from "../contexts/DatabaseContext";
|
||||||
|
|
||||||
import usePrevious from "../helpers/usePrevious";
|
import usePrevious from "../helpers/usePrevious";
|
||||||
import blobToBuffer from "../helpers/blobToBuffer";
|
import blobToBuffer from "../helpers/blobToBuffer";
|
||||||
@ -43,6 +42,7 @@ function SelectMapModal({
|
|||||||
// The map currently being view in the map screen
|
// The map currently being view in the map screen
|
||||||
currentMap,
|
currentMap,
|
||||||
}) {
|
}) {
|
||||||
|
const { database } = useContext(DatabaseContext);
|
||||||
const { userId } = useContext(AuthContext);
|
const { userId } = useContext(AuthContext);
|
||||||
|
|
||||||
const wasOpen = usePrevious(isOpen);
|
const wasOpen = usePrevious(isOpen);
|
||||||
@ -55,7 +55,7 @@ function SelectMapModal({
|
|||||||
const [maps, setMaps] = useState([]);
|
const [maps, setMaps] = useState([]);
|
||||||
// Load maps from the database and ensure state is properly setup
|
// Load maps from the database and ensure state is properly setup
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!userId) {
|
if (!userId || !database) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
async function getDefaultMaps() {
|
async function getDefaultMaps() {
|
||||||
@ -73,16 +73,16 @@ function SelectMapModal({
|
|||||||
...defaultMapProps,
|
...defaultMapProps,
|
||||||
});
|
});
|
||||||
// Add a state for the map if there isn't one already
|
// Add a state for the map if there isn't one already
|
||||||
const state = await db.table("states").get(id);
|
const state = await database.table("states").get(id);
|
||||||
if (!state) {
|
if (!state) {
|
||||||
await db.table("states").add({ ...defaultMapState, mapId: id });
|
await database.table("states").add({ ...defaultMapState, mapId: id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return defaultMapsWithIds;
|
return defaultMapsWithIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadMaps() {
|
async function loadMaps() {
|
||||||
let storedMaps = await db
|
let storedMaps = await database
|
||||||
.table("maps")
|
.table("maps")
|
||||||
.where({ owner: userId })
|
.where({ owner: userId })
|
||||||
.toArray();
|
.toArray();
|
||||||
@ -93,7 +93,7 @@ function SelectMapModal({
|
|||||||
|
|
||||||
// reload map state as is may have changed while the modal was closed
|
// reload map state as is may have changed while the modal was closed
|
||||||
if (selectedMap) {
|
if (selectedMap) {
|
||||||
const state = await db.table("states").get(selectedMap.id);
|
const state = await database.table("states").get(selectedMap.id);
|
||||||
if (state) {
|
if (state) {
|
||||||
setSelectedMapState(state);
|
setSelectedMapState(state);
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ function SelectMapModal({
|
|||||||
if (!wasOpen && isOpen) {
|
if (!wasOpen && isOpen) {
|
||||||
loadMaps();
|
loadMaps();
|
||||||
}
|
}
|
||||||
}, [userId, isOpen, wasOpen, selectedMap]);
|
}, [userId, database, isOpen, wasOpen, selectedMap]);
|
||||||
|
|
||||||
const fileInputRef = useRef();
|
const fileInputRef = useRef();
|
||||||
|
|
||||||
@ -180,21 +180,21 @@ function SelectMapModal({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleMapAdd(map) {
|
async function handleMapAdd(map) {
|
||||||
await db.table("maps").add(map);
|
await database.table("maps").add(map);
|
||||||
const state = { ...defaultMapState, mapId: map.id };
|
const state = { ...defaultMapState, mapId: map.id };
|
||||||
await db.table("states").add(state);
|
await database.table("states").add(state);
|
||||||
setMaps((prevMaps) => [map, ...prevMaps]);
|
setMaps((prevMaps) => [map, ...prevMaps]);
|
||||||
setSelectedMap(map);
|
setSelectedMap(map);
|
||||||
setSelectedMapState(state);
|
setSelectedMapState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleMapRemove(id) {
|
async function handleMapRemove(id) {
|
||||||
await db.table("maps").delete(id);
|
await database.table("maps").delete(id);
|
||||||
await db.table("states").delete(id);
|
await database.table("states").delete(id);
|
||||||
setMaps((prevMaps) => {
|
setMaps((prevMaps) => {
|
||||||
const filtered = prevMaps.filter((map) => map.id !== id);
|
const filtered = prevMaps.filter((map) => map.id !== id);
|
||||||
setSelectedMap(filtered[0]);
|
setSelectedMap(filtered[0]);
|
||||||
db.table("states").get(filtered[0].id).then(setSelectedMapState);
|
database.table("states").get(filtered[0].id).then(setSelectedMapState);
|
||||||
return filtered;
|
return filtered;
|
||||||
});
|
});
|
||||||
// Removed the map from the map screen if needed
|
// Removed the map from the map screen if needed
|
||||||
@ -204,14 +204,14 @@ function SelectMapModal({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleMapSelect(map) {
|
async function handleMapSelect(map) {
|
||||||
const state = await db.table("states").get(map.id);
|
const state = await database.table("states").get(map.id);
|
||||||
setSelectedMapState(state);
|
setSelectedMapState(state);
|
||||||
setSelectedMap(map);
|
setSelectedMap(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleMapReset(id) {
|
async function handleMapReset(id) {
|
||||||
const state = { ...defaultMapState, mapId: id };
|
const state = { ...defaultMapState, mapId: id };
|
||||||
await db.table("states").put(state);
|
await database.table("states").put(state);
|
||||||
setSelectedMapState(state);
|
setSelectedMapState(state);
|
||||||
// Reset the state of the current map if needed
|
// Reset the state of the current map if needed
|
||||||
if (currentMap && currentMap.id === selectedMap.id) {
|
if (currentMap && currentMap.id === selectedMap.id) {
|
||||||
@ -261,7 +261,7 @@ function SelectMapModal({
|
|||||||
|
|
||||||
async function handleMapSettingsChange(key, value) {
|
async function handleMapSettingsChange(key, value) {
|
||||||
const change = { [key]: value, lastModified: Date.now() };
|
const change = { [key]: value, lastModified: Date.now() };
|
||||||
db.table("maps").update(selectedMap.id, change);
|
database.table("maps").update(selectedMap.id, change);
|
||||||
const newMap = { ...selectedMap, ...change };
|
const newMap = { ...selectedMap, ...change };
|
||||||
setMaps((prevMaps) => {
|
setMaps((prevMaps) => {
|
||||||
const newMaps = [...prevMaps];
|
const newMaps = [...prevMaps];
|
||||||
@ -275,7 +275,7 @@ function SelectMapModal({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleMapStateSettingsChange(key, value) {
|
async function handleMapStateSettingsChange(key, value) {
|
||||||
db.table("states").update(selectedMap.id, { [key]: value });
|
database.table("states").update(selectedMap.id, { [key]: value });
|
||||||
setSelectedMapState((prevState) => ({ ...prevState, [key]: value }));
|
setSelectedMapState((prevState) => ({ ...prevState, [key]: value }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,20 +4,20 @@ import { Box, Label, Flex, Button, useColorMode, Checkbox } from "theme-ui";
|
|||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
|
|
||||||
import AuthContext from "../contexts/AuthContext";
|
import AuthContext from "../contexts/AuthContext";
|
||||||
|
import DatabaseContext from "../contexts/DatabaseContext";
|
||||||
import db from "../database";
|
|
||||||
|
|
||||||
function SettingsModal({ isOpen, onRequestClose }) {
|
function SettingsModal({ isOpen, onRequestClose }) {
|
||||||
|
const { database } = useContext(DatabaseContext);
|
||||||
const { userId } = useContext(AuthContext);
|
const { userId } = useContext(AuthContext);
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
|
|
||||||
async function handleEraseAllData() {
|
async function handleEraseAllData() {
|
||||||
await db.delete();
|
await database.delete();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleClearCache() {
|
async function handleClearCache() {
|
||||||
await db.table("maps").where("owner").notEqual(userId).delete();
|
await database.table("maps").where("owner").notEqual(userId).delete();
|
||||||
// TODO: With custom tokens look up all tokens that aren't being used in a state
|
// TODO: With custom tokens look up all tokens that aren't being used in a state
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
@ -131,6 +131,20 @@ function FAQ() {
|
|||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
.
|
.
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text my={1} variant="heading" as="h2" sx={{ fontSize: 3 }}>
|
||||||
|
Saving
|
||||||
|
</Text>
|
||||||
|
<Text my={1} variant="heading" as="h3">
|
||||||
|
Database is disabled.
|
||||||
|
</Text>
|
||||||
|
<Text variant="body2" as="p">
|
||||||
|
Owlbear Rodeo uses a local database to store saved data. If you are
|
||||||
|
seeing a database is disabled message this usually means you have data
|
||||||
|
storage disabled. The most common occurances of this is if you are
|
||||||
|
using Private Browsing modes or in Firefox have the Never Remember
|
||||||
|
History option enabled. The site will still function in these cases
|
||||||
|
however all data will be lost when the page closes or reloads.
|
||||||
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Footer />
|
<Footer />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -2,8 +2,6 @@ import React, { useState, useEffect, useCallback, useContext } from "react";
|
|||||||
import { Flex, Box, Text } from "theme-ui";
|
import { Flex, Box, Text } from "theme-ui";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
import db from "../database";
|
|
||||||
|
|
||||||
import { omit, isStreamStopped } from "../helpers/shared";
|
import { omit, isStreamStopped } from "../helpers/shared";
|
||||||
import useSession from "../helpers/useSession";
|
import useSession from "../helpers/useSession";
|
||||||
import useDebounce from "../helpers/useDebounce";
|
import useDebounce from "../helpers/useDebounce";
|
||||||
@ -18,10 +16,12 @@ import Link from "../components/Link";
|
|||||||
import AuthModal from "../modals/AuthModal";
|
import AuthModal from "../modals/AuthModal";
|
||||||
|
|
||||||
import AuthContext from "../contexts/AuthContext";
|
import AuthContext from "../contexts/AuthContext";
|
||||||
|
import DatabaseContext from "../contexts/DatabaseContext";
|
||||||
|
|
||||||
import { tokens as defaultTokens } from "../tokens";
|
import { tokens as defaultTokens } from "../tokens";
|
||||||
|
|
||||||
function Game() {
|
function Game() {
|
||||||
|
const { database } = useContext(DatabaseContext);
|
||||||
const { id: gameId } = useParams();
|
const { id: gameId } = useParams();
|
||||||
const { authenticationStatus, userId, nickname, setNickname } = useContext(
|
const { authenticationStatus, userId, nickname, setNickname } = useContext(
|
||||||
AuthContext
|
AuthContext
|
||||||
@ -78,11 +78,14 @@ function Game() {
|
|||||||
debouncedMapState &&
|
debouncedMapState &&
|
||||||
debouncedMapState.mapId &&
|
debouncedMapState.mapId &&
|
||||||
map &&
|
map &&
|
||||||
map.owner === userId
|
map.owner === userId &&
|
||||||
|
database
|
||||||
) {
|
) {
|
||||||
db.table("states").update(debouncedMapState.mapId, debouncedMapState);
|
database
|
||||||
|
.table("states")
|
||||||
|
.update(debouncedMapState.mapId, debouncedMapState);
|
||||||
}
|
}
|
||||||
}, [map, debouncedMapState, userId]);
|
}, [map, debouncedMapState, userId, database]);
|
||||||
|
|
||||||
function handleMapChange(newMap, newMapState) {
|
function handleMapChange(newMap, newMapState) {
|
||||||
setMapState(newMapState);
|
setMapState(newMapState);
|
||||||
@ -267,7 +270,8 @@ function Game() {
|
|||||||
const newMap = data.data;
|
const newMap = data.data;
|
||||||
// If is a file map check cache and request the full file if outdated
|
// If is a file map check cache and request the full file if outdated
|
||||||
if (newMap && newMap.type === "file") {
|
if (newMap && newMap.type === "file") {
|
||||||
db.table("maps")
|
database
|
||||||
|
.table("maps")
|
||||||
.get(newMap.id)
|
.get(newMap.id)
|
||||||
.then((cachedMap) => {
|
.then((cachedMap) => {
|
||||||
if (cachedMap && cachedMap.lastModified === newMap.lastModified) {
|
if (cachedMap && cachedMap.lastModified === newMap.lastModified) {
|
||||||
@ -291,7 +295,8 @@ function Game() {
|
|||||||
if (data.data && data.data.type === "file") {
|
if (data.data && data.data.type === "file") {
|
||||||
const newMap = { ...data.data, file: data.data.file };
|
const newMap = { ...data.data, file: data.data.file };
|
||||||
// Store in db
|
// Store in db
|
||||||
db.table("maps")
|
database
|
||||||
|
.table("maps")
|
||||||
.put(newMap)
|
.put(newMap)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setMap(newMap);
|
setMap(newMap);
|
||||||
|
57
yarn.lock
57
yarn.lock
@ -2652,6 +2652,11 @@ balanced-match@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||||
|
|
||||||
|
base64-arraybuffer-es6@0.5.0:
|
||||||
|
version "0.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.5.0.tgz#27877d01148bcfb3919c17ecf64ea163d9bdba62"
|
||||||
|
integrity sha512-UCIPaDJrNNj5jG2ZL+nzJ7czvZV/ZYX6LaIRgfVU1k1edJOQg7dkbiSKzwHkNp6aHEHER/PhlFBrMYnlvJJQEw==
|
||||||
|
|
||||||
base64-arraybuffer@0.1.5:
|
base64-arraybuffer@0.1.5:
|
||||||
version "0.1.5"
|
version "0.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
|
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
|
||||||
@ -3493,7 +3498,7 @@ core-js-pure@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a"
|
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a"
|
||||||
integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw==
|
integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw==
|
||||||
|
|
||||||
core-js@^2.4.0:
|
core-js@^2.4.0, core-js@^2.5.3:
|
||||||
version "2.6.11"
|
version "2.6.11"
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
|
||||||
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
|
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
|
||||||
@ -4751,6 +4756,14 @@ extsprintf@^1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
|
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
|
||||||
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
|
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
|
||||||
|
|
||||||
|
fake-indexeddb@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-3.0.0.tgz#1bd0ffce41b0f433409df301d334d8fd7d77da27"
|
||||||
|
integrity sha512-VrnV9dJWlVWvd8hp9MMR+JS4RLC4ZmToSkuCg91ZwpYE5mSODb3n5VEaV62Hf3AusnbrPfwQhukU+rGZm5W8PQ==
|
||||||
|
dependencies:
|
||||||
|
realistic-structured-clone "^2.0.1"
|
||||||
|
setimmediate "^1.0.5"
|
||||||
|
|
||||||
fast-deep-equal@^3.1.1:
|
fast-deep-equal@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
|
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
|
||||||
@ -9246,6 +9259,16 @@ readdirp@~3.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
picomatch "^2.0.7"
|
picomatch "^2.0.7"
|
||||||
|
|
||||||
|
realistic-structured-clone@^2.0.1:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/realistic-structured-clone/-/realistic-structured-clone-2.0.2.tgz#2f8ec225b1f9af20efc79ac96a09043704414959"
|
||||||
|
integrity sha512-5IEvyfuMJ4tjQOuKKTFNvd+H9GSbE87IcendSBannE28PTrbolgaVg5DdEApRKhtze794iXqVUFKV60GLCNKEg==
|
||||||
|
dependencies:
|
||||||
|
core-js "^2.5.3"
|
||||||
|
domexception "^1.0.1"
|
||||||
|
typeson "^5.8.2"
|
||||||
|
typeson-registry "^1.0.0-alpha.20"
|
||||||
|
|
||||||
realpath-native@^1.1.0:
|
realpath-native@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c"
|
resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c"
|
||||||
@ -9797,7 +9820,7 @@ set-value@^2.0.0, set-value@^2.0.1:
|
|||||||
is-plain-object "^2.0.3"
|
is-plain-object "^2.0.3"
|
||||||
split-string "^3.0.1"
|
split-string "^3.0.1"
|
||||||
|
|
||||||
setimmediate@^1.0.4:
|
setimmediate@^1.0.4, setimmediate@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||||
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
|
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
|
||||||
@ -10749,6 +10772,20 @@ typedarray@^0.0.6:
|
|||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||||
|
|
||||||
|
typeson-registry@^1.0.0-alpha.20:
|
||||||
|
version "1.0.0-alpha.35"
|
||||||
|
resolved "https://registry.yarnpkg.com/typeson-registry/-/typeson-registry-1.0.0-alpha.35.tgz#b86abfe440e6ee69102eebb0e8c5a916dd182ff9"
|
||||||
|
integrity sha512-a/NffrpFswBTyU6w2d6vjk62K1TZ45H64af9AfRbn7LXqNEfL+h+gw3OV2IaG+enfwqgLB5WmbkrNBaQuc/97A==
|
||||||
|
dependencies:
|
||||||
|
base64-arraybuffer-es6 "0.5.0"
|
||||||
|
typeson "5.18.2"
|
||||||
|
whatwg-url "7.1.0"
|
||||||
|
|
||||||
|
typeson@5.18.2, typeson@^5.8.2:
|
||||||
|
version "5.18.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/typeson/-/typeson-5.18.2.tgz#0d217fc0e11184a66aa7ca0076d9aa7707eb7bc2"
|
||||||
|
integrity sha512-Vetd+OGX05P4qHyHiSLdHZ5Z5GuQDrHHwSdjkqho9NSCYVSLSfRMjklD/unpHH8tXBR9Z/R05rwJSuMpMFrdsw==
|
||||||
|
|
||||||
unicode-canonical-property-names-ecmascript@^1.0.4:
|
unicode-canonical-property-names-ecmascript@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
|
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
|
||||||
@ -11175,19 +11212,19 @@ whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
|
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
|
||||||
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
|
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
|
||||||
|
|
||||||
whatwg-url@^6.4.1:
|
whatwg-url@7.1.0, whatwg-url@^7.0.0:
|
||||||
version "6.5.0"
|
version "7.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
|
||||||
integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==
|
integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==
|
||||||
dependencies:
|
dependencies:
|
||||||
lodash.sortby "^4.7.0"
|
lodash.sortby "^4.7.0"
|
||||||
tr46 "^1.0.1"
|
tr46 "^1.0.1"
|
||||||
webidl-conversions "^4.0.2"
|
webidl-conversions "^4.0.2"
|
||||||
|
|
||||||
whatwg-url@^7.0.0:
|
whatwg-url@^6.4.1:
|
||||||
version "7.1.0"
|
version "6.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"
|
||||||
integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==
|
integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
lodash.sortby "^4.7.0"
|
lodash.sortby "^4.7.0"
|
||||||
tr46 "^1.0.1"
|
tr46 "^1.0.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user