mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-13 18:38:11 +02:00
Added more social stuff like previewing gardens and stuff! Pretty
awesome.
This commit is contained in:
1 parent
d1721517cc
commit
56d70e8c0c
9 files changed
+2623
-43
No files matched your search
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
$PageTitle = "Gardener";
|
||||
$PageSection = "Social";
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" data-palette="CatppuccinMocha">
|
||||
<?php require __DIR__ . "/Bits/Head.php"; ?>
|
||||
|
||||
<body>
|
||||
<?php require __DIR__ . "/Bits/Navbar.php"; ?>
|
||||
|
||||
<main class="MainContainer">
|
||||
<p class="GardenerBackLink">
|
||||
<a href="/Pages/Social.html">Back to Social</a>
|
||||
</p>
|
||||
|
||||
<div class="GardenerHero">
|
||||
<div id="GardenerAvatar"></div>
|
||||
|
||||
<div class="GardenerHeroText">
|
||||
<h1 id="GardenerName">Gardener</h1>
|
||||
|
||||
<p id="GardenerDescription">
|
||||
Loading gardener...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p
|
||||
id="GardenerPageMessage"
|
||||
class="PageMessage"
|
||||
aria-live="polite"
|
||||
>
|
||||
Loading gardener...
|
||||
</p>
|
||||
|
||||
<section
|
||||
id="GardenerStatsSection"
|
||||
class="Panel SocialSection"
|
||||
hidden
|
||||
>
|
||||
<h2 class="PanelHeader PanelHeaderInset">Statistics</h2>
|
||||
|
||||
<div class="ProfileStatistics">
|
||||
<div class="ProfileStatistic">
|
||||
<span class="ProfileStatisticName">Current Dew</span>
|
||||
<strong id="GardenerCurrentDew">0</strong>
|
||||
</div>
|
||||
|
||||
<div class="ProfileStatistic">
|
||||
<span class="ProfileStatisticName">Lifetime Dew</span>
|
||||
<strong id="GardenerLifetimeDew">0</strong>
|
||||
</div>
|
||||
|
||||
<div class="ProfileStatistic">
|
||||
<span class="ProfileStatisticName">Plants discovered</span>
|
||||
<strong id="GardenerPlantsDiscovered">0</strong>
|
||||
</div>
|
||||
|
||||
<div class="ProfileStatistic">
|
||||
<span class="ProfileStatisticName">Mutations discovered</span>
|
||||
<strong id="GardenerMutationsDiscovered">0</strong>
|
||||
</div>
|
||||
|
||||
<div class="ProfileStatistic">
|
||||
<span class="ProfileStatisticName">Gardens owned</span>
|
||||
<strong id="GardenerGardensOwned">0</strong>
|
||||
</div>
|
||||
|
||||
<div class="ProfileStatistic">
|
||||
<span class="ProfileStatisticName">Active Garden size</span>
|
||||
<strong id="GardenerActiveGardenSize">0×0</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p
|
||||
id="GardenerStatsPrivateMessage"
|
||||
class="ProfileMessage"
|
||||
hidden
|
||||
></p>
|
||||
</section>
|
||||
|
||||
<section
|
||||
id="GardenerGardensSection"
|
||||
class="Panel SocialSection"
|
||||
hidden
|
||||
>
|
||||
<div class="PanelHeader PanelHeaderInset GardenerSectionHeader">
|
||||
<h2>Gardens</h2>
|
||||
<span id="GardenerGardenPosition"></span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="GardenerGardenControls"
|
||||
class="GardenerGardenControls"
|
||||
>
|
||||
<button
|
||||
id="PreviousGardenerGardenButton"
|
||||
class="ActionButton"
|
||||
type="button"
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
|
||||
<div class="GardenerGardenTitle">
|
||||
<strong id="GardenerGardenName">Garden</strong>
|
||||
<span id="GardenerGardenSize">0×0</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
id="NextGardenerGardenButton"
|
||||
class="ActionButton"
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="GardenGridViewport">
|
||||
<div
|
||||
id="GardenerGardenGrid"
|
||||
class="GardenGrid GardenerGardenGrid"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<p
|
||||
id="GardenerGardensMessage"
|
||||
class="ProfileMessage"
|
||||
></p>
|
||||
</section>
|
||||
|
||||
<section
|
||||
id="GardenerDonationSection"
|
||||
class="Panel SocialSection InteractivePanel"
|
||||
hidden
|
||||
>
|
||||
<h2 class="PanelHeader PanelHeaderInset">Dew donation</h2>
|
||||
|
||||
<p id="GardenerDonationBalance"></p>
|
||||
|
||||
<form
|
||||
id="GardenerDonationForm"
|
||||
class="FormRow"
|
||||
>
|
||||
<input
|
||||
id="GardenerDonationAmount"
|
||||
type="number"
|
||||
min="1"
|
||||
step="1"
|
||||
inputmode="numeric"
|
||||
placeholder="Dew amount"
|
||||
required
|
||||
>
|
||||
|
||||
<button type="submit">
|
||||
Donate Dew
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p
|
||||
id="GardenerDonationMessage"
|
||||
class="ProfileMessage"
|
||||
aria-live="polite"
|
||||
></p>
|
||||
</section>
|
||||
|
||||
<section
|
||||
id="GardenerCommentsSection"
|
||||
class="Panel SocialSection InteractivePanel"
|
||||
hidden
|
||||
>
|
||||
<h2 class="PanelHeader PanelHeaderInset">Comments</h2>
|
||||
|
||||
<form id="GardenerCommentForm">
|
||||
<label for="GardenerCommentInput">
|
||||
Leave a comment
|
||||
</label>
|
||||
|
||||
<textarea
|
||||
id="GardenerCommentInput"
|
||||
rows="3"
|
||||
maxlength="1000"
|
||||
placeholder="Write a comment..."
|
||||
required
|
||||
></textarea>
|
||||
|
||||
<div class="FormRow">
|
||||
<button type="submit">
|
||||
Leave comment
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p
|
||||
id="GardenerCommentMessage"
|
||||
class="ProfileMessage"
|
||||
aria-live="polite"
|
||||
></p>
|
||||
|
||||
<div
|
||||
id="GardenerCommentList"
|
||||
class="GardenerCommentList"
|
||||
></div>
|
||||
|
||||
<button
|
||||
id="LoadMoreGardenerCommentsButton"
|
||||
class="ActionButton GardenerLoadMoreButton"
|
||||
type="button"
|
||||
hidden
|
||||
>
|
||||
Load more comments
|
||||
</button>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="/Scripts/Garden/PlantImages.js"></script>
|
||||
<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/Gardener.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
+42
-8
@@ -312,11 +312,12 @@ $PageSection = "Guide";
|
||||
|
||||
<p>
|
||||
Your <a href="/Pages/Profile.html">Profile</a>
|
||||
contains your username, colour, Garden display settings,
|
||||
statistics, and Account Key. Garden saves can sync through
|
||||
the API, while the Account Key lets you use the same Garden
|
||||
in another browser or on another device. It's basically
|
||||
just like logging into an account.
|
||||
contains your username, colour, Gardener description,
|
||||
plant profile picture, privacy settings, Garden display
|
||||
settings, statistics, and Account Key. Garden saves can
|
||||
sync through the API, while the Account Key lets you use
|
||||
the same Garden in another browser or on another device.
|
||||
It's basically just like logging into an account.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -325,6 +326,36 @@ $PageSection = "Guide";
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section
|
||||
id="Social"
|
||||
class="GuideSection"
|
||||
>
|
||||
<h2>Social and Gardener profiles</h2>
|
||||
|
||||
<p>
|
||||
The <a href="/Pages/Social.html">Social</a> page lets
|
||||
you search for other gardeners by username and open their
|
||||
public Gardener page. Gardener profile pictures use a plant
|
||||
and growth stage the player has discovered.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Gardener pages can show statistics and read-only previews
|
||||
of every Garden the player owns. Players can independently
|
||||
disable Garden previews, comments, Dew donations, and
|
||||
public statistics from their Profile. Disabling public
|
||||
statistics also hides the player from the Dew leaderboard.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Comments are simple messages left on a Gardener page. A
|
||||
comment can be deleted by its author or by the owner of the
|
||||
Gardener page. Dew donations transfer spendable Dew between
|
||||
accounts, but donated Dew does not increase the recipient's
|
||||
Lifetime Dew.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section
|
||||
id="Privacy"
|
||||
class="GuideSection"
|
||||
@@ -343,9 +374,12 @@ $PageSection = "Guide";
|
||||
<p>
|
||||
This browser stores your Account Key and a local copy
|
||||
of your Garden save so the same account can keep working
|
||||
between visits. If you set a username, some profile
|
||||
information and statistics can also appear on the
|
||||
public leaderboard.
|
||||
between visits. If you set a username, your Gardener can
|
||||
be found through Social. Your username, profile colour,
|
||||
description, and selected plant profile picture form the
|
||||
basic public Gardener profile; the separate privacy
|
||||
settings control Garden previews, comments, Dew donations,
|
||||
and public statistics.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
+9
-7
@@ -25,7 +25,7 @@ $PageTitle = "Profile";
|
||||
|
||||
<section
|
||||
id="Player"
|
||||
class="Panel ProfileSection"
|
||||
class="Panel ProfileSection InteractivePanel"
|
||||
>
|
||||
<h2 class="PanelHeader PanelHeaderInset">Player</h2>
|
||||
|
||||
@@ -101,7 +101,7 @@ $PageTitle = "Profile";
|
||||
|
||||
<section
|
||||
id="GardenerProfile"
|
||||
class="Panel ProfileSection"
|
||||
class="Panel ProfileSection InteractivePanel"
|
||||
>
|
||||
<h2 class="PanelHeader PanelHeaderInset">Gardener profile</h2>
|
||||
|
||||
@@ -226,6 +226,8 @@ $PageTitle = "Profile";
|
||||
<p>
|
||||
Choose which parts of your Gardener profile
|
||||
other players will be able to use or view.
|
||||
Hiding statistics also removes you from the
|
||||
public Dew leaderboard.
|
||||
</p>
|
||||
|
||||
<div class="ProfileButtonRow">
|
||||
@@ -267,7 +269,7 @@ $PageTitle = "Profile";
|
||||
|
||||
<section
|
||||
id="GardenDisplay"
|
||||
class="Panel ProfileSection"
|
||||
class="Panel ProfileSection InteractivePanel"
|
||||
>
|
||||
<h2 class="PanelHeader PanelHeaderInset">Garden display</h2>
|
||||
|
||||
@@ -411,7 +413,7 @@ $PageTitle = "Profile";
|
||||
|
||||
<section
|
||||
id="Statistics"
|
||||
class="Panel ProfileSection"
|
||||
class="Panel ProfileSection InteractivePanel"
|
||||
>
|
||||
<h3 class="PanelHeader PanelHeaderInset">Statistics</h3>
|
||||
|
||||
@@ -481,7 +483,7 @@ $PageTitle = "Profile";
|
||||
|
||||
<section
|
||||
id="Contact"
|
||||
class="Panel ProfileSection"
|
||||
class="Panel ProfileSection InteractivePanel"
|
||||
>
|
||||
<h2 class="PanelHeader PanelHeaderInset">Contact</h2>
|
||||
|
||||
@@ -501,7 +503,7 @@ $PageTitle = "Profile";
|
||||
|
||||
<section
|
||||
id="Account"
|
||||
class="Panel ProfileSection"
|
||||
class="Panel ProfileSection InteractivePanel"
|
||||
>
|
||||
<h2 class="PanelHeader PanelHeaderInset">Account</h2>
|
||||
|
||||
@@ -610,7 +612,7 @@ $PageTitle = "Profile";
|
||||
|
||||
<section
|
||||
id="Import"
|
||||
class="Panel ProfileSection"
|
||||
class="Panel ProfileSection InteractivePanel"
|
||||
>
|
||||
<h3 class="PanelHeader PanelHeaderInset">Import Account</h3>
|
||||
|
||||
|
||||
+53
-1
@@ -16,6 +16,54 @@ $PageSection = "Social";
|
||||
<main class="MainContainer">
|
||||
<h1>Social</h1>
|
||||
|
||||
<p>
|
||||
Find other gardeners, visit their public profiles,
|
||||
preview their Gardens, leave comments, and donate Dew
|
||||
when they allow it.
|
||||
</p>
|
||||
|
||||
<section
|
||||
id="GardenerSearch"
|
||||
class="Panel SocialSection"
|
||||
>
|
||||
<h2 class="PanelHeader PanelHeaderInset">Find gardeners</h2>
|
||||
|
||||
<form
|
||||
id="GardenerSearchForm"
|
||||
class="CatalogueControls"
|
||||
role="search"
|
||||
>
|
||||
<input
|
||||
id="GardenerSearchInput"
|
||||
type="search"
|
||||
maxlength="64"
|
||||
placeholder="Search usernames..."
|
||||
aria-label="Search gardeners by username"
|
||||
autocomplete="off"
|
||||
>
|
||||
|
||||
<button
|
||||
class="ActionButton"
|
||||
type="submit"
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div
|
||||
id="GardenerSearchResults"
|
||||
class="GardenerSearchResults"
|
||||
></div>
|
||||
|
||||
<p
|
||||
id="GardenerSearchMessage"
|
||||
class="PageMessage"
|
||||
aria-live="polite"
|
||||
>
|
||||
Search for a gardener by username.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section
|
||||
id="Leaderboard"
|
||||
class="Panel Leaderboard"
|
||||
@@ -31,7 +79,7 @@ $PageSection = "Social";
|
||||
</th>
|
||||
|
||||
<th>
|
||||
Username
|
||||
Gardener
|
||||
</th>
|
||||
|
||||
<th class="LeaderboardDewColumn">
|
||||
@@ -46,7 +94,11 @@ $PageSection = "Social";
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="/Scripts/Garden/PlantImages.js"></script>
|
||||
<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/Leaderboard.js"></script>
|
||||
<script src="/Scripts/Garden/Social.js"></script>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -147,12 +147,12 @@ async function RenderLeaderboard(
|
||||
Rank.textContent =
|
||||
Player.Rank + ".";
|
||||
|
||||
Username.textContent =
|
||||
Player.Username;
|
||||
|
||||
ApplyPlayerColour(
|
||||
Username,
|
||||
Player.Colour
|
||||
Username.appendChild(
|
||||
CreateGardenerIdentity(
|
||||
Player.Username,
|
||||
Player.Colour,
|
||||
Player.ProfilePicture
|
||||
)
|
||||
);
|
||||
|
||||
Dew.textContent =
|
||||
|
||||
+312
-3
@@ -1,5 +1,17 @@
|
||||
let SocialCurrentUsername = null;
|
||||
let GardenerSearchRequest = 0;
|
||||
let GardenerSearchTimer = null;
|
||||
|
||||
|
||||
async function StartSocial() {
|
||||
let CurrentUsername = null;
|
||||
try {
|
||||
await LoadGameContent();
|
||||
} catch (Error) {
|
||||
console.error(
|
||||
"Couldn't load Garden content for Social:",
|
||||
Error
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const Profile = await GetProfile();
|
||||
@@ -8,7 +20,7 @@ async function StartSocial() {
|
||||
Profile.Success &&
|
||||
Profile.Username !== null
|
||||
) {
|
||||
CurrentUsername =
|
||||
SocialCurrentUsername =
|
||||
Profile.Username;
|
||||
}
|
||||
} catch (Error) {
|
||||
@@ -18,12 +30,309 @@ async function StartSocial() {
|
||||
);
|
||||
}
|
||||
|
||||
BindGardenerSearch();
|
||||
|
||||
await RenderLeaderboard(
|
||||
CurrentUsername
|
||||
SocialCurrentUsername
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function BindGardenerSearch() {
|
||||
const Form =
|
||||
document.getElementById(
|
||||
"GardenerSearchForm"
|
||||
);
|
||||
|
||||
const Input =
|
||||
document.getElementById(
|
||||
"GardenerSearchInput"
|
||||
);
|
||||
|
||||
if (
|
||||
Form === null ||
|
||||
Input === null
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
Form.addEventListener(
|
||||
"submit",
|
||||
Event => {
|
||||
Event.preventDefault();
|
||||
|
||||
SearchGardeners(
|
||||
Input.value
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Input.addEventListener(
|
||||
"input",
|
||||
() => {
|
||||
if (GardenerSearchTimer !== null) {
|
||||
clearTimeout(
|
||||
GardenerSearchTimer
|
||||
);
|
||||
}
|
||||
|
||||
const Search =
|
||||
Input.value.trim();
|
||||
|
||||
if (Search.length === 0) {
|
||||
ClearGardenerSearch();
|
||||
return;
|
||||
}
|
||||
|
||||
GardenerSearchTimer =
|
||||
setTimeout(
|
||||
() => {
|
||||
SearchGardeners(
|
||||
Search
|
||||
);
|
||||
},
|
||||
250
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const Query =
|
||||
new URLSearchParams(
|
||||
window.location.search
|
||||
);
|
||||
|
||||
const InitialSearch =
|
||||
Query.get("Search")?.trim() ?? "";
|
||||
|
||||
if (InitialSearch.length > 0) {
|
||||
Input.value = InitialSearch;
|
||||
SearchGardeners(
|
||||
InitialSearch
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function ClearGardenerSearch() {
|
||||
GardenerSearchRequest++;
|
||||
|
||||
const Results =
|
||||
document.getElementById(
|
||||
"GardenerSearchResults"
|
||||
);
|
||||
|
||||
Results?.replaceChildren();
|
||||
|
||||
SetGardenerSearchMessage(
|
||||
"Search for a gardener by username."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
async function SearchGardeners(
|
||||
SearchValue
|
||||
) {
|
||||
const Search =
|
||||
String(SearchValue ?? "")
|
||||
.trim();
|
||||
|
||||
if (Search.length === 0) {
|
||||
ClearGardenerSearch();
|
||||
return;
|
||||
}
|
||||
|
||||
const RequestId =
|
||||
++GardenerSearchRequest;
|
||||
|
||||
SetGardenerSearchMessage(
|
||||
"Searching..."
|
||||
);
|
||||
|
||||
try {
|
||||
const Query =
|
||||
new URLSearchParams({
|
||||
Search: Search
|
||||
});
|
||||
|
||||
const Response = await fetch(
|
||||
ApiUrl +
|
||||
"/Gardeners.php?" +
|
||||
Query.toString(),
|
||||
{
|
||||
cache: "no-store"
|
||||
}
|
||||
);
|
||||
|
||||
const Result =
|
||||
await Response.json();
|
||||
|
||||
if (RequestId !== GardenerSearchRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Result.Success) {
|
||||
throw new Error(
|
||||
Result.Error ??
|
||||
"Couldn't search gardeners."
|
||||
);
|
||||
}
|
||||
|
||||
RenderGardenerSearchResults(
|
||||
Array.isArray(Result.Gardeners)
|
||||
? Result.Gardeners
|
||||
: []
|
||||
);
|
||||
} catch (Error) {
|
||||
if (RequestId !== GardenerSearchRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(
|
||||
"Couldn't search gardeners:",
|
||||
Error
|
||||
);
|
||||
|
||||
document.getElementById(
|
||||
"GardenerSearchResults"
|
||||
)?.replaceChildren();
|
||||
|
||||
SetGardenerSearchMessage(
|
||||
"Couldn't search gardeners."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function RenderGardenerSearchResults(
|
||||
Gardeners
|
||||
) {
|
||||
const Results =
|
||||
document.getElementById(
|
||||
"GardenerSearchResults"
|
||||
);
|
||||
|
||||
if (Results === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Results.replaceChildren();
|
||||
|
||||
if (Gardeners.length === 0) {
|
||||
SetGardenerSearchMessage(
|
||||
"No gardeners matched that search."
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (const Gardener of Gardeners) {
|
||||
Results.appendChild(
|
||||
CreateGardenerSearchResult(
|
||||
Gardener
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
SetGardenerSearchMessage(
|
||||
Gardeners.length === 1
|
||||
? "1 gardener found."
|
||||
: Gardeners.length
|
||||
.toLocaleString() +
|
||||
" gardeners found."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function CreateGardenerSearchResult(
|
||||
Gardener
|
||||
) {
|
||||
const Link =
|
||||
document.createElement(
|
||||
"a"
|
||||
);
|
||||
|
||||
Link.className =
|
||||
"PlantTile GardenerSearchResult";
|
||||
|
||||
Link.href =
|
||||
GetGardenerPageUrl(
|
||||
Gardener.Username
|
||||
);
|
||||
|
||||
const Avatar =
|
||||
CreateGardenerAvatar(
|
||||
Gardener.ProfilePicture,
|
||||
Gardener.Username,
|
||||
"GardenerAvatarSearch"
|
||||
);
|
||||
|
||||
const Details =
|
||||
document.createElement(
|
||||
"span"
|
||||
);
|
||||
|
||||
Details.className =
|
||||
"GardenerSearchResultDetails";
|
||||
|
||||
const Name =
|
||||
document.createElement(
|
||||
"strong"
|
||||
);
|
||||
|
||||
Name.className =
|
||||
"GardenerSearchResultName";
|
||||
|
||||
Name.textContent =
|
||||
Gardener.Username;
|
||||
|
||||
ApplyPlayerColour(
|
||||
Name,
|
||||
Gardener.Colour
|
||||
);
|
||||
|
||||
const Description =
|
||||
document.createElement(
|
||||
"span"
|
||||
);
|
||||
|
||||
Description.className =
|
||||
"GardenerSearchResultDescription";
|
||||
|
||||
Description.textContent =
|
||||
typeof Gardener.Description ===
|
||||
"string" &&
|
||||
Gardener.Description.length > 0
|
||||
? Gardener.Description
|
||||
: "No description.";
|
||||
|
||||
Details.append(
|
||||
Name,
|
||||
Description
|
||||
);
|
||||
|
||||
Link.append(
|
||||
Avatar,
|
||||
Details
|
||||
);
|
||||
|
||||
return Link;
|
||||
}
|
||||
|
||||
|
||||
function SetGardenerSearchMessage(
|
||||
Text
|
||||
) {
|
||||
const Message =
|
||||
document.getElementById(
|
||||
"GardenerSearchMessage"
|
||||
);
|
||||
|
||||
if (Message !== null) {
|
||||
Message.textContent = Text;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
StartSocial
|
||||
|
||||
@@ -191,6 +191,219 @@ function ApplyPlayerColour(
|
||||
}
|
||||
|
||||
|
||||
function GetGardenerProfilePictureSource(
|
||||
ProfilePicture
|
||||
) {
|
||||
if (
|
||||
ProfilePicture === null ||
|
||||
typeof ProfilePicture !== "object" ||
|
||||
Array.isArray(ProfilePicture) ||
|
||||
typeof ProfilePicture.PlantKey !==
|
||||
"string" ||
|
||||
!Number.isInteger(
|
||||
Number(
|
||||
ProfilePicture.GrowthStage
|
||||
)
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof Plants === "undefined" ||
|
||||
typeof GetPlantImageSources !==
|
||||
"function"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const Plant =
|
||||
Plants[
|
||||
ProfilePicture.PlantKey
|
||||
];
|
||||
|
||||
if (Plant === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const Images =
|
||||
GetPlantImageSources(
|
||||
Plant
|
||||
);
|
||||
|
||||
const GrowthStage = Number(
|
||||
ProfilePicture.GrowthStage
|
||||
);
|
||||
|
||||
const RequestedSource =
|
||||
Images[GrowthStage - 1];
|
||||
|
||||
if (
|
||||
typeof RequestedSource ===
|
||||
"string" &&
|
||||
RequestedSource.length > 0
|
||||
) {
|
||||
return RequestedSource;
|
||||
}
|
||||
|
||||
for (
|
||||
let Index = Images.length - 1;
|
||||
Index >= 0;
|
||||
Index--
|
||||
) {
|
||||
if (
|
||||
typeof Images[Index] ===
|
||||
"string" &&
|
||||
Images[Index].length > 0
|
||||
) {
|
||||
return Images[Index];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
function CreateGardenerAvatar(
|
||||
ProfilePicture,
|
||||
Username = "",
|
||||
AdditionalClass = null
|
||||
) {
|
||||
const Avatar =
|
||||
document.createElement(
|
||||
"span"
|
||||
);
|
||||
|
||||
Avatar.className =
|
||||
"PlantTile GardenerAvatar";
|
||||
|
||||
if (
|
||||
typeof AdditionalClass ===
|
||||
"string" &&
|
||||
AdditionalClass.length > 0
|
||||
) {
|
||||
Avatar.classList.add(
|
||||
AdditionalClass
|
||||
);
|
||||
}
|
||||
|
||||
if (Username.length > 0) {
|
||||
Avatar.title =
|
||||
Username +
|
||||
"'s profile picture";
|
||||
}
|
||||
|
||||
const ImageSource =
|
||||
GetGardenerProfilePictureSource(
|
||||
ProfilePicture
|
||||
);
|
||||
|
||||
if (ImageSource !== null) {
|
||||
const Image =
|
||||
document.createElement(
|
||||
"img"
|
||||
);
|
||||
|
||||
Image.className =
|
||||
"PlantSprite";
|
||||
|
||||
Image.src = ImageSource;
|
||||
Image.alt = "";
|
||||
Image.draggable = false;
|
||||
|
||||
Avatar.appendChild(
|
||||
Image
|
||||
);
|
||||
|
||||
return Avatar;
|
||||
}
|
||||
|
||||
const Placeholder =
|
||||
document.createElement(
|
||||
"span"
|
||||
);
|
||||
|
||||
Placeholder.className =
|
||||
"GardenerAvatarPlaceholder";
|
||||
|
||||
Placeholder.textContent = "□";
|
||||
|
||||
Avatar.appendChild(
|
||||
Placeholder
|
||||
);
|
||||
|
||||
return Avatar;
|
||||
}
|
||||
|
||||
|
||||
function GetGardenerPageUrl(
|
||||
Username
|
||||
) {
|
||||
const Query =
|
||||
new URLSearchParams({
|
||||
Username: Username
|
||||
});
|
||||
|
||||
return (
|
||||
"/Pages/Gardener.html?" +
|
||||
Query.toString()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function CreateGardenerIdentity(
|
||||
Username,
|
||||
Colour,
|
||||
ProfilePicture,
|
||||
LinkToGardener = true
|
||||
) {
|
||||
const Container =
|
||||
document.createElement(
|
||||
LinkToGardener
|
||||
? "a"
|
||||
: "span"
|
||||
);
|
||||
|
||||
Container.className =
|
||||
"GardenerIdentity";
|
||||
|
||||
if (LinkToGardener) {
|
||||
Container.href =
|
||||
GetGardenerPageUrl(
|
||||
Username
|
||||
);
|
||||
}
|
||||
|
||||
const Avatar =
|
||||
CreateGardenerAvatar(
|
||||
ProfilePicture,
|
||||
Username
|
||||
);
|
||||
|
||||
const Name =
|
||||
document.createElement(
|
||||
"span"
|
||||
);
|
||||
|
||||
Name.className =
|
||||
"GardenerIdentityName";
|
||||
|
||||
Name.textContent = Username;
|
||||
|
||||
ApplyPlayerColour(
|
||||
Name,
|
||||
Colour
|
||||
);
|
||||
|
||||
Container.append(
|
||||
Avatar,
|
||||
Name
|
||||
);
|
||||
|
||||
return Container;
|
||||
}
|
||||
|
||||
|
||||
async function GetProfile() {
|
||||
const Response = await fetch(
|
||||
ApiUrl + "/Profile.php",
|
||||
|
||||
+276
-18
@@ -1020,7 +1020,7 @@ input, button, textarea, select {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.ProfileSection {
|
||||
.ProfileSection, .SocialSection {
|
||||
margin-top: 1.5rem;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
@@ -1029,7 +1029,7 @@ input, button, textarea, select {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.ProfileFormRow, .ProfileButtonRow {
|
||||
.FormRow, .ProfileFormRow, .ProfileButtonRow {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
@@ -1040,13 +1040,13 @@ input, button, textarea, select {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.ProfileSection label {
|
||||
.InteractivePanel label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--Blue);
|
||||
}
|
||||
|
||||
.ProfileSection input, .ProfileSection textarea, .ProfileSection select, .ProfileSection button {
|
||||
.InteractivePanel input, .InteractivePanel textarea, .InteractivePanel select, .InteractivePanel button {
|
||||
font-size: 1rem;
|
||||
color: var(--Text);
|
||||
background-color: var(--Surface0);
|
||||
@@ -1055,46 +1055,46 @@ input, button, textarea, select {
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.ProfileSection input, .ProfileSection textarea, .ProfileSection select {
|
||||
.InteractivePanel input, .InteractivePanel textarea, .InteractivePanel select {
|
||||
padding: 0.6rem;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.ProfileSection input {
|
||||
.InteractivePanel input {
|
||||
min-width: 12rem;
|
||||
}
|
||||
|
||||
.ProfileSection select {
|
||||
.InteractivePanel select {
|
||||
appearance: auto;
|
||||
}
|
||||
|
||||
.ProfileSection textarea {
|
||||
.InteractivePanel textarea {
|
||||
width: 100%;
|
||||
max-width: 24rem;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.ProfileSection input::placeholder, .ProfileSection textarea::placeholder {
|
||||
.InteractivePanel input::placeholder, .InteractivePanel textarea::placeholder {
|
||||
color: var(--Overlay1);
|
||||
}
|
||||
|
||||
.ProfileSection input:hover, .ProfileSection textarea:hover, .ProfileSection select:hover {
|
||||
.InteractivePanel input:hover, .InteractivePanel textarea:hover, .InteractivePanel select:hover {
|
||||
background-color: var(--Surface1);
|
||||
border-color: var(--Overlay2);
|
||||
}
|
||||
|
||||
.ProfileSection input:focus, .ProfileSection textarea:focus, .ProfileSection select:focus {
|
||||
.InteractivePanel input:focus, .InteractivePanel textarea:focus, .InteractivePanel select:focus {
|
||||
background-color: var(--Surface1);
|
||||
border-color: var(--Sky);
|
||||
}
|
||||
|
||||
.ProfileSection button {
|
||||
.InteractivePanel button {
|
||||
padding: 0.6rem 0.8rem;
|
||||
cursor: pointer;
|
||||
box-shadow: 3px 3px 0 var(--Crust);
|
||||
}
|
||||
|
||||
.ProfileSection button:hover {
|
||||
.InteractivePanel button:hover:not(:disabled) {
|
||||
color: var(--Sky);
|
||||
background-color: var(--Surface1);
|
||||
border-color: var(--Sky);
|
||||
@@ -1102,7 +1102,7 @@ input, button, textarea, select {
|
||||
box-shadow: 2px 2px 0 var(--Crust);
|
||||
}
|
||||
|
||||
.ProfileSection button:disabled {
|
||||
.InteractivePanel button:disabled {
|
||||
cursor: not-allowed;
|
||||
color: var(--Overlay1);
|
||||
background-color: var(--Surface0);
|
||||
@@ -1110,7 +1110,7 @@ input, button, textarea, select {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ProfileSection button:active {
|
||||
.InteractivePanel button:active:not(:disabled) {
|
||||
color: var(--Green);
|
||||
background-color: var(--Surface2);
|
||||
border-color: var(--Green);
|
||||
@@ -1118,7 +1118,7 @@ input, button, textarea, select {
|
||||
box-shadow: 1px 1px 0 var(--Crust);
|
||||
}
|
||||
|
||||
.ProfileSection button:focus-visible, .ProfileSection summary:focus-visible {
|
||||
.InteractivePanel button:focus-visible, .InteractivePanel summary:focus-visible {
|
||||
outline: 3px solid var(--Yellow);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
@@ -1259,12 +1259,12 @@ input, button, textarea, select {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.ProfileFormRow, .ProfileButtonRow {
|
||||
.FormRow, .ProfileFormRow, .ProfileButtonRow {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.ProfileSection input, .ProfileSection textarea, .ProfileSection select, .ProfileSection button {
|
||||
.InteractivePanel input, .InteractivePanel textarea, .InteractivePanel select, .InteractivePanel button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -1609,6 +1609,264 @@ input, button, textarea, select {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* * Garden social */
|
||||
|
||||
.GardenerAvatar {
|
||||
display: flex;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
flex: 0 0 3rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.GardenerAvatarPlaceholder {
|
||||
color: var(--Overlay1);
|
||||
font-size: 1.75rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.GardenerIdentity {
|
||||
display: inline-flex;
|
||||
min-width: 0;
|
||||
gap: 0.65rem;
|
||||
align-items: center;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.GardenerIdentityName {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.GardenerIdentity:hover .GardenerIdentityName {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.GardenerSearchResults {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.GardenerSearchResult {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
gap: 0.85rem;
|
||||
padding: 0.75rem;
|
||||
align-items: center;
|
||||
color: var(--Text);
|
||||
text-decoration: none;
|
||||
box-shadow: 3px 3px 0 var(--Crust);
|
||||
}
|
||||
|
||||
.GardenerSearchResult:hover {
|
||||
color: var(--Text);
|
||||
background-color: var(--Surface1);
|
||||
border-color: var(--Sky);
|
||||
transform: translate(1px, 1px);
|
||||
box-shadow: 2px 2px 0 var(--Crust);
|
||||
}
|
||||
|
||||
.GardenerAvatarSearch {
|
||||
width: 4.25rem;
|
||||
height: 4.25rem;
|
||||
flex-basis: 4.25rem;
|
||||
}
|
||||
|
||||
.GardenerSearchResultDetails {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.GardenerSearchResultName {
|
||||
overflow: hidden;
|
||||
font-size: 1.35rem;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.GardenerSearchResultDescription {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
color: var(--Subtext1);
|
||||
line-height: 1.35;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.GardenerBackLink {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.GardenerHero {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.GardenerHeroText {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.GardenerHeroText h1 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.GardenerHeroText p {
|
||||
margin-bottom: 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.GardenerAvatarHero {
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
flex-basis: 7rem;
|
||||
}
|
||||
|
||||
.GardenerSectionHeader {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.GardenerGardenControls {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(7rem, auto) minmax(0, 1fr) minmax(7rem, auto);
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.GardenerGardenTitle {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.GardenerGardenTitle strong {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.GardenerGardenTitle span,
|
||||
.GardenerSectionHeader span {
|
||||
color: var(--Subtext1);
|
||||
}
|
||||
|
||||
.GardenPlotReadOnly,
|
||||
.GardenPlotReadOnly:hover {
|
||||
cursor: default;
|
||||
transform: none;
|
||||
box-shadow: 3px 3px 0 var(--Crust);
|
||||
}
|
||||
|
||||
.GardenPlotReadOnly:hover {
|
||||
--TileBackground: var(--Surface0);
|
||||
--PlotBorderColour: var(--Surface2);
|
||||
}
|
||||
|
||||
.GardenPlotReadOnly.GardenPlotEmpty:hover {
|
||||
--TileBackground: var(--Mantle);
|
||||
}
|
||||
|
||||
.GardenPlotReadOnly.GardenPlotMature:hover {
|
||||
--GrowthColour: var(--Green);
|
||||
}
|
||||
|
||||
.GardenerCommentList {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.GardenerComment {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.GardenerCommentHeader {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.GardenerCommentTime {
|
||||
flex: 0 0 auto;
|
||||
color: var(--Overlay1);
|
||||
font-size: 0.85rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.GardenerCommentText {
|
||||
margin: 0.75rem 0 0;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.InteractivePanel .GardenerCommentDelete {
|
||||
width: auto;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.GardenerCommentsEmpty {
|
||||
margin: 0;
|
||||
color: var(--Subtext1);
|
||||
}
|
||||
|
||||
.GardenerLoadMoreButton {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.LeaderboardTable {
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
|
||||
.LeaderboardUsername .GardenerAvatar {
|
||||
width: 3.25rem;
|
||||
height: 3.25rem;
|
||||
flex-basis: 3.25rem;
|
||||
}
|
||||
|
||||
.LeaderboardUsername .GardenerIdentity {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.GardenerAvatarHero {
|
||||
width: 5.5rem;
|
||||
height: 5.5rem;
|
||||
flex-basis: 5.5rem;
|
||||
}
|
||||
|
||||
.GardenerGardenControls {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.GardenerGardenTitle {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.GardenerCommentHeader {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.GardenerCommentTime {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
/* * Garden guide */
|
||||
|
||||
.GuideSection {
|
||||
|
||||
Reference in new issue
Block a user