mirror of
https://github.com/101island/lolisland.us.git
synced 2026-03-01 11:49:43 +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>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { BACKEND_API_BASE } from "../config/loginApiBaseUrl";
|
||||||
import { fetchUsers } from "../data/users";
|
import { fetchUsers } from "../data/users";
|
||||||
import { MarbleSystem } from "../utils/marbleSystem";
|
import { MarbleSystem } from "../utils/marbleSystem";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
hasInitializedMarbleSystem: boolean;
|
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
|
// Prevent re-initialization on View Transitions
|
||||||
if (!window.hasInitializedMarbleSystem) {
|
if (!window.hasInitializedMarbleSystem) {
|
||||||
window.hasInitializedMarbleSystem = true;
|
window.hasInitializedMarbleSystem = true;
|
||||||
|
|||||||
@@ -40,46 +40,33 @@ const t = getTranslations(lang);
|
|||||||
) as HTMLButtonElement;
|
) as HTMLButtonElement;
|
||||||
|
|
||||||
async function updateAvatar() {
|
async function updateAvatar() {
|
||||||
let qq = localStorage.getItem("qq");
|
renderAvatar();
|
||||||
let avatar = localStorage.getItem("avatar");
|
await refreshUserInfo();
|
||||||
|
renderAvatar();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAvatar() {
|
||||||
|
const avatar = localStorage.getItem("avatar");
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
|
|
||||||
if (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)) {
|
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.onerror = () => {
|
||||||
avatarImg.src =
|
avatarImg.src =
|
||||||
"https://ui-avatars.com/api/?name=User&background=random";
|
"https://ui-avatars.com/api/?name=User&background=random";
|
||||||
avatarImg.onerror = null;
|
avatarImg.onerror = null;
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Default avatar or placeholder
|
// Default if no valid avatar in LS
|
||||||
avatarImg.src =
|
if (!avatarImg.src.includes("ui-avatars.com")) {
|
||||||
"https://ui-avatars.com/api/?name=User&background=random";
|
avatarImg.src =
|
||||||
|
"https://ui-avatars.com/api/?name=User&background=random";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
userDropdown.classList.remove("hidden");
|
userDropdown.classList.remove("hidden");
|
||||||
} else {
|
} 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
|
// Initial check
|
||||||
updateAvatar();
|
updateAvatar();
|
||||||
|
|
||||||
|
|||||||
@@ -80,11 +80,17 @@ const t = getTranslations(lang);
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
// Update image source to force reload
|
const { key } = (await res.json()) as { key: string };
|
||||||
if (img) {
|
|
||||||
const currentSrc = img.src.split("?")[0];
|
if (key) {
|
||||||
img.src = `${currentSrc}?t=${Date.now()}`;
|
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
|
// Dispatch event for other components (like navbar) if they listen
|
||||||
window.dispatchEvent(new CustomEvent("login-success"));
|
window.dispatchEvent(new CustomEvent("login-success"));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user