mirror of
https://github.com/101island/lolisland.us.git
synced 2026-03-01 03:49:42 +08:00
feat: optimize avatar update logic to perform instant refresh using returned hash
This commit is contained in:
@@ -20,16 +20,49 @@ import Particles from "./Particles.astro";
|
||||
</div>
|
||||
|
||||
<script>
|
||||
import { BACKEND_API_BASE } from "../config/loginApiBaseUrl";
|
||||
import { fetchUsers } from "../data/users";
|
||||
import { MarbleSystem } from "../utils/marbleSystem";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
hasInitializedMarbleSystem: boolean;
|
||||
marbleSystemInstance: unknown;
|
||||
marbleSystemInstance: MarbleSystem; // Better typing if possible, or keep unknown and cast
|
||||
}
|
||||
}
|
||||
|
||||
// Marble Update Listener
|
||||
window.addEventListener("login-success", () => {
|
||||
const sys = window.marbleSystemInstance;
|
||||
if (!sys) return;
|
||||
|
||||
const currentUsername = localStorage.getItem("username");
|
||||
const newAvatar = localStorage.getItem("avatar");
|
||||
|
||||
if (currentUsername && newAvatar && /^[0-9a-f]{16}$/.test(newAvatar)) {
|
||||
// Find marble by label (username)
|
||||
// We have to access private marbles or use getMarbles() if available
|
||||
// MarbleSystem has getMarbles() returning ReadonlyArray<Marble>
|
||||
const marbles = sys.getMarbles();
|
||||
const userMarble = marbles.find((m) => {
|
||||
// We need to find the label element text
|
||||
const label = m.node.querySelector(".marble-label");
|
||||
return label && label.textContent === currentUsername;
|
||||
});
|
||||
|
||||
if (userMarble) {
|
||||
const innerMarble = userMarble.node.querySelector(
|
||||
".marble",
|
||||
) as HTMLElement;
|
||||
if (innerMarble) {
|
||||
innerMarble.style.backgroundImage = `url("${BACKEND_API_BASE}/user/avatar/${newAvatar}")`;
|
||||
// Also update the id if the system relies on it for something else
|
||||
userMarble.id = newAvatar;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Prevent re-initialization on View Transitions
|
||||
if (!window.hasInitializedMarbleSystem) {
|
||||
window.hasInitializedMarbleSystem = true;
|
||||
|
||||
@@ -40,46 +40,33 @@ const t = getTranslations(lang);
|
||||
) as HTMLButtonElement;
|
||||
|
||||
async function updateAvatar() {
|
||||
let qq = localStorage.getItem("qq");
|
||||
let avatar = localStorage.getItem("avatar");
|
||||
renderAvatar();
|
||||
await refreshUserInfo();
|
||||
renderAvatar();
|
||||
}
|
||||
|
||||
function renderAvatar() {
|
||||
const avatar = localStorage.getItem("avatar");
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
if (token) {
|
||||
// Sync user info
|
||||
try {
|
||||
// Fetch user info from backend if token exists
|
||||
const res = await fetch(`${BACKEND_API_BASE}/me`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = (await res.json()) as {
|
||||
qq?: string;
|
||||
avatar?: string;
|
||||
};
|
||||
if (data.qq) {
|
||||
qq = String(data.qq);
|
||||
localStorage.setItem("qq", qq);
|
||||
}
|
||||
if (data.avatar) {
|
||||
avatar = String(data.avatar);
|
||||
localStorage.setItem("avatar", avatar);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch user info", e);
|
||||
}
|
||||
|
||||
if (avatar && /^[0-9a-f]{16}$/.test(avatar)) {
|
||||
avatarImg.src = `${BACKEND_API_BASE}/user/avatar/${avatar}`;
|
||||
// Basic update
|
||||
const newSrc = `${BACKEND_API_BASE}/user/avatar/${avatar}`;
|
||||
if (avatarImg.src !== newSrc) {
|
||||
avatarImg.src = newSrc;
|
||||
}
|
||||
avatarImg.onerror = () => {
|
||||
avatarImg.src =
|
||||
"https://ui-avatars.com/api/?name=User&background=random";
|
||||
avatarImg.onerror = null;
|
||||
};
|
||||
} else {
|
||||
// Default avatar or placeholder
|
||||
avatarImg.src =
|
||||
"https://ui-avatars.com/api/?name=User&background=random";
|
||||
// Default if no valid avatar in LS
|
||||
if (!avatarImg.src.includes("ui-avatars.com")) {
|
||||
avatarImg.src =
|
||||
"https://ui-avatars.com/api/?name=User&background=random";
|
||||
}
|
||||
}
|
||||
userDropdown.classList.remove("hidden");
|
||||
} else {
|
||||
@@ -87,6 +74,31 @@ const t = getTranslations(lang);
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshUserInfo() {
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
|
||||
try {
|
||||
const res = await fetch(`${BACKEND_API_BASE}/me`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = (await res.json()) as {
|
||||
qq?: string;
|
||||
avatar?: string;
|
||||
};
|
||||
if (data.qq) {
|
||||
localStorage.setItem("qq", String(data.qq));
|
||||
}
|
||||
if (data.avatar) {
|
||||
localStorage.setItem("avatar", String(data.avatar));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch user info", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Initial check
|
||||
updateAvatar();
|
||||
|
||||
|
||||
@@ -80,11 +80,17 @@ const t = getTranslations(lang);
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
// Update image source to force reload
|
||||
if (img) {
|
||||
const currentSrc = img.src.split("?")[0];
|
||||
img.src = `${currentSrc}?t=${Date.now()}`;
|
||||
const { key } = (await res.json()) as { key: string };
|
||||
|
||||
if (key) {
|
||||
localStorage.setItem("avatar", key);
|
||||
|
||||
// Update image source directly with new hash
|
||||
if (img) {
|
||||
img.src = `${BACKEND_API_BASE}/user/avatar/${key}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch event for other components (like navbar) if they listen
|
||||
window.dispatchEvent(new CustomEvent("login-success"));
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user