Added automatic cache invalidation for maps and tokens
This commit is contained in:
parent
529fd2caae
commit
e92c561a3a
@ -18,7 +18,7 @@ const listTokenClassName = "list-token";
|
|||||||
|
|
||||||
function Tokens({ onMapTokenStateCreate }) {
|
function Tokens({ onMapTokenStateCreate }) {
|
||||||
const { userId } = useContext(AuthContext);
|
const { userId } = useContext(AuthContext);
|
||||||
const { ownedTokens, tokens } = useContext(TokenDataContext);
|
const { ownedTokens, tokens, updateToken } = useContext(TokenDataContext);
|
||||||
const [fullScreen] = useSetting("map.fullScreen");
|
const [fullScreen] = useSetting("map.fullScreen");
|
||||||
|
|
||||||
function handleProxyDragEnd(isOnMap, token) {
|
function handleProxyDragEnd(isOnMap, token) {
|
||||||
@ -39,6 +39,8 @@ function Tokens({ onMapTokenStateCreate }) {
|
|||||||
locked: false,
|
locked: false,
|
||||||
visible: true,
|
visible: true,
|
||||||
});
|
});
|
||||||
|
// Update last used for cache invalidation
|
||||||
|
updateToken(token.id, { lastUsed: Date.now() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,9 @@ import { maps as defaultMaps } from "../maps";
|
|||||||
|
|
||||||
const MapDataContext = React.createContext();
|
const MapDataContext = React.createContext();
|
||||||
|
|
||||||
|
// Maximum number of maps to keep in the cache
|
||||||
|
const cachedMapMax = 15;
|
||||||
|
|
||||||
const defaultMapState = {
|
const defaultMapState = {
|
||||||
tokens: {},
|
tokens: {},
|
||||||
// An index into the draw actions array to which only actions before the
|
// An index into the draw actions array to which only actions before the
|
||||||
@ -70,12 +73,19 @@ export function MapDataProvider({ children }) {
|
|||||||
loadMaps();
|
loadMaps();
|
||||||
}, [userId, database]);
|
}, [userId, database]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a map to the database, also adds an assosiated state for that map
|
||||||
|
* @param {Object} map map to add
|
||||||
|
*/
|
||||||
async function addMap(map) {
|
async function addMap(map) {
|
||||||
await database.table("maps").add(map);
|
await database.table("maps").add(map);
|
||||||
const state = { ...defaultMapState, mapId: map.id };
|
const state = { ...defaultMapState, mapId: map.id };
|
||||||
await database.table("states").add(state);
|
await database.table("states").add(state);
|
||||||
setMaps((prevMaps) => [map, ...prevMaps]);
|
setMaps((prevMaps) => [map, ...prevMaps]);
|
||||||
setMapStates((prevStates) => [state, ...prevStates]);
|
setMapStates((prevStates) => [state, ...prevStates]);
|
||||||
|
if (map.owner !== userId) {
|
||||||
|
await updateCache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeMap(id) {
|
async function removeMap(id) {
|
||||||
@ -129,6 +139,11 @@ export function MapDataProvider({ children }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a map to the database if none exists or replaces a map if it already exists
|
||||||
|
* Note: this does not add a map state to do that use AddMap
|
||||||
|
* @param {Object} map the map to put
|
||||||
|
*/
|
||||||
async function putMap(map) {
|
async function putMap(map) {
|
||||||
await database.table("maps").put(map);
|
await database.table("maps").put(map);
|
||||||
setMaps((prevMaps) => {
|
setMaps((prevMaps) => {
|
||||||
@ -141,6 +156,31 @@ export function MapDataProvider({ children }) {
|
|||||||
}
|
}
|
||||||
return newMaps;
|
return newMaps;
|
||||||
});
|
});
|
||||||
|
if (map.owner !== userId) {
|
||||||
|
await updateCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep up to cachedMapMax amount of maps that you don't own
|
||||||
|
* Sorted by when they we're last used
|
||||||
|
*/
|
||||||
|
async function updateCache() {
|
||||||
|
const cachedMaps = await database
|
||||||
|
.table("maps")
|
||||||
|
.where("owner")
|
||||||
|
.notEqual(userId)
|
||||||
|
.sortBy("lastUsed");
|
||||||
|
if (cachedMaps.length > cachedMapMax) {
|
||||||
|
const cacheDeleteCount = cachedMaps.length - cachedMapMax;
|
||||||
|
const idsToDelete = cachedMaps
|
||||||
|
.slice(0, cacheDeleteCount)
|
||||||
|
.map((map) => map.id);
|
||||||
|
database.table("maps").where("id").anyOf(idsToDelete).delete();
|
||||||
|
setMaps((prevMaps) => {
|
||||||
|
return prevMaps.filter((map) => !idsToDelete.includes(map.id));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMap(mapId) {
|
function getMap(mapId) {
|
||||||
|
@ -7,6 +7,8 @@ import { tokens as defaultTokens } from "../tokens";
|
|||||||
|
|
||||||
const TokenDataContext = React.createContext();
|
const TokenDataContext = React.createContext();
|
||||||
|
|
||||||
|
const cachedTokenMax = 100;
|
||||||
|
|
||||||
export function TokenDataProvider({ children }) {
|
export function TokenDataProvider({ children }) {
|
||||||
const { database } = useContext(DatabaseContext);
|
const { database } = useContext(DatabaseContext);
|
||||||
const { userId } = useContext(AuthContext);
|
const { userId } = useContext(AuthContext);
|
||||||
@ -45,6 +47,9 @@ export function TokenDataProvider({ children }) {
|
|||||||
async function addToken(token) {
|
async function addToken(token) {
|
||||||
await database.table("tokens").add(token);
|
await database.table("tokens").add(token);
|
||||||
setTokens((prevTokens) => [token, ...prevTokens]);
|
setTokens((prevTokens) => [token, ...prevTokens]);
|
||||||
|
if (token.owner !== userId) {
|
||||||
|
await updateCache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeToken(id) {
|
async function removeToken(id) {
|
||||||
@ -80,6 +85,31 @@ export function TokenDataProvider({ children }) {
|
|||||||
}
|
}
|
||||||
return newTokens;
|
return newTokens;
|
||||||
});
|
});
|
||||||
|
if (token.owner !== userId) {
|
||||||
|
await updateCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep up to cachedTokenMax amount of tokens that you don't own
|
||||||
|
* Sorted by when they we're last used
|
||||||
|
*/
|
||||||
|
async function updateCache() {
|
||||||
|
const cachedTokens = await database
|
||||||
|
.table("tokens")
|
||||||
|
.where("owner")
|
||||||
|
.notEqual(userId)
|
||||||
|
.sortBy("lastUsed");
|
||||||
|
if (cachedTokens.length > cachedTokenMax) {
|
||||||
|
const cacheDeleteCount = cachedTokens.length - cachedTokenMax;
|
||||||
|
const idsToDelete = cachedTokens
|
||||||
|
.slice(0, cacheDeleteCount)
|
||||||
|
.map((token) => token.id);
|
||||||
|
database.table("tokens").where("id").anyOf(idsToDelete).delete();
|
||||||
|
setTokens((prevTokens) => {
|
||||||
|
return prevTokens.filter((token) => !idsToDelete.includes(token.id));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getToken(tokenId) {
|
function getToken(tokenId) {
|
||||||
|
@ -184,6 +184,28 @@ function loadVersions(db) {
|
|||||||
delete token.isVehicle;
|
delete token.isVehicle;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// v1.5.2 - Added automatic cache invalidation to maps
|
||||||
|
db.version(11)
|
||||||
|
.stores({})
|
||||||
|
.upgrade(async (tx) => {
|
||||||
|
return tx
|
||||||
|
.table("maps")
|
||||||
|
.toCollection()
|
||||||
|
.modify((map) => {
|
||||||
|
map.lastUsed = map.lastModified;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// v1.5.2 - Added automatic cache invalidation to tokens
|
||||||
|
db.version(12)
|
||||||
|
.stores({})
|
||||||
|
.upgrade(async (tx) => {
|
||||||
|
return tx
|
||||||
|
.table("tokens")
|
||||||
|
.toCollection()
|
||||||
|
.modify((token) => {
|
||||||
|
token.lastUsed = token.lastModified;
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the dexie database used in DatabaseContext
|
// Get the dexie database used in DatabaseContext
|
||||||
|
@ -149,6 +149,7 @@ function SelectMapModal({
|
|||||||
id: shortid.generate(),
|
id: shortid.generate(),
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
lastModified: Date.now(),
|
lastModified: Date.now(),
|
||||||
|
lastUsed: Date.now(),
|
||||||
owner: userId,
|
owner: userId,
|
||||||
...defaultMapProps,
|
...defaultMapProps,
|
||||||
});
|
});
|
||||||
@ -213,7 +214,13 @@ function SelectMapModal({
|
|||||||
}
|
}
|
||||||
if (selectedMapId) {
|
if (selectedMapId) {
|
||||||
await applyMapChanges();
|
await applyMapChanges();
|
||||||
onMapChange(selectedMapWithChanges, selectedMapStateWithChanges);
|
// Update last used for cache invalidation
|
||||||
|
const lastUsed = Date.now();
|
||||||
|
await updateMap(selectedMapId, { lastUsed });
|
||||||
|
onMapChange(
|
||||||
|
{ ...selectedMapWithChanges, lastUsed },
|
||||||
|
selectedMapStateWithChanges
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
onMapChange(null, null);
|
onMapChange(null, null);
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
type: "file",
|
type: "file",
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
lastModified: Date.now(),
|
lastModified: Date.now(),
|
||||||
|
lastUsed: Date.now(),
|
||||||
owner: userId,
|
owner: userId,
|
||||||
defaultSize: 1,
|
defaultSize: 1,
|
||||||
category: "character",
|
category: "character",
|
||||||
|
@ -32,7 +32,7 @@ function NetworkedMapAndTokens({ session }) {
|
|||||||
isLoading,
|
isLoading,
|
||||||
} = useContext(MapLoadingContext);
|
} = useContext(MapLoadingContext);
|
||||||
|
|
||||||
const { putToken, getToken } = useContext(TokenDataContext);
|
const { putToken, getToken, updateToken } = useContext(TokenDataContext);
|
||||||
const { putMap, updateMap, getMapFromDB } = useContext(MapDataContext);
|
const { putMap, updateMap, getMapFromDB } = useContext(MapDataContext);
|
||||||
|
|
||||||
const [currentMap, setCurrentMap] = useState(null);
|
const [currentMap, setCurrentMap] = useState(null);
|
||||||
@ -245,11 +245,15 @@ function NetworkedMapAndTokens({ session }) {
|
|||||||
if (newMap && newMap.type === "file") {
|
if (newMap && newMap.type === "file") {
|
||||||
const cachedMap = await getMapFromDB(newMap.id);
|
const cachedMap = await getMapFromDB(newMap.id);
|
||||||
if (cachedMap && cachedMap.lastModified >= newMap.lastModified) {
|
if (cachedMap && cachedMap.lastModified >= newMap.lastModified) {
|
||||||
setCurrentMap(cachedMap);
|
// Update last used for cache invalidation
|
||||||
|
const lastUsed = Date.now();
|
||||||
|
await updateMap(cachedMap.id, { lastUsed });
|
||||||
|
setCurrentMap({ ...cachedMap, lastUsed });
|
||||||
} else {
|
} else {
|
||||||
// Save map data but remove last modified so if there is an error
|
// Save map data but remove last modified so if there is an error
|
||||||
// during the map request the cache is invalid
|
// during the map request the cache is invalid. Also add last used
|
||||||
await putMap({ ...newMap, lastModified: 0 });
|
// for cache invalidation
|
||||||
|
await putMap({ ...newMap, lastModified: 0, lastUsed: Date.now() });
|
||||||
reply("mapRequest", newMap.id, "map");
|
reply("mapRequest", newMap.id, "map");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -326,16 +330,21 @@ function NetworkedMapAndTokens({ session }) {
|
|||||||
if (newToken && newToken.type === "file") {
|
if (newToken && newToken.type === "file") {
|
||||||
const cachedToken = getToken(newToken.id);
|
const cachedToken = getToken(newToken.id);
|
||||||
if (
|
if (
|
||||||
!cachedToken ||
|
cachedToken &&
|
||||||
cachedToken.lastModified !== newToken.lastModified
|
cachedToken.lastModified >= newToken.lastModified
|
||||||
) {
|
) {
|
||||||
|
// Update last used for cache invalidation
|
||||||
|
const lastUsed = Date.now();
|
||||||
|
await updateToken(cachedToken.id, { lastUsed });
|
||||||
|
} else {
|
||||||
reply("tokenRequest", newToken.id, "token");
|
reply("tokenRequest", newToken.id, "token");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (id === "tokenRequest") {
|
if (id === "tokenRequest") {
|
||||||
const token = getToken(data);
|
const token = getToken(data);
|
||||||
reply("tokenResponse", token, "token");
|
// Add a last used property for cache invalidation
|
||||||
|
reply("tokenResponse", { ...token, lastUsed: Date.now() }, "token");
|
||||||
}
|
}
|
||||||
if (id === "tokenResponse") {
|
if (id === "tokenResponse") {
|
||||||
const newToken = data;
|
const newToken = data;
|
||||||
|
Loading…
Reference in New Issue
Block a user