Added ability for import to automatically upgrade older db versions
This commit is contained in:
parent
631d232a09
commit
650084ac84
204
src/database.js
204
src/database.js
@ -1,22 +1,48 @@
|
||||
import Dexie from "dexie";
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import Dexie, { Version, DexieOptions } from "dexie";
|
||||
|
||||
import blobToBuffer from "./helpers/blobToBuffer";
|
||||
import { getGridDefaultInset } from "./helpers/grid";
|
||||
import { convertOldActionsToShapes } from "./actions";
|
||||
import { createThumbnail } from "./helpers/image";
|
||||
|
||||
function loadVersions(db) {
|
||||
// Helper to create a thumbnail for a file in a db
|
||||
async function createDataThumbnail(data) {
|
||||
const url = URL.createObjectURL(new Blob([data.file]));
|
||||
return await Dexie.waitFor(
|
||||
new Promise((resolve) => {
|
||||
let image = new Image();
|
||||
image.onload = async () => {
|
||||
const thumbnail = await createThumbnail(image);
|
||||
resolve(thumbnail);
|
||||
};
|
||||
image.src = url;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @callback VersionCallback
|
||||
* @param {Version} version
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mapping of version number to their upgrade function
|
||||
* @type {Object.<number, VersionCallback>}
|
||||
*/
|
||||
const versions = {
|
||||
// v1.2.0
|
||||
db.version(1).stores({
|
||||
maps: "id, owner",
|
||||
states: "mapId",
|
||||
tokens: "id, owner",
|
||||
user: "key",
|
||||
});
|
||||
1(v) {
|
||||
v.stores({
|
||||
maps: "id, owner",
|
||||
states: "mapId",
|
||||
tokens: "id, owner",
|
||||
user: "key",
|
||||
});
|
||||
},
|
||||
// v1.2.1 - Move from blob files to array buffers
|
||||
db.version(2)
|
||||
.stores({})
|
||||
.upgrade(async (tx) => {
|
||||
2(v) {
|
||||
v.stores({}).upgrade(async (tx) => {
|
||||
const maps = await Dexie.waitFor(tx.table("maps").toArray());
|
||||
let mapBuffers = {};
|
||||
for (let map of maps) {
|
||||
@ -29,10 +55,10 @@ function loadVersions(db) {
|
||||
map.file = mapBuffers[map.id];
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.3.0 - Added new default tokens
|
||||
db.version(3)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
3(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("states")
|
||||
.toCollection()
|
||||
@ -92,10 +118,10 @@ function loadVersions(db) {
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.3.1 - Added show grid option
|
||||
db.version(4)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
4(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("maps")
|
||||
.toCollection()
|
||||
@ -103,10 +129,10 @@ function loadVersions(db) {
|
||||
map.showGrid = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.4.0 - Added fog subtraction
|
||||
db.version(5)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
5(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("states")
|
||||
.toCollection()
|
||||
@ -120,10 +146,10 @@ function loadVersions(db) {
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.4.2 - Added map resolutions
|
||||
db.version(6)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
6(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("maps")
|
||||
.toCollection()
|
||||
@ -132,10 +158,10 @@ function loadVersions(db) {
|
||||
map.quality = "original";
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.5.0 - Fixed default token rogue spelling
|
||||
db.version(7)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
7(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("states")
|
||||
.toCollection()
|
||||
@ -147,10 +173,10 @@ function loadVersions(db) {
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.5.0 - Added map snap to grid option
|
||||
db.version(8)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
8(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("maps")
|
||||
.toCollection()
|
||||
@ -158,10 +184,10 @@ function loadVersions(db) {
|
||||
map.snapToGrid = true;
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.5.1 - Added lock, visibility and modified to tokens
|
||||
db.version(9)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
9(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("states")
|
||||
.toCollection()
|
||||
@ -175,10 +201,10 @@ function loadVersions(db) {
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.5.1 - Added token prop category and remove isVehicle bool
|
||||
db.version(10)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
10(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("tokens")
|
||||
.toCollection()
|
||||
@ -187,10 +213,10 @@ function loadVersions(db) {
|
||||
delete token.isVehicle;
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.5.2 - Added automatic cache invalidation to maps
|
||||
db.version(11)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
11(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("maps")
|
||||
.toCollection()
|
||||
@ -198,10 +224,10 @@ function loadVersions(db) {
|
||||
map.lastUsed = map.lastModified;
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.5.2 - Added automatic cache invalidation to tokens
|
||||
db.version(12)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
12(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("tokens")
|
||||
.toCollection()
|
||||
@ -209,10 +235,10 @@ function loadVersions(db) {
|
||||
token.lastUsed = token.lastModified;
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.6.0 - Added map grouping and grid scale and offset
|
||||
db.version(13)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
13(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("maps")
|
||||
.toCollection()
|
||||
@ -232,10 +258,10 @@ function loadVersions(db) {
|
||||
delete map.gridType;
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.6.0 - Added token grouping
|
||||
db.version(14)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
14(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("tokens")
|
||||
.toCollection()
|
||||
@ -243,10 +269,10 @@ function loadVersions(db) {
|
||||
token.group = "";
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.6.1 - Added width and height to tokens
|
||||
db.version(15)
|
||||
.stores({})
|
||||
.upgrade(async (tx) => {
|
||||
15(v) {
|
||||
v.stores({}).upgrade(async (tx) => {
|
||||
const tokens = await Dexie.waitFor(tx.table("tokens").toArray());
|
||||
let tokenSizes = {};
|
||||
for (let token of tokens) {
|
||||
@ -269,10 +295,10 @@ function loadVersions(db) {
|
||||
token.height = tokenSizes[token.id].height;
|
||||
});
|
||||
});
|
||||
},
|
||||
// v1.7.0 - Added note tool
|
||||
db.version(16)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
16(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("states")
|
||||
.toCollection()
|
||||
@ -281,11 +307,10 @@ function loadVersions(db) {
|
||||
state.editFlags = [...state.editFlags, "notes"];
|
||||
});
|
||||
});
|
||||
|
||||
},
|
||||
// 1.7.0 (hotfix) - Optimized fog shape edits to only include needed data
|
||||
db.version(17)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
17(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("states")
|
||||
.toCollection()
|
||||
@ -305,11 +330,10 @@ function loadVersions(db) {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
},
|
||||
// 1.8.0 - Added note text only mode, converted draw and fog representations
|
||||
db.version(18)
|
||||
.stores({})
|
||||
.upgrade((tx) => {
|
||||
18(v) {
|
||||
v.stores({}).upgrade((tx) => {
|
||||
return tx
|
||||
.table("states")
|
||||
.toCollection()
|
||||
@ -333,25 +357,10 @@ function loadVersions(db) {
|
||||
delete state.fogDrawActionIndex;
|
||||
});
|
||||
});
|
||||
|
||||
async function createDataThumbnail(data) {
|
||||
const url = URL.createObjectURL(new Blob([data.file]));
|
||||
return await Dexie.waitFor(
|
||||
new Promise((resolve) => {
|
||||
let image = new Image();
|
||||
image.onload = async () => {
|
||||
const thumbnail = await createThumbnail(image);
|
||||
resolve(thumbnail);
|
||||
};
|
||||
image.src = url;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
},
|
||||
// 1.8.0 - Add thumbnail to maps and add measurement to grid
|
||||
db.version(19)
|
||||
.stores({})
|
||||
.upgrade(async (tx) => {
|
||||
19(v) {
|
||||
v.stores({}).upgrade(async (tx) => {
|
||||
const maps = await Dexie.waitFor(tx.table("maps").toArray());
|
||||
const thumbnails = {};
|
||||
for (let map of maps) {
|
||||
@ -365,11 +374,10 @@ function loadVersions(db) {
|
||||
map.grid.measurement = { type: "chebyshev", scale: "5ft" };
|
||||
});
|
||||
});
|
||||
|
||||
},
|
||||
// 1.8.0 - Add thumbnail to tokens
|
||||
db.version(20)
|
||||
.stores({})
|
||||
.upgrade(async (tx) => {
|
||||
20(v) {
|
||||
v.stores({}).upgrade(async (tx) => {
|
||||
const tokens = await Dexie.waitFor(tx.table("tokens").toArray());
|
||||
const thumbnails = {};
|
||||
for (let token of tokens) {
|
||||
@ -382,11 +390,35 @@ function loadVersions(db) {
|
||||
token.thumbnail = thumbnails[token.id];
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const latestVersion = 20;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export function loadVersions(db, upTo = latestVersion) {
|
||||
for (let versionNumber = 1; versionNumber <= upTo; versionNumber++) {
|
||||
versions[versionNumber](db.version(versionNumber));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the dexie database used in DatabaseContext
|
||||
export function getDatabase(options, name="OwlbearRodeoDB") {
|
||||
/**
|
||||
* Get a Dexie database with a name and versions applied
|
||||
* @param {DexieOptions} options
|
||||
* @param {string=} name
|
||||
* @param {number=} versionNumber
|
||||
* @returns {Dexie}
|
||||
*/
|
||||
export function getDatabase(
|
||||
options,
|
||||
name = "OwlbearRodeoDB",
|
||||
versionNumber = latestVersion
|
||||
) {
|
||||
let db = new Dexie(name, options);
|
||||
loadVersions(db);
|
||||
loadVersions(db, versionNumber);
|
||||
return db;
|
||||
}
|
||||
|
@ -48,14 +48,12 @@ function ImportExportModal({ isOpen, onRequestClose }) {
|
||||
setIsLoading(true);
|
||||
backgroundTaskRunningRef.current = true;
|
||||
try {
|
||||
// Ensure import DB is cleared before importing new data
|
||||
let importDB = getDatabase({}, importDBName);
|
||||
importDB.delete();
|
||||
await worker.importData(
|
||||
file,
|
||||
importDBName,
|
||||
Comlink.proxy(handleDBProgress)
|
||||
);
|
||||
|
||||
setIsLoading(false);
|
||||
setShowImportSelector(true);
|
||||
backgroundTaskRunningRef.current = false;
|
||||
@ -106,6 +104,7 @@ function ImportExportModal({ isOpen, onRequestClose }) {
|
||||
async function handleImportSelectorClose() {
|
||||
const importDB = getDatabase({}, importDBName);
|
||||
await importDB.delete();
|
||||
importDB.close();
|
||||
setShowImportSelector(false);
|
||||
}
|
||||
|
||||
@ -157,6 +156,8 @@ function ImportExportModal({ isOpen, onRequestClose }) {
|
||||
}
|
||||
|
||||
await importDB.delete();
|
||||
importDB.close();
|
||||
db.close();
|
||||
setIsLoading(false);
|
||||
backgroundTaskRunningRef.current = false;
|
||||
window.location.reload();
|
||||
|
@ -91,8 +91,19 @@ let service = {
|
||||
if (importMeta.data.databaseName !== db.name) {
|
||||
throw new Error("Unable to import database, name mismatch");
|
||||
}
|
||||
if (importMeta.data.databaseVersion > db.verno) {
|
||||
throw new Error(
|
||||
`Database version differs. Current database is in version ${db.verno} but export is ${importMeta.data.databaseVersion}`
|
||||
);
|
||||
}
|
||||
|
||||
let importDB = getDatabase({}, databaseName);
|
||||
// Ensure import DB is cleared before importing new data
|
||||
let importDB = getDatabase({}, databaseName, 0);
|
||||
await importDB.delete();
|
||||
importDB.close();
|
||||
|
||||
// Load import database up to it's desired version
|
||||
importDB = getDatabase({}, databaseName, importMeta.data.databaseVersion);
|
||||
await importInto(importDB, data, {
|
||||
progressCallback,
|
||||
acceptNameDiff: true,
|
||||
@ -107,9 +118,10 @@ let service = {
|
||||
}
|
||||
return true;
|
||||
},
|
||||
acceptVersionDiff: true,
|
||||
});
|
||||
db.close();
|
||||
importDB.close();
|
||||
db.close();
|
||||
},
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user