Add public profiles and cosmetic shop
All checks were successful
Deploy MineGeo to Dokploy / deploy (push) Successful in 7m10s
All checks were successful
Deploy MineGeo to Dokploy / deploy (push) Successful in 7m10s
This commit is contained in:
@@ -3,6 +3,7 @@ import { useI18n } from "@/components/I18nProvider";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { UserRound, LockKeyhole, Mail } from "lucide-react";
|
||||
import { api } from "./StartGame";
|
||||
import { PasswordForm } from "./PasswordForm";
|
||||
@@ -19,6 +20,7 @@ export function AccountSettings({
|
||||
accountId,
|
||||
email,
|
||||
displayName,
|
||||
bio,
|
||||
avatarPath,
|
||||
appearance,
|
||||
passwordUpdated = false,
|
||||
@@ -26,12 +28,14 @@ export function AccountSettings({
|
||||
accountId: string;
|
||||
email: string;
|
||||
displayName: string;
|
||||
bio: string;
|
||||
avatarPath: string | null;
|
||||
appearance: ProfileAppearance;
|
||||
passwordUpdated?: boolean;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const [name, setName] = useState(displayName),
|
||||
[about, setAbout] = useState(bio),
|
||||
[message, setMessage] = useState(""),
|
||||
[error, setError] = useState(""),
|
||||
[busy, setBusy] = useState(false);
|
||||
@@ -124,13 +128,16 @@ export function AccountSettings({
|
||||
}
|
||||
setBusy(true);
|
||||
try {
|
||||
const data = await api<{ profile: { display_name: string } }>(
|
||||
const data = await api<{
|
||||
profile: { display_name: string; bio: string };
|
||||
}>(
|
||||
"profile",
|
||||
{ displayName: valid.data },
|
||||
{ displayName: valid.data, bio: about },
|
||||
"PATCH",
|
||||
);
|
||||
setName(data.profile.display_name);
|
||||
setMessage("Display name saved.");
|
||||
setAbout(data.profile.bio);
|
||||
setMessage("Public profile saved.");
|
||||
router.refresh();
|
||||
} catch (e) {
|
||||
setError((e as Error).message);
|
||||
@@ -161,15 +168,21 @@ export function AccountSettings({
|
||||
<div>
|
||||
<h2>{t("Your public profile.")}</h2>
|
||||
<p className="muted">
|
||||
{t("A name for the maps and places you share.")}
|
||||
{t("A name and description for other explorers to see.")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<Link className="text-link" href={"/players/" + accountId}>
|
||||
{t("View public profile")} →
|
||||
</Link>
|
||||
</p>
|
||||
<form onSubmit={save}>
|
||||
<div className="account-avatar-control">
|
||||
<PlayerAvatar
|
||||
id={accountId}
|
||||
avatarPath={photoPath}
|
||||
effect={appearance.profile_effect}
|
||||
className="account-avatar-preview"
|
||||
/>
|
||||
<div className="account-avatar-input">
|
||||
@@ -221,6 +234,19 @@ export function AccountSettings({
|
||||
{t("2–32 characters. Display names do not have to be unique.")}
|
||||
</small>
|
||||
</label>
|
||||
<label className="field">
|
||||
{t("About you")}
|
||||
<textarea
|
||||
name="bio"
|
||||
value={about}
|
||||
onChange={(event) => setAbout(event.target.value)}
|
||||
maxLength={280}
|
||||
rows={4}
|
||||
placeholder={t("Tell other explorers a little about yourself.")}
|
||||
disabled={busy}
|
||||
/>
|
||||
<small>{t("Shown on your public profile · Maximum 280 characters.")}</small>
|
||||
</label>
|
||||
<div className="account-email">
|
||||
<Mail size={17} />
|
||||
<div>
|
||||
@@ -242,7 +268,7 @@ export function AccountSettings({
|
||||
</p>
|
||||
)}
|
||||
<button className="button primary" disabled={busy}>
|
||||
{busy ? t("Saving…") : t("Save display name")}
|
||||
{busy ? t("Saving…") : t("Save public profile")}
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user