mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-13 18:38:11 +02:00
1931 lines
38 KiB
JavaScript
1931 lines
38 KiB
JavaScript
let ProfileSave = null;
|
||
let ProfileData = null;
|
||
let ProfilePictureSearchQuery = "";
|
||
let ProfilePictureSortMode = "IdAsc";
|
||
let ProfilePictureSelectedPlantKey = null;
|
||
let ProfilePictureSelectedGrowthStage = null;
|
||
|
||
const DefaultGardenerSettings = {
|
||
AllowGardenPreviews: true,
|
||
AllowComments: true,
|
||
AllowDewDonations: true,
|
||
ShowStats: true
|
||
};
|
||
|
||
|
||
async function StartProfile() {
|
||
await RenderProfilePlayer();
|
||
|
||
const UsernameForm =
|
||
document.getElementById(
|
||
"ProfileUsernameForm"
|
||
);
|
||
|
||
const ColourForm =
|
||
document.getElementById(
|
||
"ProfileColourForm"
|
||
);
|
||
|
||
const ColourInput =
|
||
document.getElementById(
|
||
"ProfileColourInput"
|
||
);
|
||
|
||
const ColourPreview =
|
||
document.getElementById(
|
||
"ProfileColourPreview"
|
||
);
|
||
|
||
const ResetColourButton =
|
||
document.getElementById(
|
||
"ResetProfileColourButton"
|
||
);
|
||
|
||
const DescriptionForm =
|
||
document.getElementById(
|
||
"ProfileDescriptionForm"
|
||
);
|
||
|
||
|
||
UsernameForm?.addEventListener(
|
||
"submit",
|
||
SubmitProfileUsername
|
||
);
|
||
|
||
ColourForm?.addEventListener(
|
||
"submit",
|
||
SubmitProfileColour
|
||
);
|
||
|
||
ColourInput?.addEventListener(
|
||
"input",
|
||
() => {
|
||
if (ColourPreview === null) {
|
||
return;
|
||
}
|
||
|
||
ApplyPlayerColour(
|
||
ColourPreview,
|
||
ColourInput.value
|
||
);
|
||
}
|
||
);
|
||
|
||
ResetColourButton?.addEventListener(
|
||
"click",
|
||
ResetProfileColour
|
||
);
|
||
|
||
DescriptionForm?.addEventListener(
|
||
"submit",
|
||
SubmitProfileDescription
|
||
);
|
||
|
||
BindProfilePrivacySettings();
|
||
|
||
|
||
try {
|
||
await LoadGameContent();
|
||
|
||
ProfileSave =
|
||
await LoadGame();
|
||
|
||
RenderProfileStatistics();
|
||
RenderGardenDisplayPreferences();
|
||
BindGardenDisplayPreferences();
|
||
InitialiseProfilePictureChooser();
|
||
} catch (Error) {
|
||
console.error(
|
||
"Couldn't load profile statistics:",
|
||
Error
|
||
);
|
||
|
||
RenderProfileStatisticsError();
|
||
SetProfilePictureCatalogueMessage(
|
||
"Couldn't load your discovered plants."
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
function BindGardenDisplayPreferences() {
|
||
const PreferenceButtons = [
|
||
[
|
||
"ToggleSelectQuickBoughtPlantButton",
|
||
"SelectQuickBoughtPlant"
|
||
],
|
||
[
|
||
"ToggleSelectTrowelWithInventoryPlantButton",
|
||
"SelectTrowelWithInventoryPlant"
|
||
],
|
||
[
|
||
"TogglePlantNamesButton",
|
||
"ShowPlantNames"
|
||
],
|
||
[
|
||
"ToggleGrowthTimersButton",
|
||
"ShowGrowthTimers"
|
||
],
|
||
[
|
||
"TogglePlotRotationButton",
|
||
"ShowPlotRotation"
|
||
],
|
||
[
|
||
"ToggleNextHarvestButton",
|
||
"ShowNextHarvest"
|
||
],
|
||
[
|
||
"ToggleGardenSizeButton",
|
||
"ShowGardenSize"
|
||
],
|
||
[
|
||
"ToggleEmptyPlotsButton",
|
||
"ShowEmptyPlots"
|
||
],
|
||
[
|
||
"TogglePlantedPlotsButton",
|
||
"ShowPlantedPlots"
|
||
],
|
||
[
|
||
"ToggleGrowingPlotsButton",
|
||
"ShowGrowingPlots"
|
||
],
|
||
[
|
||
"ToggleReadyPlotsButton",
|
||
"ShowReadyPlots"
|
||
]
|
||
];
|
||
|
||
|
||
for (
|
||
const [
|
||
ButtonId,
|
||
PreferenceName
|
||
]
|
||
of PreferenceButtons
|
||
) {
|
||
const Button =
|
||
document.getElementById(
|
||
ButtonId
|
||
);
|
||
|
||
if (Button === null) {
|
||
continue;
|
||
}
|
||
|
||
Button.addEventListener(
|
||
"click",
|
||
() => {
|
||
ToggleGardenDisplayPreference(
|
||
PreferenceName
|
||
);
|
||
}
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
function RenderGardenDisplayPreferences() {
|
||
RenderToolSelectionPreferences();
|
||
RenderPlantInformationPreferences();
|
||
RenderPlotRotationPreference();
|
||
RenderGardenOverviewPreferences();
|
||
}
|
||
|
||
|
||
function RenderToolSelectionPreferences() {
|
||
const QuickBuyButton =
|
||
document.getElementById(
|
||
"ToggleSelectQuickBoughtPlantButton"
|
||
);
|
||
|
||
const TrowelButton =
|
||
document.getElementById(
|
||
"ToggleSelectTrowelWithInventoryPlantButton"
|
||
);
|
||
|
||
if (
|
||
QuickBuyButton === null ||
|
||
TrowelButton === null
|
||
) {
|
||
return;
|
||
}
|
||
|
||
|
||
SetPreferenceButtonState(
|
||
QuickBuyButton,
|
||
"Select bought plant when you quick-buy one",
|
||
ProfileSave.Preferences
|
||
.SelectQuickBoughtPlant !==
|
||
false
|
||
);
|
||
|
||
SetPreferenceButtonState(
|
||
TrowelButton,
|
||
"Select trowel when you select an inventory plant",
|
||
ProfileSave.Preferences
|
||
.SelectTrowelWithInventoryPlant !==
|
||
false
|
||
);
|
||
}
|
||
|
||
|
||
function RenderPlantInformationPreferences() {
|
||
const PlantNamesButton =
|
||
document.getElementById(
|
||
"TogglePlantNamesButton"
|
||
);
|
||
|
||
const GrowthTimersButton =
|
||
document.getElementById(
|
||
"ToggleGrowthTimersButton"
|
||
);
|
||
|
||
const Message =
|
||
document.getElementById(
|
||
"GardenDisplayMessage"
|
||
);
|
||
|
||
if (
|
||
PlantNamesButton === null ||
|
||
GrowthTimersButton === null ||
|
||
Message === null
|
||
) {
|
||
return;
|
||
}
|
||
|
||
|
||
const IsOwned =
|
||
HasPlantInformationUpgrade(
|
||
ProfileSave
|
||
);
|
||
|
||
if (!IsOwned) {
|
||
SetPreferenceButtonState(
|
||
PlantNamesButton,
|
||
"Plant names",
|
||
false,
|
||
true
|
||
);
|
||
|
||
SetPreferenceButtonState(
|
||
GrowthTimersButton,
|
||
"Growth timers",
|
||
false,
|
||
true
|
||
);
|
||
|
||
Message.textContent =
|
||
"Unlock Plant information in the Shop to use these settings.";
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
SetPreferenceButtonState(
|
||
PlantNamesButton,
|
||
"Plant names",
|
||
ProfileSave.Preferences
|
||
.ShowPlantNames !== false
|
||
);
|
||
|
||
SetPreferenceButtonState(
|
||
GrowthTimersButton,
|
||
"Growth timers",
|
||
ProfileSave.Preferences
|
||
.ShowGrowthTimers !== false
|
||
);
|
||
|
||
Message.textContent =
|
||
"Plant names and growth timers can be changed independently.";
|
||
}
|
||
|
||
|
||
function RenderPlotRotationPreference() {
|
||
const Button =
|
||
document.getElementById(
|
||
"TogglePlotRotationButton"
|
||
);
|
||
|
||
const Message =
|
||
document.getElementById(
|
||
"PlotRotationDisplayMessage"
|
||
);
|
||
|
||
if (
|
||
Button === null ||
|
||
Message === null
|
||
) {
|
||
return;
|
||
}
|
||
|
||
const IsOwned =
|
||
HasRotationUpgrade(
|
||
ProfileSave
|
||
);
|
||
|
||
SetPreferenceButtonState(
|
||
Button,
|
||
"Plot rotation",
|
||
ProfileSave.Preferences
|
||
.ShowPlotRotation !== false,
|
||
!IsOwned
|
||
);
|
||
|
||
Message.textContent =
|
||
IsOwned
|
||
? "Plot directions can be hidden without changing their saved rotation."
|
||
: "Unlock Plot rotation in the Shop to use this setting.";
|
||
}
|
||
|
||
|
||
function RenderGardenOverviewPreferences() {
|
||
const Settings = [
|
||
[
|
||
"ToggleNextHarvestButton",
|
||
"Next harvest",
|
||
"ShowNextHarvest"
|
||
],
|
||
[
|
||
"ToggleGardenSizeButton",
|
||
"Garden size",
|
||
"ShowGardenSize"
|
||
],
|
||
[
|
||
"ToggleEmptyPlotsButton",
|
||
"Empty plots",
|
||
"ShowEmptyPlots"
|
||
],
|
||
[
|
||
"TogglePlantedPlotsButton",
|
||
"Planted plots",
|
||
"ShowPlantedPlots"
|
||
],
|
||
[
|
||
"ToggleGrowingPlotsButton",
|
||
"Growing",
|
||
"ShowGrowingPlots"
|
||
],
|
||
[
|
||
"ToggleReadyPlotsButton",
|
||
"Ready to harvest",
|
||
"ShowReadyPlots"
|
||
]
|
||
];
|
||
|
||
const Message =
|
||
document.getElementById(
|
||
"GardenOverviewDisplayMessage"
|
||
);
|
||
|
||
if (Message === null) {
|
||
return;
|
||
}
|
||
|
||
|
||
const IsOwned =
|
||
HasGardenOverviewUpgrade(
|
||
ProfileSave
|
||
);
|
||
|
||
for (
|
||
const [
|
||
ButtonId,
|
||
Label,
|
||
PreferenceName
|
||
]
|
||
of Settings
|
||
) {
|
||
const Button =
|
||
document.getElementById(
|
||
ButtonId
|
||
);
|
||
|
||
if (Button === null) {
|
||
continue;
|
||
}
|
||
|
||
SetPreferenceButtonState(
|
||
Button,
|
||
Label,
|
||
ProfileSave.Preferences[
|
||
PreferenceName
|
||
] !== false,
|
||
!IsOwned
|
||
);
|
||
}
|
||
|
||
|
||
Message.textContent =
|
||
IsOwned
|
||
? "Every Garden overview line can be changed independently."
|
||
: "Unlock Garden overview in the Shop to use these settings.";
|
||
}
|
||
|
||
|
||
function SetPreferenceButtonState(
|
||
Button,
|
||
Label,
|
||
IsEnabled,
|
||
IsLocked = false
|
||
) {
|
||
Button.disabled = IsLocked;
|
||
|
||
Button.textContent =
|
||
Label +
|
||
": " +
|
||
(IsLocked
|
||
? "Locked"
|
||
: IsEnabled
|
||
? "On"
|
||
: "Off");
|
||
}
|
||
|
||
|
||
async function ToggleGardenDisplayPreference(
|
||
PreferenceName
|
||
) {
|
||
const ToolSelectionPreferences = [
|
||
"SelectQuickBoughtPlant",
|
||
"SelectTrowelWithInventoryPlant"
|
||
];
|
||
|
||
const PlantInformationPreferences = [
|
||
"ShowPlantNames",
|
||
"ShowGrowthTimers"
|
||
];
|
||
|
||
const RotationPreferences = [
|
||
"ShowPlotRotation"
|
||
];
|
||
|
||
const GardenOverviewPreferences = [
|
||
"ShowNextHarvest",
|
||
"ShowGardenSize",
|
||
"ShowEmptyPlots",
|
||
"ShowPlantedPlots",
|
||
"ShowGrowingPlots",
|
||
"ShowReadyPlots"
|
||
];
|
||
|
||
|
||
const IsToolSelectionPreference =
|
||
ToolSelectionPreferences.includes(
|
||
PreferenceName
|
||
);
|
||
|
||
const IsPlantInformationPreference =
|
||
PlantInformationPreferences.includes(
|
||
PreferenceName
|
||
);
|
||
|
||
const IsRotationPreference =
|
||
RotationPreferences.includes(
|
||
PreferenceName
|
||
);
|
||
|
||
const IsGardenOverviewPreference =
|
||
GardenOverviewPreferences.includes(
|
||
PreferenceName
|
||
);
|
||
|
||
|
||
if (
|
||
!IsToolSelectionPreference &&
|
||
!IsPlantInformationPreference &&
|
||
!IsRotationPreference &&
|
||
!IsGardenOverviewPreference
|
||
) {
|
||
return;
|
||
}
|
||
|
||
if (
|
||
IsPlantInformationPreference &&
|
||
!HasPlantInformationUpgrade(
|
||
ProfileSave
|
||
)
|
||
) {
|
||
return;
|
||
}
|
||
|
||
if (
|
||
IsRotationPreference &&
|
||
!HasRotationUpgrade(
|
||
ProfileSave
|
||
)
|
||
) {
|
||
return;
|
||
}
|
||
|
||
if (
|
||
IsGardenOverviewPreference &&
|
||
!HasGardenOverviewUpgrade(
|
||
ProfileSave
|
||
)
|
||
) {
|
||
return;
|
||
}
|
||
|
||
|
||
ProfileSave.Preferences[
|
||
PreferenceName
|
||
] = !ProfileSave.Preferences[
|
||
PreferenceName
|
||
];
|
||
|
||
RenderGardenDisplayPreferences();
|
||
|
||
await SaveGame(
|
||
ProfileSave
|
||
);
|
||
}
|
||
|
||
|
||
async function RenderProfilePlayer() {
|
||
const UsernameInput =
|
||
document.getElementById(
|
||
"ProfileUsernameInput"
|
||
);
|
||
|
||
const ColourInput =
|
||
document.getElementById(
|
||
"ProfileColourInput"
|
||
);
|
||
|
||
const ColourPreview =
|
||
document.getElementById(
|
||
"ProfileColourPreview"
|
||
);
|
||
|
||
const UsernameMessage =
|
||
document.getElementById(
|
||
"ProfileUsernameMessage"
|
||
);
|
||
|
||
try {
|
||
const Profile =
|
||
await GetProfile();
|
||
|
||
if (!Profile.Success) {
|
||
throw new Error(
|
||
Profile.Error ??
|
||
"Couldn't load profile."
|
||
);
|
||
}
|
||
|
||
|
||
ProfileData = Profile;
|
||
RenderProfileDescription();
|
||
RenderProfilePrivacySettings();
|
||
|
||
|
||
if (
|
||
typeof Profile.Username ===
|
||
"string" &&
|
||
Profile.Username.length > 0
|
||
) {
|
||
UsernameInput.value =
|
||
Profile.Username;
|
||
|
||
ColourPreview.textContent =
|
||
Profile.Username;
|
||
} else {
|
||
ColourPreview.textContent =
|
||
"Preview";
|
||
}
|
||
|
||
|
||
const ProfileColour =
|
||
IsValidPlayerColour(
|
||
Profile.Colour
|
||
)
|
||
? Profile.Colour
|
||
: DefaultColourPickerValue;
|
||
|
||
ColourInput.value =
|
||
ProfileColour;
|
||
|
||
ApplyPlayerColour(
|
||
ColourPreview,
|
||
IsValidPlayerColour(
|
||
Profile.Colour
|
||
)
|
||
? Profile.Colour
|
||
: null
|
||
);
|
||
|
||
|
||
UsernameMessage.textContent =
|
||
"";
|
||
} catch (Error) {
|
||
console.error(
|
||
"Couldn't load profile:",
|
||
Error
|
||
);
|
||
|
||
UsernameMessage.textContent =
|
||
"Couldn't load profile.";
|
||
}
|
||
}
|
||
|
||
|
||
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.";
|
||
}
|
||
}
|
||
|
||
async function SubmitProfileColour(
|
||
Event
|
||
) {
|
||
Event.preventDefault();
|
||
|
||
const Input =
|
||
document.getElementById(
|
||
"ProfileColourInput"
|
||
);
|
||
|
||
const Preview =
|
||
document.getElementById(
|
||
"ProfileColourPreview"
|
||
);
|
||
|
||
const Message =
|
||
document.getElementById(
|
||
"ProfileColourMessage"
|
||
);
|
||
|
||
|
||
Message.textContent =
|
||
"Saving...";
|
||
|
||
|
||
try {
|
||
const Result =
|
||
await SetColour(
|
||
Input.value
|
||
);
|
||
|
||
if (!Result.Success) {
|
||
Message.textContent =
|
||
Result.Error ??
|
||
"Couldn't save colour.";
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
ApplyPlayerColour(
|
||
Preview,
|
||
IsValidPlayerColour(
|
||
Result.Colour
|
||
)
|
||
? Result.Colour
|
||
: Input.value
|
||
);
|
||
|
||
Message.textContent =
|
||
"Colour saved.";
|
||
} catch (Error) {
|
||
console.error(
|
||
"Couldn't save colour:",
|
||
Error
|
||
);
|
||
|
||
Message.textContent =
|
||
"Couldn't save colour.";
|
||
}
|
||
}
|
||
|
||
|
||
async function ResetProfileColour() {
|
||
const Input =
|
||
document.getElementById(
|
||
"ProfileColourInput"
|
||
);
|
||
|
||
const Preview =
|
||
document.getElementById(
|
||
"ProfileColourPreview"
|
||
);
|
||
|
||
const Message =
|
||
document.getElementById(
|
||
"ProfileColourMessage"
|
||
);
|
||
|
||
|
||
try {
|
||
const Result =
|
||
await SetColour(
|
||
null
|
||
);
|
||
|
||
if (!Result.Success) {
|
||
Message.textContent =
|
||
Result.Error ??
|
||
"Couldn't reset colour.";
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
Input.value =
|
||
DefaultColourPickerValue;
|
||
|
||
ApplyPlayerColour(
|
||
Preview,
|
||
null
|
||
);
|
||
|
||
Message.textContent =
|
||
"Colour reset.";
|
||
} catch (Error) {
|
||
console.error(
|
||
"Couldn't reset colour:",
|
||
Error
|
||
);
|
||
|
||
Message.textContent =
|
||
"Couldn't reset colour.";
|
||
}
|
||
}
|
||
|
||
function RenderProfileDescription() {
|
||
const Input =
|
||
document.getElementById(
|
||
"ProfileDescriptionInput"
|
||
);
|
||
|
||
if (Input === null) {
|
||
return;
|
||
}
|
||
|
||
Input.value =
|
||
typeof ProfileData?.Description ===
|
||
"string"
|
||
? ProfileData.Description
|
||
: "";
|
||
}
|
||
|
||
|
||
async function SubmitProfileDescription(
|
||
Event
|
||
) {
|
||
Event.preventDefault();
|
||
|
||
const Input =
|
||
document.getElementById(
|
||
"ProfileDescriptionInput"
|
||
);
|
||
|
||
const Message =
|
||
document.getElementById(
|
||
"ProfileDescriptionMessage"
|
||
);
|
||
|
||
if (
|
||
Input === null ||
|
||
Message === null
|
||
) {
|
||
return;
|
||
}
|
||
|
||
const Description =
|
||
Input.value.trim();
|
||
|
||
if (
|
||
Array.from(
|
||
Description
|
||
).length > 500
|
||
) {
|
||
Message.textContent =
|
||
"Description must be 500 characters or fewer.";
|
||
|
||
return;
|
||
}
|
||
|
||
Message.textContent =
|
||
"Saving...";
|
||
|
||
try {
|
||
const Result =
|
||
await SetUserConfig({
|
||
Description: Description
|
||
});
|
||
|
||
if (!Result.Success) {
|
||
Message.textContent =
|
||
Result.Error ??
|
||
"Couldn't save description.";
|
||
|
||
return;
|
||
}
|
||
|
||
ProfileData ??= {};
|
||
ProfileData.Description =
|
||
Result.Description ??
|
||
Description;
|
||
|
||
Input.value =
|
||
ProfileData.Description;
|
||
|
||
Message.textContent =
|
||
"Description saved.";
|
||
} catch (Error) {
|
||
console.error(
|
||
"Couldn't save profile description:",
|
||
Error
|
||
);
|
||
|
||
Message.textContent =
|
||
"Couldn't save description.";
|
||
}
|
||
}
|
||
|
||
|
||
function GetGardenerSettings() {
|
||
const StoredSettings =
|
||
ProfileData?.Settings !== null &&
|
||
typeof ProfileData?.Settings ===
|
||
"object" &&
|
||
!Array.isArray(
|
||
ProfileData.Settings
|
||
)
|
||
? ProfileData.Settings
|
||
: {};
|
||
|
||
return {
|
||
...DefaultGardenerSettings,
|
||
...StoredSettings
|
||
};
|
||
}
|
||
|
||
|
||
function BindProfilePrivacySettings() {
|
||
const Settings = [
|
||
[
|
||
"ToggleGardenPreviewsButton",
|
||
"AllowGardenPreviews"
|
||
],
|
||
[
|
||
"ToggleCommentsButton",
|
||
"AllowComments"
|
||
],
|
||
[
|
||
"ToggleDewDonationsButton",
|
||
"AllowDewDonations"
|
||
],
|
||
[
|
||
"ToggleProfileStatsButton",
|
||
"ShowStats"
|
||
]
|
||
];
|
||
|
||
for (
|
||
const [
|
||
ButtonId,
|
||
SettingName
|
||
]
|
||
of Settings
|
||
) {
|
||
const Button =
|
||
document.getElementById(
|
||
ButtonId
|
||
);
|
||
|
||
Button?.addEventListener(
|
||
"click",
|
||
() => {
|
||
ToggleProfilePrivacySetting(
|
||
SettingName,
|
||
Button
|
||
);
|
||
}
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
function RenderProfilePrivacySettings() {
|
||
const Settings =
|
||
GetGardenerSettings();
|
||
|
||
const Buttons = [
|
||
[
|
||
"ToggleGardenPreviewsButton",
|
||
"Garden previews",
|
||
"AllowGardenPreviews"
|
||
],
|
||
[
|
||
"ToggleCommentsButton",
|
||
"Comments",
|
||
"AllowComments"
|
||
],
|
||
[
|
||
"ToggleDewDonationsButton",
|
||
"Dew donations",
|
||
"AllowDewDonations"
|
||
],
|
||
[
|
||
"ToggleProfileStatsButton",
|
||
"Statistics",
|
||
"ShowStats"
|
||
]
|
||
];
|
||
|
||
for (
|
||
const [
|
||
ButtonId,
|
||
Label,
|
||
SettingName
|
||
]
|
||
of Buttons
|
||
) {
|
||
const Button =
|
||
document.getElementById(
|
||
ButtonId
|
||
);
|
||
|
||
if (Button === null) {
|
||
continue;
|
||
}
|
||
|
||
SetPreferenceButtonState(
|
||
Button,
|
||
Label,
|
||
Settings[SettingName] !==
|
||
false
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
async function ToggleProfilePrivacySetting(
|
||
SettingName,
|
||
Button
|
||
) {
|
||
const Message =
|
||
document.getElementById(
|
||
"ProfilePrivacyMessage"
|
||
);
|
||
|
||
const Settings =
|
||
GetGardenerSettings();
|
||
|
||
const NewValue =
|
||
Settings[SettingName] ===
|
||
false;
|
||
|
||
Button.disabled = true;
|
||
|
||
if (Message !== null) {
|
||
Message.textContent =
|
||
"Saving...";
|
||
}
|
||
|
||
try {
|
||
const Result =
|
||
await SetUserConfig({
|
||
Settings: {
|
||
[SettingName]:
|
||
NewValue
|
||
}
|
||
});
|
||
|
||
if (!Result.Success) {
|
||
if (Message !== null) {
|
||
Message.textContent =
|
||
Result.Error ??
|
||
"Couldn't save profile privacy settings.";
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
ProfileData ??= {};
|
||
ProfileData.Settings =
|
||
Result.Settings ?? {
|
||
...Settings,
|
||
[SettingName]: NewValue
|
||
};
|
||
|
||
RenderProfilePrivacySettings();
|
||
|
||
if (Message !== null) {
|
||
Message.textContent =
|
||
"Privacy setting saved.";
|
||
}
|
||
} catch (Error) {
|
||
console.error(
|
||
"Couldn't save profile privacy setting:",
|
||
Error
|
||
);
|
||
|
||
if (Message !== null) {
|
||
Message.textContent =
|
||
"Couldn't save profile privacy settings.";
|
||
}
|
||
} finally {
|
||
Button.disabled = false;
|
||
}
|
||
}
|
||
|
||
|
||
function InitialiseProfilePictureChooser() {
|
||
const SearchInput =
|
||
document.getElementById(
|
||
"ProfilePictureSearchInput"
|
||
);
|
||
|
||
const SortSelect =
|
||
document.getElementById(
|
||
"ProfilePictureSortSelect"
|
||
);
|
||
|
||
const GrowthStageSelect =
|
||
document.getElementById(
|
||
"ProfilePictureGrowthStageSelect"
|
||
);
|
||
|
||
const SaveButton =
|
||
document.getElementById(
|
||
"SaveProfilePictureButton"
|
||
);
|
||
|
||
|
||
if (SearchInput !== null) {
|
||
SearchInput.value =
|
||
ProfilePictureSearchQuery;
|
||
|
||
SearchInput.addEventListener(
|
||
"input",
|
||
() => {
|
||
ProfilePictureSearchQuery =
|
||
SearchInput.value;
|
||
|
||
RenderProfilePictureGrid();
|
||
}
|
||
);
|
||
}
|
||
|
||
if (SortSelect !== null) {
|
||
SortSelect.value =
|
||
ProfilePictureSortMode;
|
||
|
||
SortSelect.addEventListener(
|
||
"change",
|
||
() => {
|
||
ProfilePictureSortMode =
|
||
SortSelect.value;
|
||
|
||
RenderProfilePictureGrid();
|
||
}
|
||
);
|
||
}
|
||
|
||
GrowthStageSelect?.addEventListener(
|
||
"change",
|
||
() => {
|
||
const GrowthStage =
|
||
Number(
|
||
GrowthStageSelect.value
|
||
);
|
||
|
||
if (
|
||
Number.isInteger(
|
||
GrowthStage
|
||
) &&
|
||
GrowthStage > 0
|
||
) {
|
||
ProfilePictureSelectedGrowthStage =
|
||
GrowthStage;
|
||
|
||
RenderProfilePictureGrid();
|
||
}
|
||
}
|
||
);
|
||
|
||
SaveButton?.addEventListener(
|
||
"click",
|
||
SaveProfilePicture
|
||
);
|
||
|
||
|
||
const SavedProfilePicture =
|
||
ProfileData?.ProfilePicture;
|
||
|
||
if (
|
||
SavedProfilePicture !== null &&
|
||
typeof SavedProfilePicture ===
|
||
"object" &&
|
||
!Array.isArray(
|
||
SavedProfilePicture
|
||
) &&
|
||
typeof SavedProfilePicture.PlantKey ===
|
||
"string" &&
|
||
IsProfilePicturePlantAvailable(
|
||
SavedProfilePicture.PlantKey
|
||
)
|
||
) {
|
||
ProfilePictureSelectedPlantKey =
|
||
SavedProfilePicture.PlantKey;
|
||
|
||
ProfilePictureSelectedGrowthStage =
|
||
Number(
|
||
SavedProfilePicture.GrowthStage
|
||
);
|
||
} else {
|
||
const DefaultPlantKey =
|
||
DefaultGardenerProfilePicture
|
||
.PlantKey;
|
||
|
||
const FirstEntry =
|
||
GetProfilePicturePlantEntries()
|
||
.sort(
|
||
(A, B) =>
|
||
A.Plant.Id -
|
||
B.Plant.Id
|
||
)[0] ?? null;
|
||
|
||
ProfilePictureSelectedPlantKey =
|
||
IsProfilePicturePlantAvailable(
|
||
DefaultPlantKey
|
||
)
|
||
? DefaultPlantKey
|
||
: FirstEntry?.PlantKey ??
|
||
null;
|
||
|
||
ProfilePictureSelectedGrowthStage =
|
||
ProfilePictureSelectedPlantKey ===
|
||
DefaultPlantKey
|
||
? DefaultGardenerProfilePicture
|
||
.GrowthStage
|
||
: null;
|
||
}
|
||
|
||
RenderProfilePictureChooser();
|
||
}
|
||
|
||
|
||
function GetProfilePicturePlantEntries() {
|
||
if (ProfileSave === null) {
|
||
return [];
|
||
}
|
||
|
||
return Object.entries(
|
||
Plants
|
||
)
|
||
.filter(
|
||
([, Plant]) =>
|
||
HasDiscoveredPlant(
|
||
ProfileSave,
|
||
Plant.Id
|
||
)
|
||
)
|
||
.map(
|
||
([PlantKey, Plant]) => ({
|
||
PlantKey: PlantKey,
|
||
Plant: Plant
|
||
})
|
||
);
|
||
}
|
||
|
||
|
||
function IsProfilePicturePlantAvailable(
|
||
PlantKey
|
||
) {
|
||
const Plant =
|
||
Plants[PlantKey];
|
||
|
||
return (
|
||
Plant !== undefined &&
|
||
ProfileSave !== null &&
|
||
HasDiscoveredPlant(
|
||
ProfileSave,
|
||
Plant.Id
|
||
)
|
||
);
|
||
}
|
||
|
||
|
||
function GetProfilePictureAvailableStages(
|
||
Plant
|
||
) {
|
||
return GetPlantImageSources(
|
||
Plant
|
||
)
|
||
.map(
|
||
(Source, Index) => ({
|
||
Stage: Index + 1,
|
||
Source: Source
|
||
})
|
||
)
|
||
.filter(
|
||
Item =>
|
||
typeof Item.Source ===
|
||
"string" &&
|
||
Item.Source.length > 0
|
||
);
|
||
}
|
||
|
||
|
||
function GetProfilePictureMatureStage(
|
||
Plant
|
||
) {
|
||
const Stages =
|
||
GetProfilePictureAvailableStages(
|
||
Plant
|
||
);
|
||
|
||
return Stages.length === 0
|
||
? null
|
||
: Stages[
|
||
Stages.length - 1
|
||
].Stage;
|
||
}
|
||
|
||
|
||
function GetProfilePictureStageSource(
|
||
Plant,
|
||
GrowthStage
|
||
) {
|
||
const Stages =
|
||
GetProfilePictureAvailableStages(
|
||
Plant
|
||
);
|
||
|
||
const RequestedStage =
|
||
Stages.find(
|
||
Item =>
|
||
Item.Stage ===
|
||
GrowthStage
|
||
);
|
||
|
||
if (RequestedStage !== undefined) {
|
||
return RequestedStage.Source;
|
||
}
|
||
|
||
return Stages.length === 0
|
||
? null
|
||
: Stages[
|
||
Stages.length - 1
|
||
].Source;
|
||
}
|
||
|
||
|
||
function RenderProfilePictureChooser() {
|
||
RenderProfilePictureSelection();
|
||
RenderProfilePictureGrid();
|
||
}
|
||
|
||
|
||
function RenderProfilePictureSelection() {
|
||
const Name =
|
||
document.getElementById(
|
||
"ProfilePictureSelectedName"
|
||
);
|
||
|
||
const GrowthStageSelect =
|
||
document.getElementById(
|
||
"ProfilePictureGrowthStageSelect"
|
||
);
|
||
|
||
const SaveButton =
|
||
document.getElementById(
|
||
"SaveProfilePictureButton"
|
||
);
|
||
|
||
if (
|
||
Name === null ||
|
||
GrowthStageSelect === null ||
|
||
SaveButton === null
|
||
) {
|
||
return;
|
||
}
|
||
|
||
const Plant =
|
||
ProfilePictureSelectedPlantKey ===
|
||
null
|
||
? null
|
||
: Plants[
|
||
ProfilePictureSelectedPlantKey
|
||
] ?? null;
|
||
|
||
GrowthStageSelect.replaceChildren();
|
||
|
||
if (Plant === null) {
|
||
Name.textContent =
|
||
"None";
|
||
|
||
const Option =
|
||
document.createElement(
|
||
"option"
|
||
);
|
||
|
||
Option.value = "";
|
||
Option.textContent = "None";
|
||
|
||
GrowthStageSelect.appendChild(
|
||
Option
|
||
);
|
||
|
||
GrowthStageSelect.disabled = true;
|
||
SaveButton.disabled = true;
|
||
|
||
return;
|
||
}
|
||
|
||
Name.textContent =
|
||
Plant.Name;
|
||
|
||
const Stages =
|
||
GetProfilePictureAvailableStages(
|
||
Plant
|
||
);
|
||
|
||
if (Stages.length === 0) {
|
||
const Option =
|
||
document.createElement(
|
||
"option"
|
||
);
|
||
|
||
Option.value = "";
|
||
Option.textContent =
|
||
"No image";
|
||
|
||
GrowthStageSelect.appendChild(
|
||
Option
|
||
);
|
||
|
||
GrowthStageSelect.disabled = true;
|
||
SaveButton.disabled = true;
|
||
|
||
return;
|
||
}
|
||
|
||
const ValidSelectedStage =
|
||
Stages.some(
|
||
Item =>
|
||
Item.Stage ===
|
||
ProfilePictureSelectedGrowthStage
|
||
);
|
||
|
||
if (!ValidSelectedStage) {
|
||
ProfilePictureSelectedGrowthStage =
|
||
Stages[
|
||
Stages.length - 1
|
||
].Stage;
|
||
}
|
||
|
||
for (
|
||
let Index = 0;
|
||
Index < Stages.length;
|
||
Index++
|
||
) {
|
||
const Stage =
|
||
Stages[Index];
|
||
|
||
const Option =
|
||
document.createElement(
|
||
"option"
|
||
);
|
||
|
||
Option.value =
|
||
String(
|
||
Stage.Stage
|
||
);
|
||
|
||
Option.textContent =
|
||
Index === Stages.length - 1
|
||
? "Mature"
|
||
: "Stage " +
|
||
Stage.Stage;
|
||
|
||
GrowthStageSelect.appendChild(
|
||
Option
|
||
);
|
||
}
|
||
|
||
GrowthStageSelect.value =
|
||
String(
|
||
ProfilePictureSelectedGrowthStage
|
||
);
|
||
|
||
GrowthStageSelect.disabled = false;
|
||
SaveButton.disabled = false;
|
||
}
|
||
|
||
|
||
function RenderProfilePictureGrid() {
|
||
const Grid =
|
||
document.getElementById(
|
||
"ProfilePictureGrid"
|
||
);
|
||
|
||
if (Grid === null) {
|
||
return;
|
||
}
|
||
|
||
Grid.replaceChildren();
|
||
|
||
const Entries =
|
||
GetProfilePicturePlantEntries();
|
||
|
||
const SearchQuery =
|
||
ProfilePictureSearchQuery
|
||
.trim()
|
||
.toLocaleLowerCase();
|
||
|
||
const VisibleEntries =
|
||
Entries
|
||
.filter(
|
||
Entry =>
|
||
DoesPlantCatalogueMatchSearch(
|
||
Entry.Plant,
|
||
SearchQuery,
|
||
ProfileSave
|
||
)
|
||
)
|
||
.sort(
|
||
(A, B) =>
|
||
ComparePlantCataloguePlants(
|
||
A.Plant,
|
||
B.Plant,
|
||
ProfilePictureSortMode,
|
||
ProfileSave
|
||
)
|
||
);
|
||
|
||
if (Entries.length === 0) {
|
||
SetProfilePictureCatalogueMessage(
|
||
"No plants discovered yet."
|
||
);
|
||
|
||
return;
|
||
}
|
||
|
||
if (VisibleEntries.length === 0) {
|
||
SetProfilePictureCatalogueMessage(
|
||
"No discovered plants match your search."
|
||
);
|
||
|
||
return;
|
||
}
|
||
|
||
for (
|
||
const Entry
|
||
of VisibleEntries
|
||
) {
|
||
Grid.appendChild(
|
||
CreateProfilePictureOption(
|
||
Entry
|
||
)
|
||
);
|
||
}
|
||
|
||
if (SearchQuery.length > 0) {
|
||
SetProfilePictureCatalogueMessage(
|
||
"Showing " +
|
||
VisibleEntries.length
|
||
.toLocaleString() +
|
||
" of " +
|
||
Entries.length
|
||
.toLocaleString() +
|
||
" discovered plants."
|
||
);
|
||
|
||
return;
|
||
}
|
||
|
||
SetProfilePictureCatalogueMessage(
|
||
Entries.length === 1
|
||
? "1 plant available."
|
||
: Entries.length
|
||
.toLocaleString() +
|
||
" plants available."
|
||
);
|
||
}
|
||
|
||
|
||
function CreateProfilePictureOption(
|
||
Entry
|
||
) {
|
||
const Button =
|
||
document.createElement(
|
||
"button"
|
||
);
|
||
|
||
Button.type = "button";
|
||
Button.className =
|
||
"ProfilePictureOption";
|
||
|
||
Button.setAttribute(
|
||
"aria-pressed",
|
||
Entry.PlantKey ===
|
||
ProfilePictureSelectedPlantKey
|
||
? "true"
|
||
: "false"
|
||
);
|
||
|
||
|
||
const Name =
|
||
document.createElement(
|
||
"span"
|
||
);
|
||
|
||
Name.className =
|
||
"ProfilePictureOptionName";
|
||
|
||
Name.textContent =
|
||
Entry.Plant.Name;
|
||
|
||
|
||
const Visual =
|
||
document.createElement(
|
||
"span"
|
||
);
|
||
|
||
Visual.className =
|
||
"PlantTile ProfilePictureOptionVisual";
|
||
|
||
|
||
const ImageSource =
|
||
GetProfilePictureStageSource(
|
||
Entry.Plant,
|
||
ProfilePictureSelectedGrowthStage
|
||
);
|
||
|
||
if (ImageSource === null) {
|
||
const Missing =
|
||
document.createElement(
|
||
"span"
|
||
);
|
||
|
||
Missing.className =
|
||
"ProfilePictureOptionMissing";
|
||
|
||
Missing.textContent =
|
||
"No image";
|
||
|
||
Visual.appendChild(
|
||
Missing
|
||
);
|
||
} else {
|
||
const Image =
|
||
document.createElement(
|
||
"img"
|
||
);
|
||
|
||
Image.className =
|
||
"PlantSprite";
|
||
|
||
Image.src =
|
||
ImageSource;
|
||
|
||
Image.alt =
|
||
Entry.Plant.Name;
|
||
|
||
Visual.appendChild(
|
||
Image
|
||
);
|
||
}
|
||
|
||
|
||
Button.append(
|
||
Name,
|
||
Visual
|
||
);
|
||
|
||
Button.addEventListener(
|
||
"click",
|
||
() => {
|
||
ProfilePictureSelectedPlantKey =
|
||
Entry.PlantKey;
|
||
|
||
const AvailableStages =
|
||
GetProfilePictureAvailableStages(
|
||
Entry.Plant
|
||
);
|
||
|
||
if (
|
||
!AvailableStages.some(
|
||
Item =>
|
||
Item.Stage ===
|
||
ProfilePictureSelectedGrowthStage
|
||
)
|
||
) {
|
||
ProfilePictureSelectedGrowthStage =
|
||
GetProfilePictureMatureStage(
|
||
Entry.Plant
|
||
);
|
||
}
|
||
|
||
RenderProfilePictureChooser();
|
||
}
|
||
);
|
||
|
||
|
||
return Button;
|
||
}
|
||
|
||
|
||
function SetProfilePictureCatalogueMessage(
|
||
Text
|
||
) {
|
||
const Message =
|
||
document.getElementById(
|
||
"ProfilePictureCatalogueMessage"
|
||
);
|
||
|
||
if (Message !== null) {
|
||
Message.textContent =
|
||
Text;
|
||
}
|
||
}
|
||
|
||
|
||
async function SaveProfilePicture() {
|
||
const Message =
|
||
document.getElementById(
|
||
"ProfilePictureMessage"
|
||
);
|
||
|
||
const SaveButton =
|
||
document.getElementById(
|
||
"SaveProfilePictureButton"
|
||
);
|
||
|
||
if (
|
||
ProfilePictureSelectedPlantKey ===
|
||
null ||
|
||
!Number.isInteger(
|
||
ProfilePictureSelectedGrowthStage
|
||
) ||
|
||
ProfilePictureSelectedGrowthStage < 1
|
||
) {
|
||
if (Message !== null) {
|
||
Message.textContent =
|
||
"Choose a plant with an image first.";
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
if (SaveButton !== null) {
|
||
SaveButton.disabled = true;
|
||
}
|
||
|
||
if (Message !== null) {
|
||
Message.textContent =
|
||
"Saving...";
|
||
}
|
||
|
||
try {
|
||
const Result =
|
||
await SetUserConfig({
|
||
ProfilePicture: {
|
||
PlantKey:
|
||
ProfilePictureSelectedPlantKey,
|
||
GrowthStage:
|
||
ProfilePictureSelectedGrowthStage
|
||
}
|
||
});
|
||
|
||
if (!Result.Success) {
|
||
if (Message !== null) {
|
||
Message.textContent =
|
||
Result.Error ??
|
||
"Couldn't save profile picture.";
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
ProfileData ??= {};
|
||
ProfileData.ProfilePicture =
|
||
Result.ProfilePicture;
|
||
|
||
if (Message !== null) {
|
||
Message.textContent =
|
||
"Profile picture saved.";
|
||
}
|
||
} catch (Error) {
|
||
console.error(
|
||
"Couldn't save profile picture:",
|
||
Error
|
||
);
|
||
|
||
if (Message !== null) {
|
||
Message.textContent =
|
||
"Couldn't save profile picture.";
|
||
}
|
||
} finally {
|
||
if (SaveButton !== null) {
|
||
SaveButton.disabled = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
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 GardensOwned =
|
||
ProfileSave
|
||
?.Gardens
|
||
?.length ??
|
||
1;
|
||
|
||
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(
|
||
"ProfileGardensOwned",
|
||
FormatProfileNumber(
|
||
GardensOwned
|
||
)
|
||
);
|
||
|
||
SetProfileStatistic(
|
||
"ProfileGardenSize",
|
||
GardenWidth +
|
||
"×" +
|
||
GardenHeight
|
||
);
|
||
}
|
||
|
||
|
||
function RenderProfileStatisticsError() {
|
||
const StatisticIds = [
|
||
"ProfileCurrentDew",
|
||
"ProfileLifetimeDew",
|
||
"ProfilePlantsDiscovered",
|
||
"ProfileMutationsDiscovered",
|
||
"ProfileGardensOwned",
|
||
"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
|
||
);
|