feat(frontend): you can now update your icon

This commit is contained in:
Maieul BOYER 2026-01-16 15:46:50 +01:00 committed by Nigel
parent 3cccc18e9a
commit 5884407f35
3 changed files with 64 additions and 11 deletions

View file

@ -31,6 +31,19 @@
</div> </div>
</div> </div>
</div> </div>
<div id="iconBox" class="mb-1 grid grid-cols-[auto_1fr] gap-2 hidden">
<div id="viewIconBox" class="h-[128px] w-[128px] p-2 border-gray-700 border-2 rounded-lg">
<img/>
</div>
<div id="updateIconBox">
<form id="updateIconForm" class="flex flex-col justify-evenly h-full">
<input type="file" name="upload" accept="image/png, image/jpeg" class="text-black border-black border-2 rounded-lg px-4 w-[230px]"></input>
<span class="text-black font-base">Select Image</span>
<button type="submit" class="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700"> Update </button>
</form>
</div>
</div>
<!-- Display Name --> <!-- Display Name -->
<div id="displayNameWrapper" class="py-2"> <div id="displayNameWrapper" class="py-2">
<label class="block font-medium mb-1 text-gray-700">Display Name</label> <label class="block font-medium mb-1 text-gray-700">Display Name</label>
@ -52,8 +65,7 @@
<label class="block font-medium mb-1 text-gray-700">Description</label> <label class="block font-medium mb-1 text-gray-700">Description</label>
<input id="descBox" type="text" placeholder="Description..." name="Description" <input id="descBox" type="text" placeholder="Description..." name="Description"
class="w-full px-4 py-2 border border-gray-300 text-gray-700 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500" /> class="w-full px-4 py-2 border border-gray-300 text-gray-700 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500" />
<button id="descButton" <button id="descButton" class="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700">Update</button>
class="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700">Update</button>
</div> </div>
<!-- TOTP --> <!-- TOTP -->
<div class="border rounded p-4" id="totpWrapper" hidden> <div class="border rounded p-4" id="totpWrapper" hidden>

View file

@ -40,6 +40,45 @@ function removeBgColor(...elem: HTMLElement[]) {
} }
} }
async function setup_profile_image(container: HTMLDivElement, url: string) {
let imgNode = container.querySelector<HTMLImageElement>("img");
let formNode = container.querySelector<HTMLFormElement>("form");
if (!imgNode || !formNode) return;
imgNode.src = url;
container.classList.remove("hidden");
formNode.addEventListener("submit", async (e) => {
e.preventDefault();
let form = e.target;
if (!form) return;
let data = new FormData(form as HTMLFormElement);
let req = await fetch("/api/icons/set", {
body: data,
method: "POST",
});
if (req.status === 200 || req.status === 400) {
let json = await req.json();
if (!("kind" in json) || !("msg" in json))
return showError("Unknown Error");
if (typeof json.kind !== "string" || typeof json.msg !== "string")
return showError("Unknown Error");
const pjson: { kind: string; msg: string } = json;
if (pjson.kind === "success") {
showSuccess("Updated image !");
return handleRoute();
} else {
console.log(`Failed to upload image: ${pjson.msg}`);
showError("Failed to change image");
}
} if (req.status === 413)
{
showError("Image too big");
}
else {
showError("Unknown Error");
}
});
}
async function route(url: string, _args: { [k: string]: string }) { async function route(url: string, _args: { [k: string]: string }) {
setTitle("Edit Profile"); setTitle("Edit Profile");
return { return {
@ -99,8 +138,7 @@ async function route(url: string, _args: { [k: string]: string }) {
let descWrapper = let descWrapper =
app.querySelector<HTMLDivElement>("#descWrapper")!; app.querySelector<HTMLDivElement>("#descWrapper")!;
let descBox = let descBox = app.querySelector<HTMLInputElement>("#descBox")!;
app.querySelector<HTMLInputElement>("#descBox")!;
let descButton = let descButton =
app.querySelector<HTMLButtonElement>("#descButton")!; app.querySelector<HTMLButtonElement>("#descButton")!;
@ -126,6 +164,8 @@ async function route(url: string, _args: { [k: string]: string }) {
let totpWrapper = let totpWrapper =
app.querySelector<HTMLDivElement>("#totpWrapper")!; app.querySelector<HTMLDivElement>("#totpWrapper")!;
let imgBox = app.querySelector<HTMLDivElement>("#iconBox")!;
descBox.value = user.desc; descBox.value = user.desc;
if (user.guest) { if (user.guest) {
@ -138,10 +178,7 @@ async function route(url: string, _args: { [k: string]: string }) {
descButton, descButton,
); );
descButton.classList.add( descButton.classList.add("bg-gray-700", "hover:bg-gray-700");
"bg-gray-700",
"hover:bg-gray-700",
);
descButton.disabled = true; descButton.disabled = true;
descBox.disabled = true; descBox.disabled = true;
@ -174,6 +211,7 @@ async function route(url: string, _args: { [k: string]: string }) {
passwordWrapper.hidden = false; passwordWrapper.hidden = false;
accountTypeBox.innerText = "Normal"; accountTypeBox.innerText = "Normal";
setup_profile_image(imgBox, `/icons/${user.id}`);
} else if ( } else if (
!isNullish(user.selfInfo?.providerId) && !isNullish(user.selfInfo?.providerId) &&
!isNullish(user.selfInfo?.providerUser) !isNullish(user.selfInfo?.providerUser)
@ -195,6 +233,7 @@ async function route(url: string, _args: { [k: string]: string }) {
totpWrapper.hidden = true; totpWrapper.hidden = true;
accountTypeBox.innerText = "Provider"; accountTypeBox.innerText = "Provider";
setup_profile_image(imgBox, `/icons/${user.id}`);
} }
// ---- Update UI ---- // ---- Update UI ----
@ -269,12 +308,13 @@ async function route(url: string, _args: { [k: string]: string }) {
} }
}; };
descButton.onclick = async () => { descButton.onclick = async () => {
let req = await client.changeDesc({ changeDescRequest: { desc: descBox.value } }); let req = await client.changeDesc({
changeDescRequest: { desc: descBox.value },
});
if (req.kind === "success") { if (req.kind === "success") {
showSuccess("Successfully changed description"); showSuccess("Successfully changed description");
handleRoute(); handleRoute();
} } else {
else {
showError(`Failed to update`); showError(`Failed to update`);
} }
}; };

View file

@ -6,4 +6,5 @@ location /api/icons/ {
location /icons/ { location /icons/ {
root /volumes/; root /volumes/;
default_type image/png; default_type image/png;
add_header Cache-Control "max-age=30";
} }