mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-13 18:38:11 +02:00
Added profile page to edit your profile and see stats.
This commit is contained in:
1 parent
39a8f18fa3
commit
11b26d972e
6 files changed
+1105
-6
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
@@ -3,7 +3,8 @@
|
||||
$NavbarPages = [
|
||||
"Home" => "/",
|
||||
"Sarahtonin" => "/Pages/Sarahtonin.html",
|
||||
"Garden" => "/Pages/Garden.html"
|
||||
"Garden" => "/Pages/Garden.html",
|
||||
"Profile" => "/Pages/Profile.html"
|
||||
];
|
||||
|
||||
?>
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
$PageTitle = "Profile";
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" data-palette="CatppuccinMocha">
|
||||
<?php require __DIR__ . "/Bits/Head.php"; ?>
|
||||
|
||||
<body>
|
||||
<?php require __DIR__ . "/Bits/Navbar.php"; ?>
|
||||
|
||||
<main class="MainContainer">
|
||||
<h1>Profile</h1>
|
||||
|
||||
<p>
|
||||
Manage your Garden profile, view your
|
||||
statistics, and move your account between
|
||||
devices.
|
||||
</p>
|
||||
|
||||
|
||||
<section class="ProfileSection">
|
||||
<h2>Player</h2>
|
||||
|
||||
<form id="ProfileUsernameForm">
|
||||
<label
|
||||
for="ProfileUsernameInput"
|
||||
>
|
||||
Username
|
||||
</label>
|
||||
|
||||
<div class="ProfileFormRow">
|
||||
<input
|
||||
id="ProfileUsernameInput"
|
||||
type="text"
|
||||
minlength="3"
|
||||
maxlength="24"
|
||||
autocomplete="username"
|
||||
placeholder="Username"
|
||||
required
|
||||
>
|
||||
|
||||
<button type="submit">
|
||||
Save username
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p
|
||||
id="ProfileUsernameMessage"
|
||||
class="ProfileMessage"
|
||||
></p>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="ProfileSection">
|
||||
<h2>Statistics</h2>
|
||||
|
||||
<div class="ProfileStatistics">
|
||||
<div class="ProfileStatistic">
|
||||
<span class="ProfileStatisticName">
|
||||
Current Dew
|
||||
</span>
|
||||
|
||||
<strong id="ProfileCurrentDew">
|
||||
...
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<div class="ProfileStatistic">
|
||||
<span class="ProfileStatisticName">
|
||||
Lifetime Dew
|
||||
</span>
|
||||
|
||||
<strong id="ProfileLifetimeDew">
|
||||
...
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<div class="ProfileStatistic">
|
||||
<span class="ProfileStatisticName">
|
||||
Plants discovered
|
||||
</span>
|
||||
|
||||
<strong id="ProfilePlantsDiscovered">
|
||||
...
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<div class="ProfileStatistic">
|
||||
<span class="ProfileStatisticName">
|
||||
Mutations discovered
|
||||
</span>
|
||||
|
||||
<strong id="ProfileMutationsDiscovered">
|
||||
...
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<div class="ProfileStatistic">
|
||||
<span class="ProfileStatisticName">
|
||||
Garden size
|
||||
</span>
|
||||
|
||||
<strong id="ProfileGardenSize">
|
||||
...
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="ProfileSection">
|
||||
<h2>Account</h2>
|
||||
|
||||
<p>
|
||||
Your Account Key is the secret key used
|
||||
to identify your Garden account.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can use the same Account Key on
|
||||
another device to access the same
|
||||
username, save, and leaderboard entry.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
Anyone with your Account Key can
|
||||
access your Garden account.
|
||||
</strong>
|
||||
Keep it private and treat it like a
|
||||
password.
|
||||
</p>
|
||||
|
||||
|
||||
<details class="AccountKeySpoiler">
|
||||
<summary>
|
||||
Show Account Key
|
||||
</summary>
|
||||
|
||||
<div class="AccountKeyContent">
|
||||
<p>
|
||||
Do not share this key with
|
||||
anyone you do not trust.
|
||||
</p>
|
||||
|
||||
<code id="AccountKeyDisplay">
|
||||
Loading...
|
||||
</code>
|
||||
|
||||
<div class="ProfileButtonRow">
|
||||
<button
|
||||
id="CopyAccountKeyButton"
|
||||
type="button"
|
||||
>
|
||||
Copy Account Key
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="ExportAccountKeyButton"
|
||||
type="button"
|
||||
>
|
||||
Export Account Key
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p
|
||||
id="AccountKeyMessage"
|
||||
class="ProfileMessage"
|
||||
></p>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="ProfileSection">
|
||||
<h2>Import Account</h2>
|
||||
|
||||
<p>
|
||||
To use an existing Garden account on
|
||||
this device, paste its Account Key
|
||||
below.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Importing an Account Key will replace
|
||||
the account currently used by this
|
||||
browser. It will not delete the old
|
||||
account or its server save.
|
||||
</p>
|
||||
|
||||
<form id="ImportAccountForm">
|
||||
<label
|
||||
for="ImportAccountKeyInput"
|
||||
>
|
||||
Account Key
|
||||
</label>
|
||||
|
||||
<textarea
|
||||
id="ImportAccountKeyInput"
|
||||
rows="3"
|
||||
spellcheck="false"
|
||||
autocomplete="off"
|
||||
placeholder="Paste Account Key here"
|
||||
required
|
||||
></textarea>
|
||||
|
||||
<div class="ProfileButtonRow">
|
||||
<button type="submit">
|
||||
Import Account
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="ImportAccountKeyFileButton"
|
||||
type="button"
|
||||
>
|
||||
Import From File
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<input
|
||||
id="ImportAccountKeyFile"
|
||||
type="file"
|
||||
accept=".txt,text/plain"
|
||||
hidden
|
||||
>
|
||||
|
||||
<p
|
||||
id="ImportAccountMessage"
|
||||
class="ProfileMessage"
|
||||
></p>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
|
||||
<script src="/Scripts/Garden/Save.js"></script>
|
||||
<script src="/Scripts/Garden/Users.js"></script>
|
||||
<script src="/Scripts/Garden/Profile.js"></script>
|
||||
<script src="/Scripts/Garden/Account.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,581 @@
|
||||
function StartAccount() {
|
||||
const AccountKeySpoiler =
|
||||
document.querySelector(
|
||||
".AccountKeySpoiler"
|
||||
);
|
||||
|
||||
const CopyButton =
|
||||
document.getElementById(
|
||||
"CopyAccountKeyButton"
|
||||
);
|
||||
|
||||
const ExportButton =
|
||||
document.getElementById(
|
||||
"ExportAccountKeyButton"
|
||||
);
|
||||
|
||||
const ImportForm =
|
||||
document.getElementById(
|
||||
"ImportAccountForm"
|
||||
);
|
||||
|
||||
const ImportFileButton =
|
||||
document.getElementById(
|
||||
"ImportAccountKeyFileButton"
|
||||
);
|
||||
|
||||
const ImportFileInput =
|
||||
document.getElementById(
|
||||
"ImportAccountKeyFile"
|
||||
);
|
||||
|
||||
|
||||
AccountKeySpoiler.addEventListener(
|
||||
"toggle",
|
||||
RenderAccountKeySpoiler
|
||||
);
|
||||
|
||||
CopyButton.addEventListener(
|
||||
"click",
|
||||
CopyAccountKey
|
||||
);
|
||||
|
||||
ExportButton.addEventListener(
|
||||
"click",
|
||||
ExportAccountKey
|
||||
);
|
||||
|
||||
ImportForm.addEventListener(
|
||||
"submit",
|
||||
SubmitAccountImport
|
||||
);
|
||||
|
||||
ImportFileButton.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
ImportFileInput.click();
|
||||
}
|
||||
);
|
||||
|
||||
ImportFileInput.addEventListener(
|
||||
"change",
|
||||
ImportAccountKeyFile
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function RenderAccountKeySpoiler() {
|
||||
const Spoiler =
|
||||
document.querySelector(
|
||||
".AccountKeySpoiler"
|
||||
);
|
||||
|
||||
const Summary =
|
||||
Spoiler.querySelector(
|
||||
"summary"
|
||||
);
|
||||
|
||||
const Display =
|
||||
document.getElementById(
|
||||
"AccountKeyDisplay"
|
||||
);
|
||||
|
||||
|
||||
if (Spoiler.open) {
|
||||
Display.textContent =
|
||||
GetSaveKey();
|
||||
|
||||
Summary.textContent =
|
||||
"Hide Account Key";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Remove the key from the page when
|
||||
* the spoiler is closed again.
|
||||
*/
|
||||
|
||||
Display.textContent =
|
||||
"Hidden";
|
||||
|
||||
Summary.textContent =
|
||||
"Show Account Key";
|
||||
}
|
||||
|
||||
|
||||
async function CopyAccountKey() {
|
||||
const AccountKey =
|
||||
GetSaveKey();
|
||||
|
||||
const Message =
|
||||
document.getElementById(
|
||||
"AccountKeyMessage"
|
||||
);
|
||||
|
||||
|
||||
try {
|
||||
if (
|
||||
navigator.clipboard !==
|
||||
undefined
|
||||
) {
|
||||
await navigator.clipboard.writeText(
|
||||
AccountKey
|
||||
);
|
||||
} else {
|
||||
CopyTextFallback(
|
||||
AccountKey
|
||||
);
|
||||
}
|
||||
|
||||
Message.textContent =
|
||||
"Account Key copied.";
|
||||
} catch (Error) {
|
||||
console.error(
|
||||
"Couldn't copy Account Key:",
|
||||
Error
|
||||
);
|
||||
|
||||
try {
|
||||
CopyTextFallback(
|
||||
AccountKey
|
||||
);
|
||||
|
||||
Message.textContent =
|
||||
"Account Key copied.";
|
||||
} catch (FallbackError) {
|
||||
console.error(
|
||||
"Couldn't copy Account Key:",
|
||||
FallbackError
|
||||
);
|
||||
|
||||
Message.textContent =
|
||||
"Couldn't copy Account Key.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function CopyTextFallback(
|
||||
Text
|
||||
) {
|
||||
const TextArea =
|
||||
document.createElement(
|
||||
"textarea"
|
||||
);
|
||||
|
||||
TextArea.value =
|
||||
Text;
|
||||
|
||||
TextArea.setAttribute(
|
||||
"readonly",
|
||||
""
|
||||
);
|
||||
|
||||
TextArea.style.position =
|
||||
"fixed";
|
||||
|
||||
TextArea.style.opacity =
|
||||
"0";
|
||||
|
||||
|
||||
document.body.appendChild(
|
||||
TextArea
|
||||
);
|
||||
|
||||
TextArea.select();
|
||||
|
||||
|
||||
const Success =
|
||||
document.execCommand(
|
||||
"copy"
|
||||
);
|
||||
|
||||
|
||||
TextArea.remove();
|
||||
|
||||
|
||||
if (!Success) {
|
||||
throw new Error(
|
||||
"Copy command failed."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function ExportAccountKey() {
|
||||
const AccountKey =
|
||||
GetSaveKey();
|
||||
|
||||
const Message =
|
||||
document.getElementById(
|
||||
"AccountKeyMessage"
|
||||
);
|
||||
|
||||
|
||||
try {
|
||||
const File =
|
||||
new Blob(
|
||||
[
|
||||
AccountKey +
|
||||
"\n"
|
||||
],
|
||||
{
|
||||
type:
|
||||
"text/plain;charset=utf-8"
|
||||
}
|
||||
);
|
||||
|
||||
const FileUrl =
|
||||
URL.createObjectURL(
|
||||
File
|
||||
);
|
||||
|
||||
const Link =
|
||||
document.createElement(
|
||||
"a"
|
||||
);
|
||||
|
||||
Link.href =
|
||||
FileUrl;
|
||||
|
||||
Link.download =
|
||||
"SarahtoninGardenAccountKey.txt";
|
||||
|
||||
|
||||
document.body.appendChild(
|
||||
Link
|
||||
);
|
||||
|
||||
Link.click();
|
||||
|
||||
Link.remove();
|
||||
|
||||
|
||||
URL.revokeObjectURL(
|
||||
FileUrl
|
||||
);
|
||||
|
||||
|
||||
Message.textContent =
|
||||
"Account Key exported.";
|
||||
} catch (Error) {
|
||||
console.error(
|
||||
"Couldn't export Account Key:",
|
||||
Error
|
||||
);
|
||||
|
||||
Message.textContent =
|
||||
"Couldn't export Account Key.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function SubmitAccountImport(
|
||||
Event
|
||||
) {
|
||||
Event.preventDefault();
|
||||
|
||||
const Input =
|
||||
document.getElementById(
|
||||
"ImportAccountKeyInput"
|
||||
);
|
||||
|
||||
await ImportAccountKey(
|
||||
Input.value
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
async function ImportAccountKeyFile(
|
||||
Event
|
||||
) {
|
||||
const File =
|
||||
Event.target.files[0];
|
||||
|
||||
if (File === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const Message =
|
||||
document.getElementById(
|
||||
"ImportAccountMessage"
|
||||
);
|
||||
|
||||
|
||||
try {
|
||||
const AccountKey =
|
||||
await File.text();
|
||||
|
||||
const Input =
|
||||
document.getElementById(
|
||||
"ImportAccountKeyInput"
|
||||
);
|
||||
|
||||
Input.value =
|
||||
AccountKey.trim();
|
||||
|
||||
await ImportAccountKey(
|
||||
AccountKey
|
||||
);
|
||||
} catch (Error) {
|
||||
console.error(
|
||||
"Couldn't read Account Key file:",
|
||||
Error
|
||||
);
|
||||
|
||||
Message.textContent =
|
||||
"Couldn't read that Account Key file.";
|
||||
} finally {
|
||||
/*
|
||||
* Allows the same file to be
|
||||
* selected again later.
|
||||
*/
|
||||
|
||||
Event.target.value =
|
||||
"";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function ImportAccountKey(
|
||||
RawAccountKey
|
||||
) {
|
||||
const Message =
|
||||
document.getElementById(
|
||||
"ImportAccountMessage"
|
||||
);
|
||||
|
||||
const AccountKey =
|
||||
NormalizeAccountKey(
|
||||
RawAccountKey
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
!IsValidAccountKey(
|
||||
AccountKey
|
||||
)
|
||||
) {
|
||||
Message.textContent =
|
||||
"That isn't a valid Account Key.";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const CurrentAccountKey =
|
||||
GetSaveKey();
|
||||
|
||||
|
||||
if (
|
||||
AccountKey ===
|
||||
CurrentAccountKey
|
||||
) {
|
||||
Message.textContent =
|
||||
"You're already using that account.";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Message.textContent =
|
||||
"Checking account...";
|
||||
|
||||
|
||||
/*
|
||||
* Check that the key actually belongs
|
||||
* to an existing Garden account before
|
||||
* replacing this browser's key.
|
||||
*
|
||||
* This prevents a typo from silently
|
||||
* creating a completely new account.
|
||||
*/
|
||||
|
||||
let AccountExists;
|
||||
|
||||
try {
|
||||
AccountExists =
|
||||
await CheckAccountKeyExists(
|
||||
AccountKey
|
||||
);
|
||||
} catch (Error) {
|
||||
console.error(
|
||||
"Couldn't verify Account Key:",
|
||||
Error
|
||||
);
|
||||
|
||||
Message.textContent =
|
||||
"Couldn't verify that account right now.";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!AccountExists) {
|
||||
Message.textContent =
|
||||
"No Garden account exists for that key.";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const Confirmed =
|
||||
window.confirm(
|
||||
"Import this Garden account?\n\n" +
|
||||
"This browser will stop using its current " +
|
||||
"account and switch to the imported one.\n\n" +
|
||||
"The current account and its server save " +
|
||||
"will not be deleted."
|
||||
);
|
||||
|
||||
|
||||
if (!Confirmed) {
|
||||
Message.textContent =
|
||||
"Import cancelled.";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Try to synchronize the current
|
||||
* browser save one last time before
|
||||
* switching away from it.
|
||||
*/
|
||||
|
||||
const CurrentSave =
|
||||
ReadLocalSave();
|
||||
|
||||
|
||||
if (CurrentSave !== null) {
|
||||
Message.textContent =
|
||||
"Saving current account...";
|
||||
|
||||
try {
|
||||
await WriteRemoteSave(
|
||||
CurrentSave
|
||||
);
|
||||
} catch (Error) {
|
||||
console.error(
|
||||
"Couldn't synchronize current account:",
|
||||
Error
|
||||
);
|
||||
|
||||
const SwitchAnyway =
|
||||
window.confirm(
|
||||
"The current account couldn't be " +
|
||||
"synchronized with the server.\n\n" +
|
||||
"Switch accounts anyway?"
|
||||
);
|
||||
|
||||
|
||||
if (!SwitchAnyway) {
|
||||
Message.textContent =
|
||||
"Import cancelled.";
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Change identity first, then remove
|
||||
* the cached save.
|
||||
*
|
||||
* On reload, LoadGame() sees no local
|
||||
* save and downloads the imported
|
||||
* account's save from the server.
|
||||
*/
|
||||
|
||||
localStorage.setItem(
|
||||
SaveKeyName,
|
||||
AccountKey
|
||||
);
|
||||
|
||||
localStorage.removeItem(
|
||||
LocalSaveName
|
||||
);
|
||||
|
||||
|
||||
Message.textContent =
|
||||
"Account imported. Reloading...";
|
||||
|
||||
|
||||
location.reload();
|
||||
}
|
||||
|
||||
|
||||
function NormalizeAccountKey(
|
||||
AccountKey
|
||||
) {
|
||||
return AccountKey
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
function IsValidAccountKey(
|
||||
AccountKey
|
||||
) {
|
||||
return /^[a-f0-9]{64}$/.test(
|
||||
AccountKey
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
async function CheckAccountKeyExists(
|
||||
AccountKey
|
||||
) {
|
||||
const Response =
|
||||
await fetch(
|
||||
ApiUrl + "/Load.php",
|
||||
{
|
||||
method: "POST",
|
||||
|
||||
headers: {
|
||||
"Content-Type":
|
||||
"application/json"
|
||||
},
|
||||
|
||||
body: JSON.stringify({
|
||||
SaveKey:
|
||||
AccountKey
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
if (!Response.ok) {
|
||||
throw new Error(
|
||||
"Load API returned HTTP " +
|
||||
Response.status
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const Result =
|
||||
await Response.json();
|
||||
|
||||
|
||||
if (!Result.Success) {
|
||||
throw new Error(
|
||||
Result.Error ??
|
||||
"Account lookup failed."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
Result.Exists === true
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
StartAccount
|
||||
);
|
||||
@@ -0,0 +1,268 @@
|
||||
let ProfileSave = null;
|
||||
|
||||
|
||||
async function StartProfile() {
|
||||
await RenderProfileUsername();
|
||||
|
||||
try {
|
||||
ProfileSave =
|
||||
await LoadGame();
|
||||
|
||||
RenderProfileStatistics();
|
||||
} catch (Error) {
|
||||
console.error(
|
||||
"Couldn't load profile statistics:",
|
||||
Error
|
||||
);
|
||||
|
||||
RenderProfileStatisticsError();
|
||||
}
|
||||
|
||||
|
||||
const UsernameForm =
|
||||
document.getElementById(
|
||||
"ProfileUsernameForm"
|
||||
);
|
||||
|
||||
UsernameForm.addEventListener(
|
||||
"submit",
|
||||
SubmitProfileUsername
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
async function RenderProfileUsername() {
|
||||
const Input =
|
||||
document.getElementById(
|
||||
"ProfileUsernameInput"
|
||||
);
|
||||
|
||||
const Message =
|
||||
document.getElementById(
|
||||
"ProfileUsernameMessage"
|
||||
);
|
||||
|
||||
try {
|
||||
const Profile =
|
||||
await GetProfile();
|
||||
|
||||
if (!Profile.Success) {
|
||||
throw new Error(
|
||||
Profile.Error ??
|
||||
"Couldn't load profile."
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
Profile.Username !== null
|
||||
) {
|
||||
Input.value =
|
||||
Profile.Username;
|
||||
}
|
||||
|
||||
Message.textContent = "";
|
||||
} catch (Error) {
|
||||
console.error(
|
||||
"Couldn't load username:",
|
||||
Error
|
||||
);
|
||||
|
||||
Message.textContent =
|
||||
"Couldn't load username.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function SubmitProfileUsername(
|
||||
Event
|
||||
) {
|
||||
Event.preventDefault();
|
||||
|
||||
const Input =
|
||||
document.getElementById(
|
||||
"ProfileUsernameInput"
|
||||
);
|
||||
|
||||
const Message =
|
||||
document.getElementById(
|
||||
"ProfileUsernameMessage"
|
||||
);
|
||||
|
||||
const Username =
|
||||
Input.value.trim();
|
||||
|
||||
|
||||
if (
|
||||
Username.length < 3 ||
|
||||
Username.length > 24
|
||||
) {
|
||||
Message.textContent =
|
||||
"Username must be between 3 and 24 characters.";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Message.textContent =
|
||||
"Saving...";
|
||||
|
||||
|
||||
try {
|
||||
const Result =
|
||||
await SetUsername(
|
||||
Username
|
||||
);
|
||||
|
||||
if (!Result.Success) {
|
||||
Message.textContent =
|
||||
Result.Error ??
|
||||
"Couldn't change username.";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Input.value =
|
||||
Username;
|
||||
|
||||
Message.textContent =
|
||||
"Username saved.";
|
||||
} catch (Error) {
|
||||
console.error(
|
||||
"Couldn't change username:",
|
||||
Error
|
||||
);
|
||||
|
||||
Message.textContent =
|
||||
"Couldn't change username.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function RenderProfileStatistics() {
|
||||
const CurrentDew =
|
||||
ProfileSave
|
||||
?.Currency
|
||||
?.Dew ??
|
||||
0;
|
||||
|
||||
const LifetimeDew =
|
||||
ProfileSave
|
||||
?.Statistics
|
||||
?.CurrencyEarned
|
||||
?.Dew ??
|
||||
CurrentDew;
|
||||
|
||||
const PlantsDiscovered =
|
||||
ProfileSave
|
||||
?.Discoveries
|
||||
?.Plants
|
||||
?.length ??
|
||||
0;
|
||||
|
||||
const MutationsDiscovered =
|
||||
ProfileSave
|
||||
?.Discoveries
|
||||
?.Mutations
|
||||
?.length ??
|
||||
0;
|
||||
|
||||
const GardenWidth =
|
||||
ProfileSave
|
||||
?.Garden
|
||||
?.Width ??
|
||||
0;
|
||||
|
||||
const GardenHeight =
|
||||
ProfileSave
|
||||
?.Garden
|
||||
?.Height ??
|
||||
0;
|
||||
|
||||
|
||||
SetProfileStatistic(
|
||||
"ProfileCurrentDew",
|
||||
FormatProfileNumber(
|
||||
CurrentDew
|
||||
)
|
||||
);
|
||||
|
||||
SetProfileStatistic(
|
||||
"ProfileLifetimeDew",
|
||||
FormatProfileNumber(
|
||||
LifetimeDew
|
||||
)
|
||||
);
|
||||
|
||||
SetProfileStatistic(
|
||||
"ProfilePlantsDiscovered",
|
||||
FormatProfileNumber(
|
||||
PlantsDiscovered
|
||||
)
|
||||
);
|
||||
|
||||
SetProfileStatistic(
|
||||
"ProfileMutationsDiscovered",
|
||||
FormatProfileNumber(
|
||||
MutationsDiscovered
|
||||
)
|
||||
);
|
||||
|
||||
SetProfileStatistic(
|
||||
"ProfileGardenSize",
|
||||
GardenWidth +
|
||||
"×" +
|
||||
GardenHeight
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function RenderProfileStatisticsError() {
|
||||
const StatisticIds = [
|
||||
"ProfileCurrentDew",
|
||||
"ProfileLifetimeDew",
|
||||
"ProfilePlantsDiscovered",
|
||||
"ProfileMutationsDiscovered",
|
||||
"ProfileGardenSize"
|
||||
];
|
||||
|
||||
for (
|
||||
const StatisticId
|
||||
of StatisticIds
|
||||
) {
|
||||
SetProfileStatistic(
|
||||
StatisticId,
|
||||
"?"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function SetProfileStatistic(
|
||||
ElementId,
|
||||
Value
|
||||
) {
|
||||
const Element =
|
||||
document.getElementById(
|
||||
ElementId
|
||||
);
|
||||
|
||||
if (Element === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Element.textContent =
|
||||
Value;
|
||||
}
|
||||
|
||||
|
||||
function FormatProfileNumber(
|
||||
Number
|
||||
) {
|
||||
return Number.toLocaleString();
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
StartProfile
|
||||
);
|
||||
@@ -145,18 +145,21 @@ async function WriteRemoteSave(SaveData) {
|
||||
}
|
||||
|
||||
|
||||
async function ReadRemoteSave() {
|
||||
async function ReadRemoteSave(
|
||||
SaveKey = GetSaveKey()
|
||||
) {
|
||||
const Response = await fetch(
|
||||
ApiUrl + "/Load.php",
|
||||
{
|
||||
method: "POST",
|
||||
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
"Content-Type":
|
||||
"application/json"
|
||||
},
|
||||
|
||||
body: JSON.stringify({
|
||||
SaveKey: GetSaveKey()
|
||||
SaveKey: SaveKey
|
||||
})
|
||||
}
|
||||
);
|
||||
@@ -168,7 +171,8 @@ async function ReadRemoteSave() {
|
||||
);
|
||||
}
|
||||
|
||||
const Result = await Response.json();
|
||||
const Result =
|
||||
await Response.json();
|
||||
|
||||
if (!Result.Success) {
|
||||
throw new Error(
|
||||
@@ -184,7 +188,6 @@ async function ReadRemoteSave() {
|
||||
return Result.SaveData;
|
||||
}
|
||||
|
||||
|
||||
async function SaveGame(SaveData) {
|
||||
SaveData.Revision++;
|
||||
SaveData.LastSavedAt = Date.now();
|
||||
|
||||
Reference in new issue
Block a user