Cache local storage to improve performance

This commit is contained in:
Florine W. Dekker 2020-08-12 22:01:57 +02:00
parent 9fb009c8f7
commit 329a030f28
Signed by: FWDekker
GPG Key ID: B1B567AF58D6EE0F
4 changed files with 137 additions and 107 deletions

View File

@ -1,6 +1,6 @@
{
"name": "minesweeper",
"version": "0.81.1",
"version": "0.81.2",
"description": "Just Minesweeper!",
"author": "Felix W. Dekker",
"browser": "dist/bundle.js",

View File

@ -8,6 +8,7 @@ import {BasicIconFont, Display, ForkAwesomeFont} from "./Display";
import {Field} from "./Field";
import {Solver} from "./Solver";
import {LocalStatistics} from "./Statistics";
import {Storage} from "./Storage";
import {Overlay} from "./UI";
@ -444,36 +445,14 @@ export class Game {
* The player's preferences.
*/
export class Preferences {
private static readonly storageKey = "/tools/minesweeper//preferences";
/**
* Reads the object stored in local storage.
*
* @return the object stored in local storage, or an empty object if there is nothing in the local storage
* @private
*/
private read(): { [key: string]: string } {
return JSON.parse(localStorage.getItem(Preferences.storageKey) ?? "{}");
}
/**
* Writes the given object to local storage.
*
* @param statistics the object to write to local storage
* @private
*/
private write(statistics: { [key: string]: string }): void {
localStorage.setItem(Preferences.storageKey, JSON.stringify(statistics));
}
private readonly storage = new Storage("/tools/minesweeper//preferences");
get marksEnabled(): boolean {
return (this.read()["marksEnabled"] ?? "true") === "true";
return this.storage.getBoolean("marksEnabled", true);
}
set marksEnabled(value: boolean) {
const preferences = this.read();
preferences["marksEnabled"] = "" + value;
this.write(preferences);
this.storage.setBoolean("marksEnabled", value);
}
}

View File

@ -1,4 +1,5 @@
import {formatTime} from "./Common";
import {Storage} from "./Storage";
/**
@ -35,181 +36,132 @@ export interface Statistics {
* Stores game statistics in the browser's localstorage.
*/
export class LocalStatistics implements Statistics {
private static readonly storageKey = "/tools/minesweeper//statistics";
private readonly storage = new Storage("/tools/minesweeper//statistics");
/**
* Reads the object stored in local storage.
*
* @return the object stored in local storage, or an empty object if there is nothing in the local storage
* @private
*/
private read(): { [key: string]: string } {
return JSON.parse(localStorage.getItem(LocalStatistics.storageKey) ?? "{}");
}
/**
* Writes the given object to local storage.
*
* @param statistics the object to write to local storage
* @private
*/
private write(statistics: { [key: string]: string }): void {
localStorage.setItem(LocalStatistics.storageKey, JSON.stringify(statistics));
}
clear(): void {
localStorage.removeItem(LocalStatistics.storageKey);
this.storage.clear();
}
get actionsUndone(): number {
return +(this.read()["actionsUndone"] ?? 0);
return this.storage.getNumber("actionsUndone", 0);
}
set actionsUndone(value: number) {
const statistics = this.read();
statistics["actionsUndone"] = "" + value;
this.write(statistics);
this.storage.setNumber("actionsUndone", value);
}
get actionsRedone(): number {
return +(this.read()["actionsRedone"] ?? 0);
return this.storage.getNumber("actionsRedone", 0);
}
set actionsRedone(value: number) {
const statistics = this.read();
statistics["actionsRedone"] = "" + value;
this.write(statistics);
this.storage.setNumber("actionsRedone", value);
}
get lossesUndone(): number {
return +(this.read()["lossesUndone"] ?? 0);
return this.storage.getNumber("lossesUndone", 0);
}
set lossesUndone(value: number) {
const statistics = this.read();
statistics["lossesUndone"] = "" + value;
this.write(statistics);
this.storage.setNumber("lossesUndone", value);
}
get timeSpent(): number {
return +(this.read()["timeSpent"] ?? 0);
return this.storage.getNumber("timeSpent", 0);
}
set timeSpent(value: number) {
const statistics = this.read();
statistics["timeSpent"] = "" + value;
this.write(statistics);
this.storage.setNumber("timeSpent", value);
}
get gamesStarted(): number {
return +(this.read()["gamesStarted"] ?? 0);
return this.storage.getNumber("gamesStarted", 0);
}
set gamesStarted(value: number) {
const statistics = this.read();
statistics["gamesStarted"] = "" + value;
this.write(statistics);
this.storage.setNumber("gamesStarted", value);
}
get gamesLost(): number {
return +(this.read()["gamesLost"] ?? 0);
return this.storage.getNumber("gamesLost", 0);
}
set gamesLost(value: number) {
const statistics = this.read();
statistics["gamesLost"] = "" + value;
this.write(statistics);
this.storage.setNumber("gamesLost", value);
}
get gamesWon(): number {
return +(this.read()["gamesWon"] ?? 0);
return this.storage.getNumber("gamesWon", 0);
}
set gamesWon(value: number) {
const statistics = this.read();
statistics["gamesWon"] = "" + value;
this.write(statistics);
this.storage.setNumber("gamesWon", value);
}
get gamesWonWithoutLosing(): number {
return +(this.read()["gamesWonWithoutLosing"] ?? 0);
return this.storage.getNumber("gamesWonWithoutLosing", 0);
}
set gamesWonWithoutLosing(value: number) {
const statistics = this.read();
statistics["gamesWonWithoutLosing"] = "" + value;
this.write(statistics);
this.storage.setNumber("gamesWonWithoutLosing", value);
}
get squaresChorded(): number {
return +(this.read()["squaresChorded"] ?? 0);
return this.storage.getNumber("squaresChorded", 0);
}
set squaresChorded(value: number) {
const statistics = this.read();
statistics["squaresChorded"] = "" + value;
this.write(statistics);
this.storage.setNumber("squaresChorded", value);
}
get squaresChordedLeadingToLoss(): number {
return +(this.read()["squaresChordedLeadingToLoss"] ?? 0);
return this.storage.getNumber("squaresChordedLeadingToLoss", 0);
}
set squaresChordedLeadingToLoss(value: number) {
const statistics = this.read();
statistics["squaresChordedLeadingToLoss"] = "" + value;
this.write(statistics);
this.storage.setNumber("squaresChordedLeadingToLoss", value);
}
get squaresFlagged(): number {
return +(this.read()["squaresFlagged"] ?? 0);
return this.storage.getNumber("squaresFlagged", 0);
}
set squaresFlagged(value: number) {
const statistics = this.read();
statistics["squaresFlagged"] = "" + value;
this.write(statistics);
this.storage.setNumber("squaresFlagged", value);
}
get squaresMarked(): number {
return +(this.read()["squaresMarked"] ?? 0);
return this.storage.getNumber("squaresMarked", 0);
}
set squaresMarked(value: number) {
const statistics = this.read();
statistics["squaresMarked"] = "" + value;
this.write(statistics);
this.storage.setNumber("squaresMarked", value);
}
get squaresUncovered(): number {
return +(this.read()["squaresUncovered"] ?? 0);
return this.storage.getNumber("squaresUncovered", 0);
}
set squaresUncovered(value: number) {
const statistics = this.read();
statistics["squaresUncovered"] = "" + value;
this.write(statistics);
this.storage.setNumber("squaresUncovered", value);
}
get hintsRequested(): number {
return +(this.read()["hintsRequested"] ?? 0);
return this.storage.getNumber("hintsRequested", 0);
}
set hintsRequested(value: number) {
const statistics = this.read();
statistics["hintsRequested"] = "" + value;
this.write(statistics);
this.storage.setNumber("hintsRequested", value);
}
get solverUsages(): number {
return +(this.read()["solverUsages"] ?? 0);
return this.storage.getNumber("solverUsages", 0);
}
set solverUsages(value: number) {
const statistics = this.read();
statistics["solverUsages"] = "" + value;
this.write(statistics);
this.storage.setNumber("solverUsages", value);
}

99
src/main/js/Storage.ts Normal file
View File

@ -0,0 +1,99 @@
/**
* Persistent storage, in particular `localStorage`.
*/
export class Storage {
private readonly key: string;
private _cache: { [key: string]: string } | null = null;
/**
* Constructs a new persistent storage item under the given key.
*
* @param key the unique identifier to store the data under
*/
constructor(key: string) {
this.key = key;
}
/**
* Reads the object stored in local storage.
*
* @return the object stored in local storage, or an empty object if there is nothing in the local storage
* @private
*/
private read(): { [key: string]: string } {
if (this._cache === null)
this._cache = JSON.parse(localStorage.getItem(this.key) ?? "{}");
return this._cache!;
}
/**
* Writes the given object to local storage.
*
* @param item the object to write to local storage
* @private
*/
private write(item: { [key: string]: string }): void {
this._cache = item;
localStorage.setItem(this.key, JSON.stringify(item));
}
/**
* Removes the data from storage.
*/
clear(): void {
this._cache = null;
localStorage.removeItem(this.key);
}
/**
* Retrieves a boolean from storage.
*
* @param name the name of the boolean to retrieve
* @param def the value to return if no boolean is stored with the given name
* @protected
*/
getBoolean(name: string, def: boolean = false): boolean {
return (this.read()[name] ?? `${def}`) === "true";
}
/**
* Stores a boolean.
*
* @param name the name of the boolean to store
* @param value the boolean to store under the given name
* @protected
*/
setBoolean(name: string, value: boolean): void {
const item = this.read();
item[name] = "" + value;
this.write(item);
}
/**
* Retrieves a number from storage.
*
* @param name the name of the number to retrieve
* @param def the value to return if no number is stored with the given name
* @protected
*/
getNumber(name: string, def: number = 0): number {
return +(this.read()[name] ?? def);
}
/**
* Stores a number.
*
* @param name the name of the number to store
* @param value the number to store under the given name
* @protected
*/
setNumber(name: string, value: number): void {
const item = this.read();
item[name] = "" + value;
this.write(item);
}
}