Compare commits

..

1 Commits

59 changed files with 1635 additions and 1043 deletions

153
README.md Normal file
View File

@ -0,0 +1,153 @@
# ft_transcendence
## Description
ft_transcendence est le projet final du tronc commun de l'École 42. Il s'agit de créer une application web complète permettant de jouer au Pong en ligne avec des fonctionnalités modernes comme les tournois, le chat en temps réel, et l'authentification.
## Fonctionnalités principales
- 🎮 **Jeu Pong** en temps réel multijoueur
- 🏆 **Système de tournois** avec brackets
- 💬 **Chat en temps réel** avec channels
- 👤 **Profils utilisateurs** et amis
- 🔐 **Authentification 2FA** (Google, 42)
- 📊 **Statistiques** et historique des parties
- 🎨 **Interface moderne** et responsive
- 🔒 **Sécurité** web avancée
## Technologies utilisées
### Frontend
- **Framework** : React/Vue.js/Angular ou Vanilla JS
- **Styling** : CSS3, Bootstrap/Tailwind
- **WebSockets** : Pour le temps réel
### Backend
- **Framework** : Django/Flask/Node.js
- **Base de données** : PostgreSQL
- **API** : REST ou GraphQL
- **Authentication** : OAuth2, JWT
### DevOps
- **Containerisation** : Docker & Docker Compose
- **Reverse Proxy** : Nginx
- **SSL/TLS** : Certificats HTTPS
- **Base de données** : PostgreSQL en conteneur
## Architecture
```
ft_transcendence/
├── docker-compose.yml # Orchestration des services
├── frontend/ # Application client
│ ├── src/
│ ├── public/
│ └── Dockerfile
├── backend/ # API serveur
│ ├── api/
│ ├── models/
│ ├── services/
│ └── Dockerfile
├── database/ # Configuration PostgreSQL
├── nginx/ # Configuration reverse proxy
└── ssl/ # Certificats SSL
```
## Modules bonus
- **Module Web** : Framework moderne (React/Vue/Angular)
- **Module User Management** : Authentification avancée
- **Module Gaming** : Variantes de Pong ou autres jeux
- **Module AI-Algo** : IA pour jouer contre
- **Module Cybersecurity** : Sécurité renforcée
- **Module DevOps** : Infrastructure monitoring
- **Module Graphics** : Interface 3D avancée
## Installation et déploiement
### Prérequis
- Docker et Docker Compose
- Domaine avec certificat SSL
- Clés API (42, Google) pour OAuth
### Lancement
```bash
git clone <repository-url>
cd ft_transcendence
docker-compose up --build
```
### Configuration
1. Configurer les variables d'environnement
2. Générer les certificats SSL
3. Configurer OAuth applications
4. Initialiser la base de données
## Gameplay
### Pong multijoueur
- Contrôles clavier fluides
- Synchronisation en temps réel
- Système de score et timer
- Reconnexion automatique
### Tournois
- Création de tournois public/privé
- Système d'élimination
- Classements et récompenses
- Notifications en temps réel
## Sécurité implémentée
- 🔒 **HTTPS** obligatoire
- 🛡️ **Protection CSRF/XSS**
- 🔑 **Authentification 2FA**
- 💾 **Hashage sécurisé** des mots de passe
- 🚫 **Protection injection SQL**
- 🔐 **Validation** côté serveur
- 🍪 **Gestion sécurisée** des sessions
## Fonctionnalités sociales
- **Profils utilisateurs** personnalisables
- **Système d'amis** avec invitations
- **Chat global** et channels privés
- **Statut en ligne** des utilisateurs
- **Historique des parties**
- **Achievements** et badges
## Performance et scalabilité
- **WebSockets** pour le temps réel
- **Cache Redis** pour les sessions
- **Optimisation** des requêtes DB
- **Load balancing** pour la montée en charge
- **Monitoring** des performances
## Tests et qualité
- **Tests unitaires** backend
- **Tests d'intégration** API
- **Tests end-to-end** Selenium
- **Sécurité** avec OWASP ZAP
- **Performance** avec JMeter
## Compétences développées
- Architecture d'applications web modernes
- Développement full-stack
- Sécurité web avancée
- DevOps et containerisation
- Gestion de projet complexe
- Programmation temps réel
- UI/UX design
## Contraintes 42
- **Single Page Application** obligatoire
- **3 frameworks/langages** différents minimum
- **Docker** pour tous les services
- **Sécurité** de niveau production
- **Code maintenable** et documenté
## Auteur
Alexandre Pommier (apommier) - École 42
## Équipe (si applicable)
- Développeur Frontend
- Développeur Backend
- DevOps Engineer
- Security Specialist
## Licence
Projet académique - École 42

View File

@ -3,14 +3,14 @@ server {
# listen 80 ssl; # listen 80 ssl;
# listen 443 ssl; # listen 443 ssl;
# listen ${NGINX_PORT}; # listen ${NGINX_PORT};
listen 80; listen 8080;
location /{ location /{
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://react_app:8080; proxy_pass http://react_app:8001;
} }
location /api { location /api {
@ -20,4 +20,18 @@ server {
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://api:3000/api; proxy_pass http://api:3000/api;
} }
location /socket {
# Forward requests to socket server running on port 4001
if ($request_uri ~ ^/socket/4001) {
proxy_pass http://chat:4001;
break;
}
# Forward requests to socket server running on port 4000
if ($request_uri ~ ^/socket/4000) {
proxy_pass http://pong:4000;
break;
}
}
} }

View File

@ -6,7 +6,7 @@
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */ /* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/17 01:00:00 by apommier #+# #+# */ /* Created: 2023/06/17 01:00:00 by apommier #+# #+# */
/* Updated: 2023/06/18 13:30:50 by apommier ### ########.fr */ /* Updated: 2023/06/21 01:19:01 by apommier ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@ -27,6 +27,7 @@ import { generateOTP } from './users/2fa';
import { VerifyOTP } from './users/2fa'; import { VerifyOTP } from './users/2fa';
import { ValidateOTP } from './users/2fa'; import { ValidateOTP } from './users/2fa';
import { privateDecrypt } from 'crypto'; import { privateDecrypt } from 'crypto';
import { formatWithOptions } from 'util';
//2fa //2fa
@ -128,6 +129,8 @@ export class AppController {
async newBlocked(@Request() req, @Body() data: any) { async newBlocked(@Request() req, @Body() data: any) {
// return await this.userService.getFriends(req.user.username); // return await this.userService.getFriends(req.user.username);
console.log(`user= ${req.user.username}`) console.log(`user= ${req.user.username}`)
if (data.username === req.user.username)
return (0);
const user = await this.userService.findOne(req.user.username) const user = await this.userService.findOne(req.user.username)
return await this.userService.addBlocked(user, data.username); return await this.userService.addBlocked(user, data.username);
} }
@ -136,6 +139,8 @@ export class AppController {
@Post('/invite') @Post('/invite')
async newInvite(@Request() req, @Body() data: any) { async newInvite(@Request() req, @Body() data: any) {
console.log(`user= ${req.user.username}`) console.log(`user= ${req.user.username}`)
if (data.username === req.user.username)
return (0);
const user = await this.userService.findOne(data.username) const user = await this.userService.findOne(data.username)
if (!user) if (!user)
return (0); return (0);
@ -215,7 +220,10 @@ export class AppController {
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@Post('/win') @Post('/win')
async addWin(@Request() req, @Body() data: any) { async addWin(@Request() req, @Body() data: any) {
console.log("WIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIN: ", req.user.username)
const user = await this.userService.findOne(req.user.username); const user = await this.userService.findOne(req.user.username);
console.log("User", user)
// const user2 = await this.userService.findOne(data.opName);
user.win++; user.win++;
const Esp = 1 / (1 + Math.pow(10, (data.opRank - user.rank) / this.scaleFactor)) const Esp = 1 / (1 + Math.pow(10, (data.opRank - user.rank) / this.scaleFactor))
const newRank = user.rank + this.kFactor * (1 - Esp); const newRank = user.rank + this.kFactor * (1 - Esp);
@ -229,14 +237,16 @@ export class AppController {
newMatch.opScore = data.opScore; newMatch.opScore = data.opScore;
newMatch.opponent = data.opName; newMatch.opponent = data.opName;
newMatch.parent = user; newMatch.parent = user;
console.log(`newMatch WIIIN = ${newMatch}`);
await this.userService.saveChild(user, newMatch); await this.userService.saveChild(user, newMatch);
} }
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@Post('/loss') @Post('/loss')
async addLoss(@Request() req, @Body() data: any) { async addLoss(@Request() req, @Body() data: any) {
console.log("LOOOOOOOOOOOOOOOSE: ", req.user.username)
const user = await this.userService.findOne(req.user.username); const user = await this.userService.findOne(req.user.username);
console.log("User", user)
user.loss++; user.loss++;
const Esp = 1 / (1 + Math.pow(10, (data.opRank - user.rank) / this.scaleFactor)) const Esp = 1 / (1 + Math.pow(10, (data.opRank - user.rank) / this.scaleFactor))
@ -251,7 +261,7 @@ export class AppController {
newMatch.opScore = data.opScore; newMatch.opScore = data.opScore;
newMatch.opponent = data.opName; newMatch.opponent = data.opName;
newMatch.parent = user; newMatch.parent = user;
console.log(`newMatch Loose= ${newMatch}`);
await this.userService.saveChild(user, newMatch); await this.userService.saveChild(user, newMatch);
} }
@ -270,6 +280,49 @@ export class AppController {
return await this.userService.getRanking(); return await this.userService.getRanking();
} }
@UseGuards(JwtAuthGuard)
@Post('/partyInvite')
async partyInvite(@Request() req, @Body() data: any)
{
//find data.username and add invite to list
console.log("data post priv invite=", data);
const user = await this.userService.findOne(data.username);
user.partyInvite = user.partyInvite || [];
user.partyInvite.push({ username: req.user.username, gameId: data.gameId });
console.log("usr === ", user)
await this.userService.save(user);
// user.partyInvite.push(data);
console.log("invite === ", user.partyInvite)
}
@UseGuards(JwtAuthGuard)
@Get('/partyInvite')
async getPartyInvite(@Request() req, @Body() data: any)
{
//find data.username and add invite to list
const user = await this.userService.findOne(req.user.username);
user.partyInvite = user.partyInvite || [];
// this.userService.save(user);
// user.partyInvite.push(data);
// console.log("data invite === ", data.username)
return user.partyInvite;
}
@UseGuards(JwtAuthGuard)
@Post('/deleteInvite')
async deleteInvite(@Request() req, @Body() data: any)
{
console.log("delete invite user= ", data.username)
const user = await this.userService.findOne(req.user.username);
// user.partyInvite = user.partyInvite.filter(item => Object.values(item)[1] !== req.user.username);
console.log("user.partyInvite before", user.partyInvite)
user.partyInvite = user.partyInvite.filter((item) => Object.values(item)[1] !== data.username);
console.log("user.partyInvite after", user.partyInvite)
this.userService.save(user);
}
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@Post('/history') @Post('/history')
async getHistory(@Body() data: any) async getHistory(@Body() data: any)
@ -419,6 +472,8 @@ export class AppController {
if (!amIhere) if (!amIhere)
data.members.push(req.user.username) data.members.push(req.user.username)
// let test = {id: 2, members: "cc"}; // let test = {id: 2, members: "cc"};
data.admin = []
data.admin.push(req.user.username)
data.owner = req.user.username data.owner = req.user.username
data.group = true; data.group = true;
return await this.chatService.createConv(data); return await this.chatService.createConv(data);
@ -439,6 +494,13 @@ export class AppController {
return await this.chatService.findAll(); return await this.chatService.findAll();
} }
@UseGuards(JwtAuthGuard)
@Post('/convId')
async getConvById(@Body() data: any) {
return await this.chatService.findConv(data.convId);
}
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@Post('/message') @Post('/message')
async postMessage(@Request() req, @Body() data: any) { async postMessage(@Request() req, @Body() data: any) {
@ -479,12 +541,6 @@ export class AppController {
// res.json(messages); // res.json(messages);
} }
@UseGuards(JwtAuthGuard)
@Post('/ban')
async banUser(@Body() data: any) {
return await this.chatService.banUser(data.convId, data.username)
}
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@Post('/name') @Post('/name')
async setName(@Body() data: any) { async setName(@Body() data: any) {
@ -493,12 +549,6 @@ export class AppController {
return await this.chatService.setName(data.convId, data.name) return await this.chatService.setName(data.convId, data.name)
} }
@UseGuards(JwtAuthGuard)
@Post('/invite')
async inviteUser(@Body() data: any) {
return await this.chatService.inviteUser(data.convId, data.username)
}
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@Post('/password') @Post('/password')
async setPassword(@Body() data: any) { async setPassword(@Body() data: any) {
@ -511,12 +561,36 @@ export class AppController {
return await this.chatService.verifyPassword(data.convId, data.password) return await this.chatService.verifyPassword(data.convId, data.password)
} }
@UseGuards(JwtAuthGuard)
@Post('/invite')
async inviteUser(@Body() data: any) {
return await this.chatService.inviteUser(data.convId, data.username)
}
@UseGuards(JwtAuthGuard)
@Post('/ban')
async banUser(@Body() data: any) {
if (!data.username)
return ;
return await this.chatService.banUser(data.convId, data.username)
}
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@Post('/admin') @Post('/admin')
async setAdmin(@Body() data: any) { async setAdmin(@Body() data: any) {
if (!data.username)
return ;
return await this.chatService.setAdmin(data.convId, data.username) return await this.chatService.setAdmin(data.convId, data.username)
} }
@UseGuards(JwtAuthGuard)
@Post('/mute')
async muteUser(@Body() data: any) {
if (!data.username)
return ;
return await this.chatService.muteUser(data.convId, data.username)
}
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@Post('/isAdmin') @Post('/isAdmin')
async isAdmin(@Request() req, @Body() data: any) { async isAdmin(@Request() req, @Body() data: any) {
@ -524,11 +598,6 @@ export class AppController {
return await this.chatService.isAdmin(data.convId, req.user.username) return await this.chatService.isAdmin(data.convId, req.user.username)
} }
@UseGuards(JwtAuthGuard)
@Post('/mute')
async muteUser(@Body() data: any) {
return await this.chatService.muteUser(data.convId, data.username)
}
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@Post('/private') @Post('/private')

View File

@ -58,6 +58,7 @@ export class loginClass {
console.log(`no user, creating one`); console.log(`no user, creating one`);
user = { user = {
id: null, id: null,
partyInvite: null,
password: null, password: null,
username: userName, username: userName,
nickname: userName, nickname: userName,

View File

@ -6,7 +6,7 @@
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */ /* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/17 01:00:25 by apommier #+# #+# */ /* Created: 2023/06/17 01:00:25 by apommier #+# #+# */
/* Updated: 2023/06/18 13:14:51 by apommier ### ########.fr */ /* Updated: 2023/06/20 16:47:02 by apommier ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@ -85,6 +85,7 @@ async banUser(convId: number, username: string) {
conv.banned = conv.banned || []; conv.banned = conv.banned || [];
if (conv.banned.find(item => item === username)) if (conv.banned.find(item => item === username))
return (1); return (1);
conv.members = conv.members.filter((item) => item !== username);
conv.banned.push(username); conv.banned.push(username);
this.save(conv); this.save(conv);
} }

View File

@ -6,7 +6,7 @@
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */ /* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/04/09 14:53:49 by apommier #+# #+# */ /* Created: 2023/04/09 14:53:49 by apommier #+# #+# */
/* Updated: 2023/06/12 14:51:44 by apommier ### ########.fr */ /* Updated: 2023/06/22 20:42:32 by apommier ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@ -17,7 +17,7 @@ export const getTypeOrmConfig = (): TypeOrmModuleOptions => ({
host: process.env.POSTGRES_HOST || 'postgresql', host: process.env.POSTGRES_HOST || 'postgresql',
port: parseInt(process.env.POSTGRES_PORT, 10) || 5432, port: parseInt(process.env.POSTGRES_PORT, 10) || 5432,
username: process.env.POSTGRES_USER || 'postgres', username: process.env.POSTGRES_USER || 'postgres',
password: process.env.POSTGRES_PASSWORD || 'pass', password: process.env.POSTGRES_PASSWORD || 'postgres',
database: process.env.POSTGRES_DATABASE || 'postgres', database: process.env.POSTGRES_DATABASE || 'postgres',
entities: ["dist/**/*.entity.js"], entities: ["dist/**/*.entity.js"],
// entities: [join(__dirname, '**', '*.entity.{ts,js}')] // entities: [join(__dirname, '**', '*.entity.{ts,js}')]

View File

@ -6,7 +6,7 @@
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */ /* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/17 01:00:20 by apommier #+# #+# */ /* Created: 2023/06/17 01:00:20 by apommier #+# #+# */
/* Updated: 2023/06/17 01:31:29 by apommier ### ########.fr */ /* Updated: 2023/06/23 15:18:19 by apommier ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */

View File

@ -65,6 +65,12 @@ export class User {
@Column('text', { array: true, nullable: true }) @Column('text', { array: true, nullable: true })
friendRequest: string[]; friendRequest: string[];
@Column({ type: 'jsonb', nullable: true })
partyInvite: Record<string, string>[];
// @Column('text', { array: true, nullable: true })
// friendRequest: string[];
@Column('text', { array: true, nullable: true }) @Column('text', { array: true, nullable: true })
friends: string[]; friends: string[];

View File

@ -6,7 +6,7 @@
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */ /* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/17 01:00:07 by apommier #+# #+# */ /* Created: 2023/06/17 01:00:07 by apommier #+# #+# */
/* Updated: 2023/06/17 01:00:08 by apommier ### ########.fr */ /* Updated: 2023/06/21 01:31:44 by apommier ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@ -47,6 +47,7 @@ export class UsersService {
async saveChild(user: User, match: MatchLog): Promise<User> { async saveChild(user: User, match: MatchLog): Promise<User> {
// user.match = savedChild; // user.match = savedChild;
user.children.push(match)
await this.matchRepository.save(match); await this.matchRepository.save(match);
return await this.userRepository.save(user); return await this.userRepository.save(user);
} }

View File

@ -8,16 +8,20 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
@WebSocketServer() @WebSocketServer()
server: Server; server: Server;
private clients: Record<string, Socket> = {}; private clients: Record<string, Socket> = {};
// private clientsNames: Record<string, Socket[]> = {}; // private clientsNames: Record<string, Socket[]> = {};
private clientsNames: Map<string, string[]> = new Map(); private clientsNames: Map<string, string[]> = new Map();
// private games: Map<string, Socket[]> = new Map();// Chat en cours, identifiées par un ID // private games: Map<string, Socket[]> = new Map();// Chat en cours, identifiées par un ID
afterInit(server: Server) afterInit(server: Server)
{ {
console.log('ChatGateway initialized'); console.log('ChatGateway initialized');
} }
handleConnection(client: Socket, ...args: any[]) handleConnection(client: Socket, ...args: any[])
{ {
console.log(`Client connected: ${client.id}`); console.log(`Client connected: ${client.id}`);
@ -92,93 +96,82 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
console.log("create") console.log("create")
this.clientsNames.set(payload.username, [client.id]); // Create a new array with the new client as the value this.clientsNames.set(payload.username, [client.id]); // Create a new array with the new client as the value
} }
// let clientLenght = Object.keys(this.clientsNames[payload.username]).length
// const clientArray = this.clientsNames.get(payload.username)
// let clientLenght = clientArray.
// console.log(`lenght= ${clientLenght}`)
// this.clientsNames[payload.username][clientLenght] = this.clients[client.id];
// console.log(`clients: ${Object.keys(this.clientsNames).length}`)
// this.clients[clientId] = client;
//add a new client with socket and name for key
//payload.username
} }
// @SubscribeMessage('socket.io')
// socketConnect(client: any, payload: any): void {
// console.log("/socket.io")
// }
// @SubscribeMessage('sendMessage') @SubscribeMessage('ban')
// handleMessage(user: any, payload: any): void { banUser(client: any, payload: any): void {
// console.log(`message recceveid: ${payload}`) if (!this.clientsNames.has(payload.username))
// console.log(`message recceveid: ${payload.sender}`) {
// console.log(`message recceveid: ${payload.convId}`) console.log("No user found.");
// console.log(`message recceveid: ${payload.members}`) return;
}
const bannedClients = this.clientsNames.get(payload.username);
bannedClients.forEach(client => {
console.log("Banning client:", client);
// Perform ban operation on each client, e.g., emit a 'ban' event
console.log("clietn socket=", this.clients[client])
this.clients[client].emit('ban', payload);
});
// console.log("/ban")
// console.log("in ban username=", payload.username)
// if (!this.clientsNames[payload.username])
// {
// console.log("no user ??")
// return ;
// }
// this.clientsNames[payload.username].forEach()
// console.log("client=", this.clientsNames)
// this.clients[payload.username].emit('ban', payload)
}
// console.log(`client id: ${user.id}`) @SubscribeMessage('mute')
muteUser(client: any, payload: any): void {
if (!this.clientsNames.has(payload.username))
{
console.log("No user found.");
return;
}
const mutedClients = this.clientsNames.get(payload.username);
mutedClients.forEach(client => {
console.log("Banning client:", client);
// Perform ban operation on each client, e.g., emit a 'ban' event
console.log("clietn socket=", this.clients[client])
this.clients[client].emit('mute', payload);
});
console.log("/mute")
}
// this.clientsNames.forEach((clientArray, clientName) => {
// console.log(`Clients with name ${clientName}:`);
// // clientArray.forEach((client) => {
// this.clientsNames[clientName]
// // .forEach(client => {
// // // if(client.id != user.id)
// // // {
// // console.log("send to someone")
// // console.log(client); // Perform actions on each client
// // // clients.emit('message', payload)
// // client.emit('message')
// // // }
// // });
// // .forEach((client) => {
// // if(client.id != user.id)
// // {
// // console.log("send to someone")
// // console.log(client); // Perform actions on each client
// // // clients.emit('message', payload)
// // client.emit('message')
// // }
// });
// };
@SubscribeMessage('sendMessage') @SubscribeMessage('sendMessage')
handleMessage(client: Socket, payload: any): void { handleMessage(client: Socket, payload: any): void {
// console.log(`message received: ${payload}`); // console.log(`message received: ${payload}`);
console.log(`message sender: ${payload.sender}`); // console.log(`message sender: ${payload.sender}`);
console.log(`client id: ${client.id}`); // console.log(`client id: ${client.id}`);
console.log(`conversation ID: ${payload.convId}`); // console.log(`conversation ID: ${payload.convId}`);
console.log(`members: ${payload.members}`); // console.log(`members: ${payload.members}`);
this.clientsNames.forEach((clientArray, clientName) => this.clientsNames.forEach((clientArray, clientName) =>
{ {
console.log(` 5Clients with name ${clientName}:`); // console.log(` 5Clients with name ${clientName}:`);
if (payload.members.includes(clientName)) if (payload.members.includes(clientName))
{ {
clientArray.forEach((targetClient, index) => clientArray.forEach((targetClient, index) =>
{ {
console.log(`client id: ${client.id}`); // console.log(`client id: ${client.id}`);
console.log(`target: ${targetClient}`); // console.log(`target: ${targetClient}`);
console.log(`target id: ${targetClient}`); // console.log(`target id: ${targetClient}`);
if (targetClient && targetClient !== client.id) if (targetClient && targetClient !== client.id)
{ {
console.log("Sending to someone"); // console.log("Sending to someone");
console.log(`index= ${index}`); // console.log(`index= ${index}`);
console.log(`target: ${targetClient}`); // Perform actions on each target client // console.log(`target: ${targetClient}`); // Perform actions on each target client
// targetClient.emit('message')
// this.clientsNames[clientName].emit('message')
// this.clientsNames["apommier"].emit('message')
this.clients[targetClient].emit('message', payload) this.clients[targetClient].emit('message', payload)
// console.log(test)
// console.log(test)
// this.clientsNames[clientName][index].emit('message');
// const socket = this.server.sockets.sockets.get(targetClient.id);
// if (socket) {
// socket.emit('message', payload);
// } else {
// console.log(`Socket with ID ${client.id} not found.`);
// }
// targetClient.emit('message', payload);
// targetClient.emit('message', payload);
} }
else { else {
console.log("not sending"); console.log("not sending");
@ -193,41 +186,3 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
// for (let key in this.clientsNames) {
// if (payload.members.includes(key)) {
// console.log("Key exists in the array");
// // if (key !== payload.sender)
// // {
// for (let key2 in this.clientsNames[key])
// {
// if (client.id !== this.clientsNames[key][key2])
// {
// console.log("send to someone")
// this.clientsNames[key][key2].emit('message', payload)
// }
// }
// // }
// //if member socket different from mine
// //send
// } else {
// console.log("Key does not exist in the array");
// }
//if key is in member
// let socket = this.clients[key];
// console.log("Clé:", key);
// console.log("Socket:", socket);
// }
// payload.convId // conv user instead ?
//find user to send message to
//const res = {
//convId: payload.convId
//sender: payload.sender
// }
//while (user of conv)//how to get conv user
// if (user connected)
//send res to user
// }

View File

@ -1,7 +1,26 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* pong.gateway.ts :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/19 15:18:38 by apommier #+# #+# */
/* Updated: 2023/06/23 15:19:12 by apommier ### ########.fr */
/* */
/* ************************************************************************** */
import { SubscribeMessage, WebSocketGateway, OnGatewayInit, WebSocketServer, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets'; import { SubscribeMessage, WebSocketGateway, OnGatewayInit, WebSocketServer, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets';
import { Server, Socket } from 'socket.io'; import { Server, Socket } from 'socket.io';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
//========================================================================================================
//========================================================================================================
// Connection/Disconnection
//========================================================================================================
//========================================================================================================
@WebSocketGateway({ cors: true }) @WebSocketGateway({ cors: true })
export class PongGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect { export class PongGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer() server: Server; @WebSocketServer() server: Server;
@ -31,50 +50,82 @@ export class PongGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
handleDisconnect(client: Socket) handleDisconnect(client: Socket)
{ {
console.log(`Client disconnected: ${client.id}`); console.log(`Normal disconnected: ${client.id}`);
// this.waitingClients.delete(client);
this.waitingClients.forEach((waitingClient) => { this.waitingClients.forEach((item) => {
if (waitingClient.client === client) { if (item.client === client)
this.waitingClients.delete(waitingClient); this.waitingClients.delete(item);
}}) });
// Delete the socket from the 'games' map
this.games.forEach((sockets, gameId) => {
const index = sockets.indexOf(client);
if (index !== -1)
{
if (index === 0)
{
console.log("emit boy1")
sockets[1].emit("pong:win")
// sockets[0].emit("/win")
}
else
{
console.log("emit boy2")
sockets[0].emit("pong:win")
// sockets[1].emit("/win")
}
this.games.delete(gameId);
delete this.clients[client.id]; delete this.clients[client.id];
}
})
console.log(`Total connected clients: ${Object.keys(this.clients).length}`); console.log(`Total connected clients: ${Object.keys(this.clients).length}`);
} }
// @SubscribeMessage('pong:matchmaking')
// addMatchmaking(client: Socket, payload: any): void
// {
// console.log("matchmaking");
// console.log(payload);
// // this.waitingClients.add(client);
// this.waitingClients.add(client);
// console.log("Adding client to waiting list...");
// if (this.waitingClients.size >= 2) {
// console.log("Creating new game...");
// const players = Array.from(this.waitingClients).slice(0, 2);
// players.forEach((player) => {
// this.waitingClients.delete(player);
// });
// const gameId = uuidv4();
// this.games.set(gameId, players);
// players.forEach((player) => {
// player.join(gameId);
// console.log(`Player ${player.id} joined game ${gameId}`);
// });
// players.forEach((player) => {
// // const playersIds = game.map(socket => socket.id);
// player.emit('pong:gameId', gameId);
// });
// }
// // console.log(`from: ${client.id}`);
// }
@SubscribeMessage('pong:invite')
createPrivateGame(client: Socket, payload: any): void {
//after invite accepted ?
//set the two user in a game ?
@SubscribeMessage('pong:disconnect')
disconnectClient(client: Socket, payload: any): void {
console.log("disconnect forced client= ", client.id)
for (const key in this.clients) {
if (this.clients.hasOwnProperty(key) && this.clients[key] === client)
delete this.clients[key];
}
// Delete the socket from the 'waitingClients' set
this.waitingClients.forEach((item) => {
if (item.client === client)
this.waitingClients.delete(item);
});
// Delete the socket from the 'games' map
this.games.forEach((sockets, gameId) => {
const index = sockets.indexOf(client);
if (index !== -1)
{
if (index === 0)
{
console.log("emit boy1")
sockets[1].emit("pong:win")
// sockets[0].emit("/win")
}
else
{
console.log("emit boy2")
sockets[0].emit("pong:win")
// sockets[1].emit("/win")
}
this.games.delete(gameId);
delete this.clients[client.id];
} }
})
}
//========================================================================================================
//========================================================================================================
// Matchmaking
//========================================================================================================
//========================================================================================================
@SubscribeMessage('pong:matchmaking') @SubscribeMessage('pong:matchmaking')
addMatchmaking(client: Socket, payload: any): void { addMatchmaking(client: Socket, payload: any): void {
@ -91,6 +142,7 @@ addMatchmaking(client: Socket, payload: any): void {
waitingClient.option === payload.option && waitingClient.client !== client waitingClient.option === payload.option && waitingClient.client !== client
); );
if (matchingClients.length > 0) { if (matchingClients.length > 0) {
console.log("Creating new game..."); console.log("Creating new game...");
const players = [matchingClients[0].client, client]; // Add the current client to the players array const players = [matchingClients[0].client, client]; // Add the current client to the players array
@ -117,43 +169,76 @@ addMatchmaking(client: Socket, payload: any): void {
player.emit('pong:gameId', gameId); player.emit('pong:gameId', gameId);
}); });
} }
// console.log(`from: ${client.id}`); // console.log(`from: ${client.id}`);
} }
// @SubscribeMessage('pong:message')
// handleMessage(client: Socket, payload: any): void
// {
// console.log(`from: ${client.id}`);
// console.log(payload);
// const game = this.games.get(payload.gameId);
// const playersIds = game.map(socket => socket.id);
// // const players = Object.keys(game);
// // if (Object.keys(this.clients).length === 2) //========================================================================================================
// // { //========================================================================================================
// // const clientIds = Object.keys(this.clients); // Private Match
// // console.log(`id of 0= ${clientIds[0]}`); //========================================================================================================
//========================================================================================================
// @SubscribeMessage('pong:invite')
// createPrivateGame(client: Socket, payload: any): void {
// //after invite accepted ?
// //set the two user in a game ?
// }
@SubscribeMessage('pong:joinParty')
joinPrivateParty(client: Socket, payload: any): void {
console.log(" join PrivateParty")
const game = this.games.get(payload.gameId);
if (game)
{
game.push(client);
const playersIds = game.map(socket => socket.id);
this.clients[playersIds[0]].emit('pong:gameId', payload.gameId);
this.clients[playersIds[1]].emit('pong:gameId', payload.gameId);
}
else
{
console.log("emit else")
client.emit("pong:win")
}
// console.log("no game ???")
}
@SubscribeMessage('pong:privateParty')
addPrivateParty(client: Socket, payload: any): void {
console.log("addPrivateParty")
const gameId = uuidv4();
const players = [client];
this.games.set(gameId, players);
console.log("game created private")
client.emit('pong:privateId', gameId);
//create game
//emit private gameId to canvas (don't launch canvas)
}
//========================================================================================================
//========================================================================================================
// In Game
//========================================================================================================
//========================================================================================================
// // payload.ballX
// // payload.ballY
// // payload.
// if (clientIds[0] === payload.id)
// {
// // console.log("client 0 true");
// if (payload.ballX <= payload.width / 2)
// this.clients[clientIds[1]].emit('pong:info', payload);
// }
// else if (clientIds[1] === payload.id)
// {
// if (payload.ballX < payload.width / 2)
// this.clients[clientIds[0]].emit('pong:info', payload);
// // console.log("client 0 true");
// }
// // }
// console.log("END OF HANDLE");
// }
@SubscribeMessage('pong:power') @SubscribeMessage('pong:power')
sendPower(client: Socket, payload: any): void sendPower(client: Socket, payload: any): void
@ -191,29 +276,6 @@ addMatchmaking(client: Socket, payload: any): void {
console.log("END OF HANDLE"); console.log("END OF HANDLE");
} }
// @SubscribeMessage('pong:forced')
// forcedMessage(client: Socket, payload: any): void
// {
// console.log(`from: ${client.id}`);
// console.log(payload);
// if (Object.keys(this.clients).length === 2)
// {
// const clientIds = Object.keys(this.clients);
// console.log(`id of 0= ${clientIds[0]}`);
// if (clientIds[0] === payload.id)
// {
// this.clients[clientIds[1]].emit('pong:info', payload);
// }
// else if (clientIds[1] === payload.id)
// {
// this.clients[clientIds[0]].emit('pong:info', payload);
// }
// }
// console.log("END OF HANDLE");
// }
@SubscribeMessage('pong:forced') @SubscribeMessage('pong:forced')
forcedMessage(client: Socket, payload: any): void forcedMessage(client: Socket, payload: any): void
{ {
@ -236,29 +298,6 @@ addMatchmaking(client: Socket, payload: any): void {
console.log("END OF HANDLE"); console.log("END OF HANDLE");
} }
// @SubscribeMessage('pong:paddle')
// handlePaddle(client: Socket, payload: any): void
// {
// console.log(`from: ${client.id}`);
// console.log(payload);
// if (Object.keys(this.clients).length === 2)
// {
// const clientIds = Object.keys(this.clients);
// console.log(`id of 0= ${clientIds[0]}`);
// if (clientIds[0] === payload.id)
// {
// this.clients[clientIds[1]].emit('pong:paddle', payload);
// }
// else if (clientIds[1] === payload.id)
// {
// this.clients[clientIds[0]].emit('pong:paddle', payload);
// }
// }
// console.log("END OF HANDLE");
// }
@SubscribeMessage('pong:paddle') @SubscribeMessage('pong:paddle')
handlePaddle(client: Socket, payload: any): void handlePaddle(client: Socket, payload: any): void
{ {

View File

@ -1,2 +1,5 @@
REACT_APP_BASE_URL=localhost REACT_APP_BASE_URL=localhost:8080
REACT_APP_API_SECRET=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
REACT_APP_CLIENT_UID=u-s4t2ud-6d29dfa49ba7146577ffd8bf595ae8d9e5aaa3e0a9615df18777171ebf836a41
# REACT_APP_BASE_URL=92.143.191.152
# REACT_APP_BASE_URL=192.168.1.19 # REACT_APP_BASE_URL=192.168.1.19

View File

@ -23,8 +23,11 @@
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"socket.io-client": "^4.6.1", "socket.io-client": "^4.6.1",
"styled-components": "^5.3.10", "styled-components": "^5.3.10",
"typescript": "^3.9.10", "typescript": "^3.2.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
},
"devDependencies": {
"@types/node": "^20.3.1"
} }
}, },
"node_modules/@adobe/css-tools": { "node_modules/@adobe/css-tools": {
@ -4535,9 +4538,9 @@
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.1.4", "version": "20.3.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.4.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz",
"integrity": "sha512-At4pvmIOki8yuwLtd7BNHl3CiWNbtclUbNtScGx4OHfBd4/oWoJC8KRCIxXwkdndzhxOsPXihrsOoydxBjlE9Q==" "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg=="
}, },
"node_modules/@types/parse-json": { "node_modules/@types/parse-json": {
"version": "4.0.0", "version": "4.0.0",
@ -20221,9 +20224,9 @@
} }
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "3.9.10", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.1.tgz",
"integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", "integrity": "sha512-jw7P2z/h6aPT4AENXDGjcfHTu5CSqzsbZc6YlUIebTyBAq8XaKp78x7VcSh30xwSCcsu5irZkYZUSFP1MrAMbg==",
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@ -24452,9 +24455,9 @@
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
}, },
"@types/node": { "@types/node": {
"version": "20.1.4", "version": "20.3.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.4.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz",
"integrity": "sha512-At4pvmIOki8yuwLtd7BNHl3CiWNbtclUbNtScGx4OHfBd4/oWoJC8KRCIxXwkdndzhxOsPXihrsOoydxBjlE9Q==" "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg=="
}, },
"@types/parse-json": { "@types/parse-json": {
"version": "4.0.0", "version": "4.0.0",
@ -35362,9 +35365,9 @@
} }
}, },
"typescript": { "typescript": {
"version": "3.9.10", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.1.tgz",
"integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==" "integrity": "sha512-jw7P2z/h6aPT4AENXDGjcfHTu5CSqzsbZc6YlUIebTyBAq8XaKp78x7VcSh30xwSCcsu5irZkYZUSFP1MrAMbg=="
}, },
"unbox-primitive": { "unbox-primitive": {
"version": "1.0.2", "version": "1.0.2",

View File

@ -18,11 +18,11 @@
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"socket.io-client": "^4.6.1", "socket.io-client": "^4.6.1",
"styled-components": "^5.3.10", "styled-components": "^5.3.10",
"typescript": "^3.9.10", "typescript": "^3.2.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
"scripts": { "scripts": {
"start": "HOST=0.0.0.0 PORT=8080 react-scripts start", "start": "HOST=0.0.0.0 PORT=8001 react-scripts start",
"start:dev": "npm run start --watch", "start:dev": "npm run start --watch",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
@ -45,5 +45,8 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
},
"devDependencies": {
"@types/node": "^20.3.1"
} }
} }

View File

@ -2,6 +2,7 @@ import Backdrop from "../Sidebar/Backdrop.tsx"
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { AiOutlineCheckCircle } from "react-icons/ai"; import { AiOutlineCheckCircle } from "react-icons/ai";
import '../../styles/Messages.css' import '../../styles/Messages.css'
import React from "react";
const dropIn = { const dropIn = {
@ -16,14 +17,19 @@ const dropIn = {
}, },
}; };
function GreenAlert ({handleClose, text}){ interface AlertProps {
handleClose: Function,
text: string
}
function GreenAlert ({handleClose, text}: AlertProps){
return( return(
<Backdrop> <Backdrop onClick={handleClose}>
<motion.div <motion.div
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
className="greenAlert" className="greenAlert"
variant={dropIn} // variant={dropIn}
initial="hidden" initial="hidden"
animate="visible" animate="visible"
exit="exit" exit="exit"

View File

@ -2,6 +2,7 @@ import Backdrop from "../Sidebar/Backdrop.tsx"
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { BiErrorCircle } from "react-icons/bi"; import { BiErrorCircle } from "react-icons/bi";
import '../../styles/Messages.css' import '../../styles/Messages.css'
import React from "react";
const dropIn = { const dropIn = {
@ -16,13 +17,18 @@ const dropIn = {
}, },
}; };
function RedAlert ({handleClose, text}) { interface AlertProps {
handleClose: Function,
text: string
}
function RedAlert ({handleClose, text}: AlertProps) {
return( return(
<Backdrop> <Backdrop onClick={handleClose}>
<motion.div <motion.div
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
className="redAlert" className="redAlert"
variant={dropIn} // variant={dropIn}
initial="hidden" initial="hidden"
animate="visible" animate="visible"
exit="exit" exit="exit"

View File

@ -2,6 +2,9 @@ import Backdrop from "../Sidebar/Backdrop.tsx"
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { GrTrophy } from "react-icons/gr"; import { GrTrophy } from "react-icons/gr";
import '../../styles/Messages.css' import '../../styles/Messages.css'
import React from "react";
import { MdQrCodeScanner } from "react-icons/md";
import { GiCrownedSkull, GiWingedSword } from "react-icons/gi";
const dropIn = { const dropIn = {
hidden: { hidden: {
@ -15,19 +18,40 @@ const dropIn = {
}, },
}; };
function YellowAlert ({handleClose, text}) { interface AlertProps {
handleClose: Function,
text: string,
icon: number
}
function YellowAlert ({handleClose, text, icon}: AlertProps) {
return( return(
<Backdrop> <Backdrop onClick={handleClose}>
<motion.div <motion.div
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
className="yellowAlert" className="yellowAlert"
variant={dropIn} // variant={dropIn}
initial="hidden" initial="hidden"
animate="visible" animate="visible"
exit="exit" exit="exit"
> >
{icon === 0 ? (
<GrTrophy/> <GrTrophy/>
<p>{text}</p> ):("")}
{icon === 1 ? (
<MdQrCodeScanner/>
):("")}
{icon === 2 ? (
<GiCrownedSkull/>
):("")}
{icon === 3 ? (
<GiWingedSword/>
):("")}
<h5>{text}</h5>
</motion.div> </motion.div>
{setTimeout(handleClose, 3000)} {setTimeout(handleClose, 3000)}
</Backdrop> </Backdrop>

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import {Routes, Route} from 'react-router-dom'; import {Routes, Route} from 'react-router-dom';
import Home from "../pages/Home.jsx"; import Home from "../pages/Home.jsx";
import PlayButton from "../pages/PlayButton.js"; import PlayButton from "./Game/PlayButton.tsx";
import Field from "../pages/Field"; import Field from "../pages/Field";
import Login42 from "../pages/Login42.js"; import Login42 from "../pages/Login42.js";
import Messages from "../pages/Messages.jsx"; import Messages from "../pages/Messages.jsx";
@ -13,11 +13,11 @@ function AnimatedRoute () {
return ( return (
<AnimatePresence> <AnimatePresence>
<Routes location={location} key={location.pathname}> <Routes location={location} key={location.pathname}>
<Route exact path="/" element={<Home/>}/> <Route path="/" element={<Home/>}/>
<Route path="/game" element={<PlayButton />}/> <Route path="/game" element={<PlayButton />}/>
<Route exact path="/pong/play" element={<Field />}/> <Route path="/pong/play" element={<Field />}/>
<Route exact path="/login42" element={<Login42 />}/> <Route path="/login42" element={<Login42 />}/>
<Route exact path="/messages" element={<Messages />}/> <Route path="/messages" element={<Messages />}/>
</Routes> </Routes>
</AnimatePresence> </AnimatePresence>
) )

View File

@ -1,3 +1,4 @@
import React from 'react';
import '../../styles/field.css'; import '../../styles/field.css';
// import { useHistory } from 'react-router-dom'; // import { useHistory } from 'react-router-dom';
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@ -13,18 +14,18 @@ function PlayButton() {
const handleButtonClick = () => { const handleButtonClick = () => {
let path = `play?`; let path = `play?`;
const superpowerCheckbox = document.querySelector('input[value="superpower"]'); const superpowerCheckbox = document.querySelector<HTMLInputElement>('input[value="superpower"]');
if (superpowerCheckbox.checked) { if (superpowerCheckbox && superpowerCheckbox.checked) {
path += 'superpower=true&'; path += 'superpower=true&';
} }
const obstacleCheckbox = document.querySelector('input[value="obstacle"]'); const obstacleCheckbox = document.querySelector<HTMLInputElement>('input[value="obstacle"]');
if (obstacleCheckbox.checked) { if (obstacleCheckbox && obstacleCheckbox.checked) {
path += 'obstacle=true&'; path += 'obstacle=true&';
} }
const speedCheckbox = document.querySelector('input[value="speed"]'); const speedCheckbox = document.querySelector<HTMLInputElement>('input[value="speed"]');
if (speedCheckbox.checked) { if (speedCheckbox && speedCheckbox.checked) {
path += 'speed=true&'; path += 'speed=true&';
} }

View File

@ -1,25 +1,31 @@
/* ************************************************************************** */ /* ************************************************************************** */
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* Rank.jsx :+: :+: :+: */ /* Rank.tsx :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */ /* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/09 08:49:24 by apommier #+# #+# */ /* Created: 2023/06/09 08:49:24 by apommier #+# #+# */
/* Updated: 2023/06/09 08:55:22 by apommier ### ########.fr */ /* Updated: 2023/06/20 13:06:35 by apommier ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
// import React from "react" // import React from "react"
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
// import {Rank} from '../../DataBase/DataRank.js' // import {Rank} from '../../DataBase/DataRank.js'
// import DefaultPicture from '../../assets/profile.jpg' import DefaultPicture from '../../assets/profile.jpg'
import api from '../../script/axiosApi'; import api from '../../script/axiosApi.tsx';
import {Matchlog, User} from "../../../interfaces.tsx"
// import { Match } from "@testing-library/react";
function Rank({user, index}){ interface RankProps {
user: User
index: number
}
function Rank({user, index}: RankProps){
const [profilePicture, setProfilePicture] = useState(''); const [profilePicture, setProfilePicture] = useState('');
const DefaultPicture:string = '../../assets/profile.jpg';
useEffect(() => { useEffect(() => {
const fetchProfilePicture = async () => { const fetchProfilePicture = async () => {
@ -37,10 +43,12 @@ function Rank({user, index}){
fetchProfilePicture(); fetchProfilePicture();
}) })
// console.log(index);
return ( return (
<div className='rank_elements'> <div className='rank_elements'>
<div> <div >
<p>{index + 1}</p> {/* <p>{(index + 1).toString()}</p> */}
<p>{(index + 1)}</p>
<h4>{user.rank}: {user.nickname} <h4>{user.rank}: {user.nickname}
{profilePicture ? ( {profilePicture ? (
<img className="profilePic" src={`data:image/jpeg;base64,${profilePicture}`} /> <img className="profilePic" src={`data:image/jpeg;base64,${profilePicture}`} />
@ -50,7 +58,7 @@ function Rank({user, index}){
{/* <img className="profilePic" src={defaultpic}/> */} {/* <img className="profilePic" src={defaultpic}/> */}
</h4> </h4>
</div> </div>
<h4 className='content'>{user.opponent}</h4> {/* <h4 className='content'>{user.opponent}</h4> */}
</div> </div>
) )
} }

View File

@ -3,11 +3,12 @@ import React, { useState, useEffect, useRef } from "react";
import Rank from './Rank.tsx' import Rank from './Rank.tsx'
import defaultpic from '../../assets/profile.jpg' import defaultpic from '../../assets/profile.jpg'
import api from '../../script/axiosApi.tsx'; import api from '../../script/axiosApi.tsx';
import {User} from "../../../interfaces.tsx"
function Ranking(){ function Ranking(){
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [ranking, setRanking] = useState([]); const [ranking, setRanking] = useState<User[]>([]);
useEffect(()=> { useEffect(()=> {
@ -41,16 +42,7 @@ function Ranking(){
// <h1 className='title'>Ranking</h1> // <h1 className='title'>Ranking</h1>
<div className='scroll'> <div className='scroll'>
{ranking.map((user, index) => ( {ranking.map((user, index) => (
<Rank user={user} index={index}/> <Rank user={user} index={index} key={user.username}/>
// return (
// <div className='rank_elements'>
// <div>
// <p>{index + 1}</p>
// <h4>{user.rank}: {user.nickname} <img className="profilePic" src={defaultpic}/></h4>
// </div>
// <h4 className='content'>{user.opponent}</h4>
// </div>
// )
))} ))}
</div> </div>
)} )}

View File

@ -4,10 +4,14 @@ import {Link} from 'react-router-dom';
import DefaultPicture from '../assets/profile.jpg' import DefaultPicture from '../assets/profile.jpg'
import { motion, AnimatePresence } from 'framer-motion' import { motion, AnimatePresence } from 'framer-motion'
import Modal from './Sidebar/Modal.tsx'; import Modal from './Sidebar/Modal.tsx';
import YellowAlert from './Alert/YellowAlert.tsx';
import '../styles/Header.css'; import '../styles/Header.css';
import api from '../script/axiosApi.tsx'; import api from '../script/axiosApi.tsx';
import { MdQrCodeScanner } from 'react-icons/md';
import { GiWingedSword, GiCrownedSkull } from 'react-icons/gi';
function Header() { function Header() {
// const [sidebar, setSidebar] = useState(false); // const [sidebar, setSidebar] = useState(false);
// const showSidebar = () => setSidebar(!sidebar); // const showSidebar = () => setSidebar(!sidebar);
@ -15,6 +19,8 @@ function Header() {
const close = () => setModalOpen(false); const close = () => setModalOpen(false);
const open = () => setModalOpen(true); const open = () => setModalOpen(true);
const [success, setSuccess] = useState([]);
const [profilePicture, setProfilePicture] = useState(''); const [profilePicture, setProfilePicture] = useState('');
useEffect(() => { useEffect(() => {
@ -23,6 +29,8 @@ function Header() {
const user = await api.get("/profile"); const user = await api.get("/profile");
const pic = await api.post("/getPicture", {username: user.data.username}) const pic = await api.post("/getPicture", {username: user.data.username})
setProfilePicture(pic.data); setProfilePicture(pic.data);
// console.log("test ===", user.data)
setSuccess(user.data);
// console.log(`profile pic222= ${pic.data}`) // console.log(`profile pic222= ${pic.data}`)
} catch (error) { } catch (error) {
console.error('Error fetching profile picture:', error); console.error('Error fetching profile picture:', error);
@ -32,10 +40,6 @@ function Header() {
fetchProfilePicture(); fetchProfilePicture();
}, []); }, []);
// console.log(`profile pic= ${profilePicture}`)
// photo.toString('base64')
return ( return (
<div className='Header'> <div className='Header'>
<motion.div <motion.div
@ -45,6 +49,7 @@ function Header() {
</Link> </Link>
</motion.div> </motion.div>
<div className='end'> <div className='end'>
<Link to="/profile" className='menu-bars'> <Link to="/profile" className='menu-bars'>
<div> <div>
@ -62,7 +67,7 @@ function Header() {
<AnimatePresence <AnimatePresence
initial={false} initial={false}
onExitComplete={() => null}> onExitComplete={() => null}>
{modalOpen && <Modal modalOpen={modalOpen} handleclose={close}/>} {modalOpen && <Modal handleclose={close}/>}
</AnimatePresence> </AnimatePresence>
</div> </div>
); );

View File

@ -1,13 +1,14 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import io from 'socket.io-client'; import io, { Socket } from 'socket.io-client';
import '../../styles/Messages.css' import '../../styles/Messages.css'
import styled from "styled-components"; import styled from "styled-components";
import DefaultPic from '../../assets/profile.jpg' import DefaultPic from '../../assets/profile.jpg'
import api from '../../script/axiosApi.tsx' import api from '../../script/axiosApi.tsx';
import { motion , AnimatePresence} from "framer-motion"; import { motion , AnimatePresence} from "framer-motion";
import Modal from "./Modal.tsx"; import Modal from "./Modal.tsx";
import GameModal from "./GameModal.tsx";
import Message from './Message.tsx'; import Message from "./Message.tsx"
// import Input from "./Input"; // import Input from "./Input";
//react icons //react icons
@ -17,12 +18,15 @@ import { MdOutlineGroupAdd } from 'react-icons/md';
import { GrAdd } from 'react-icons/gr'; import { GrAdd } from 'react-icons/gr';
import { RiListSettingsLine } from 'react-icons/ri' import { RiListSettingsLine } from 'react-icons/ri'
import { Rank } from "../../DataBase/DataRank"; // import { Rank } from "../../DataBase/DataRank";
import GreenAlert from "../Alert/GreenAlert.tsx"; import GreenAlert from "../Alert/GreenAlert.tsx";
import RedAlert from "../Alert/RedAlert.tsx"; import RedAlert from "../Alert/RedAlert.tsx";
import YellowAlert from "../Alert/YellowAlert"; import YellowAlert from "../Alert/YellowAlert";
import ModalSetting from "./ModalSetting.tsx"; import ModalSetting from "./ModalSetting.tsx";
import PartyInvite from "./PartyInvite.tsx";
// import {User, Conv, Message} from "../../../interfaces.tsx"
import {User, Conv} from "../../../interfaces.tsx"
const TouchDiv = styled.div` const TouchDiv = styled.div`
margin-left: 10px; margin-left: 10px;
@ -70,20 +74,31 @@ const SideP = styled.p`
//======================================================================================================== //========================================================================================================
//======================================================================================================== //========================================================================================================
interface MessageProps {
id: number;
convId: number;
sender: string;
text: string;
createdAt?: Date;
}
function Chats(){ function Chats(){
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState<boolean>(true);
const [conversations, setConversation] = useState([]); const [conversations, setConversation] = useState([]);
const [user, setUser] = useState(null); const [partyInvite, setPartyInvite] = useState([]);
const [currentChat, setCurrentChat] = useState(false); // false is good? const [user, setUser] = useState<User>();
const [isAdmin, setIsAdmin] = useState(false); // false is good? const [currentChat, setCurrentChat] = useState<Conv>(); // false is good?
const [isAdmin, setIsAdmin] = useState<boolean>(false); // false is good?
// const [currentChat, setCurrentChat] = useState(false); // false is good? // const [currentChat, setCurrentChat] = useState(false); // false is good?
const [messages, setMessage] = useState([]); const [messages, setMessage] = useState<MessageProps[]>([]);
const [newMessages, setNewMessage] = useState(""); const [newMessages, setNewMessage] = useState("");
const [incomingMessage, setIncomingMessage] = useState(""); const [incomingMessage, setIncomingMessage] = useState<MessageProps>();
const socket = useRef();
// let socket: Socket;
const socket = useRef<Socket | null>(null);
// const socket = Socket<DefaultEventsMap, DefaultEventsMap> | null
// socket = useRef( useRef<SocketIOClient.Socket | null>(null));
useEffect(()=> { useEffect(()=> {
@ -91,29 +106,55 @@ function Chats(){
const getConv = async ()=>{ const getConv = async ()=>{
try{ try{
const convs = await api.get("/conv") const convs = await api.get("/conv")
const tmpInvite = await api.get("/partyInvite")
const tmpUser = await api.get("/profile") const tmpUser = await api.get("/profile")
const tmpUsers = await api.get("/users");
console.log(convs); console.log(convs);
// console.log("invite data use effect= ", tmpInvite.data);
setPartyInvite(tmpInvite.data);
setUser(tmpUser.data); setUser(tmpUser.data);
setConversation(convs.data); setConversation(convs.data);
socket.current = io('http://' + process.env.REACT_APP_BASE_URL + ':4001'); setUsers(tmpUsers.data);
console.log(`connection....`);
// console.log(`connection....`);
socket.current = io('http://' + process.env.REACT_APP_BASE_URL + ':4001', { transports: ['polling'] });
// console.log(`connection done`);
socket.current.emit('connection', {username: tmpUser.data.username}) socket.current.emit('connection', {username: tmpUser.data.username})
socket.current.on('message', (data) => { //data should be a message ? socket.current.on('message', (data) => { //data should be a message ?)
console.log(`message received data= ${data.sender}`)
console.log(`message received data= ${data.convId}`)
console.log(`message received data= ${data.sender}`)
console.log(`current chat = ${currentChat}`)
setIncomingMessage(data); setIncomingMessage(data);
}); });
socket.current.on('ban', (data) => {
// setIncomingMessage(data);
console.log("banned hehe");
window.location.reload()
});
socket.current.on('mute', (data) => {
console.log("muted hehe");
//set mute var to true and do nothing
});
setIsLoading(false) setIsLoading(false)
} }
catch(err){ catch(err){
console.log("ERRORRRRR")
console.log(err); console.log(err);
} }
}; };
getConv(); getConv();
return () => {
console.log("Cleanup");
if (socket.current)
socket.current.disconnect();
// cleanup(); // Call the cleanup function to stop the ongoing process or perform necessary cleanup tasks
// cleanup();
};
}, []) }, [])
useEffect(()=> { useEffect(()=> {
@ -133,9 +174,12 @@ function Chats(){
} }
} }
// console.log(`result1 = ${currentChat.id !== incomingMessage.convId}`) // console.log(`result1 = ${currentChat.id !== incomingMessage.convId}`)
if (currentChat !== null && currentChat.id === incomingMessage.convId) if (currentChat && incomingMessage && currentChat.id === incomingMessage.convId)
{ {
console.log("incoming meaasge=",incomingMessage)
// if (user && !user.blocked.find(incomingMessage.sender))
// setMessage((prev) => [...prev, incomingMessage, key: incomingMessage.id]);
// setMessage((prev) => [...prev, { ...incomingMessage, key: incomingMessage.id }]);
setMessage((prev) => [...prev, incomingMessage]); setMessage((prev) => [...prev, incomingMessage]);
} }
} }
@ -146,10 +190,13 @@ function Chats(){
useEffect(()=> { useEffect(()=> {
const getMessage = async ()=> const getMessage = async ()=>
{ {
if (!currentChat)
return ;
const data = {convId: currentChat.id}; const data = {convId: currentChat.id};
try { try {
const res = await api.post('/getMessage', data); const res = await api.post('/getMessage', data);
console.log("message of conv=", res.data)
setMessage(res.data); setMessage(res.data);
} catch(err) { } catch(err) {
@ -158,30 +205,36 @@ function Chats(){
getMessage(); getMessage();
}, [currentChat]); }, [currentChat]);
const handleSubmit = async (e)=>{ const handleSubmit = async (e: { preventDefault: () => void; })=>{
e.preventDefault(); e.preventDefault();
// console.log(`e= ${e.key}`) // console.log(`e= ${e.key}`)
// console.log(`name= ${user.username}`) // console.log(`name= ${user.username}`)
// let message;
if (!user || !currentChat)
return ;
const message = { const message = {
sender: user.username, sender: user.username,
text: newMessages, text: newMessages,
convId: currentChat.id, convId: currentChat.id,
members: null members: null,
id: null,
}; };
try{ try{
console.log(`id= ${currentChat.id}`) const allowed = await api.post('/allowed', {convId: currentChat.id});
const allowed = await api.post('/allowed', {convId: message.convId, username: user.username}); console.log("convid:", currentChat.id);
console.log("allowed= ", allowed.data)
if (!allowed.data) if (!allowed.data)
{
console.log("muted or banned");
return ; return ;
}
console.log("not muted or banned");
const res = await api.post('/message', message); const res = await api.post('/message', message);
const convMember = await api.post('/member', message); const convMember = await api.post('/member', message);
message.members = convMember.data.members; message.members = convMember.data.members;
console.log(convMember); message.id = res.data.id
// console.log(`currentChat= ${currentChat.id}`)
setMessage([...messages, res.data]); setMessage([...messages, res.data]);
setNewMessage(""); setNewMessage("");
if (socket.current)
socket.current.emit('sendMessage', message); socket.current.emit('sendMessage', message);
} }
catch(err){ catch(err){
@ -189,27 +242,28 @@ function Chats(){
} }
} }
const handleKeyPress = async (e)=>{ const handleKeyPress = async (e: { key: string; })=> {
// console.log(`e in press= ${e.key}`) // console.log(`e in press= ${e.key}`)
if (e.key !== "Enter") if (e.key !== "Enter")
return ; return ;
// console.log(`name= ${user.username}`) // console.log(`name= ${user.username}`)
if (!user || !currentChat)
return ;
const message = { const message = {
sender: user.username, sender: user.username,
text: newMessages, text: newMessages,
convId: currentChat.id, convId: currentChat.id,
members: null members: null,
id: null,
}; };
try{ try{
console.log(`id= ${currentChat.id}`)
const res = await api.post('/message', message); const res = await api.post('/message', message);
const convMember = await api.post('/member', message); const convMember = await api.post('/member', message);
message.members = convMember.data.members; message.members = convMember.data.members;
console.log(convMember); message.id = res.data.id
// console.log(`currentChat= ${currentChat.id}`)
setMessage([...messages, res.data]); setMessage([...messages, res.data]);
setNewMessage(""); setNewMessage("");
if (socket.current)
socket.current.emit('sendMessage', message); socket.current.emit('sendMessage', message);
} }
catch(err){ catch(err){
@ -220,7 +274,7 @@ function Chats(){
const [friend, setFriend] = useState(""); const [friend, setFriend] = useState("");
const [modalOpen, setModalOpen] = useState(false); // const [modalOpen, setModalOpen] = useState(false);
const [addFriend, setAddFriend] = useState(false); const [addFriend, setAddFriend] = useState(false);
const [block, setBlock] = useState(false); const [block, setBlock] = useState(false);
@ -228,8 +282,32 @@ function Chats(){
const [showBlockAlert, setShowBlockAlert] = useState(false); const [showBlockAlert, setShowBlockAlert] = useState(false);
const [setting, setSetting] = useState(false); const [setting, setSetting] = useState(false);
const close = () => setModalOpen(false);
const open = () => setModalOpen(true); const [newGameModalOpen, setNewGameModalOpen] = useState(false);
const [newConversationModalOpen, setNewConversationModalOpen] = useState(false);
const [selectTags, setSelectTag] = useState([{ id: 1, selectedOption: ''}]);
const [users, setUsers] = useState<User[]>([]);
const openNewGameModal = () => {
setNewGameModalOpen(true);
};
const closeNewGameModal = () => {
setNewGameModalOpen(false);
};
const openNewConversationModal = () => {
setNewConversationModalOpen(true);
};
const closeNewConversationModal = () => {
setNewConversationModalOpen(false);
};
// const close = () => setModalOpen(false);
// const open = () => setModalOpen(true);
// const closeAddFriend = () => setAddFriend(false); // const closeAddFriend = () => setAddFriend(false);
// const closeBlock = () => setBlock(false); // const closeBlock = () => setBlock(false);
const closeSetting = () => setSetting(false); const closeSetting = () => setSetting(false);
@ -239,7 +317,7 @@ function Chats(){
// const closeBlock = () => setBlock(false); // const closeBlock = () => setBlock(false);
const handleFriend = (event) => { const handleFriend = (event: { target: { value: React.SetStateAction<string>; }; }) => {
setFriend(event.target.value); setFriend(event.target.value);
}; };
@ -291,6 +369,15 @@ function Chats(){
setShowBlockAlert(false); setShowBlockAlert(false);
}; };
const handleOptionChange = (selectId: number, selectedOption: string) => {
console.log("selected Option=", selectedOption)
setSelectTag((prevTags) =>
prevTags.map((tag) =>
tag.id === selectId ? { ...tag, selectedOption } : tag
)
);
};
//======================================================================================================== //========================================================================================================
//======================================================================================================== //========================================================================================================
// HTML // HTML
@ -304,7 +391,7 @@ function Chats(){
<div className='navbar'> <div className='navbar'>
<img src={DefaultPic} alt="profile" className="pic"/> <img src={DefaultPic} alt="profile" className="pic"/>
<span> <span>
{isLoading ? ( {isLoading || !user ? (
<h4>Loading...</h4> <h4>Loading...</h4>
) : ( ) : (
<h4>{user.nickname}</h4> <h4>{user.nickname}</h4>
@ -355,8 +442,25 @@ function Chats(){
):("")} ):("")}
</div> */} </div> */}
<div className="end"> <div className="end">
<input className="lookForFriends" type="text" value={friend} onChange={handleFriend} /> {selectTags.map((selectTag) => (
<div key={selectTag.id}>
<select
value={selectTag.selectedOption}
className="lookForFriends"
onChange={(a) => handleOptionChange(selectTag.id, a.target.value)}
>
<option value="">{
selectTag.selectedOption ? selectTag.selectedOption : "Select an option"
}</option>
{users.filter((item) => !selectTags.some((tag) => tag.selectedOption === item.username)).map((item, index) => (
<option key={index} value={item.username}>
{item.username}
</option>
))}
</select>
</div>
))}
<TouchDiv> <TouchDiv>
<motion.div onClick={handleAddFriend}> <motion.div onClick={handleAddFriend}>
<MdOutlineGroupAdd /> <MdOutlineGroupAdd />
@ -393,7 +497,7 @@ function Chats(){
initial={false} initial={false}
onExitComplete={() => null} onExitComplete={() => null}
> >
{setting && <ModalSetting handleClose={closeSetting} convId={currentChat.id}/>} {setting && <ModalSetting handleClose={closeSetting} convId={currentChat.id.toString()} socket={socket.current}/>}
</AnimatePresence> </AnimatePresence>
</motion.div> </motion.div>
</TouchDiv> </TouchDiv>
@ -404,18 +508,37 @@ function Chats(){
</div> </div>
<div className="messages_box"> <div className="messages_box">
<div className="contact"> <div className="contact">
<UserChat>
<motion.div className="newMessage" <UserChat>
onClick={() => (modalOpen ? close() : open())} <motion.div className="newMessage" onClick={openNewGameModal}>
> <GrAdd />
<GrAdd/> <span>New Game</span>
</motion.div>
{newGameModalOpen && <GameModal handleClose={closeNewGameModal} />}
</UserChat>
<UserChat>
<motion.div className="newMessage" onClick={openNewConversationModal}>
<GrAdd />
<span>New Conversation</span> <span>New Conversation</span>
</motion.div> </motion.div>
{modalOpen && <Modal modalOpen={modalOpen} handleClose={close}/>} {newConversationModalOpen && (
<Modal handleClose={closeNewConversationModal} />
)}
</UserChat> </UserChat>
{conversations.map((c, index ) => {
{/* {partyInvite.map((c) => {
return (
)})
} */}
{partyInvite.map( i =>(
<PartyInvite currentInvite={i}/>
))}
{conversations.map((c: Conv, index ) => {
return ( return (
<div key={index} <div key={index}
onClick={() => setCurrentChat(c)}> onClick={() => setCurrentChat(c)}>
@ -423,7 +546,7 @@ function Chats(){
<img className="pic-user" src={DefaultPic} alt="User" /> <img className="pic-user" src={DefaultPic} alt="User" />
<div className="infoSideBar"> <div className="infoSideBar">
<span>{c.name}</span> <span>{c.name}</span>
<SideP>Desc?</SideP> {/* <SideP>Desc?</SideP> */}
</div> </div>
</UserChat> </UserChat>
</div> </div>
@ -432,12 +555,12 @@ function Chats(){
</div> </div>
{ {
currentChat ? ( currentChat && user ? (
<> <>
<div className="messages"> <div className="messages">
<div className="scroll"> <div className="scroll">
{messages.map(m=>( {messages.map(m=>(
<Message message = {m} own={m.sender === user.username} user={m}/> <Message key={m.id} message= {m} own={m.sender === user.username}/>
))} ))}
</div> </div>
{/* <Input/> */} {/* <Input/> */}

View File

@ -1,3 +1,4 @@
import React from 'react';
import { TbSend } from 'react-icons/tb'; import { TbSend } from 'react-icons/tb';

View File

@ -1,23 +1,24 @@
/* ************************************************************************** */ /* ************************************************************************** */
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* Message.jsx :+: :+: :+: */ /* Message.tsx :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */ /* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/01 18:24:46 by apommier #+# #+# */ /* Created: 2023/06/01 18:24:46 by apommier #+# #+# */
/* Updated: 2023/06/12 17:05:08 by apommier ### ########.fr */ /* Updated: 2023/06/20 19:05:10 by apommier ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
import React, { HtmlHTMLAttributes } from "react";
import { useEffect, useState, useRef } from "react"; import { useEffect, useState, useRef } from "react";
import api from '../../script/axiosApi'; import api from '../../script/axiosApi.tsx';
import styled from "styled-components" import styled from "styled-components"
// import DefaultPicture from '../../assets/profile.jpg' import DefaultPicture from '../../assets/profile.jpg'
// import { useRef } from "react"; // import { useRef } from "react";
// import { useEffect } from "react"; // import { useEffect } from "react";
import '../../styles/Messages.css' import '../../styles/Messages.css'
import {User, Conv, Message} from "../../../interfaces.tsx"
import React from "react";
const MeStyleP = styled.p` const MeStyleP = styled.p`
background-color: #5843e4; background-color: #5843e4;
@ -26,11 +27,23 @@ const MeStyleP = styled.p`
color: white; color: white;
margin-right: 20px; margin-right: 20px;
` `
function MessageMe({message, own}){
interface MessageMeProps {
message: Message;
own: boolean;
}
function MessageMe({message, own}: MessageMeProps){
const [profilePicture, setProfilePicture] = useState(''); const [profilePicture, setProfilePicture] = useState('');
const [sender, setSender] = useState<User>();
const [conv, setConv] = useState<Conv>();
const [user, setUser] = useState<User>();
const scrollRef = useRef<HTMLDivElement>(null); const scrollRef = useRef<HTMLDivElement>(null);
const DefaultPicture: string = '../../assets/profile.jpg'
// console.log("Message eher")
useEffect(() => { useEffect(() => {
if (scrollRef.current) if (scrollRef.current)
{ {
@ -39,9 +52,16 @@ function MessageMe({message, own}){
const fetchProfilePicture = async () => { const fetchProfilePicture = async () => {
try { try {
// const user = await api.get("/profile"); // const user = await api.get("/profile");
const tmpSender = await api.post("/user", {username: message.sender})
const tmpConv = await api.post("/convId", {convId: message.convId})
// const tmpSender = await api.post("/user", {username: message.sender})
const tmpUser = await api.get("/profile")
const pic = await api.post("/getPicture", {username: message.sender}) const pic = await api.post("/getPicture", {username: message.sender})
// console.log(`user naem profile pic222= ${currentUser.username}`) // console.log(`user naem profile pic222= ${currentUser.username}`)
// console.log(` profile pic222= ${pic.data}`) // console.log(` profile pic222= ${pic.data}`)
setConv(tmpConv.data);
setUser(tmpUser.data);
setSender(tmpSender.data);
setProfilePicture(pic.data); setProfilePicture(pic.data);
} catch (error) { } catch (error) {
console.error('Error fetching profile picture:', error); console.error('Error fetching profile picture:', error);
@ -50,23 +70,52 @@ function MessageMe({message, own}){
fetchProfilePicture(); fetchProfilePicture();
}, []) }, [])
const handleButtonClick = () => {
if (!sender)
return ;
let path = `http://` + process.env.REACT_APP_BASE_URL + `/profile/${sender.username}`;
// console.log("path= ", path)
// history(path, { replace: true });
// window.location.replace(path);
window.history.pushState({}, '', path);
window.location.reload();
};
if (!user || !sender || !conv)
{
// console.log("return")
return (<></>);
}
// console.log("result includes=", conv.banned.includes(user.username))
// console.log("result includes=", conv.blocked.includes(user.username))
if (user.blocked && user.blocked.includes(message.sender))
return (<></>);
// else if (conv.banned && conv.banned.includes(user.username))
// {
// console.log("return2")
// return (<></>);
// }
// console.log("noy return")
// if (user.blocked.includes(message.sender))/
return ( return (
<div className={own ? "meMessage" : "youMessage"} ref={scrollRef}> <div className={own ? "meMessage" : "youMessage"} ref={scrollRef}>
<div> <div>
{/* <img className="messageInfo" src={DefaultPic} alt="profile" />
*/}
{profilePicture ? ( {profilePicture ? (
<img className="messageInfo" src={`data:image/jpeg;base64,${profilePicture}`} /> <img className="messageInfo" onClick={() => handleButtonClick()} src={`data:image/jpeg;base64,${profilePicture}`} />
) : ( ) : (
<img className="messageInfo" src={DefaultPicture} alt="Default Profile Picture" /> <img className="messageInfo" onClick={() => handleButtonClick()} src={DefaultPicture} alt="Default Profile Picture" />
)} )}
</div> </div>
{/* <div className="usernameMesage">{message.senderNickname}</div> */} {/* <div className="usernameMesage">{message.senderNickname}</div> */}
<div className="usernameMesage">{message.sender}</div> {sender ? (
<div className="usernameMesage">{sender.nickname}</div>
): ""}
<div className="messageContent"> <div className="messageContent">
<MeStyleP>{message.text}</MeStyleP> <MeStyleP>{message.text}</MeStyleP>
</div> </div>
</div> </div>
) )
} }

View File

@ -14,7 +14,8 @@ const MeStyleP = styled.p`
` `
function MessageMe(){ function MessageMe(){
const scrollRef = useRef(); // const scrollRef = useRef();
const scrollRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
scrollRef.current?.scrollIntoView({ behavior: "smooth"}) scrollRef.current?.scrollIntoView({ behavior: "smooth"})

View File

@ -14,7 +14,7 @@ const StyleP = styled.p`
` `
function MessageYou(){ function MessageYou(){
const scrollRef = useRef(); const scrollRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
scrollRef.current?.scrollIntoView({ behavior: "smooth"}) scrollRef.current?.scrollIntoView({ behavior: "smooth"})

View File

@ -1,11 +1,11 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import Backdrop from "../Sidebar/Backdrop"; import Backdrop from "../Sidebar/Backdrop.tsx";
// import { Rank } from "../../DataBase/DataRank" // import { Rank } from "../../DataBase/DataRank"
import '../../styles/Messages.css' import '../../styles/Messages.css'
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { GrAdd } from "react-icons/gr"; import { GrAdd } from "react-icons/gr";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import api from "../../script/axiosApi"; import api from "../../script/axiosApi.tsx";
import React from "react"; import React from "react";
const dropIn = { const dropIn = {
@ -77,7 +77,7 @@ const Modal = ({handleClose}) => {
} }
}; };
const saveSelectedOptions = () => { const saveSelectedOptions = async () => {
// const selectedOptions = selectTags.map((tag) => tag.selectedOption); // const selectedOptions = selectTags.map((tag) => tag.selectedOption);
const selectedOptions = selectTags.map((tag) => tag.selectedOption).filter((option) => option !== ''); const selectedOptions = selectTags.map((tag) => tag.selectedOption).filter((option) => option !== '');
@ -88,8 +88,9 @@ const Modal = ({handleClose}) => {
} }
try{ try{
// test // test
api.post("/conv", data); await api.post("/conv", data);
handleClose(); handleClose();
window.location.reload();
} catch(err) { } catch(err) {
console.log(err); console.log(err);
} }
@ -99,17 +100,15 @@ const Modal = ({handleClose}) => {
// let new_name; // let new_name;
return ( return (
<Backdrop > <Backdrop onClick={handleClose}>
<motion.div <motion.div
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
className="modal" className="modal"
variant={dropIn} // variant={dropIn}
initial="hidden" initial="hidden"
animate="visible" animate="visible"
exit="exit" exit="exit"
> >
{/* <p>New Conversation</p> */}
{selectTags.map((selectTag) => ( {selectTags.map((selectTag) => (
<div key={selectTag.id}> <div key={selectTag.id}>
<select <select
@ -146,7 +145,7 @@ const Modal = ({handleClose}) => {
> >
<option value="">Select an option</option> <option value="">Select an option</option>
{convs.map((conv) => ( {convs.map((conv) => (
!(!conv.group || conv.private || (conv.banned && conv.banned.includes(channel)) || (conv.members && conv.members.includes(user.username))) && ( !(!conv.group || conv.private || (conv.banned && conv.banned.includes(user.username)) || (conv.members && conv.members.includes(user.username))) && (
<option key={conv.id} value={conv.id}> <option key={conv.id} value={conv.id}>
{conv.name} {conv.name}
</option> </option>
@ -154,36 +153,16 @@ const Modal = ({handleClose}) => {
))} ))}
</select> </select>
)} )}
{channel.private ? (
<input className="mdp" placeholder="password" type="text" />
):("")}
<div className="div_submit"> <div className="div_submit">
<Link to='#' className="submit" onClick={ joinChannel }>Join</Link> <Link to='#' className="submit" onClick={ joinChannel }>Join</Link>
</div> </div>
{/* {selectTags.map((selectTag) => (
<div key={selectTag.id}>
<select
value={selectTag.selectedOption}
onChange={(a) => handleOptionChange(selectTag.id, a.target.value)}
>
<option value="">{
selectTag.selectedOption ? selectTag.selectedOption : "Select an option"
}</option>
{convs.filter((item) => !selectTags.some((tag) => tag.selectedOption === item.name)).map((item, index) => (
<option key={index} value={item.name}>
{item.name}
</option>
))}
</select>
</div>
))} */}
{/* <div>
<GrAdd onClick={addNewSelectedTag}/>
</div> */}
</motion.div> </motion.div>
</Backdrop> </Backdrop>

View File

@ -1,11 +1,14 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import Backdrop from "../Sidebar/Backdrop.tsx"; import Backdrop from "../Sidebar/Backdrop.tsx";
import { Rank } from "../../DataBase/DataRank" // import { Rank } from "../../DataBase/DataRank"
import '../../styles/Messages.css' import '../../styles/Messages.css'
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { GrAdd } from "react-icons/gr"; import { GrAdd } from "react-icons/gr";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import api from "../../script/axiosApi.tsx"; import api from "../../script/axiosApi.tsx";
import React from "react";
import {User} from "../../../interfaces.tsx"
import { Socket } from "socket.io-client";
const dropIn = { const dropIn = {
@ -24,13 +27,26 @@ const dropIn = {
}; };
const ModalSetting = ({handleClose, convId}) => { interface ModalSettingProps {
handleClose: Function,
convId: string,
socket: Socket | null,
}
const ModalSetting = ({handleClose, convId, socket }: ModalSettingProps) => {
const [password, setPassword] = useState(false); const [password, setPassword] = useState(false);
const [users, setUsers] = useState([]); const [users, setUsers] = useState<User[]>([]);
const [selectTags, setSelectTag] = useState([{ id: 1, selectedOption: ''}]); const [selectTags, setSelectTag] = useState([{ id: 1, selectedOption: ''}]);
const [selectedUser, setSelectedUser] = useState([]); const [selectedUser, setSelectedUser] = useState("");
const [newName, setNewName] = useState(""); const [newName, setNewName] = useState("");
const [newPassword, setNewPassword] = useState(""); const [newPassword, setNewPassword] = useState("");
const [privateConv, setPrivateConv] = useState(false);
const dark = () => setPrivateConv(true);
const light = () => setPrivateConv(false);
const [mute, setMute] = useState(false);
const darkMute = () => setMute(false);
const lightMute = () => setMute(true);
useEffect(()=> { useEffect(()=> {
@ -51,7 +67,7 @@ const ModalSetting = ({handleClose, convId}) => {
// const [selectedOptionArray, setSelectedOptionArray] = useState([]); // const [selectedOptionArray, setSelectedOptionArray] = useState([]);
const handleOptionChange = (selectId, selectedOption) => { const handleOptionChange = (selectId: number, selectedOption: string) => {
console.log("tag= ", selectTags) console.log("tag= ", selectTags)
console.log("option= ", selectedOption) console.log("option= ", selectedOption)
setSelectTag((prevTags) => setSelectTag((prevTags) =>
@ -62,12 +78,12 @@ const ModalSetting = ({handleClose, convId}) => {
setSelectedUser(selectedOption) setSelectedUser(selectedOption)
}; };
const handleCheckPass = (e) => { const handleCheckPass = (e: { target: { checked: boolean | ((prevState: boolean) => boolean); }; }) => {
setPassword(e.target.checked); setPassword(e.target.checked);
console.log("password??", e.target.checked) console.log("password??", e.target.checked)
} }
const handleCheckPriv = (e) => { const handleCheckPriv = (e: { target: { checked: any; }; }) => {
// setPassword(e.target.checked); // setPassword(e.target.checked);
if (e.target.checked) if (e.target.checked)
{ {
@ -89,59 +105,72 @@ const ModalSetting = ({handleClose, convId}) => {
} }
} }
const handleName = async (e)=>{ const handleName = async (e: { key: string; })=>{
if (e.key !== "Enter") if (e.key !== "Enter")
return ; return ;
try{ try{
api.post("/name", {convId: convId, name: newName}) api.post("/name", {convId: convId, name: newName})
window.location.reload()
} catch(err) { } catch(err) {
console.log(err); console.log(err);
} }
handleClose(); handleClose();
} }
const handlePassword = async (e)=>{ const handlePassword = async (e: { key: string; })=>{
if (e.key !== "Enter") if (e.key !== "Enter")
return ; return ;
try{ try{
api.post("/password", {convId: convId, password: newPassword}) await api.post("/password", {convId: convId, password: newPassword})
} catch(err) { } catch(err) {
console.log(err); console.log(err);
} }
handleClose(); handleClose();
} }
const handleBan = () => { const handleBan = async () => {
// console.log("ban option= ", selectedUser) // console.log("ban option= ", selectedUser)
try{ try{
api.post("/ban", {convId: convId, username: selectedUser}) // console.log("user select=", selectedUser.length)
if (!selectedUser.length)
return ;
await api.post("/ban", {convId: convId, username: selectedUser})
if (socket)
{
console.log("emit to ban server")
socket.emit("ban", {username: selectedUser})
}
} catch(err) { } catch(err) {
console.log(err); console.log(err);
} }
handleClose(); handleClose();
}; };
const handleAdmin = () => { const handleAdmin = async () => {
if (!selectedUser.length)
return ;
try{ try{
api.post("/admin", {convId: convId, username: selectedUser}) await api.post("/admin", {convId: convId, username: selectedUser})
} catch(err) { } catch(err) {
console.log(err); console.log(err);
} }
handleClose(); handleClose();
}; };
const handleMute = () => { const handleMute = async () => {
if (!selectedUser.length)
return ;
try{ try{
api.post("/mute", {convId: convId, username: selectedUser}) await api.post("/mute", {convId: convId, username: selectedUser})
} catch(err) { } catch(err) {
console.log(err); console.log(err);
} }
handleClose(); handleClose();
}; };
const handleInvite = () => { const handleInvite = async () => {
try{ try{
api.post("/invite", {convId: convId, username: selectedUser}) await api.post("/invite", {convId: convId, username: selectedUser})
} catch(err) { } catch(err) {
console.log(err); console.log(err);
} }
@ -149,11 +178,10 @@ const ModalSetting = ({handleClose, convId}) => {
}; };
return ( return (
<Backdrop> <Backdrop onClick={handleClose}>
<motion.div <motion.div
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
className="modalSetting" className="modalSetting"
variant={dropIn}
initial="hidden" initial="hidden"
animate="visible" animate="visible"
exit="exit" exit="exit"
@ -162,15 +190,19 @@ const ModalSetting = ({handleClose, convId}) => {
{/* First selection */} {/* First selection */}
<div className="settingFirstPart"> <div className="settingFirstPart">
<div> <div>
<p className="checkbox">Private<input class="check"type="checkbox" value="private" onChange={handleCheckPriv}/></p> <div>
<Link to="#" onClick={light} className={ privateConv ? "submit" : "darkSubmit"}>Public</Link>
<Link to="#" onClick={dark} className={ privateConv ? "darkSubmit" : "submit"}>Private</Link>
</div>
{/* <p className="checkbox">Private<input className="check"type="checkbox" value="private" onChange={handleCheckPriv}/></p> */}
<p className="checkbox">Password<input type="checkbox" value="password" checked={password} onChange={handleCheckPass}/> </p> <p className="checkbox">Password<input type="checkbox" value="password" checked={password} onChange={handleCheckPass}/> </p>
{password ? ( {password || privateConv ? (
<input <input
onChange={(e) => setNewPassword(e.target.value)} onChange={(e) => setNewPassword(e.target.value)}
onKeyDown={handlePassword} onKeyDown={handlePassword}
type="text" type="password"
className="in" className="in"
placeholder="Password"/> placeholder="Password"/>
): ):
@ -216,11 +248,14 @@ const ModalSetting = ({handleClose, convId}) => {
<div> <div>
<Link to="#" onClick={handleInvite} className="submit">Send</Link> <Link to="#" onClick={handleInvite} className="submit">Send</Link>
<Link to="#" onClick={handleBan} className="submit">Ban</Link> <Link to="#" onClick={handleBan} className="submit">Ban</Link>
<Link to="#" onClick={handleMute} className="submit">Mute</Link> <Link to="#" onClick={mute ? darkMute : lightMute} className={mute ? "darkSubmit": "submit"}>Mute</Link>
<Link to="#" onClick={handleAdmin} className="submit">Admin</Link> <Link to="#" onClick={handleAdmin} className="submit">Admin</Link>
</div> </div>
</div> </div>
{mute ? (
<input type="text" className="in_howLong" placeholder="How long ?" />
):("")}
</motion.div> </motion.div>
</Backdrop> </Backdrop>

View File

@ -33,6 +33,7 @@ const ModalEdit = ( handleClose ) => {
const handler = e => const handler = e =>
{ {
setNickname(e.target.value); setNickname(e.target.value);
console.log("testeeeee")
const postNickname = async ()=>{ const postNickname = async ()=>{
try{ try{
await api.post("/nickname", {nickname: nickname}) await api.post("/nickname", {nickname: nickname})
@ -45,6 +46,22 @@ const ModalEdit = ( handleClose ) => {
}; };
postNickname(); postNickname();
} }
const handlePostNickname = async () =>
{
console.log("nickname=" ,nickname)
try{
await api.post("/nickname", {nickname: nickname})
window.location.reload();
// setUser(tmpUser.data);
// setIsLoading(false)
}
catch(err){
console.log(err);
}
}
// function handleClose(){ // function handleClose(){
// //do nothing // //do nothing
// } // }
@ -56,10 +73,11 @@ const ModalEdit = ( handleClose ) => {
animate="visible" animate="visible"
exit="exit"> exit="exit">
<h2>Type your new name</h2> <h2>Type your new name</h2>
<input className="text" type="text" value={nickname} onChange={handler} handleClose/> <input className="text" maxLength="10" type="text" value={nickname} onChange={handler} handleClose/>
<div onClick={handleClose}> <div>
<div onClick={() => {UserProfile.UserName = nickname;}}> <div className="button" onClick={ () => handlePostNickname()}>
<Link className="button" to={""}>change</Link> change
{/* <Link className="button" to={""}>change</Link> */}
</div> </div>
</div> </div>
</motion.div> </motion.div>

View File

@ -1,3 +1,4 @@
import React from "react";
@ -8,8 +9,9 @@ function Logout(){
// history(path, { replace: true }); // history(path, { replace: true });
// window.location.replace(path); // window.location.replace(path);
// window.history.pushState({}, '', path); // window.history.pushState({}, '', path);
window.history.pushState({}, null, path); window.history.pushState({}, '', path);
window.location.reload(false); window.location.reload();
return (<></>)
} }
export default Logout; export default Logout;

View File

@ -4,6 +4,7 @@
// import '../DataBase/DataProfileUser.js' // import '../DataBase/DataProfileUser.js'
// import { DBWinLoss } from '../../DataBase/DummyDBWinLoss.js'; // import { DBWinLoss } from '../../DataBase/DummyDBWinLoss.js';
import '../../styles/Win_Loss.css' import '../../styles/Win_Loss.css'
import { User, Matchlog } from "../../../interfaces.tsx"
// import { UserProfile } from '../../DataBase/DataUserProfile'; // import { UserProfile } from '../../DataBase/DataUserProfile';
// import color from '../../utils/style/color.js'; // import color from '../../utils/style/color.js';
@ -46,12 +47,12 @@ import '../../styles/Win_Loss.css'
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import api from '../../script/axiosApi'; import api from '../../script/axiosApi.tsx';
function WinLoss() { function WinLoss() {
const [user, setUser] = useState(null); const [user, setUser] = useState<User>();
const [history, setHistory] = useState([]); const [history, setHistory] = useState([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
@ -88,19 +89,19 @@ function WinLoss() {
<div className='tab'> <div className='tab'>
{isLoading ? ( {isLoading || !history || !user ? (
<h1>Loading...</h1> <h1>Loading...</h1>
// <span>Loading...</span> // <span>Loading...</span>
) : ( ) : (
<div className='scroll'> <div className='scroll'>
<h2 className='title'>Match history Win/Loss</h2> <h2 className='title'>Match history Win/Loss</h2>
{history.map((c, index) => { {history.map((c: Matchlog, index) => {
return ( return (
<div key={index} className='elements'> <div key={index} className='elements'>
<li key={index}> <li key={index}>
{/* <h4 className='content'>{c.id}</h4> */} {/* <h4 className='content'>{c.id}</h4> */}
<div key={index} className='content2nd'> <div className='content2nd'>
<h4 key={index} className='me'>{user.username}</h4> <h4 key={index} className='score'>{c.myScore} - {c.opScore} </h4> <h4 key={index} className="opponent">{c.opponent}</h4> <h4 className='me'>{user.username}</h4> <h4 className='score'>{c.myScore} - {c.opScore} </h4> <h4 className="opponent">{c.opponent}</h4>
</div> </div>
{/* <h4 className='content'>{c.openent}</h4> */} {/* <h4 className='content'>{c.openent}</h4> */}
</li> </li>

View File

@ -1,7 +1,13 @@
import React, { MouseEventHandler, ReactNode, HTMLAttributes } from "react";
import { motion } from "framer-motion" import { motion } from "framer-motion"
import "../../styles/Header.css" import "../../styles/Header.css"
const Backdrop = ({ children, onClick }) => { interface backProps {
children: ReactNode,
onClick: any
}
const Backdrop = ({ children, onClick }: backProps) => {
return ( return (
<motion.div className="backdrop" <motion.div className="backdrop"
onClick={onClick} onClick={onClick}

View File

@ -1,12 +1,12 @@
import React from "react";
import {motion} from "framer-motion" import {motion} from "framer-motion"
import Backdrop from "./Backdrop" import Backdrop from "./Backdrop.tsx"
import { SidebarData } from "./SidebarData" import { SidebarData } from "./SidebarData.tsx"
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import * as AiIcons from 'react-icons/ai'; import * as AiIcons from 'react-icons/ai';
import "../../styles/Header.css" import "../../styles/Header.css"
import React from "react";
const dropIn = { const dropIn = {
hidden: { hidden: {
@ -20,7 +20,11 @@ const dropIn = {
}, },
} }
const Modal = ({ handleclose }) => { interface CloseProps {
handleclose: Function;
}
const Modal = ({ handleclose }: CloseProps) => {
return ( return (
<Backdrop onClick={handleclose}> <Backdrop onClick={handleclose}>
<motion.div <motion.div

View File

@ -26,7 +26,7 @@ export const SidebarData = [
cName: 'nav-text' cName: 'nav-text'
}, },
{ {
title: 'Social', title: 'Friend',
path: '/social', path: '/social',
icon: <IoIcons.IoMdPeople />, icon: <IoIcons.IoMdPeople />,
cName: 'nav-text' cName: 'nav-text'

View File

@ -6,7 +6,7 @@
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */ /* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/09 08:18:58 by apommier #+# #+# */ /* Created: 2023/06/09 08:18:58 by apommier #+# #+# */
/* Updated: 2023/06/18 13:12:26 by apommier ### ########.fr */ /* Updated: 2023/06/20 13:41:44 by apommier ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
@ -18,6 +18,7 @@ import styled from "styled-components";
import { RxCircle } from "react-icons/rx"; import { RxCircle } from "react-icons/rx";
import { CgFontSpacing } from "react-icons/cg"; import { CgFontSpacing } from "react-icons/cg";
import React from "react"; import React from "react";
import {User} from "../../../interfaces.tsx"
const UserChat = styled.div ` const UserChat = styled.div `
padding: 5px; padding: 5px;
@ -37,8 +38,12 @@ const SideP = styled.p`
color: lightgray; color: lightgray;
margin-left: 15px; margin-left: 15px;
` `
interface UserProps {
currentUser: User
}
export default function Friend({currentUser}) // export default function Friend({currentUser})
export default function Friend({currentUser}: UserProps)
{ {
const [profilePicture, setProfilePicture] = useState(''); const [profilePicture, setProfilePicture] = useState('');
@ -58,7 +63,7 @@ export default function Friend({currentUser})
fetchProfilePicture(); fetchProfilePicture();
}) })
function getStatus(friend) function getStatus(friend: User)
{ {
let status = friend.status let status = friend.status
console.log(`status= ${status}`) console.log(`status= ${status}`)
@ -73,19 +78,19 @@ export default function Friend({currentUser})
return statusColor; return statusColor;
} }
const handleSpectate = (user) => { const handleSpectate = (user: User) => {
//socket connection and add to party with one with username //socket connection and add to party with one with username
console.log(`spectate hehe`) console.log(`spectate hehe`)
console.log(`user= ${user}`) console.log(`user= ${user}`)
}; };
const handleButtonClick = (user) => { const handleButtonClick = (user: User) => {
let path = `http://` + process.env.REACT_APP_BASE_URL + `/profile/${user.username}`; let path = `http://` + process.env.REACT_APP_BASE_URL + `/profile/${user.username}`;
console.log("path= ", path) console.log("path= ", path)
// history(path, { replace: true }); // history(path, { replace: true });
// window.location.replace(path); // window.location.replace(path);
window.history.pushState({}, null, path); window.history.pushState({}, '', path);
window.location.reload(false); window.location.reload();
}; };
return ( return (
@ -97,7 +102,7 @@ export default function Friend({currentUser})
)} )}
<div className="infoSideBar"> <div className="infoSideBar">
<span onClick={() => handleButtonClick(currentUser)}>{currentUser.nickname}</span> <span onClick={() => handleButtonClick(currentUser)}>{currentUser.nickname}</span>
<RxCircle icon={RxCircle} color={getStatus(currentUser)} /> <RxCircle color={getStatus(currentUser)} />
<button onClick={() => handleSpectate(currentUser)} >Invite</button> <button onClick={() => handleSpectate(currentUser)} >Invite</button>
{getStatus(currentUser) !== 'blue' ? ( {getStatus(currentUser) !== 'blue' ? (
<></> <></>

View File

@ -1,111 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Friend.jsx :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/09 08:18:58 by apommier #+# #+# */
/* Updated: 2023/06/15 19:05:14 by apommier ### ########.fr */
/* */
/* ************************************************************************** */
import { useEffect, useState } from "react";
import api from '../../script/axiosApi.tsx';
import DefaultPicture from '../../assets/profile.jpg'
import styled from "styled-components";
import { RxCircle } from "react-icons/rx";
import { CgFontSpacing } from "react-icons/cg";
const UserChat = styled.div `
padding: 5px;
display: flex;
align-items: center;
gap: 5px;
color: white;
cursor: pointer;
&:hover{
background-color: #3e3c61;
}
`
const SideP = styled.p`
font-size: 14px;
color: lightgray;
margin-left: 15px;
`
export default function Friend({currentUser})
{
const [profilePicture, setProfilePicture] = useState('');
useEffect(() => {
const fetchProfilePicture = async () => {
try {
// const user = await api.get("/profile");
const pic = await api.post("/getPicture", {username: currentUser.username})
// console.log(`user naem profile pic222= ${currentUser.username}`)
// console.log(` profile pic222= ${pic.data}`)
setProfilePicture(pic.data);
} catch (error) {
console.error('Error fetching profile picture:', error);
}
};
fetchProfilePicture();
})
function getStatus(friend)
{
let status = friend.status
console.log(`status= ${status}`)
let statusColor;
if (status === 0)
statusColor = 'grey';
else if (status === 1)
statusColor = 'green';
else if (status === 2)
statusColor = 'blue';
return statusColor;
}
const handleSpectate = (user) => {
//socket connection and add to party with one with username
console.log(`spectate hehe`)
console.log(`user= ${user}`)
};
const handleButtonClick = (user) => {
let path = `http://` + process.env.REACT_APP_BASE_URL + `/profile/${user.username}`;
console.log("path= ", path)
// history(path, { replace: true });
// window.location.replace(path);
window.history.pushState({}, null, path);
window.location.reload(false);
};
return (
<UserChat>
{profilePicture ? (
<img className="pic-user" src={`data:image/jpeg;base64,${profilePicture}`} />
) : (
<img className="pic-user" src={DefaultPicture} alt="Default Profile Picture" />
)}
<div className="infoSideBar">
<span onClick={() => handleButtonClick(currentUser)}>{currentUser.nickname}</span>
<RxCircle icon={RxCircle} color={getStatus(currentUser)} />
<button onClick={() => handleSpectate(currentUser)} >Invite</button>
{getStatus(currentUser) !== 'blue' ? (
<></>
) : (
<button onClick={() => handleSpectate(currentUser)} >Spectate</button>
)}
</div>
</UserChat>
)
}

View File

@ -5,6 +5,8 @@ import DefaultPicture from '../../assets/profile.jpg'
import styled from "styled-components"; import styled from "styled-components";
import { RxCheckCircled, RxCircleBackslash } from "react-icons/rx"; import { RxCheckCircled, RxCircleBackslash } from "react-icons/rx";
import React from "react";
import {User} from "../../../interfaces.tsx"
const UserChat = styled.div ` const UserChat = styled.div `
padding: 5px; padding: 5px;
@ -25,10 +27,14 @@ const SideP = styled.p`
margin-left: 15px; margin-left: 15px;
` `
export default function Friend({currentUser}) interface UserProps {
currentUser: User
}
export default function Friend({currentUser}: UserProps)
{ {
const [profilePicture, setProfilePicture] = useState(''); const [profilePicture, setProfilePicture] = useState('');
const [request, setRequest] = useState(''); //user who invite const [request, setRequest] = useState<User>(); //user who invite
const [clickEvent, setClickEvent] = useState(false); const [clickEvent, setClickEvent] = useState(false);
// const [user, setUser] = useState(null); // const [user, setUser] = useState(null);
@ -52,15 +58,15 @@ export default function Friend({currentUser})
fetchProfilePicture(); fetchProfilePicture();
}, [clickEvent]) }, [clickEvent])
const handleButtonClick = (user) => { const handleButtonClick = (user: User) => {
let path = `http://` + process.env.REACT_APP_BASE_URL + `/profile/${user.username}`; let path = `http://` + process.env.REACT_APP_BASE_URL + `/profile/${user.username}`;
// history(path, { replace: true }); // history(path, { replace: true });
// window.location.replace(path); // window.location.replace(path);
window.history.pushState({}, null, path); window.history.pushState({}, '', path);
window.location.reload(false); window.location.reload();
}; };
const Accept = async (request) => { const Accept = async (request: User) => {
try{ try{
await api.post("/friend", {username: request.username}) await api.post("/friend", {username: request.username})
setClickEvent(true); setClickEvent(true);
@ -71,7 +77,7 @@ export default function Friend({currentUser})
console.log(`request = ${request}`) console.log(`request = ${request}`)
} }
const Refuse = async (request) => { const Refuse = async (request: User) => {
try{ try{
await api.post("/refuseInvite", {username: request.username}) await api.post("/refuseInvite", {username: request.username})
setClickEvent(true); setClickEvent(true);
@ -94,12 +100,13 @@ export default function Friend({currentUser})
) : ( ) : (
<img className="pic-user" src={DefaultPicture} alt="Default Profile Picture" /> <img className="pic-user" src={DefaultPicture} alt="Default Profile Picture" />
)} )}
{request ? (
<div className="infoSideBar"> <div className="infoSideBar">
<span onClick={() => handleButtonClick(currentUser)}>{currentUser.nickname}</span> <span onClick={() => handleButtonClick(currentUser)}>{currentUser.nickname}</span>
<RxCheckCircled onClick={() => Accept(request)} color={'green'}/> <RxCheckCircled onClick={() => Accept(request)} color={'green'}/>
<RxCircleBackslash onClick={() => Refuse(request)} color={'red'}/> <RxCircleBackslash onClick={() => Refuse(request)} color={'red'}/>
</div> </div>
) : ( "" )}
</UserChat> </UserChat>
) )
} }

View File

@ -1,106 +0,0 @@
import { useEffect, useState } from "react";
import api from '../../script/axiosApi.tsx';
import DefaultPicture from '../../assets/profile.jpg'
import styled from "styled-components";
import { RxCheckCircled, RxCircleBackslash } from "react-icons/rx";
const UserChat = styled.div `
padding: 5px;
display: flex;
align-items: center;
gap: 5px;
color: white;
cursor: pointer;
&:hover{
background-color: #3e3c61;
}
`
const SideP = styled.p`
font-size: 14px;
color: lightgray;
margin-left: 15px;
`
export default function Friend({currentUser})
{
const [profilePicture, setProfilePicture] = useState('');
const [request, setRequest] = useState(''); //user who invite
const [clickEvent, setClickEvent] = useState(false);
// const [user, setUser] = useState(null);
useEffect(() => {
const fetchProfilePicture = async () => {
try {
// const user = await api.get("/profile");\
// const tmpUser = await api.get("/profile")
const pic = await api.post("/getPicture", {username: currentUser.username})
const tmpRequest = await api.post("/user", {username: currentUser.username})
// setUser(tmpUser.data);
setRequest(tmpRequest.data);
// console.log(`user naem profile pic222= ${currentUser.username}`)
// console.log(` profile pic222= ${pic.data}`)
setProfilePicture(pic.data);
} catch (error) {
console.error('Error fetching profile picture:', error);
}
};
fetchProfilePicture();
}, [clickEvent])
const handleButtonClick = (user) => {
let path = `http://` + process.env.REACT_APP_BASE_URL + `/profile/${user.username}`;
// history(path, { replace: true });
// window.location.replace(path);
window.history.pushState({}, null, path);
window.location.reload(false);
};
const Accept = async (request) => {
try{
await api.post("/friend", {username: request.username})
setClickEvent(true);
} catch(err) {
console.log(err);
}
console.log("accept")
console.log(`request = ${request}`)
}
const Refuse = async (request) => {
try{
await api.post("/refuseInvite", {username: request.username})
setClickEvent(true);
} catch(err) {
console.log(err);
}
console.log("refuse")
console.log(`request = ${request}`)
}
// Vérifier si le contenu doit être caché
if (clickEvent) {
return null; // Rendre null pour ne pas afficher le contenu
}
return (
<UserChat>
{profilePicture ? (
<img className="pic-user" src={`data:image/jpeg;base64,${profilePicture}`} />
) : (
<img className="pic-user" src={DefaultPicture} alt="Default Profile Picture" />
)}
<div className="infoSideBar">
<span onClick={() => handleButtonClick(currentUser)}>{currentUser.nickname}</span>
<RxCheckCircled onClick={() => Accept(request)} color={'green'}/>
<RxCircleBackslash onClick={() => Refuse(request)} color={'red'}/>
</div>
</UserChat>
)
}

View File

@ -9,6 +9,7 @@ import FriendRequest from './FriendRequest.tsx';
import { ImBlocked } from 'react-icons/im'; import { ImBlocked } from 'react-icons/im';
import { MdOutlineGroupAdd } from 'react-icons/md'; import { MdOutlineGroupAdd } from 'react-icons/md';
import {User} from "../../../interfaces.tsx"
// import React from "react"; // import React from "react";
@ -34,12 +35,11 @@ function Social (){
const [friends, setFriends] = useState([]); const [friends, setFriends] = useState([]);
const [invite, setInvite] = useState([]); const [invite, setInvite] = useState([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState<boolean>(true);
const [user, setUser] = useState(null); const [user, setUser] = useState<User>();
const [profilePicture, setProfilePicture] = useState(''); const [profilePicture, setProfilePicture] = useState('');
useEffect(()=> { useEffect(()=> {
const getFriend = async ()=>{ const getFriend = async ()=>{
try{ try{
const tmpFriends = await api.get("/friends") const tmpFriends = await api.get("/friends")
@ -94,7 +94,7 @@ function Social (){
<img className="pic" src={DefaultPicture} alt="Default Profile Picture" /> <img className="pic" src={DefaultPicture} alt="Default Profile Picture" />
)} )}
<span> <span>
{isLoading ? ( {isLoading || !user ? (
<h4>Loading...</h4> <h4>Loading...</h4>
) : ( ) : (
<h4>{user.nickname}</h4> <h4>{user.nickname}</h4>

View File

@ -1,14 +1,20 @@
import React from 'react';
import { useEffect } from 'react'; import { useEffect } from 'react';
// import { useState, useRef } from 'react'; // import { useState, useRef } from 'react';
import DrawCanvas from './canvas'; import DrawCanvas from './canvas.tsx';
import queryString from 'query-string'; import queryString from 'query-string';
import '../styles/field.css'; import '../styles/field.css';
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import React from 'react';
// import { withRouter } from 'react-router-dom'; // import { withRouter } from 'react-router-dom';
interface GameProps {
privateParty: boolean,
username?: string
gameId?: number
}
function Field() function Field()
{ {
useEffect(() => { useEffect(() => {
@ -17,6 +23,7 @@ function Field()
console.log("launch canva hehe") console.log("launch canva hehe")
let Modifiers = 0; let Modifiers = 0;
let info: GameProps;
if (queryParams.superpower === 'true') { if (queryParams.superpower === 'true') {
Modifiers += 1; Modifiers += 1;
@ -29,81 +36,37 @@ function Field()
if (queryParams.speed === 'true') { if (queryParams.speed === 'true') {
Modifiers += 4; Modifiers += 4;
} }
// console.log(`modifiers= ${Modifiers}`)
// DrawCanvas(Modifiers);
// return () => {
// console.log("000000000000000000000000000000000")
// // socketRef.current.disconnect();
// };
// console.log(`modifiers= ${Modifiers}`) info = {
const cleanup = DrawCanvas(Modifiers); privateParty: false,
}
if (queryParams.username)
{
console.log("user= ", queryParams.username)
info = {
privateParty: true,
username: queryParams.username as string,
gameId: queryParams.gameId as unknown as number
}
console.log("info of param vefore canvas=", info)
}
const cleanup = DrawCanvas(Modifiers, info);
return () => { return () => {
console.log("Cleanup"); console.log("Cleanup");
cleanup(); // Call the cleanup function to stop the ongoing process or perform necessary cleanup tasks // cleanup(); // Call the cleanup function to stop the ongoing process or perform necessary cleanup tasks
if (cleanup)
cleanup();
}; };
}, []); }, []);
// const [buttonClicked, setButtonClicked] = useState(false);
// const handleButtonClick = () => {
// drawCanvas();
// setButtonClicked(true);
// };
return ( return (
<div className="field" id="canvas_container"> <div className="field" id="canvas_container">
<canvas id="myCanvas"></canvas> <canvas id="myCanvas"></canvas>
{/* <button onClick={handleButtonClick}>Draw on Canvas</button> */}
{/* {buttonClicked && <canvas id="myCanvas"></canvas>}
{!buttonClicked && <button onClick={handleButtonClick}>Draw on Canvas</button>} */}
</div> </div>
); );
} }
export default Field; export default Field;
// export default withRouter(Field);
// function Field() {
// const [buttonClicked, setButtonClicked] = useState(false);
// const handleButtonClick = () => {
// const canvas = document.createElement('canvas');
// canvas.id = 'myCanvas';
// console.log("button clicked")
// document.getElementById('canvas_container').appendChild(canvas);
// setButtonClicked(true);
// drawCanvas(canvas);
// };
// setButtonClicked(true);
// return (
// // <div className="field" id="canvas_container">
// <div className={`notClicked ${buttonClicked ? 'clicked' : ''}`} id="canvas_container">
// {!buttonClicked && <button className="playButton" onClick={handleButtonClick}>Play</button>}
// </div>
// );
// }
// export default Field;
// function draw() {
// // Effacer le canvas
// ctx.clearRect(0, 0, canvas.width, canvas.height);
// // Dessiner la raquette
// ctx.fillRect(canvas.width - paddleWidth, paddleY, paddleWidth, paddleHeight);
// // Appeler la fonction draw à chaque frame
// requestAnimationFrame(draw);
// }
// draw(); // Appeler la fonction draw pour la première fois
// const canvas = document.getElementById('myCanvas');
// canvas.width = 500;
// canvas.height = 500;
// const ctx = canvas.getContext('2d');
// ctx.fillRect(50, 50, 1000, 1000);

View File

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import PlayButton from "../components/Game/PlayButton"; import PlayButton from "../components/Game/PlayButton.tsx";
import Ranking from "../components/Game/Ranking"; import Ranking from "../components/Game/Ranking.tsx";
import '../styles/Game.css' import '../styles/Game.css'
function Game(){ function Game(){

View File

@ -9,7 +9,7 @@ function Head()
<title>BEST PONG EVER</title> <title>BEST PONG EVER</title>
{/* <script src="./script/login.js"></script> */} {/* <script src="./script/login.js"></script> */}
<link rel="preconnect" href="https://fonts.googleapis.com"></link> <link rel="preconnect" href="https://fonts.googleapis.com"></link>
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="true"></link> {/* <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="true"></link> */}
<link href="https://fonts.googleapis.com/css2?family=Rubik+Iso&display=swap" rel="stylesheet"></link> <link href="https://fonts.googleapis.com/css2?family=Rubik+Iso&display=swap" rel="stylesheet"></link>
</div> </div>
); );

View File

@ -1,37 +1,42 @@
/* ************************************************************************** */ /* ************************************************************************** */
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* Home.jsx :+: :+: :+: */ /* Home.tsx :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */ /* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/09 08:19:04 by apommier #+# #+# */ /* Created: 2023/06/09 08:19:04 by apommier #+# #+# */
/* Updated: 2023/06/09 08:19:05 by apommier ### ########.fr */ /* Updated: 2023/06/23 15:58:14 by apommier ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /* ************************************************************************** */
// import { React, useState } from "react"; // import { React, useState } from "react";
import '../styles/Profile.css' import '../styles/Profile.css'
// import '../styles/App.css' // import '../styles/App.css'
// import DefaultPicture from "../assets/profile.jpg"; import DefaultPicture from "../assets/profile.jpg";
import WinLoss from "../components/Profile/Win_Loss"; import WinLoss from "../components/Profile/Win_Loss.tsx";
import { motion, AnimatePresence } from 'framer-motion' import { motion, AnimatePresence } from 'framer-motion'
// import { AiFillEdit } from 'react-icons/ai' // import { AiFillEdit } from 'react-icons/ai'
// import { GrClose } from 'react-icons/gr' // import { GrClose } from 'react-icons/gr'
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import ModalEdit from "../components/Profile/EditName"; import ModalEdit from "../components/Profile/EditName.tsx";
import {AiOutlineHistory} from 'react-icons/ai' import {AiOutlineHistory} from 'react-icons/ai'
import { MdQrCodeScanner, MdOutlinePhotoLibrary } from 'react-icons/md';
import { GiWingedSword, GiCrownedSkull } from 'react-icons/gi';
// import { Link } from "react-router-dom"; // import { Link } from "react-router-dom";
// import {UserProfile} from "../DataBase/DataUserProfile"; // import {UserProfile} from "../DataBase/DataUserProfile";
// import axios from "axios"; // import axios from "axios";
import api from '../script/axiosApi'; import api from '../script/axiosApi.tsx';
import { CgEditMarkup } from 'react-icons/cg' import { CgEditMarkup } from 'react-icons/cg'
import { IoCloseCircleOutline } from "react-icons/io5"; import { IoCloseCircleOutline } from "react-icons/io5";
// import * as React from 'react'; // import * as React from 'react';
// import { useState, useEffect, useParams} from "react"; // import { useState, useEffect, useParams} from "react";
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef, ChangeEventHandler } from "react";
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import {User, Conv} from "../../interfaces.tsx"
import YellowAlert from '../components/Alert/YellowAlert.tsx';
// axios.get("http://localhost/api") // axios.get("http://localhost/api")
// .then((response) => { // .then((response) => {
// response = response.json() // response = response.json()
@ -43,44 +48,71 @@ import { useParams } from 'react-router-dom';
function Profile () { function Profile () {
const DefaultPicture: string = "../assets/profile.jpg"; const [user, setUser] = useState<User>();
const [user, setUser] = useState(null); const [isLoading, setIsLoading] = useState<boolean>(true);
const [isLoading, setIsLoading] = useState(true); const [modalOpen, setModalOpen] = useState<boolean>(false);
const [modalOpen, setModalOpen] = useState(false); const [mine, setMine] = useState<boolean>(false);
const [mine, setMine] = useState(false);
const close = () => setModalOpen(false); const close = () => setModalOpen(false);
const open = () => setModalOpen(true); const open = () => setModalOpen(true);
const { username } = useParams(); const { username } = useParams();
const [selectedPhoto, setSelectedPhoto] = useState(null); // const [selectedPhoto, setSelectedPhoto] = useState();
// const [selectedPhoto, setSelectedPhoto] = useState(null);
const [profilePicture, setProfilePicture] = useState(''); const [profilePicture, setProfilePicture] = useState('');
const handleFileChange = (event) => { const handleFileChange = async (event: { target: { files: any; }; }) => {
// const file = event.target.files[0]; // const files = event.target.files;
setSelectedPhoto(event.target.files[0]); // if (files && files.length > 0) {
// try{ const photo = (event.target.files[0]);
// api.post("/picture", {picture: URL.createObjectURL(file)}) console.log("file selected")
// } if (photo) {
// catch(err){ console.log("selected photo")
// console.log(err); const formData = new FormData();
// } formData.append('photo', photo);
};
const handleUpload = async () => {
const formData: FormData = new FormData();
if (selectedPhoto){
formData.append('photo', selectedPhoto);
}
try { try {
await api.post('/picture', formData); await api.post('/picture', formData);
console.log('File uploaded successfully'); console.log('File uploaded successfully');
window.location.reload(false); window.location.reload();
} catch (error) { } catch (error) {
console.error('Error uploading file:', error); console.error('Error uploading file:', error);
} }
}
// }
}; };
// const handleUpload = async () => {
// const formData = new FormData();
// formData.append('photo', selectedPhoto);
// try {
// await api.post('/picture', formData);
// console.log('File uploaded successfully');
// window.location.reload();
// } catch (error) {
// console.error('Error uploading file:', error);
// }
// };
// const handleUpload = async (event: React.FormEvent) => {
// event.preventDefault()
// console.log("up photo")
// if (selectedPhoto) {
// console.log("selected photo")
// const formData = new FormData();
// formData.append('photo', selectedPhoto);
// try {
// await api.post('/picture', formData);
// console.log('File uploaded successfully');
// window.location.reload();
// } catch (error) {
// console.error('Error uploading file:', error);
// }
// } else {
// console.log('No file selected');
// }
// };
useEffect(()=> { useEffect(()=> {
const getUser = async ()=>{ const getUser = async ()=>{
console.log(`username= ${username}`) console.log(`username= ${username}`)
@ -125,12 +157,10 @@ function Profile () {
<img className="profile-pic" src={DefaultPicture} alt="Default Profile Picture" /> <img className="profile-pic" src={DefaultPicture} alt="Default Profile Picture" />
)} )}
<span> <span>
{isLoading ? ( {isLoading || !user ? (
<h1>Loading...</h1> <h1>Loading...</h1>
) : ( ) : (
user ? (
<h1>{user.nickname}</h1> <h1>{user.nickname}</h1>
) : ("")
)} )}
</span> </span>
@ -138,16 +168,22 @@ function Profile () {
{mine ? ( {mine ? (
<div> <div>
<motion.div onClick={() => (modalOpen ? close() : open())}> <motion.div >
<Link to="#" className="edit_name"> <Link to="#" className="edit_name" onClick={() => (modalOpen ? close() : open())}>
{modalOpen === true ? <IoCloseCircleOutline/> : <CgEditMarkup/>} {modalOpen === true ? <IoCloseCircleOutline/> : <CgEditMarkup/>}
</Link> </Link>
{modalOpen === true ? ("") : (
<>
<label htmlFor="file-input" className="edit_name"><MdOutlinePhotoLibrary/></label>
<input type="file" id="file-input" className="file-input" accept="image/*" onChange={handleFileChange} />
</>
)}
</motion.div> </motion.div>
<div> {/* <div className="file-upload-container"> */}
<input type="file" accept="image/*" onChange={handleFileChange} /> {/* <button onClick={handleUpload} className="upload-button">Upload</button> */}
<button onClick={handleUpload}>Upload</button> {/* <button onClick={handleUpload} className="upload-button">Upload</button> */}
</div> {/* </div> */}
</div> </div>
) : ( ) : (
<></> <></>
@ -166,13 +202,49 @@ function Profile () {
function Home () { function Home () {
const [move, setmove ] = useState(false); const [move, setmove ] = useState(false);
const [user, setUser] = useState([]);
const [successQr, setSuccessQr] = useState(false);
const [successSword, setSuccessSword] = useState(false);
const [successCrown, setSuccessCrown] = useState(false);
const closeQr = () => setSuccessQr(false);
const closeSword = () => setSuccessSword(false);
const closeCrown = () => setSuccessCrown(false);
useEffect(() => {
const fetchSuccess = async () => {
try {
const tmpUser = await api.get("/profile");
setUser(tmpUser.data);
}
catch (error)
{
console.log(error);
}
};
fetchSuccess();
})
return ( return (
<motion.div className="page" <motion.div className="page"
initial={{opacity: -1}} initial={{opacity: -1}}
animate={{opacity: 1}} animate={{opacity: 1}}
exit={{opacity: -1}}> exit={{opacity: -1}}>
<div>
{user.otp_verified ? (
<MdQrCodeScanner className='success' onClick={() => setSuccessQr(true)}/>
):("")}
{user.win >= 2 ? (
<GiWingedSword className="success" onClick={() => setSuccessSword(true)}/>
):("")}
{user.win >= 5 ? (
<GiCrownedSkull className="success" onClick={() => setSuccessCrown(true)}/>
):("")}
</div>
<div className="home"> <div className="home">
<motion.div animate={{x: move ? -200: 170}} <motion.div animate={{x: move ? -200: 120}}
transition={{type: "tween", duration: 0.5}}> transition={{type: "tween", duration: 0.5}}>
<Profile/> <Profile/>
</motion.div> </motion.div>
@ -186,6 +258,19 @@ function Home () {
onClick={ () => setmove(!move)}> onClick={ () => setmove(!move)}>
<Link to="#" className="history"><AiOutlineHistory/> Match History</Link> <Link to="#" className="history"><AiOutlineHistory/> Match History</Link>
</motion.div> </motion.div>
<AnimatePresence initial={false} onExitComplete={() => null}>
{successQr ? (
<YellowAlert handleClose={closeQr} text={"Success: You have the 2fa success!"} icon={1} />
) : ("")}
{successCrown ? (
<YellowAlert handleClose={closeCrown} text={"Success: 5 victory ? You won king slayer success!"} icon={2}/>
) : ("")}
{successSword ? (
<YellowAlert handleClose={closeSword} text={"Success: 2 victory ? You won the noobi warrior success!"} icon={3}/>
) : ("")}
</AnimatePresence>
</motion.div> </motion.div>
) )
} }

View File

@ -16,12 +16,15 @@ function Login42()
const data = { const data = {
grant_type: 'authorization_code', grant_type: 'authorization_code',
client_id: 'u-s4t2ud-6d29dfa49ba7146577ffd8bf595ae8d9e5aaa3e0a9615df18777171ebf836a41', // client_id: 'u-s4t2ud-6d29dfa49ba7146577ffd8bf595ae8d9e5aaa3e0a9615df18777171ebf836a41',
client_secret: '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', // client_secret: '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',
client_id: process.env.REACT_APP_CLIENT_UID,
client_secret: process.env.REACT_APP_API_SECRET,
code: code, code: code,
redirect_uri: 'http://localhost:8080/login42', redirect_uri: 'http://' + process.env.REACT_APP_BASE_URL + '/login42',
}; };
axios.post('https://api.intra.42.fr/oauth/token', data) axios.post('https://api.intra.42.fr/oauth/token', data)
.then(response => { .then(response => {
// handle success response // handle success response
@ -35,7 +38,7 @@ function Login42()
return ( return (
<div> <div>
<p>"COUCOU LOGIN$@ jeje" </p> <p>"COUCOU LOGIN" </p>
{/* <script src="../script/login42.js"></script> */} {/* <script src="../script/login42.js"></script> */}
</div> </div>
); );

View File

@ -1,14 +1,31 @@
import React from "react"; import React from "react";
// import Sidebar from '../components/Messages/Sidebar' // import Sidebar from '../components/Messages/Sidebar'
import Chats from "../components/Messages/Chats" import Chats from "../components/Messages/Chats.tsx"
import '../styles/Messages.css' import '../styles/Messages.css'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
// import {io} from 'socket.io-client' // import {io} from 'socket.io-client'
function Messages() { function Messages() {
// const socket = useRef(io("ws://localhost:8900"))
// useEffect(() => {
// setSocket(io("ws://localhost:8900"))
// }, [])
// const socket = socketIO.connect('http://localhost:4000');
// axios.get('http://localhost/api/user/id')
// .then(function());
// console.log(socket)
// useEffect(() => {
// socket.current.emit("addUser", user._id);
// socket.current.on("getUsers", users=>{
// console.log(users)
// })
// }, [user])
return ( return (
<div> <>
<motion.div className="home" <motion.div className="home"
initial={{opacity: 0}} initial={{opacity: 0}}
animate={{opacity: 1}} animate={{opacity: 1}}
@ -18,7 +35,7 @@ function Messages() {
<Chats/> <Chats/>
</div> </div>
</motion.div> </motion.div>
</div> </>
); );
} }

View File

@ -3,11 +3,10 @@ import React, { useEffect, useRef, useState } from "react";
// import { redirect } from "react-router-dom"; // import { redirect } from "react-router-dom";
import "../styles/App.css"; import "../styles/App.css";
import api from '../script/axiosApi'; import api from '../script/axiosApi.tsx';
import QRCodeStyling from "qr-code-styling"; import QRCodeStyling from "qr-code-styling";
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { MutableRefObject } from "react";
@ -31,9 +30,9 @@ const qrCode = new QRCodeStyling({
function QrCode () { function QrCode () {
// const url = "https://www.youtube.com"; // const url = "https://www.youtube.com";
// const ref = useRef(null); // const ref = useRef(null);
const ref: MutableRefObject<HTMLElement | null> = {current: null}; const ref = useRef<HTMLDivElement>(null);
const [user, setUser] = useState(false); const [user, setUser] = useState(false);
const [url, setUrl] = useState(false); const [url, setUrl] = useState("");
const [secret, setSecret] = useState(false); const [secret, setSecret] = useState(false);
const [code, setCode] = useState(''); const [code, setCode] = useState('');
const [activated, setActivated] = useState(false); const [activated, setActivated] = useState(false);
@ -41,10 +40,8 @@ function QrCode () {
// const history = useHistory(); // const history = useHistory();
useEffect(() => { useEffect(() => {
if (ref.current){ if (ref.current)
qrCode.append(ref.current); qrCode.append(ref.current);
}
const getUser = async ()=>{ const getUser = async ()=>{
try{ try{
const tmpUser = await api.get("/profile"); const tmpUser = await api.get("/profile");
@ -70,13 +67,11 @@ function QrCode () {
}, []); }, []);
useEffect(() => { useEffect(() => {
qrCode.update({ qrCode.update({data: url});
data: url
});
}, [url]); }, [url]);
const handleKeyPress = async (e)=>{ const handleKeyPress = async (e: { key: string; })=>{
// console.log(`e in press= ${e.key}`) // console.log(`e in press= ${e.key}`)
if (e.key !== "Enter") if (e.key !== "Enter")
return ; return ;
@ -91,8 +86,8 @@ function QrCode () {
// history.push('/login') // history.push('/login')
const path = 'http://' + process.env.REACT_APP_BASE_URL + '/'; const path = 'http://' + process.env.REACT_APP_BASE_URL + '/';
window.history.pushState({}, null, path); window.history.pushState({}, '', path);
window.location.reload(false); window.location.reload();
} }
else else
@ -111,8 +106,8 @@ function QrCode () {
try { try {
await api.post("/deleteOtp") await api.post("/deleteOtp")
// const path = 'http://' + process.env.REACT_APP_BASE_URL + '/'; // const path = 'http://' + process.env.REACT_APP_BASE_URL + '/';
// window.history.pushState({}, null, path); // window.history.pushState({}, '', path);
window.location.reload(false); window.location.reload();
} catch(err) { } catch(err) {
console.log(err); console.log(err);
} }
@ -143,6 +138,7 @@ function QrCode () {
<h3>{secret}</h3> <h3>{secret}</h3>
<h1>Or Scan The QRCode</h1> <h1>Or Scan The QRCode</h1>
<div ref={ref} /> <div ref={ref} />
{/* <div>{ref}</div> */}
</> </>
)} )}

View File

@ -1,6 +1,6 @@
// import io from 'socket.io-client'; // import io from 'socket.io-client';
import api from '../script/axiosApi'; import api from '../script/axiosApi.tsx';
// import { useEffect, useRef } from 'react'; // import { useEffect, useRef } from 'react';
import io from 'socket.io-client'; import io from 'socket.io-client';
@ -8,9 +8,13 @@ import io from 'socket.io-client';
// const socket = io('http://86.209.110.20:4000'); // const socket = io('http://86.209.110.20:4000');
// const socket = io('http://172.29.113.91:4000'); // const socket = io('http://172.29.113.91:4000');
function DrawCanvas(option) { interface GameProps {
privateParty: boolean,
username?: string
gameId?: number
}
function DrawCanvas(option: number, gameParam: GameProps) {
console.log(`option= ${option}`); console.log(`option= ${option}`);
const superpowerModifier = option & 1; // Retrieves the superpower modifier const superpowerModifier = option & 1; // Retrieves the superpower modifier
@ -25,12 +29,39 @@ function DrawCanvas(option) {
// const socketRef = useRef(null); // const socketRef = useRef(null);
// socketRef.current = io('http://localhost:4000'); // socketRef.current = io('http://localhost:4000');
const socket = io('http://' + process.env.REACT_APP_BASE_URL + ':4000');
function launchGame()
{
if (!gameParam.privateParty)
{
console.log("laucnh matchmaking")
matchmaking();
}
else if (!gameParam.gameId)
{
console.log("laucnh private")
privateParty();
}
else
{
console.log("join private")
joinPrivateParty();
}
}
// const socket = socketRef.current // const socket = socketRef.current
console.log("start function"); console.log("start function");
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// let canvas: HTMLElement | null;
const canvas = document.getElementById('myCanvas') as HTMLCanvasElement | null;;
if (!canvas)
return ;
const ctx = canvas.getContext('2d');
if(!ctx)
return ;
const socket = io('http://localhost:4000', { transports: ['polling'] });
// useEffect(() => { // useEffect(() => {
// console.log("useeffect?????????????????") // console.log("useeffect?????????????????")
// return () => { // return () => {
@ -49,8 +80,8 @@ function DrawCanvas(option) {
//socket //socket
let myId = 0; let myId = 0;
let gameId = 0; let gameId = 0;
let opName; let opName: string
let opRank; let opRank: number;
//general canvas //general canvas
let running = true; let running = true;
@ -99,48 +130,45 @@ function DrawCanvas(option) {
//======================================================================================================== //========================================================================================================
//======================================================================================================== //========================================================================================================
// Socket handler // Socket ON
//======================================================================================================== //========================================================================================================
//======================================================================================================== //========================================================================================================
function matchmaking() socket.on('pong:win', async () => {
{ myScore = maxScore;
console.log(`id ion matcj= ${myId}`) console.log("instant win opponent disconnect")
const info = { // const data = {
id: myId, // myScore: myScore,
option: option, // opScore: hisScore,
}; // opName: opName,
socket.emit('pong:matchmaking', info); // opRank: opRank,
}
// socket.on('pong:gameId', (data) => {
// console.log("gameId received")
// gameId = data;
// // api.get('/profile');
// let myName;
// api.get('/profile').then((data) => {
// // Faire quelque chose avec les données
// console.log(data);
// myName = data.data.username;
// console.log(`myname= ${myName}`);
// }).catch((error) => {
// console.log(error);
// // exit() ;
// return;
// });
// const info = {
// id: myId,
// name: myName,
// gameId: gameId,
// }; // };
// console.log("emit to name")
// socket.emit('pong:name', info);
// });
socket.on('pong:gameId', async (data) => { // await api.post('/win', data);
console.log("after request1")
await api.post('/status', {status: 1});
console.log("after request2")
//disconnect ?
running = false;
socket.emit('pong:disconnect', {id: myId});
console.log("before reload")
// window.location.replace("http://" + process.env.REACT_APP_BASE_URL + "/pong");
// window.location.reload();
return ;
// console.log("send all ?? win");
});
socket.on('pong:privateId', async (data) => {
console.log("private id = ", data)
try{
await api.post("/partyInvite", {username: gameParam.username, gameId: data});
} catch(err) {
console.log(err)
}
});
socket.on('pong:gameId', async (data) => {
console.log("gameId received"); console.log("gameId received");
gameId = data; gameId = data;
@ -169,37 +197,38 @@ function DrawCanvas(option) {
} }
}); });
socket.on('pong:name', (data) => { socket.on('pong:name', (data) => {
opName = data; opName = data;
console.log(`opponent Name= ${opName}`) console.log(`opponent Name= ${opName}`)
}); });
socket.on('connect', () => { socket.on('connect', () => {
console.log('Connected to NestJS server'); console.log('Connected to NestJS server');
}); });
socket.on('pong:clientId', (data) => { socket.on('pong:clientId', (data) => {
console.log("receive id") console.log("receive id")
myId = data; myId = data;
console.log(`id is= ${myId}`) console.log(`id is= ${myId}`)
}); launchGame();
});
socket.on('pong:info', (data) => { socket.on('pong:info', (data) => {
oPaddleY = (data.paddleY / data.height) * canvas.height//canvas.height - data.ballY; oPaddleY = (data.paddleY / data.height) * canvas.height//canvas.height - data.ballY;
ballX = canvas.width - (data.ballX * (canvas.width / data.width));//- data.ballX; ballX = canvas.width - (data.ballX * (canvas.width / data.width));//- data.ballX;
ballY = ((data.ballY / data.height) * canvas.height)//canvas.height - data.ballY; ballY = ((data.ballY / data.height) * canvas.height)//canvas.height - data.ballY;
vX = -data.vX; vX = -data.vX;
vY = data.vY; vY = data.vY;
}); });
socket.on('pong:paddle', (data) => { socket.on('pong:paddle', (data) => {
console.log("paddle info receive") console.log("paddle info receive")
oPaddleY = (data.paddleY / data.height) * canvas.height oPaddleY = (data.paddleY / data.height) * canvas.height
}); });
socket.on('pong:power', (data) => { socket.on('pong:power', (data) => {
console.log("paddle info receive") console.log("paddle info receive")
oPaddleY = 0; oPaddleY = 0;
@ -212,11 +241,62 @@ function DrawCanvas(option) {
console.log('Cinq secondes se sont écoulées.'); console.log('Cinq secondes se sont écoulées.');
}, 5000); }, 5000);
// oPaddleY = (data.paddleY / data.height) * canvas.height // oPaddleY = (data.paddleY / data.height) * canvas.height
}); });
socket.on('pong:point', (data) => {
// hisScore += 1;
console.log("gain point");
// if (vX != 0)
// {
// console.log("up point");
myScore = data.point;
// }
vX = 0;
vY = 0;
ballX = canvas.width / 2;
ballY = canvas.height / 2;
});
//========================================================================================================
//========================================================================================================
// Socket EMIT
//========================================================================================================
//========================================================================================================
function matchmaking()
{
console.log(`id ion matcj= ${myId}`)
const info = {
id: myId,
option: option,
};
socket.emit('pong:matchmaking', info);
}
function privateParty()
{
console.log(`id private party= ${myId}`)
const info = {
id: myId,
option: option,
};
socket.emit('pong:privateParty', info);
}
function joinPrivateParty()
{
console.log(`id private party= ${myId}`)
const info = {
id: myId,
gameId: gameParam.gameId,
option: option,
};
socket.emit('pong:joinParty', info);
}
function send_info() function send_info()
{ {
if (gameId === 0) if (!gameId || !canvas)
return ; return ;
const info = { const info = {
id: myId, id: myId,
@ -234,7 +314,7 @@ function DrawCanvas(option) {
function send_point() function send_point()
{ {
if (gameId === 0) if (!gameId || !canvas)
return ; return ;
console.log("send point"); console.log("send point");
const info = { const info = {
@ -245,23 +325,9 @@ function DrawCanvas(option) {
socket.emit('pong:point', info); socket.emit('pong:point', info);
} }
socket.on('pong:point', (data) => {
// hisScore += 1;
console.log("gain point");
// if (vX != 0)
// {
// console.log("up point");
myScore = data.point;
// }
vX = 0;
vY = 0;
ballX = canvas.width / 2;
ballY = canvas.height / 2;
});
function send_paddle_info() function send_paddle_info()
{ {
if (gameId === 0) if (!gameId || !canvas)
return ; return ;
const info = { const info = {
id: myId, id: myId,
@ -275,6 +341,8 @@ function DrawCanvas(option) {
function use_power() function use_power()
{ {
if (!canvas)
return ;
const info = { const info = {
gameId: gameId, gameId: gameId,
width: canvas.width, width: canvas.width,
@ -286,7 +354,7 @@ function DrawCanvas(option) {
function send_forced_info() function send_forced_info()
{ {
if (gameId === 0) if (!gameId || !canvas)
return ; return ;
const info = { const info = {
gameId: gameId, gameId: gameId,
@ -311,6 +379,8 @@ function DrawCanvas(option) {
function drawcenter() function drawcenter()
{ {
// ctx.restore(); // ctx.restore();
if (!ctx || !canvas)
return ;
ctx.fillStyle = 'white'; ctx.fillStyle = 'white';
ctx.fillRect(canvas.width / 2 - ctx.lineWidth / 2, 0, canvas.width / 300, canvas.height); ctx.fillRect(canvas.width / 2 - ctx.lineWidth / 2, 0, canvas.width / 300, canvas.height);
@ -323,11 +393,13 @@ function DrawCanvas(option) {
ctx.font = canvas.width * 0.1 + "px Arial"; ctx.font = canvas.width * 0.1 + "px Arial";
ctx.textAlign = "center"; ctx.textAlign = "center";
ctx.textBaseline = "middle"; ctx.textBaseline = "middle";
ctx.fillText(myScore, canvas.width/4, canvas.height/8); ctx.fillText(myScore.toString(), canvas.width/4, canvas.height/8);
ctx.fillText(hisScore, canvas.width/1.25, canvas.height/8); ctx.fillText(hisScore.toString(), canvas.width/1.25, canvas.height/8);
} }
function drawPaddle() { function drawPaddle() {
if (!ctx || !canvas)
return ;
ctx.fillStyle = 'white'; ctx.fillStyle = 'white';
ctx.fillRect(paddleX, paddleY, paddleWidth, paddleHeight); ctx.fillRect(paddleX, paddleY, paddleWidth, paddleHeight);
ctx.fillRect(canvas.width - paddleX - paddleWidth, oPaddleY, paddleWidth, opPaddleHeight); ctx.fillRect(canvas.width - paddleX - paddleWidth, oPaddleY, paddleWidth, opPaddleHeight);
@ -335,6 +407,8 @@ function DrawCanvas(option) {
function drawball() function drawball()
{ {
if (!ctx)
return ;
ctx.beginPath(); ctx.beginPath();
ctx.arc(ballX, ballY, ballRadius, 0, 2 * Math.PI); ctx.arc(ballX, ballY, ballRadius, 0, 2 * Math.PI);
// ctx.lineWidth = 2; // ctx.lineWidth = 2;
@ -349,29 +423,52 @@ function DrawCanvas(option) {
//======================================================================================================== //========================================================================================================
//======================================================================================================== //========================================================================================================
matchmaking();
// while (!gameId) // while (!gameId)
// ; // ;
// Define a function to stop the drawing process // Define a function to stop the drawing process
const stopDrawCanvas = () => { const stopDrawCanvas = async () => {
running = false; running = false;
console.log("stopDrawCanvas 1")
if (gameParam.privateParty && !gameId) //delete invite
{
console.log("stopDrawCanvas2")
try{
// const info = {
// id: myId,
// option: option,
// };
await api.post("deleteInvite", {username: gameParam.username})
}
catch (err){
console.log(err)
}
}
socket.emit('pong:disconnect', {id: myId});
window.location.replace("http://" + process.env.REACT_APP_BASE_URL + "/pong");
// window.location.reload();
// Perform any necessary cleanup tasks // Perform any necessary cleanup tasks
// ... // ...
}; };
function draw(timestamp) async function draw(timestamp: number)
{ {
console.log("turning"); console.log("turning, running= ", running);
if (!running) if (!running)
return ; return ;
if (gameId === 0 ) if (!gameId || !canvas )
{ {
// console.log("nogameid score= ", myScore);
requestAnimationFrame(draw); requestAnimationFrame(draw);
return ; return ;
} }
if (myScore === maxScore || hisScore === maxScore) if (myScore === maxScore || hisScore === maxScore)
{ {
console.log("maxScore!!!!")
const data = { const data = {
myScore: myScore, myScore: myScore,
opScore: hisScore, opScore: hisScore,
@ -380,17 +477,23 @@ function draw(timestamp)
}; };
if (myScore === maxScore) if (myScore === maxScore)
{ {
api.post('/win', data); await api.post('/win', data);
api.post('/status', {status: 1}); await api.post('/status', {status: 1});
console.log("send win"); //disconnect ?
socket.emit('pong:disconnect', {id: myId});
console.log("send all ?? win");
} }
else else
{ {
api.post('/loss', data); await api.post('/loss', data);
api.post('/status', {status: 1}); await api.post('/status', {status: 1});
socket.emit('pong:disconnect', {id: myId});
//disconnect ?
console.log("send loose"); console.log("send loose");
} }
window.location.replace("http://" + process.env.REACT_APP_BASE_URL + "/pong"); window.location.replace("http://" + process.env.REACT_APP_BASE_URL + "/pong");
// window.location.reload();
return ; return ;
} }
@ -399,6 +502,9 @@ function draw(timestamp)
ballX += vX * deltaTime * canvas.width; ballX += vX * deltaTime * canvas.width;
ballY += vY * deltaTime * canvas.height; ballY += vY * deltaTime * canvas.height;
if (!ctx)
return ;
// requestAnimationFrame(draw);
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPaddle(); drawPaddle();
drawcenter(); drawcenter();
@ -435,14 +541,17 @@ function draw(timestamp)
} }
function updatePaddlePosition(newY) function updatePaddlePosition(newY: number)
{ {
if (newY >= 0 && newY <= canvas.height - paddleHeight)
if (canvas && newY >= 0 && newY <= canvas.height - paddleHeight)
paddleY = newY; paddleY = newY;
} }
function is_collision() function is_collision()
{ {
if (!canvas)
return ;
if (ballX <= paddleX + paddleWidth + ballRadius) if (ballX <= paddleX + paddleWidth + ballRadius)
{ {
if (ballY <= paddleY + paddleHeight + ballRadius && ballY >= paddleY - ballRadius)//touch paddle if (ballY <= paddleY + paddleHeight + ballRadius && ballY >= paddleY - ballRadius)//touch paddle
@ -467,15 +576,12 @@ function draw(timestamp)
ballY = ballRadius + 2 ballY = ballRadius + 2
// send_info(); // send_info();
} }
// else if (ballX + ballRadius + 2 >= canvas.width) //touch right wall
// {
// vX = -vX;
// // send_info();
// }
} }
function is_out() function is_out()
{ {
if (!canvas)
return ;
if (ballX < 0) if (ballX < 0)
{ {
if (ballY <= paddleY + paddleHeight + ballRadius && ballY >= paddleY - ballRadius) if (ballY <= paddleY + paddleHeight + ballRadius && ballY >= paddleY - ballRadius)
@ -495,7 +601,9 @@ function draw(timestamp)
} }
if (ballX > canvas.width) if (ballX > canvas.width)
{ {
console.log("win point") // if (ballX > canvas.width * 2)
// socket.emit
// console.log("win point")
// if (ballY <= paddleY + paddleHeight + ballRadius && ballY >= paddleY - ballRadius) // if (ballY <= paddleY + paddleHeight + ballRadius && ballY >= paddleY - ballRadius)
// { // {
// console.log('true hehe'); // console.log('true hehe');
@ -518,17 +626,10 @@ function draw(timestamp)
//======================================================================================================== //========================================================================================================
//======================================================================================================== //========================================================================================================
// Listener // Key/Mouse/Finger Listener
//======================================================================================================== //========================================================================================================
//======================================================================================================== //========================================================================================================
document.addEventListener('resize', event => {
// event.height
// event.width
const { clientWidth, clientHeight } = canvas.parentElement;
console.log(`resize detected widht= ${clientWidth} height= ${clientHeight}`)
});
document.addEventListener('mousemove', event => { document.addEventListener('mousemove', event => {
const mouseY = event.clientY; const mouseY = event.clientY;

View File

@ -68,8 +68,8 @@ function SuccessToken() {
window.location.replace("http://" + process.env.REACT_APP_BASE_URL + "/pong"); window.location.replace("http://" + process.env.REACT_APP_BASE_URL + "/pong");
// const path = 'http://' + process.env.REACT_APP_BASE_URL + '/'; // const path = 'http://' + process.env.REACT_APP_BASE_URL + '/';
// window.history.pushState({}, null, path); // window.history.pushState({}, '', path);
// window.location.reload(false); // window.location.reload();
} }
else else
@ -197,8 +197,8 @@ function SuccessToken() {
// // history.push('/login') // // history.push('/login')
// const path = 'http://' + process.env.REACT_APP_BASE_URL + '/'; // const path = 'http://' + process.env.REACT_APP_BASE_URL + '/';
// window.history.pushState({}, null, path); // window.history.pushState({}, '', path);
// window.location.reload(false); // window.location.reload();
// } // }
// else // else

View File

@ -9,7 +9,7 @@
input.qr{ input.qr{
width: 20%; width: 20%;
border-radius: 5px; border-radius: 5px;
background-color: rgb(66, 66, 66); background-color: rgb(0, 0, 0);
margin : 1%; margin : 1%;
color:white; color:white;
} }

View File

@ -89,6 +89,17 @@ span {
border-radius: 50%; border-radius: 50%;
} }
.success {
height: 2%;
width: 2%;
/* border: solid; */
margin-top: 1%;
margin: 3vh;
/* margin-bottom: -12vh; */
/* border-color: black; */
/* border-radius: 50%; */
}
.header-pic{ .header-pic{
text-align: end; text-align: end;
/* id: right; */ /* id: right; */

View File

@ -204,6 +204,18 @@ p {
margin: 1%; margin: 1%;
} }
.darkSubmit{
display: inline-block;
color: white;
background-color: #282155;
border-radius: 10px;
padding: 5px;
font-size: 1.2rem;
text-decoration: none;
font-weight:lighter;
margin: 1%;
}
.div_submit { .div_submit {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
@ -276,6 +288,12 @@ p {
background-color: rgba(212, 175, 55, 0.7); background-color: rgba(212, 175, 55, 0.7);
font-size: 25px; font-size: 25px;
color: rgba(255, 255, 255, 1); color: rgba(255, 255, 255, 1);
flex-wrap: wrap;
}
.yellowAlert::p {
overflow-wrap: break-word;
max-width: 1000px;
} }
.modalSetting{ .modalSetting{
@ -298,7 +316,7 @@ p {
.settingSecondPart{ .settingSecondPart{
margin-top: 10%; margin-top: 10%;
margin-left: 10%; margin-left: 5%;
/* margin-left: 20%; */ /* margin-left: 20%; */
} }
@ -316,3 +334,20 @@ input.in{
border-radius: 12px; border-radius: 12px;
width: 70%; width: 70%;
} }
input.in_howLong{
margin-top: 14.5%;
margin-left: 0px;
background-color: black;
color: white;
border-radius: 12px;
width: 15%;
}
.mdp{
background-color : black;
border-radius: 8px;
color: white;
width: 20%;
}

View File

@ -1,11 +1,75 @@
/* Container style */
.file-upload-container {
margin-left: 2rem;
font-size: 1.7rem;
/* background: #5843e4; */
/* color:#f5f5f5; */
margin: 0 16px;
text-decoration: none;
padding: 10px 16px;
border-radius: 20px;
display: flex;
align-items: center;
gap: 10px;
}
/* File input style */
.file-input {
/* background: #5843e4; */
/* color:#f5f5f5; */
display: none; /* Hide the default file input */
}
.file-label {
padding: 8px 12px;
background-color: #f2f2f2;
color: #555;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.file-label:hover {
background-color: #e0e0e0;
}
/* Button style */
.upload-button {
padding: 8px 12px;
background-color: #5843e4;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.upload-button:hover {
background-color: #0056b3;
}
.page { .page {
text-align: center; text-align: center;
overflow-y: scroll;
/* height: 50vh; */ /* height: 50vh; */
/* width: 50vh; */ /* width: 50vh; */
/* background-color: black; */ /* background-color: black; */
} }
.profile { .profile {
align-items: center;
text-align: center;
flex-direction: row; flex-direction: row;
color: white; color: white;
} }
@ -18,7 +82,7 @@
border-color: red; border-color: red;
/* border-image: linear-gradient(90deg, #5843e4, #5a0760); */ /* border-image: linear-gradient(90deg, #5843e4, #5a0760); */
margin-top: 20px; /* margin-top: 20px; */
} }
.home{ .home{
@ -45,7 +109,7 @@
.div_history { .div_history {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
margin-top: -80px; /* margin-top: -80px; */
} }
.edit_name { .edit_name {

View File

@ -2,7 +2,7 @@
/* display: flex; */ /* display: flex; */
/* flex-direction: column; */ /* flex-direction: column; */
/* background-color: red; */ /* background-color: red; */
height: 70vh; height: 60vh;
/* padding: 15px; */ /* padding: 15px; */
/* overflow: scroll; */ /* overflow: scroll; */

View File

@ -1,16 +1,21 @@
.playButton { .playButton {
background-image: linear-gradient(90deg, #5843e4, #5a0760);; background-image: linear-gradient(90deg, #5843e4, #5a0760);
display: flex;
flex-wrap: wrap;
overflow: hidden;
border-radius: 5vh; border-radius: 5vh;
color: white; color: white;
display: block; /* display: block; */
margin: auto; margin: auto;
margin-top: 30vh; margin-top: 30vh;
padding: 2vh 5vw; padding: 2vh 4vw;
height: 10vh; height: 10vh;
width: 20vw; width: 20vw;
font-size: 300%; font-size: 250%;
text-align: center;
} }
.field { .field {
background-color: rgb(249, 249, 249); background-color: rgb(249, 249, 249);
/* border-radius: 5vh; */ /* border-radius: 5vh; */
@ -22,7 +27,7 @@
height: 80%; height: 80%;
width: 80%; width: 80%;
/* font-size: 300%; */ /* font-size: 300%; */
} }
.clicked{ .clicked{
/* justify-content: center; */ /* justify-content: center; */
@ -40,6 +45,29 @@
/* padding-top: 25; */ /* padding-top: 25; */
/* padding-top: 177.77% */ /* padding-top: 177.77% */
} }
@media screen and (max-width: 900px){
.playButton{
font-size: 200%;
}
}
@media screen and (max-width: 700px){
.playButton{
font-size: 150%;
}
}
@media screen and (max-width: 530px){
.playButton{
font-size: 100%;
}
}
@media screen and (max-width: 350px){
.playButton{
font-size: 50%;
}
}
#myCanvas { #myCanvas {
background-color: rgb(124, 47, 47); background-color: rgb(124, 47, 47);

View File

@ -77,11 +77,6 @@ input{
text-decoration: none; text-decoration: none;
} }
label{
}
.login {
}
.loginForm { .loginForm {
align-items: center; align-items: center;

View File

@ -1,8 +0,0 @@
{
"compiledOptions": {
"composite": true,
"allowJs": true,
"allowImportingTsExtensions": true,
"jsx": "react"
}
}

View File

@ -1,2 +0,0 @@
cc toi
cc 2

View File

@ -11,7 +11,7 @@ services:
# command: sh -c "envsubst < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" # command: sh -c "envsubst < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
ports: ports:
- 80:80 - 8080:8080
volumes: volumes:
- ./conf/nginx.conf:/etc/nginx/conf.d/default.conf - ./conf/nginx.conf:/etc/nginx/conf.d/default.conf
# volumes: # volumes:
@ -34,7 +34,7 @@ services:
# depends_on: # depends_on:
# - nginx # - nginx
ports: ports:
- 8080:8080 - 8001:8001
volumes: volumes:
- ./containers/react:/app - ./containers/react:/app
# - ./containers/react:/app # - ./containers/react:/app