Merge pull request #12 from OlaketalAmigo/avatarWindows

Avatar windows
This commit is contained in:
H3XploR
2026-01-22 20:18:12 +01:00
committed by GitHub
7 changed files with 177 additions and 19 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

+1 -1
View File
@@ -51,4 +51,4 @@ async function startServer()
}); });
} }
startServer(); startServer();
+1 -1
View File
@@ -47,4 +47,4 @@ router.get('/user/:userId', async(req, res) =>
res.status(result.status).json(result.data); res.status(result.status).json(result.data);
}); });
export default router; export default router;
+7 -8
View File
@@ -1,8 +1,7 @@
import {Element, MenuElement} from "./element.js"; import {Element, MenuElement} from "./element.js";
import {fenetre} from "./windows.js";
import {LoginWindow} from "./login.js"; import {LoginWindow} from "./login.js";
import { GlobalChat } from "./global_chat.js"; import { GlobalChat } from "./global_chat.js";
import {avatarWindows} from "./avatarWindows.js"; import { AvatarWindow } from "./avatar.js";
function direBonjour() { function direBonjour() {
alert("clicked !"); alert("clicked !");
@@ -17,9 +16,9 @@ const accueilElement = new MenuElement("accueil");
const globalChatElement = new MenuElement("global_chat"); const globalChatElement = new MenuElement("global_chat");
const avatarElement = new MenuElement("avatar"); const avatarElement = new MenuElement("avatar");
// Windows and screens // Windows and screens
export const avatarWindow = new AvatarWindow();
const loginWindow = new LoginWindow(); const loginWindow = new LoginWindow();
const global_chat = new GlobalChat(); const global_chat = new GlobalChat();
const avatar_windows = new avatarWindows();
// Actions UI // Actions UI
@@ -43,10 +42,10 @@ document.getElementById("global_chat").addEventListener("click", () => {
document.getElementById("avatar").addEventListener("click", () => { document.getElementById("avatar").addEventListener("click", () => {
// Toggle global chat visibility // Toggle avatar window visibility
if (avatarWindows.main && avatarWindows.main.style.display !== "none") { if (avatarWindow.main && avatarWindow.main.style.display !== "none") {
avatarWindows.hide(); avatarWindow.hide();
} else { } else {
avatarWindows.show(); avatarWindow.show();
} }
}); });
+165
View File
@@ -0,0 +1,165 @@
import {fenetre} from "./windows.js";
export class AvatarWindow extends fenetre {
constructor() {
super(360, 320, "Avatar");
// Avatar preview
this.avatarPreview = document.createElement("img");
this.avatarPreview.style.width = "120px";
this.avatarPreview.style.height = "120px";
this.avatarPreview.style.objectFit = "cover";
this.avatarPreview.style.borderRadius = "50%";
this.avatarPreview.style.border = "2px solid #fff";
this.fileInput = document.createElement("input");
this.fileInput.type = "file";
this.fileInput.accept = "image/*";
// Hide the raw file input to keep only one visible control
this.fileInput.style.display = "none";
this.chooseBtn = document.createElement("button");
this.chooseBtn.textContent = "Choisir image";
this.saveBtn = document.createElement("button");
this.saveBtn.textContent = "Enregistrer avatar";
// Refresh button to re-fetch avatar from server
this.refreshBtn = document.createElement("button");
this.refreshBtn.textContent = "Rafraîchir photo";
this.message = document.createElement("div");
this.message.style.fontSize = "0.9em";
this.body.append(
this.avatarPreview,
this.fileInput,
this.chooseBtn,
this.saveBtn,
this.refreshBtn,
this.message
);
this.applyStyles();
this.bindEvents();
// Load current avatar on initialization
this.getPhoto();
}
applyStyles() {
// Center avatar in the window body
this.body.style.display = "flex";
this.body.style.flexDirection = "column";
this.body.style.alignItems = "center";
this.body.style.gap = "12px";
// Style helpers
this.avatarPreview.style.boxShadow = "0 0 8px rgba(0,0,0,0.5)";
this.chooseBtn.style.padding = "6px 12px";
this.chooseBtn.style.cursor = "pointer";
this.saveBtn.style.padding = "6px 12px";
this.saveBtn.style.cursor = "pointer";
}
bindEvents() {
this.fileInput.addEventListener("change", (e) => {
const file = e.target.files && e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (ev) => {
this.avatarPreview.src = ev.target.result;
};
reader.readAsDataURL(file);
});
this.chooseBtn.addEventListener("click", () => {
// trigger file input
this.fileInput.click();
});
this.saveBtn.addEventListener("click", () => {
// Send the selected photo to the server
this.postPhoto();
});
// Bind refresh button to re-fetch avatar from server
this.refreshBtn.addEventListener("click", () => {
this.getPhoto();
});
}
async getPhoto(){
console.log("getPhoto launched...");
const token = localStorage.getItem("auth_token");
if (!token) {
console.log("No auth token found; skipping avatar fetch");
return;
}
try {
const response = await fetch("/api/avatar/me", {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`
}
});
if (!response.ok) {
console.warn("Failed to fetch avatar (status", response.status, ")");
return;
}
const data = await response.json();
console.log(data);
if (data && data.avatar_url) {
this.avatarPreview.src = data.avatar_url;
} else {
console.warn("Avatar URL not found in response");
}
} catch (err) {
console.error("Error while fetching avatar:", err);
}
}
async postPhoto(){
console.log("postPhoto launched...");
const token = localStorage.getItem("auth_token");
if (!token) {
this.message.textContent = "No auth. plz connect.";
this.message.style.color = "#f00";
return;
}
const file = this.fileInput.files && this.fileInput.files[0];
if (!file) {
this.message.textContent = "take image before";
this.message.style.color = "#f00";
return;
}
const formData = new FormData();
formData.append('avatar', file);
try {
const response = await fetch('/api/avatar/upload', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
},
body: formData
});
const data = await response.json();
if (!response.ok) {
const err = data?.error || data?.message || 'Upload failed';
this.message.textContent = err;
this.message.style.color = '#f00';
return;
}
if (data && data.avatar_url) {
this.avatarPreview.src = data.avatar_url;
}
this.message.textContent = 'Avatar enregistré !';
this.message.style.color = '#3cff01';
} catch (err) {
console.error('Avatar upload error:', err);
this.message.textContent = 'Erreur lors de lenvoi';
this.message.style.color = '#f00';
}
}
}
-7
View File
@@ -1,7 +0,0 @@
import {fenetre} from "./windows.js";
export class avatarWindows extends fenetre {
constructor(){
super(320, 240, "Avatar");
}
}
+3 -2
View File
@@ -1,5 +1,5 @@
import {fenetre} from "./windows.js"; import {fenetre} from "./windows.js";
import {avatarWindow} from "./app.js";
export class LoginWindow extends fenetre { export class LoginWindow extends fenetre {
constructor() { constructor() {
super(320, 240, "Connexion"); super(320, 240, "Connexion");
@@ -50,6 +50,7 @@ export class LoginWindow extends fenetre {
if (ev.data && ev.data.token) { if (ev.data && ev.data.token) {
localStorage.setItem('auth_token', ev.data.token); localStorage.setItem('auth_token', ev.data.token);
this.message.innerText = 'Connexion GitHub réussie ! Bienvenue.'; this.message.innerText = 'Connexion GitHub réussie ! Bienvenue.';
avatarWindow.getPhoto();
this.message.style.color = '#3cff01'; this.message.style.color = '#3cff01';
window.removeEventListener('message', listener); window.removeEventListener('message', listener);
if (popup) popup.close(); if (popup) popup.close();
@@ -98,7 +99,7 @@ export class LoginWindow extends fenetre {
localStorage.setItem("auth_token", data.token); localStorage.setItem("auth_token", data.token);
this.message.innerText = "Connexion réussie ! Bienvenue."; this.message.innerText = "Connexion réussie ! Bienvenue.";
this.message.style.color = "#3cff01"; this.message.style.color = "#3cff01";
avatarWindow.getPhoto();
// mask the window after 1.5s // mask the window after 1.5s
setTimeout(() => this.hide(), 1500); setTimeout(() => this.hide(), 1500);