mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-13 18:38:11 +02:00
Added catalogue version to make webpages not need to repeatedly fetch
info from API.
This commit is contained in:
1 parent
dde47ff5a6
commit
ce6dced3ad
6 files changed
+374
-85
No files matched your search
@@ -135,6 +135,7 @@ $PageSection = "Garden";
|
||||
<script src="/Scripts/Garden/Plants.js"></script>
|
||||
<script src="/Scripts/Garden/Mutations.js"></script>
|
||||
<script src="/Scripts/Garden/Save.js"></script>
|
||||
<script src="/Scripts/Garden/Content.js"></script>
|
||||
<script src="/Scripts/Garden/Economy.js"></script>
|
||||
<script src="/Scripts/Garden/MutationEngine.js"></script>
|
||||
<script src="/Scripts/Garden/Users.js"></script>
|
||||
|
||||
@@ -293,6 +293,7 @@ $PageTitle = "Profile";
|
||||
<script src="/Scripts/Garden/Plants.js"></script>
|
||||
<script src="/Scripts/Garden/Mutations.js"></script>
|
||||
<script src="/Scripts/Garden/Save.js"></script>
|
||||
<script src="/Scripts/Garden/Content.js"></script>
|
||||
<script src="/Scripts/Garden/Users.js"></script>
|
||||
<script src="/Scripts/Garden/Profile.js"></script>
|
||||
<script src="/Scripts/Garden/Account.js"></script>
|
||||
|
||||
@@ -61,6 +61,7 @@ $PageSection = "Shop";
|
||||
<script src="/Scripts/Garden/Plants.js"></script>
|
||||
<script src="/Scripts/Garden/Mutations.js"></script>
|
||||
<script src="/Scripts/Garden/Save.js"></script>
|
||||
<script src="/Scripts/Garden/Content.js"></script>
|
||||
<script src="/Scripts/Garden/Economy.js"></script>
|
||||
<script src="/Scripts/Garden/Shop.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
const GameContentCacheName =
|
||||
"SarahtoninGardenContent";
|
||||
|
||||
const GameContentCacheSchemaVersion = 1;
|
||||
|
||||
/*
|
||||
* Once content has been checked, page loads for the next
|
||||
* five minutes can use the local cache without any API call.
|
||||
* After that, only the tiny ContentVersion endpoint is needed
|
||||
* unless the catalogue has actually changed.
|
||||
*/
|
||||
const GameContentVersionCheckInterval =
|
||||
5 * 60 * 1000;
|
||||
|
||||
|
||||
let GameContentLoadPromise = null;
|
||||
|
||||
|
||||
async function LoadGameContent() {
|
||||
if (GameContentLoadPromise === null) {
|
||||
GameContentLoadPromise =
|
||||
LoadGameContentInternal();
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
return await GameContentLoadPromise;
|
||||
} catch (Error) {
|
||||
GameContentLoadPromise = null;
|
||||
|
||||
throw Error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function LoadGameContentInternal() {
|
||||
const Cache =
|
||||
ReadGameContentCache();
|
||||
|
||||
const Now = Date.now();
|
||||
|
||||
|
||||
if (
|
||||
Cache !== null &&
|
||||
Now - Cache.CheckedAt <
|
||||
GameContentVersionCheckInterval
|
||||
) {
|
||||
ApplyGameContent(
|
||||
Cache.Plants,
|
||||
Cache.Mutations
|
||||
);
|
||||
|
||||
return GetLoadedGameContent();
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const ContentVersion =
|
||||
await FetchGameContentVersion();
|
||||
|
||||
|
||||
if (
|
||||
Cache !== null &&
|
||||
Cache.ContentVersion ===
|
||||
ContentVersion
|
||||
) {
|
||||
Cache.CheckedAt = Now;
|
||||
|
||||
WriteGameContentCache(
|
||||
Cache
|
||||
);
|
||||
|
||||
ApplyGameContent(
|
||||
Cache.Plants,
|
||||
Cache.Mutations
|
||||
);
|
||||
|
||||
return GetLoadedGameContent();
|
||||
}
|
||||
|
||||
|
||||
return await RefreshGameContent(
|
||||
ContentVersion
|
||||
);
|
||||
} catch (VersionError) {
|
||||
console.warn(
|
||||
"Couldn't check Garden content version. " +
|
||||
"Refreshing the catalogues directly.",
|
||||
VersionError
|
||||
);
|
||||
|
||||
|
||||
try {
|
||||
return await RefreshGameContent(
|
||||
null
|
||||
);
|
||||
} catch (RefreshError) {
|
||||
if (Cache !== null) {
|
||||
console.warn(
|
||||
"Couldn't refresh Garden content. " +
|
||||
"Using the last cached catalogue.",
|
||||
RefreshError
|
||||
);
|
||||
|
||||
ApplyGameContent(
|
||||
Cache.Plants,
|
||||
Cache.Mutations
|
||||
);
|
||||
|
||||
return GetLoadedGameContent();
|
||||
}
|
||||
|
||||
throw RefreshError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function FetchGameContentVersion() {
|
||||
const Response =
|
||||
await fetch(
|
||||
ApiUrl +
|
||||
"/ContentVersion.php",
|
||||
{
|
||||
method: "GET",
|
||||
|
||||
headers: {
|
||||
"Accept":
|
||||
"application/json"
|
||||
},
|
||||
|
||||
cache: "no-store"
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
if (!Response.ok) {
|
||||
throw new Error(
|
||||
"Content version API returned HTTP " +
|
||||
Response.status
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const Result =
|
||||
await Response.json();
|
||||
|
||||
|
||||
if (
|
||||
!Result.Success ||
|
||||
typeof Result.Version !==
|
||||
"string" ||
|
||||
Result.Version.length === 0
|
||||
) {
|
||||
throw new Error(
|
||||
Result.Error ??
|
||||
"Content version API returned invalid data."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return Result.Version;
|
||||
}
|
||||
|
||||
|
||||
async function RefreshGameContent(
|
||||
ContentVersion
|
||||
) {
|
||||
const [
|
||||
FreshPlants,
|
||||
FreshMutations
|
||||
] =
|
||||
await Promise.all([
|
||||
FetchPlantsFromApi(),
|
||||
FetchMutationsFromApi()
|
||||
]);
|
||||
|
||||
|
||||
const Cache = {
|
||||
SchemaVersion:
|
||||
GameContentCacheSchemaVersion,
|
||||
|
||||
ContentVersion:
|
||||
ContentVersion,
|
||||
|
||||
CheckedAt:
|
||||
Date.now(),
|
||||
|
||||
Plants:
|
||||
FreshPlants,
|
||||
|
||||
Mutations:
|
||||
FreshMutations
|
||||
};
|
||||
|
||||
|
||||
WriteGameContentCache(
|
||||
Cache
|
||||
);
|
||||
|
||||
ApplyGameContent(
|
||||
FreshPlants,
|
||||
FreshMutations
|
||||
);
|
||||
|
||||
|
||||
return GetLoadedGameContent();
|
||||
}
|
||||
|
||||
|
||||
function ApplyGameContent(
|
||||
LoadedPlants,
|
||||
LoadedMutations
|
||||
) {
|
||||
Plants = LoadedPlants;
|
||||
MutationSets = LoadedMutations;
|
||||
}
|
||||
|
||||
|
||||
function GetLoadedGameContent() {
|
||||
return {
|
||||
Plants: Plants,
|
||||
Mutations: MutationSets
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function ReadGameContentCache() {
|
||||
let RawCache;
|
||||
|
||||
|
||||
try {
|
||||
RawCache =
|
||||
localStorage.getItem(
|
||||
GameContentCacheName
|
||||
);
|
||||
} catch (Error) {
|
||||
console.warn(
|
||||
"Couldn't read the Garden content cache.",
|
||||
Error
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
if (RawCache === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const Cache =
|
||||
JSON.parse(
|
||||
RawCache
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
!IsValidGameContentCache(
|
||||
Cache
|
||||
)
|
||||
) {
|
||||
RemoveGameContentCache();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return Cache;
|
||||
} catch (Error) {
|
||||
RemoveGameContentCache();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function IsValidGameContentCache(
|
||||
Cache
|
||||
) {
|
||||
return (
|
||||
Cache !== null &&
|
||||
typeof Cache === "object" &&
|
||||
!Array.isArray(Cache) &&
|
||||
|
||||
Cache.SchemaVersion ===
|
||||
GameContentCacheSchemaVersion &&
|
||||
|
||||
Number.isFinite(
|
||||
Cache.CheckedAt
|
||||
) &&
|
||||
|
||||
Cache.CheckedAt >= 0 &&
|
||||
|
||||
(
|
||||
Cache.ContentVersion ===
|
||||
null ||
|
||||
typeof Cache.ContentVersion ===
|
||||
"string"
|
||||
) &&
|
||||
|
||||
IsGameContentObject(
|
||||
Cache.Plants
|
||||
) &&
|
||||
|
||||
IsGameContentObject(
|
||||
Cache.Mutations
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function IsGameContentObject(
|
||||
Value
|
||||
) {
|
||||
return (
|
||||
Value !== null &&
|
||||
typeof Value === "object" &&
|
||||
!Array.isArray(Value)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function WriteGameContentCache(
|
||||
Cache
|
||||
) {
|
||||
try {
|
||||
localStorage.setItem(
|
||||
GameContentCacheName,
|
||||
JSON.stringify(
|
||||
Cache
|
||||
)
|
||||
);
|
||||
} catch (Error) {
|
||||
console.warn(
|
||||
"Couldn't write the Garden content cache.",
|
||||
Error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function RemoveGameContentCache() {
|
||||
try {
|
||||
localStorage.removeItem(
|
||||
GameContentCacheName
|
||||
);
|
||||
} catch (Error) {
|
||||
console.warn(
|
||||
"Couldn't clear the Garden content cache.",
|
||||
Error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function ClearGameContentCache() {
|
||||
RemoveGameContentCache();
|
||||
GameContentLoadPromise = null;
|
||||
}
|
||||
@@ -1,30 +1,7 @@
|
||||
let MutationSets = {};
|
||||
|
||||
let MutationsLoadPromise = null;
|
||||
let GameContentLoadPromise = null;
|
||||
|
||||
|
||||
async function LoadMutations() {
|
||||
if (MutationsLoadPromise !== null) {
|
||||
return MutationsLoadPromise;
|
||||
}
|
||||
|
||||
|
||||
MutationsLoadPromise =
|
||||
LoadMutationsFromApi();
|
||||
|
||||
|
||||
try {
|
||||
return await MutationsLoadPromise;
|
||||
} catch (Error) {
|
||||
MutationsLoadPromise = null;
|
||||
|
||||
throw Error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function LoadMutationsFromApi() {
|
||||
async function FetchMutationsFromApi() {
|
||||
const Response =
|
||||
await fetch(
|
||||
ApiUrl + "/Mutations.php",
|
||||
@@ -34,7 +11,9 @@ async function LoadMutationsFromApi() {
|
||||
headers: {
|
||||
"Accept":
|
||||
"application/json"
|
||||
}
|
||||
},
|
||||
|
||||
cache: "no-store"
|
||||
}
|
||||
);
|
||||
|
||||
@@ -67,35 +46,5 @@ async function LoadMutationsFromApi() {
|
||||
}
|
||||
|
||||
|
||||
MutationSets =
|
||||
Result.Mutations;
|
||||
|
||||
|
||||
return MutationSets;
|
||||
}
|
||||
|
||||
|
||||
async function LoadGameContent() {
|
||||
if (GameContentLoadPromise === null) {
|
||||
GameContentLoadPromise =
|
||||
Promise.all([
|
||||
LoadPlants(),
|
||||
LoadMutations()
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
await GameContentLoadPromise;
|
||||
} catch (Error) {
|
||||
GameContentLoadPromise = null;
|
||||
|
||||
throw Error;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
Plants: Plants,
|
||||
Mutations: MutationSets
|
||||
};
|
||||
return Result.Mutations;
|
||||
}
|
||||
@@ -1,29 +1,7 @@
|
||||
let Plants = {};
|
||||
|
||||
let PlantsLoadPromise = null;
|
||||
|
||||
|
||||
async function LoadPlants() {
|
||||
if (PlantsLoadPromise !== null) {
|
||||
return PlantsLoadPromise;
|
||||
}
|
||||
|
||||
|
||||
PlantsLoadPromise =
|
||||
LoadPlantsFromApi();
|
||||
|
||||
|
||||
try {
|
||||
return await PlantsLoadPromise;
|
||||
} catch (Error) {
|
||||
PlantsLoadPromise = null;
|
||||
|
||||
throw Error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function LoadPlantsFromApi() {
|
||||
async function FetchPlantsFromApi() {
|
||||
const Response =
|
||||
await fetch(
|
||||
ApiUrl + "/Plants.php",
|
||||
@@ -33,7 +11,9 @@ async function LoadPlantsFromApi() {
|
||||
headers: {
|
||||
"Accept":
|
||||
"application/json"
|
||||
}
|
||||
},
|
||||
|
||||
cache: "no-store"
|
||||
}
|
||||
);
|
||||
|
||||
@@ -66,9 +46,5 @@ async function LoadPlantsFromApi() {
|
||||
}
|
||||
|
||||
|
||||
Plants =
|
||||
Result.Plants;
|
||||
|
||||
|
||||
return Plants;
|
||||
return Result.Plants;
|
||||
}
|
||||
Reference in new issue
Block a user