Update static/js/09-profile.js
This commit is contained in:
parent
f0d7f26c63
commit
ca7eae57d2
@ -1,6 +1,5 @@
|
|||||||
// static/js/09-profile.js
|
// static/js/09-profile.js
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
window.Glow = window.Glow || {};
|
window.Glow = window.Glow || {};
|
||||||
|
|
||||||
Glow.escapeHtml = value =>
|
Glow.escapeHtml = value =>
|
||||||
@ -13,16 +12,13 @@ Glow.escapeHtml = value =>
|
|||||||
Glow.renderProfileBody = profile => {
|
Glow.renderProfileBody = profile => {
|
||||||
const wrap = document.createElement("div");
|
const wrap = document.createElement("div");
|
||||||
wrap.className = "profile-rendered";
|
wrap.className = "profile-rendered";
|
||||||
|
|
||||||
const content = String(profile?.bio_html || "").trim();
|
const content = String(profile?.bio_html || "").trim();
|
||||||
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
const empty = document.createElement("div");
|
const empty = document.createElement("div");
|
||||||
empty.className = "profile-empty";
|
empty.className = "profile-empty";
|
||||||
empty.textContent = "No profile bio yet.";
|
empty.textContent = "No profile bio yet.";
|
||||||
return empty;
|
return empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
wrap.innerHTML = content;
|
wrap.innerHTML = content;
|
||||||
return wrap;
|
return wrap;
|
||||||
};
|
};
|
||||||
@ -31,11 +27,9 @@ Glow.openProfile = async username => {
|
|||||||
Glow.state.activeView = "profile";
|
Glow.state.activeView = "profile";
|
||||||
Glow.state.activeProfile = null;
|
Glow.state.activeProfile = null;
|
||||||
Glow.updateViewUI();
|
Glow.updateViewUI();
|
||||||
|
|
||||||
const box = Glow.els.profileBox;
|
const box = Glow.els.profileBox;
|
||||||
box.setAttribute("aria-busy", "true");
|
box.setAttribute("aria-busy", "true");
|
||||||
box.replaceChildren();
|
box.replaceChildren();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await Glow.apiGetPublicProfile(username);
|
const data = await Glow.apiGetPublicProfile(username);
|
||||||
Glow.state.activeProfile = data.profile;
|
Glow.state.activeProfile = data.profile;
|
||||||
@ -44,14 +38,11 @@ Glow.openProfile = async username => {
|
|||||||
box.replaceChildren();
|
box.replaceChildren();
|
||||||
const state = document.createElement("div");
|
const state = document.createElement("div");
|
||||||
state.className = "empty-state";
|
state.className = "empty-state";
|
||||||
|
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.className = "empty-card";
|
card.className = "empty-card";
|
||||||
card.textContent = error.message || "Could not load profile.";
|
card.textContent = error.message || "Could not load profile.";
|
||||||
|
|
||||||
state.append(card);
|
state.append(card);
|
||||||
box.append(state);
|
box.append(state);
|
||||||
|
|
||||||
Glow.showToast(error.message || "Could not load profile.", "error");
|
Glow.showToast(error.message || "Could not load profile.", "error");
|
||||||
} finally {
|
} finally {
|
||||||
box.setAttribute("aria-busy", "false");
|
box.setAttribute("aria-busy", "false");
|
||||||
@ -63,15 +54,12 @@ Glow.openMyProfile = async (edit = false) => {
|
|||||||
Glow.showToast("Sign in to view your profile.", "error");
|
Glow.showToast("Sign in to view your profile.", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Glow.state.activeView = "profile";
|
Glow.state.activeView = "profile";
|
||||||
Glow.state.activeProfile = null;
|
Glow.state.activeProfile = null;
|
||||||
Glow.updateViewUI();
|
Glow.updateViewUI();
|
||||||
|
|
||||||
const box = Glow.els.profileBox;
|
const box = Glow.els.profileBox;
|
||||||
box.setAttribute("aria-busy", "true");
|
box.setAttribute("aria-busy", "true");
|
||||||
box.replaceChildren();
|
box.replaceChildren();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await Glow.apiGetMyProfile();
|
const data = await Glow.apiGetMyProfile();
|
||||||
Glow.state.activeProfile = data.profile;
|
Glow.state.activeProfile = data.profile;
|
||||||
@ -83,30 +71,45 @@ Glow.openMyProfile = async (edit = false) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Glow.uploadBannerForProfile = async file => {
|
||||||
|
const result = await Glow.uploadAttachment(file);
|
||||||
|
return result?.attachment || null;
|
||||||
|
};
|
||||||
|
|
||||||
Glow.renderProfileView = (editing = false) => {
|
Glow.renderProfileView = (editing = false) => {
|
||||||
const box = Glow.els.profileBox;
|
const box = Glow.els.profileBox;
|
||||||
const profile = Glow.state.activeProfile;
|
const profile = Glow.state.activeProfile;
|
||||||
|
|
||||||
box.replaceChildren();
|
box.replaceChildren();
|
||||||
|
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
const state = document.createElement("div");
|
const state = document.createElement("div");
|
||||||
state.className = "empty-state";
|
state.className = "empty-state";
|
||||||
|
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.className = "empty-card";
|
card.className = "empty-card";
|
||||||
card.textContent = "Profile not found.";
|
card.textContent = "Profile not found.";
|
||||||
|
|
||||||
state.append(card);
|
state.append(card);
|
||||||
box.append(state);
|
box.append(state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
box.style.background =
|
||||||
|
profile.profile_background_color || "transparent";
|
||||||
|
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.className = "profile-card";
|
card.className = "profile-card";
|
||||||
|
|
||||||
const banner = document.createElement("div");
|
const banner = document.createElement("div");
|
||||||
banner.className = "profile-banner";
|
banner.className = "profile-banner";
|
||||||
|
if (profile.profile_banner_color) {
|
||||||
|
banner.style.background =
|
||||||
|
`linear-gradient(135deg, ${profile.profile_banner_color}, transparent 72%), ${profile.profile_banner_color}`;
|
||||||
|
}
|
||||||
|
if (profile.banner_image_url) {
|
||||||
|
banner.style.backgroundImage =
|
||||||
|
`linear-gradient(135deg, rgba(0,0,0,0.18), rgba(0,0,0,0.04)), url("${profile.banner_image_url}")`;
|
||||||
|
banner.style.backgroundSize = "cover";
|
||||||
|
banner.style.backgroundPosition = "center";
|
||||||
|
}
|
||||||
|
|
||||||
const content = document.createElement("div");
|
const content = document.createElement("div");
|
||||||
content.className = "profile-content";
|
content.className = "profile-content";
|
||||||
@ -122,7 +125,6 @@ Glow.renderProfileView = (editing = false) => {
|
|||||||
|
|
||||||
const actions = document.createElement("div");
|
const actions = document.createElement("div");
|
||||||
actions.className = "profile-actions";
|
actions.className = "profile-actions";
|
||||||
|
|
||||||
if (profile.is_self) {
|
if (profile.is_self) {
|
||||||
const editButton = document.createElement("button");
|
const editButton = document.createElement("button");
|
||||||
editButton.className = "button";
|
editButton.className = "button";
|
||||||
@ -139,10 +141,8 @@ Glow.renderProfileView = (editing = false) => {
|
|||||||
settingsButton.addEventListener("click", () => {
|
settingsButton.addEventListener("click", () => {
|
||||||
Glow.openSettings?.();
|
Glow.openSettings?.();
|
||||||
});
|
});
|
||||||
|
|
||||||
actions.append(editButton, settingsButton);
|
actions.append(editButton, settingsButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
header.append(avatar, actions);
|
header.append(avatar, actions);
|
||||||
|
|
||||||
const name = document.createElement("div");
|
const name = document.createElement("div");
|
||||||
@ -172,7 +172,11 @@ Glow.renderProfileView = (editing = false) => {
|
|||||||
statusBadge.className = "profile-badge";
|
statusBadge.className = "profile-badge";
|
||||||
statusBadge.textContent = profile.status;
|
statusBadge.textContent = profile.status;
|
||||||
|
|
||||||
badges.append(roleBadge, statusBadge);
|
const presenceBadge = document.createElement("span");
|
||||||
|
presenceBadge.className = "profile-badge";
|
||||||
|
presenceBadge.textContent = profile.presence_mode || "online";
|
||||||
|
|
||||||
|
badges.append(roleBadge, statusBadge, presenceBadge);
|
||||||
|
|
||||||
const grid = document.createElement("div");
|
const grid = document.createElement("div");
|
||||||
grid.className = "profile-grid";
|
grid.className = "profile-grid";
|
||||||
@ -182,38 +186,38 @@ Glow.renderProfileView = (editing = false) => {
|
|||||||
} else {
|
} else {
|
||||||
const bioSection = document.createElement("section");
|
const bioSection = document.createElement("section");
|
||||||
bioSection.className = "profile-section";
|
bioSection.className = "profile-section";
|
||||||
|
|
||||||
const bioTitle = document.createElement("h3");
|
const bioTitle = document.createElement("h3");
|
||||||
bioTitle.textContent = "Bio";
|
bioTitle.textContent = "Bio";
|
||||||
|
|
||||||
bioSection.append(bioTitle, Glow.renderProfileBody(profile));
|
bioSection.append(bioTitle, Glow.renderProfileBody(profile));
|
||||||
|
|
||||||
const metaSection = document.createElement("section");
|
const metaSection = document.createElement("section");
|
||||||
metaSection.className = "profile-section";
|
metaSection.className = "profile-section";
|
||||||
|
|
||||||
const metaTitle = document.createElement("h3");
|
const metaTitle = document.createElement("h3");
|
||||||
metaTitle.textContent = "Profile";
|
metaTitle.textContent = "Profile";
|
||||||
|
|
||||||
const metaList = document.createElement("dl");
|
const metaList = document.createElement("dl");
|
||||||
metaList.className = "profile-meta-list";
|
metaList.className = "profile-meta-list";
|
||||||
|
|
||||||
const addMeta = (label, value) => {
|
const addMeta = (label, value) => {
|
||||||
const dt = document.createElement("dt");
|
const dt = document.createElement("dt");
|
||||||
dt.textContent = label;
|
dt.textContent = label;
|
||||||
|
|
||||||
const dd = document.createElement("dd");
|
const dd = document.createElement("dd");
|
||||||
dd.textContent = value || "—";
|
dd.textContent = value || "—";
|
||||||
|
|
||||||
metaList.append(dt, dd);
|
metaList.append(dt, dd);
|
||||||
};
|
};
|
||||||
|
|
||||||
addMeta("Display name", profile.display_name || profile.username);
|
addMeta("Display name", profile.display_name || profile.username);
|
||||||
|
addMeta("Status message", profile.status_message || "");
|
||||||
|
addMeta("Presence mode", profile.presence_mode || "online");
|
||||||
addMeta("Website", profile.website || "");
|
addMeta("Website", profile.website || "");
|
||||||
addMeta("Location", profile.location || "");
|
addMeta("Location", profile.location || "");
|
||||||
addMeta(
|
addMeta(
|
||||||
"Joined",
|
"Joined",
|
||||||
profile.created_at ? new Date(profile.created_at).toLocaleDateString() : ""
|
profile.created_at ? new Date(profile.created_at).toLocaleDateString() : ""
|
||||||
);
|
);
|
||||||
|
addMeta(
|
||||||
|
"Last active",
|
||||||
|
profile.last_active_at ? new Date(profile.last_active_at).toLocaleString() : ""
|
||||||
|
);
|
||||||
addMeta("Bio mode", profile.bio_mode || "txt");
|
addMeta("Bio mode", profile.bio_mode || "txt");
|
||||||
addMeta("Name color", profile.name_color || "role default");
|
addMeta("Name color", profile.name_color || "role default");
|
||||||
|
|
||||||
@ -221,7 +225,16 @@ Glow.renderProfileView = (editing = false) => {
|
|||||||
grid.append(bioSection, metaSection);
|
grid.append(bioSection, metaSection);
|
||||||
}
|
}
|
||||||
|
|
||||||
content.append(header, name, handle, badges, grid);
|
content.append(header, name, handle);
|
||||||
|
|
||||||
|
if (profile.status_message) {
|
||||||
|
const statusMessage = document.createElement("div");
|
||||||
|
statusMessage.className = "profile-status-message";
|
||||||
|
statusMessage.textContent = profile.status_message;
|
||||||
|
content.append(statusMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
content.append(badges, grid);
|
||||||
card.append(banner, content);
|
card.append(banner, content);
|
||||||
box.append(card);
|
box.append(card);
|
||||||
};
|
};
|
||||||
@ -229,7 +242,6 @@ Glow.renderProfileView = (editing = false) => {
|
|||||||
Glow.buildProfileEditor = profile => {
|
Glow.buildProfileEditor = profile => {
|
||||||
const section = document.createElement("section");
|
const section = document.createElement("section");
|
||||||
section.className = "profile-section";
|
section.className = "profile-section";
|
||||||
|
|
||||||
const title = document.createElement("h3");
|
const title = document.createElement("h3");
|
||||||
title.textContent = "Edit profile";
|
title.textContent = "Edit profile";
|
||||||
|
|
||||||
@ -243,6 +255,13 @@ Glow.buildProfileEditor = profile => {
|
|||||||
displayName.placeholder = "Display name";
|
displayName.placeholder = "Display name";
|
||||||
displayName.value = profile.display_name || "";
|
displayName.value = profile.display_name || "";
|
||||||
|
|
||||||
|
const statusMessage = document.createElement("input");
|
||||||
|
statusMessage.className = "field";
|
||||||
|
statusMessage.type = "text";
|
||||||
|
statusMessage.maxLength = 160;
|
||||||
|
statusMessage.placeholder = "Status message";
|
||||||
|
statusMessage.value = profile.status_message || "";
|
||||||
|
|
||||||
const website = document.createElement("input");
|
const website = document.createElement("input");
|
||||||
website.className = "field";
|
website.className = "field";
|
||||||
website.type = "url";
|
website.type = "url";
|
||||||
@ -257,9 +276,13 @@ Glow.buildProfileEditor = profile => {
|
|||||||
location.placeholder = "Location";
|
location.placeholder = "Location";
|
||||||
location.value = profile.location || "";
|
location.value = profile.location || "";
|
||||||
|
|
||||||
|
const bannerInput = document.createElement("input");
|
||||||
|
bannerInput.className = "field";
|
||||||
|
bannerInput.type = "file";
|
||||||
|
bannerInput.accept = ".png,.jpg,.jpeg,.gif,.webp";
|
||||||
|
|
||||||
const modeRow = document.createElement("div");
|
const modeRow = document.createElement("div");
|
||||||
modeRow.className = "profile-mode-row";
|
modeRow.className = "profile-mode-row";
|
||||||
|
|
||||||
let selectedMode = profile.bio_mode || "txt";
|
let selectedMode = profile.bio_mode || "txt";
|
||||||
|
|
||||||
const body = document.createElement("textarea");
|
const body = document.createElement("textarea");
|
||||||
@ -273,14 +296,12 @@ Glow.buildProfileEditor = profile => {
|
|||||||
button.className = `mode-pill${selectedMode === mode ? " active" : ""}`;
|
button.className = `mode-pill${selectedMode === mode ? " active" : ""}`;
|
||||||
button.type = "button";
|
button.type = "button";
|
||||||
button.textContent = mode.toUpperCase();
|
button.textContent = mode.toUpperCase();
|
||||||
|
|
||||||
button.addEventListener("click", () => {
|
button.addEventListener("click", () => {
|
||||||
selectedMode = mode;
|
selectedMode = mode;
|
||||||
for (const el of modeRow.querySelectorAll(".mode-pill")) {
|
for (const el of modeRow.querySelectorAll(".mode-pill")) {
|
||||||
el.classList.toggle("active", el.textContent.toLowerCase() === mode);
|
el.classList.toggle("active", el.textContent.toLowerCase() === mode);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
modeRow.append(button);
|
modeRow.append(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,17 +319,28 @@ Glow.buildProfileEditor = profile => {
|
|||||||
save.textContent = "Save profile";
|
save.textContent = "Save profile";
|
||||||
save.addEventListener("click", async () => {
|
save.addEventListener("click", async () => {
|
||||||
try {
|
try {
|
||||||
|
let bannerAttachmentId = profile.banner_attachment_id || null;
|
||||||
|
const file = bannerInput.files?.[0];
|
||||||
|
if (file) {
|
||||||
|
const uploaded = await Glow.uploadBannerForProfile(file);
|
||||||
|
bannerAttachmentId = uploaded?.id || bannerAttachmentId;
|
||||||
|
}
|
||||||
|
|
||||||
const data = await Glow.apiUpdateMyProfile({
|
const data = await Glow.apiUpdateMyProfile({
|
||||||
display_name: displayName.value,
|
display_name: displayName.value,
|
||||||
|
status_message: statusMessage.value,
|
||||||
website: website.value,
|
website: website.value,
|
||||||
location: location.value,
|
location: location.value,
|
||||||
bio: body.value,
|
bio: body.value,
|
||||||
bio_mode: selectedMode
|
bio_mode: selectedMode,
|
||||||
|
banner_attachment_id: bannerAttachmentId
|
||||||
});
|
});
|
||||||
|
|
||||||
Glow.state.activeProfile = {
|
Glow.state.activeProfile = {
|
||||||
...data.profile,
|
...data.profile,
|
||||||
name_color: Glow.state.settings?.name_color || profile.name_color || null
|
name_color: Glow.state.settings?.name_color || profile.name_color || null,
|
||||||
|
profile_background_color: Glow.state.settings?.profile_background_color || null,
|
||||||
|
profile_banner_color: Glow.state.settings?.profile_banner_color || null
|
||||||
};
|
};
|
||||||
Glow.showToast("Profile updated.");
|
Glow.showToast("Profile updated.");
|
||||||
Glow.renderProfileView(false);
|
Glow.renderProfileView(false);
|
||||||
@ -323,17 +355,28 @@ Glow.buildProfileEditor = profile => {
|
|||||||
preview.textContent = "Preview";
|
preview.textContent = "Preview";
|
||||||
preview.addEventListener("click", async () => {
|
preview.addEventListener("click", async () => {
|
||||||
try {
|
try {
|
||||||
|
let bannerAttachmentId = profile.banner_attachment_id || null;
|
||||||
|
const file = bannerInput.files?.[0];
|
||||||
|
if (file) {
|
||||||
|
const uploaded = await Glow.uploadBannerForProfile(file);
|
||||||
|
bannerAttachmentId = uploaded?.id || bannerAttachmentId;
|
||||||
|
}
|
||||||
|
|
||||||
const data = await Glow.apiUpdateMyProfile({
|
const data = await Glow.apiUpdateMyProfile({
|
||||||
display_name: displayName.value,
|
display_name: displayName.value,
|
||||||
|
status_message: statusMessage.value,
|
||||||
website: website.value,
|
website: website.value,
|
||||||
location: location.value,
|
location: location.value,
|
||||||
bio: body.value,
|
bio: body.value,
|
||||||
bio_mode: selectedMode
|
bio_mode: selectedMode,
|
||||||
|
banner_attachment_id: bannerAttachmentId
|
||||||
});
|
});
|
||||||
|
|
||||||
Glow.state.activeProfile = {
|
Glow.state.activeProfile = {
|
||||||
...data.profile,
|
...data.profile,
|
||||||
name_color: Glow.state.settings?.name_color || profile.name_color || null
|
name_color: Glow.state.settings?.name_color || profile.name_color || null,
|
||||||
|
profile_background_color: Glow.state.settings?.profile_background_color || null,
|
||||||
|
profile_banner_color: Glow.state.settings?.profile_banner_color || null
|
||||||
};
|
};
|
||||||
Glow.renderProfileView(false);
|
Glow.renderProfileView(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -342,8 +385,17 @@ Glow.buildProfileEditor = profile => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
actions.append(save, preview);
|
actions.append(save, preview);
|
||||||
editor.append(displayName, website, location, modeRow, help, body, actions);
|
editor.append(
|
||||||
|
displayName,
|
||||||
|
statusMessage,
|
||||||
|
website,
|
||||||
|
location,
|
||||||
|
bannerInput,
|
||||||
|
modeRow,
|
||||||
|
help,
|
||||||
|
body,
|
||||||
|
actions
|
||||||
|
);
|
||||||
section.append(title, editor);
|
section.append(title, editor);
|
||||||
|
|
||||||
return section;
|
return section;
|
||||||
};
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user