Working avatar + english comments
This commit is contained in:
@@ -39,10 +39,16 @@ BACKEND
|
|||||||
Aucun moyen de changer l'etat de la room de waiting a en cours ou finished
|
Aucun moyen de changer l'etat de la room de waiting a en cours ou finished
|
||||||
ca attendra le systeme du jeu
|
ca attendra le systeme du jeu
|
||||||
|
|
||||||
|
21/01 Ajout du service/route pour le systeme d'avatar
|
||||||
|
permet aux utilisateurs de changer ou supprimer leur avatar actuel
|
||||||
|
Ajout egalement d'une simple fonction pour recuperer l'avatar d'un utilisateur (pour le frontend)
|
||||||
|
|
||||||
DATABASE
|
DATABASE
|
||||||
|
|
||||||
17/01 Ajout des tables game_rooms, game_players, game_rounds, words
|
17/01 Ajout des tables game_rooms, game_players, game_rounds, words
|
||||||
- nom, status et parametres de la game
|
- nom, status et parametres de la game
|
||||||
- joueurs dans la game, leur scores et leur role actuel (dessinateur, devineur)
|
- joueurs dans la game, leur scores et leur role actuel (dessinateur, devineur)
|
||||||
- historique de la game, qui a dessine quoi precedemment ainsi que les timers des rounds, sera aussi utile si on veut faire les stats de compte a l'avenir.
|
- historique de la game, qui a dessine quoi precedemment ainsi que les timers des rounds, sera aussi utile si on veut faire les stats de compte a l'avenir.
|
||||||
- contient la liste des mots utilisable par les joueurs
|
- contient la liste des mots utilisable par les joueurs
|
||||||
|
|
||||||
|
21/01 Ajout de avatar_url dans la table users
|
||||||
+2
-2
@@ -28,8 +28,8 @@ services:
|
|||||||
# - "3001:3001"
|
# - "3001:3001"
|
||||||
depends_on:
|
depends_on:
|
||||||
- database
|
- database
|
||||||
# volumes:
|
volumes:
|
||||||
# //
|
- ./srcs/backend/avatar:/app/avatar
|
||||||
env_file:
|
env_file:
|
||||||
- ../.env
|
- ../.env
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ async function createTables()
|
|||||||
username VARCHAR(50) UNIQUE NOT NULL,
|
username VARCHAR(50) UNIQUE NOT NULL,
|
||||||
password_hash TEXT NOT NULL,
|
password_hash TEXT NOT NULL,
|
||||||
email VARCHAR(100),
|
email VARCHAR(100),
|
||||||
|
avatar_url TEXT DEFAULT '/avatar/default.png',
|
||||||
created_at TIMESTAMP DEFAULT NOW()
|
created_at TIMESTAMP DEFAULT NOW()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import {Server} from 'socket.io';
|
|||||||
import authRouter from './routes/auth.js';
|
import authRouter from './routes/auth.js';
|
||||||
import chatRouter from './routes/global_chat.js';
|
import chatRouter from './routes/global_chat.js';
|
||||||
import gameRoomRouter from './routes/game_room.js';
|
import gameRoomRouter from './routes/game_room.js';
|
||||||
// import avatarRouter from './routes/avatar.js';
|
import avatarRouter from './routes/avatar.js';
|
||||||
import {waitForDb, createTables, ensureOauthClient} from './db.js';
|
import {waitForDb, createTables, ensureOauthClient} from './db.js';
|
||||||
import setupSocketIO from './services/socket.js';
|
import setupSocketIO from './services/socket.js';
|
||||||
// import avatarService from './services/avatar.js';
|
import avatarService from './services/avatar.js';
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
@@ -38,11 +38,11 @@ async function startServer()
|
|||||||
console.warn('OAuth client might already exist or failed to register:', e.message);
|
console.warn('OAuth client might already exist or failed to register:', e.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// app.use('/avatar', express.static(avatarService.AVATAR_DIR));
|
app.use('/avatar', express.static(avatarService.AVATAR_DIR));
|
||||||
app.use('/api/auth', authRouter);
|
app.use('/api/auth', authRouter);
|
||||||
app.use('/api/global_chat', chatRouter);
|
app.use('/api/global_chat', chatRouter);
|
||||||
app.use('/api/rooms', gameRoomRouter);
|
app.use('/api/rooms', gameRoomRouter);
|
||||||
// app.use('/api/avatar', avatarRouter);
|
app.use('/api/avatar', avatarRouter);
|
||||||
app.get('/api', (req, res) => res.send('Backend running'));
|
app.get('/api', (req, res) => res.send('Backend running'));
|
||||||
|
|
||||||
server.listen(3001, () =>
|
server.listen(3001, () =>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import multer from 'multer';
|
import multer from 'multer';
|
||||||
import avatarService from '../services/avatar.js';
|
import avatarService from '../services/avatar.js';
|
||||||
import {authenticateToken} from '../middleware/auth.js';
|
import authenticateToken from '../middleware/auth.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ function direBonjour() {
|
|||||||
alert("clicked !");
|
alert("clicked !");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Définir les éléments du menu (structure logique)
|
// Define the elements of the menu (logical structure)
|
||||||
const menuElement = new Element("menu");
|
const menuElement = new Element("menu");
|
||||||
const loginElement = new MenuElement("login");
|
const loginElement = new MenuElement("login");
|
||||||
const registeredElement = new MenuElement("registered");
|
const registeredElement = new MenuElement("registered");
|
||||||
@@ -17,11 +17,11 @@ const explorerElement = new MenuElement("explorer");
|
|||||||
const accueilElement = new MenuElement("accueil");
|
const accueilElement = new MenuElement("accueil");
|
||||||
const globalChatElement = new MenuElement("global_chat");
|
const globalChatElement = new MenuElement("global_chat");
|
||||||
|
|
||||||
// Lancement de la grille (commentés si on ne l’utilise pas tout de suite)
|
// Start of the grid (commented if we dont use it yet)
|
||||||
// const gridgreen = new Grid('#143a0fff', -1, 25, 0.12, "normal");
|
// const gridgreen = new Grid('#143a0fff', -1, 25, 0.12, "normal");
|
||||||
// const gridReverseRed = new Grid('#3a0f0f75', -1, 12.5, 0.09, "reverse");
|
// const gridReverseRed = new Grid('#3a0f0f75', -1, 12.5, 0.09, "reverse");
|
||||||
|
|
||||||
// Fenêtres et écrans
|
// Windows and screens
|
||||||
const test = new fenetre();
|
const test = new fenetre();
|
||||||
const loginWindow = new LoginWindow();
|
const loginWindow = new LoginWindow();
|
||||||
const global_chat = new GlobalChat();
|
const global_chat = new GlobalChat();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ export class GlobalChat extends fenetre {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super(320, 240, "Global Chat");
|
super(320, 240, "Global Chat");
|
||||||
|
|
||||||
// Création des éléments
|
// Creation of the elements
|
||||||
this.output = document.createElement("div");
|
this.output = document.createElement("div");
|
||||||
this.output.className = "chat-output";
|
this.output.className = "chat-output";
|
||||||
|
|
||||||
@@ -24,12 +24,12 @@ export class GlobalChat extends fenetre {
|
|||||||
|
|
||||||
this.applyStyles();
|
this.applyStyles();
|
||||||
this.applyEvents();
|
this.applyEvents();
|
||||||
// Connexion au chat global est déclenchée via des contrôles dédiés
|
// Connection to the global chat is started via dedicated controls
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
this.createConnectControls();
|
this.createConnectControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crée les contrôles Connect / Reconnect dans la fenêtre du chat
|
// Create the controls (Connect / Reconnect) in the chat window
|
||||||
createConnectControls() {
|
createConnectControls() {
|
||||||
this.controls = document.createElement("div");
|
this.controls = document.createElement("div");
|
||||||
this.controls.style.display = "flex";
|
this.controls.style.display = "flex";
|
||||||
@@ -62,7 +62,7 @@ export class GlobalChat extends fenetre {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async reconnect_sockio_global_chat() {
|
async reconnect_sockio_global_chat() {
|
||||||
// Déconnecte et reconnecte le socket si nécessaire
|
// Disconnect and reconnect the socket if necessary
|
||||||
if (this.socket) {
|
if (this.socket) {
|
||||||
try {
|
try {
|
||||||
this.socket.close();
|
this.socket.close();
|
||||||
@@ -77,7 +77,7 @@ export class GlobalChat extends fenetre {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyStyles() {
|
applyStyles() {
|
||||||
// Conteneur principal en flex column
|
// Main container in flex collumn
|
||||||
this.body.style.display = "flex";
|
this.body.style.display = "flex";
|
||||||
this.body.style.flexDirection = "column";
|
this.body.style.flexDirection = "column";
|
||||||
this.body.style.height = "100%";
|
this.body.style.height = "100%";
|
||||||
@@ -85,7 +85,7 @@ export class GlobalChat extends fenetre {
|
|||||||
this.body.style.boxSizing = "border-box";
|
this.body.style.boxSizing = "border-box";
|
||||||
this.body.style.gap = "10px";
|
this.body.style.gap = "10px";
|
||||||
|
|
||||||
// Zone des messages
|
// Messages zone
|
||||||
this.output.style.flex = "1";
|
this.output.style.flex = "1";
|
||||||
this.output.style.overflowY = "auto";
|
this.output.style.overflowY = "auto";
|
||||||
this.output.style.padding = "8px";
|
this.output.style.padding = "8px";
|
||||||
@@ -95,7 +95,7 @@ export class GlobalChat extends fenetre {
|
|||||||
this.output.style.flexDirection = "column";
|
this.output.style.flexDirection = "column";
|
||||||
this.output.style.gap = "10px";
|
this.output.style.gap = "10px";
|
||||||
|
|
||||||
// Conteneur input + bouton
|
// Input container + button
|
||||||
this.inputContainer.style.display = "flex";
|
this.inputContainer.style.display = "flex";
|
||||||
this.inputContainer.style.gap = "8px";
|
this.inputContainer.style.gap = "8px";
|
||||||
this.inputContainer.style.paddingTop = "8px";
|
this.inputContainer.style.paddingTop = "8px";
|
||||||
@@ -107,7 +107,7 @@ export class GlobalChat extends fenetre {
|
|||||||
this.input.style.borderRadius = "6px";
|
this.input.style.borderRadius = "6px";
|
||||||
this.input.style.fontSize = "14px";
|
this.input.style.fontSize = "14px";
|
||||||
|
|
||||||
// Bouton envoyer
|
// Sender button
|
||||||
this.sendButton.style.padding = "8px 16px";
|
this.sendButton.style.padding = "8px 16px";
|
||||||
this.sendButton.style.background = "#0066cc";
|
this.sendButton.style.background = "#0066cc";
|
||||||
this.sendButton.style.color = "white";
|
this.sendButton.style.color = "white";
|
||||||
@@ -118,10 +118,10 @@ export class GlobalChat extends fenetre {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyEvents() {
|
applyEvents() {
|
||||||
// Envoi avec le bouton
|
// Send with the button
|
||||||
this.sendButton.addEventListener("click", () => this.sendMessage());
|
this.sendButton.addEventListener("click", () => this.sendMessage());
|
||||||
|
|
||||||
// Envoi avec Entrée
|
// Send with Enter key
|
||||||
this.input.addEventListener("keypress", (e) => {
|
this.input.addEventListener("keypress", (e) => {
|
||||||
if (e.key === "Enter" && !e.shiftKey) {
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -130,7 +130,7 @@ export class GlobalChat extends fenetre {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Envoie le message courant via Socket.IO
|
// Send current message via Socket.IO
|
||||||
sendMessage() {
|
sendMessage() {
|
||||||
const content = this.input.value.trim();
|
const content = this.input.value.trim();
|
||||||
if (!content) return;
|
if (!content) return;
|
||||||
@@ -143,7 +143,7 @@ export class GlobalChat extends fenetre {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Affichage immédiat dans l'interface pour feedback utilisateur
|
// Immediate display in the interface pour user feedback
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.className = "chat-message";
|
div.className = "chat-message";
|
||||||
div.innerHTML = `<strong>Moi:</strong> ${content}`;
|
div.innerHTML = `<strong>Moi:</strong> ${content}`;
|
||||||
@@ -167,7 +167,7 @@ export class GlobalChat extends fenetre {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si déjà connecté, ne pas tenter de nouveau
|
// If already connected, dont retry
|
||||||
if (this.socket && this.socket.connected) {
|
if (this.socket && this.socket.connected) {
|
||||||
this.output.innerHTML += '<div class="system">Déjà connecté au chat global</div>';
|
this.output.innerHTML += '<div class="system">Déjà connecté au chat global</div>';
|
||||||
return;
|
return;
|
||||||
@@ -194,7 +194,7 @@ export class GlobalChat extends fenetre {
|
|||||||
reconnectionDelay: 1000,
|
reconnectionDelay: 1000,
|
||||||
transports: ["websocket", "polling"]
|
transports: ["websocket", "polling"]
|
||||||
};
|
};
|
||||||
// Optionnel: se connecter via un port alternatif si défini (ex: pour contourner le proxy)
|
// Optional: connect from an alternative port (ex: to dodge the proxy)
|
||||||
const altPort = window.GLOBAL_CHAT_ALT_PORT;
|
const altPort = window.GLOBAL_CHAT_ALT_PORT;
|
||||||
if (altPort) {
|
if (altPort) {
|
||||||
const host = location.hostname || 'localhost';
|
const host = location.hostname || 'localhost';
|
||||||
@@ -218,7 +218,7 @@ export class GlobalChat extends fenetre {
|
|||||||
this.output.innerHTML += `<div class="system">Déconnecté du chat (${reason})</div>`;
|
this.output.innerHTML += `<div class="system">Déconnecté du chat (${reason})</div>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Réception des messages
|
// Messages reception
|
||||||
this.socket.on("chat-message", (msg) => {
|
this.socket.on("chat-message", (msg) => {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.className = "chat-message";
|
div.className = "chat-message";
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
export class Grid {
|
|
||||||
constructor(color = '#143a0fff', zIndex = 1, gridSize = 25, speed = 0.35, sens = "normal") {
|
|
||||||
// Initialisation des propriétés de l'instance
|
|
||||||
this.canvas = document.createElement('canvas');
|
|
||||||
this.ctx = this.canvas.getContext('2d');
|
|
||||||
this.offset = 0;
|
|
||||||
this.gridSize = gridSize;
|
|
||||||
this.speed = speed;
|
|
||||||
this.color = color;
|
|
||||||
|
|
||||||
// Configuration du style
|
|
||||||
document.body.appendChild(this.canvas);
|
|
||||||
this.canvas.style.position = 'fixed';
|
|
||||||
this.canvas.style.top = '0';
|
|
||||||
this.canvas.style.left = '0';
|
|
||||||
this.canvas.style.zIndex = zIndex;
|
|
||||||
this.canvas.style.pointerEvents = 'none'; // Pour ne pas bloquer les clics sur les boutons
|
|
||||||
|
|
||||||
// Gestion du redimensionnement
|
|
||||||
window.addEventListener('resize', () => this.resize());
|
|
||||||
this.resize();
|
|
||||||
this.draw(sens);
|
|
||||||
}
|
|
||||||
|
|
||||||
resize() {
|
|
||||||
this.canvas.width = window.innerWidth;
|
|
||||||
this.canvas.height = window.innerHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
draw(sens = "normal") {
|
|
||||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
||||||
this.ctx.strokeStyle = this.color;
|
|
||||||
this.ctx.lineWidth = 1;
|
|
||||||
|
|
||||||
// Mise à jour de l'offset
|
|
||||||
this.offset = (this.offset + this.speed) % this.gridSize;
|
|
||||||
if (sens === "normal") {
|
|
||||||
// Lignes verticales
|
|
||||||
for (let x = this.offset; x < this.canvas.width; x += this.gridSize) {
|
|
||||||
this.ctx.beginPath();
|
|
||||||
this.ctx.moveTo(x, 0);
|
|
||||||
this.ctx.lineTo(x, this.canvas.height);
|
|
||||||
this.ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lignes horizontales
|
|
||||||
for (let y = this.offset; y < this.canvas.height; y += this.gridSize) {
|
|
||||||
this.ctx.beginPath();
|
|
||||||
this.ctx.moveTo(0, y);
|
|
||||||
this.ctx.lineTo(this.canvas.width, y);
|
|
||||||
this.ctx.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//pareillement pour le sens reverse
|
|
||||||
else if (sens === "reverse") {
|
|
||||||
// Lignes verticales
|
|
||||||
for (let x = this.gridSize - this.offset; x < this.canvas.width; x += this.gridSize) {
|
|
||||||
this.ctx.beginPath();
|
|
||||||
this.ctx.moveTo(x, 0);
|
|
||||||
this.ctx.lineTo(x, this.canvas.height);
|
|
||||||
this.ctx.stroke();
|
|
||||||
}
|
|
||||||
// Lignes horizontales
|
|
||||||
for (let y = this.gridSize - this.offset; y < this.canvas.height; y += this.gridSize) {
|
|
||||||
this.ctx.beginPath();
|
|
||||||
this.ctx.moveTo(0, y);
|
|
||||||
this.ctx.lineTo(this.canvas.width, y);
|
|
||||||
this.ctx.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Appel récursif pour l'animation
|
|
||||||
requestAnimationFrame(() => this.draw(sens));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -4,13 +4,13 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>scribl.lidl_edition</title>
|
<title>scribl.lidl_edition</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script src="https://cdn.socket.io/4.8.1/socket.io.min.js"></script>
|
<script src="https://cdn.socket.io/4.8.1/socket.io.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>scribl.lidl_edition</h1>
|
<h1>scribl.lidl_edition</h1>
|
||||||
|
|
||||||
<!-- Menu principal -->
|
<!-- Main menu -->
|
||||||
<nav id="menu" aria-label="Menu principal">
|
<nav id="menu" aria-label="Menu principal">
|
||||||
<button id="accueil" aria-label="Accueil">Accueil</button>
|
<button id="accueil" aria-label="Accueil">Accueil</button>
|
||||||
<button id="explorer" aria-label="Explorer">Explorer</button>
|
<button id="explorer" aria-label="Explorer">Explorer</button>
|
||||||
@@ -30,4 +30,4 @@
|
|||||||
|
|
||||||
</html></script>
|
</html></script>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -33,14 +33,14 @@ export class LoginWindow extends fenetre {
|
|||||||
this.applyStyles();
|
this.applyStyles();
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
|
|
||||||
// **** AJOUT FONCTION GITHUB ****
|
// **** ADDED GITHUB FUNCTION ****
|
||||||
// Dans constructor() de LoginWindow
|
// In constructor() of LoginWindow
|
||||||
this.githubBtn = document.createElement("button");
|
this.githubBtn = document.createElement("button");
|
||||||
this.githubBtn.innerText = "Se connecter avec GitHub";
|
this.githubBtn.innerText = "Se connecter avec GitHub";
|
||||||
this.githubBtn.style.backgroundColor = "#24292e";
|
this.githubBtn.style.backgroundColor = "#24292e";
|
||||||
this.githubBtn.style.color = "white";
|
this.githubBtn.style.color = "white";
|
||||||
this.githubBtn.onclick = () => {
|
this.githubBtn.onclick = () => {
|
||||||
// Ouvre le OAuth GitHub dans une popup et reçoit le token via postMessage
|
// Open the OAUTH Github in a popup and receive the token from postMessage
|
||||||
const w = 600;
|
const w = 600;
|
||||||
const h = 700;
|
const h = 700;
|
||||||
const left = (screen.width - w) / 2;
|
const left = (screen.width - w) / 2;
|
||||||
@@ -59,7 +59,7 @@ export class LoginWindow extends fenetre {
|
|||||||
};
|
};
|
||||||
this.body.appendChild(this.githubBtn);
|
this.body.appendChild(this.githubBtn);
|
||||||
|
|
||||||
this.checkIfAlreadyLoggedIn(); //verifie si l'utilisateur est connecté au démarrage
|
this.checkIfAlreadyLoggedIn(); // Verify if the user is connected on startup
|
||||||
}
|
}
|
||||||
|
|
||||||
applyStyles() {
|
applyStyles() {
|
||||||
@@ -93,13 +93,13 @@ export class LoginWindow extends fenetre {
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
console.log("connexion ok", data);
|
console.log("connexion ok", data);
|
||||||
// *** STOCKAGE DU TOKEN ***
|
// *** TOKEN STORAGE ***
|
||||||
if (data.token) {
|
if (data.token) {
|
||||||
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";
|
||||||
|
|
||||||
// masquer la fenêtre après 1.5s
|
// mask the window after 1.5s
|
||||||
setTimeout(() => this.hide(), 1500);
|
setTimeout(() => this.hide(), 1500);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ export class LoginWindow extends fenetre {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Afficher une erreur utilisateur plus visible
|
// Show a more visible user error
|
||||||
const errMsg = data && data.message ? data.message : "Échec de la connexion";
|
const errMsg = data && data.message ? data.message : "Échec de la connexion";
|
||||||
this.message.innerText = errMsg;
|
this.message.innerText = errMsg;
|
||||||
this.message.style.color = "#ff4d4d";
|
this.message.style.color = "#ff4d4d";
|
||||||
|
|||||||
Reference in New Issue
Block a user