Binary file not shown.
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -51,4 +51,4 @@ async function startServer()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
startServer();
|
startServer();
|
||||||
@@ -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;
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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 l’envoi';
|
||||||
|
this.message.style.color = '#f00';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import {fenetre} from "./windows.js";
|
|
||||||
|
|
||||||
export class avatarWindows extends fenetre {
|
|
||||||
constructor(){
|
|
||||||
super(320, 240, "Avatar");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user