mirror of
https://github.com/ae-utbm/sith.git
synced 2026-05-19 15:30:54 +00:00
feat: versionedLocalStorage
This commit is contained in:
@@ -1,13 +1,21 @@
|
||||
/**
|
||||
* For more detailed infos on how to use this file,
|
||||
* check /docs/tutorial/front/localstorage.md,
|
||||
* or https://ae-utbm.github.io/sith/tutorial/front/localstorage/
|
||||
*/
|
||||
|
||||
// increment this number when a breaking change is made with localStorage
|
||||
const CURRENT_CACHE_VERSION = 1;
|
||||
|
||||
/**
|
||||
* Remove keys that are no longer used from localStorage
|
||||
*/
|
||||
export function cacheBuster() {
|
||||
const version = Number.parseInt(localStorage.getItem("version") ?? "0", 10);
|
||||
if (version === CURRENT_CACHE_VERSION) {
|
||||
// The cache schema is up-to-date. Nothing to do.
|
||||
return;
|
||||
}
|
||||
localStorage.removeItem("basket");
|
||||
localStorage.removeItem("basket1");
|
||||
// remove all storage items which key is in the form
|
||||
// `userXXXPictures` or `userXXXPicturesNumber`
|
||||
@@ -22,3 +30,43 @@ export function cacheBuster() {
|
||||
});
|
||||
localStorage.setItem("version", CURRENT_CACHE_VERSION.toString());
|
||||
}
|
||||
|
||||
interface VersionedStorageItem<T> {
|
||||
version?: number;
|
||||
val: T | undefined;
|
||||
}
|
||||
|
||||
export const versionedLocalStorage = {
|
||||
...localStorage,
|
||||
/**
|
||||
* set this item in localStorage, alongside its version.
|
||||
*
|
||||
* Note: this expects an object, not a JSON string, because the parsing
|
||||
* into JSON needs to be done inside the function.
|
||||
*/
|
||||
setItem<T>(key: string, value: T, { version }: { version: number }) {
|
||||
localStorage.setItem(key, JSON.stringify({ version: version, val: value }));
|
||||
},
|
||||
/**
|
||||
* Get the item linked with the given key and version from localStorage.
|
||||
*
|
||||
* Note: if the given key exists in localStorage but doesn't satisfy
|
||||
* the given version, it will be cleared from cache.
|
||||
*
|
||||
* @return the object if found and with the good version, else null;
|
||||
*/
|
||||
getItem<T>(key: string, { version }: { version: number }): T | null {
|
||||
const stored = localStorage.getItem(key);
|
||||
if (!stored) {
|
||||
// this key doesn't exist, return null;
|
||||
return null;
|
||||
}
|
||||
const obj: VersionedStorageItem<T> = JSON.parse(stored);
|
||||
if (obj.version !== version || obj.val === undefined) {
|
||||
// The version is wrong, return null and remove this item from cache
|
||||
localStorage.removeItem(key);
|
||||
return null;
|
||||
}
|
||||
return obj.val;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -22,19 +22,9 @@ Pour résoudre le premier de ces deux problèmes, il y a un script permettant
|
||||
d'annuler une partie du cache.
|
||||
Ce dernier se trouve dans le fichier `core/static/bundled/core/cache.ts`.
|
||||
|
||||
Vous devrez modifier ce cache chaque fois que vous effectuerez
|
||||
un changement de schéma, c'est-à-dire dans un des cas suivants :
|
||||
|
||||
- une des clefs du cache n'est plus utilisée
|
||||
- la clef n'a pas changé, mais la manière dont les données attachées à cette clef
|
||||
sont formées a été modifiée.
|
||||
|
||||
!!!Note
|
||||
|
||||
Si vous ne faites qu'ajouter des données, sans modifier ni supprimer
|
||||
celles qui sont là, vous n'avez pas besoin d'invalider le cache.
|
||||
|
||||
Vous devez effectuer deux modifications dans ce fichier :
|
||||
Vous devrez modifier ce fichier chaque fois qu'un élément du localStorage
|
||||
cessera d'être utilisé.
|
||||
Les modifications à apporter sont les suivantes :
|
||||
|
||||
- incrémenter la version du cache
|
||||
- ajouter une ligne permettant de retirer votre clef du cache
|
||||
@@ -54,3 +44,32 @@ export function cacheBuster() {
|
||||
localStorage.setItem("version", CURRENT_CACHE_VERSION.toString());
|
||||
}
|
||||
```
|
||||
|
||||
## Versionnage d'une clef
|
||||
|
||||
Dans le cas où une paire clef-valeur du localStorage subit un changement
|
||||
dans son schéma de données, utilisez `versionedLocalStorage` :
|
||||
|
||||
```typescript
|
||||
import { versionedLocalStorage } from "#core:core/cache";
|
||||
|
||||
const foo = () => {
|
||||
let obj = versionedLocalStorage.getItem("<key>", { version: 1 });
|
||||
if (obj === null) {
|
||||
obj = fetchMyObject();
|
||||
versionedLocalStorage.setItem("<key>", obj, { version: 1 })
|
||||
}
|
||||
// Do something with obj...
|
||||
}
|
||||
```
|
||||
|
||||
!!!Warning
|
||||
|
||||
Il existe une différence d'usage entre `localStorage` et `versionedLocalStorage` :
|
||||
les valeurs données à `localStorage` doivent être des strings (généralement
|
||||
obtenus avec `JSON.stringify`), tandis que `versionedLocalStorage` utilise
|
||||
directement des objets JS.
|
||||
|
||||
Cette différence résulte du fait que `versionedLocalStorage` doit légèrement
|
||||
modifier les données pour y inclure la version, et gérer en interne
|
||||
la conversion en JSON.
|
||||
@@ -1,4 +1,4 @@
|
||||
export {};
|
||||
import { versionedLocalStorage } from "#core:core/cache";
|
||||
|
||||
interface BasketItem {
|
||||
priceId: number;
|
||||
@@ -8,6 +8,7 @@ interface BasketItem {
|
||||
}
|
||||
|
||||
const BASKET_CACHE_KEY = "basket";
|
||||
const BASKET_CACHE_VERSION = 1;
|
||||
|
||||
document.addEventListener("alpine:init", () => {
|
||||
Alpine.data("basket", (lastPurchaseTime?: number) => ({
|
||||
@@ -33,18 +34,16 @@ document.addEventListener("alpine:init", () => {
|
||||
},
|
||||
|
||||
loadBasket(): BasketItem[] {
|
||||
if (localStorage.getItem(BASKET_CACHE_KEY) === null) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem(BASKET_CACHE_KEY));
|
||||
} catch (_err) {
|
||||
return [];
|
||||
}
|
||||
const cached = versionedLocalStorage.getItem<BasketItem[]>(BASKET_CACHE_KEY, {
|
||||
version: BASKET_CACHE_VERSION,
|
||||
});
|
||||
return cached ?? [];
|
||||
},
|
||||
|
||||
saveBasket() {
|
||||
localStorage.setItem(BASKET_CACHE_KEY, JSON.stringify(this.basket));
|
||||
versionedLocalStorage.setItem(BASKET_CACHE_KEY, this.basket, {
|
||||
version: BASKET_CACHE_VERSION,
|
||||
});
|
||||
localStorage.setItem("basketTimestamp", Date.now().toString());
|
||||
},
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ document.addEventListener("alpine:init", () => {
|
||||
// cache only the pictures of the last 4 visited profiles
|
||||
sessionStorage.setItem(storageKey, JSON.stringify(cacheContent.slice(-4)));
|
||||
} catch {
|
||||
// an exception is raised if the localstorage is entirely filled.
|
||||
// an exception is raised if the storage is entirely filled.
|
||||
// To be as safe as possible, delete the cached pictures.
|
||||
// A cache hit is not worth the page breaking.
|
||||
sessionStorage.removeItem(storageKey);
|
||||
|
||||
Reference in New Issue
Block a user