Compare commits

..

16 Commits

Author SHA1 Message Date
82633fb869 docs: add comprehensive README for full-stack Pong gaming platform 2025-10-02 10:15:28 +02:00
Alexandre POMMIER
cdd2836e7c merge 2023-06-23 16:01:09 +02:00
Alexandre POMMIER
78c3c19de8 project work in 42 2023-06-23 15:47:57 +02:00
Little Dipper
f04ea25574 remise des success a 0 ajout de l'interaction 2023-06-22 00:31:59 +02:00
Little Dipper
6bf99bd55e success on profile add input for time mute modif change picture button 2023-06-21 20:59:26 +02:00
9cb31f88de env to localhost 2023-06-21 08:16:09 +02:00
Little Dipper
7ae25c68ba success 2023-06-21 05:53:47 +02:00
Little Dipper
b8692d064c ajout bouton private/public 2023-06-21 03:45:13 +02:00
Little Dipper
660088fb74 button 2023-06-21 02:49:02 +02:00
90c3e3e0b7 fix bug pong decconection and win/loss fix history fix mute ? ban 2023-06-21 02:11:11 +02:00
Little Dipper
93ed2a7eff input to select ajout mot de si channel prive 2023-06-21 01:57:37 +02:00
b53151ac5f fix all error tsx add tsconfig and some type in interface.tsx 2023-06-20 15:37:05 +02:00
0c04e29cc7 fix error everywhere fix name, pong private party, blocked effective ? 2023-06-19 22:07:06 +02:00
f137b84893 Merge branch 'online' into apommier 2023-06-18 21:47:15 +02:00
8d6e3e95b3 little error canvas 2023-06-18 21:45:02 +02:00
3ca457a380 reload when create conv 2023-06-18 21:43:19 +02:00
68 changed files with 2003 additions and 960 deletions

10
.env
View File

@ -14,22 +14,22 @@
NGINX_ENVSUBST_TEMPLATE_SUFFIX=".conf" NGINX_ENVSUBST_TEMPLATE_SUFFIX=".conf"
# BASE_URL=http://localhost # BASE_URL=http://localhost
BASE_URL=localhost BASE_URL=localhost:8080
REACT_APP_BASE_URL=localhost REACT_APP_BASE_URL=localhost:8080
REDIRECT_URI=http://localhost/api/auth/login REDIRECT_URI=http://localhost:8080/api/auth/login
#postgres var #postgres var
# POSTGRES_HOST=127.0.0.1 # POSTGRES_HOST=127.0.0.1
# DB_TYPE=postgres # DB_TYPE=postgres
POSTGRES_HOST=postgresql POSTGRES_HOST=postgresql
POSTGRES_USER=postgres POSTGRES_USER=postgres
POSTGRES_PASSWORD=pass POSTGRES_PASSWORD=postgres
POSTGRES_DATABASE=postgres POSTGRES_DATABASE=postgres
MODE=DEV MODE=DEV
#port #port
API_PORT=3000 API_PORT=3000
# REACT_PORT=3000 (current = 8080) # REACT_PORT=3000 (current = 8080)
NGINX_PORT=80 NGINX_PORT=8080
PONG_PORT=4000 PONG_PORT=4000
CHAT_PORT=4001 CHAT_PORT=4001
POSTGRES_PORT=5432 POSTGRES_PORT=5432

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,44 +169,77 @@ 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]}`); //========================================================================================================
//========================================================================================================
// // payload.ballX
// // payload.ballY
// // payload.
// if (clientIds[0] === payload.id)
// { // @SubscribeMessage('pong:invite')
// // console.log("client 0 true"); // createPrivateGame(client: Socket, payload: any): void {
// if (payload.ballX <= payload.width / 2) // //after invite accepted ?
// this.clients[clientIds[1]].emit('pong:info', payload); // //set the two user in a game ?
// }
// 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: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
//========================================================================================================
//========================================================================================================
@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

2
containers/react/assets.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
declare module '*.jpg';
declare module 'styled-components'

View File

@ -0,0 +1,48 @@
export interface User {
id: number;
otp_enabled: boolean;
otp_verified: boolean;
otp_base32: string;
nickname: string;
username: string;
photo: Buffer;
password: string;
win: number;
loss: number;
rank: number;
status: number;
userId: number;
friendRequest: string[];
partyInvite: Record<string, string>[];
friends: string[];
blocked: string[];
}
export interface Conv {
id: number;
members?: string[];
name: string
group: boolean
private: boolean
banned?: string[];
muted?: string[];
admin?: string[];
owner?: string;
password?: string;
}
export interface Message {
id: number;
convId: number;
sender: string;
text: string;
createdAt?: Date;
}
export interface Matchlog {
id: number;
opponent: string;
myScore: number;
opScore: number;
parent: User;
}

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"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

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>
@ -356,7 +443,24 @@ 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 />
<span>New Game</span>
</motion.div>
{newGameModalOpen && <GameModal handleClose={closeNewGameModal} />}
</UserChat>
<UserChat>
<motion.div className="newMessage" onClick={openNewConversationModal}>
<GrAdd /> <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

@ -0,0 +1,152 @@
import { motion } from "framer-motion";
import Backdrop from "../Sidebar/Backdrop.tsx";
import '../../styles/Messages.css';
import { useState, useEffect } from "react";
import api from "../../script/axiosApi.tsx";
import React from "react";
import {User} from "../../../interfaces.tsx"
// import { useNavigate } from "react-router-dom";
const dropIn = {
hidden: { y: "-100vh", opacity: 0 },
visible: {
y: "0",
opacity: 1,
transition: {
duration: 0.3,
type: "spring",
damping: 100,
stiffness: 500,
},
},
exit: { y: "100vh", opacity: 0 },
};
interface ModalGame {
handleClose: Function,
// convId: string
}
const GameModal = ({ handleClose }: ModalGame) => {
const [users, setUsers] = useState([]);
// const [user, setUser] = useState();
const [selectedUser, setSelectedUser] = useState('');
// const [convs, setConvs] = useState([]);
const [channel, setChannel] = useState('');
// const history = useNavigate();
useEffect(() => {
const fetchData = async () => {
try {
const tmpUsers = await api.get("/users");
// const tmpUser = await api.get("/profile");
// const tmpConvs = await api.get("/convs");
setUsers(tmpUsers.data);
// setUser(tmpUser.data);
// setConvs(tmpConvs.data);
} catch (err) {
console.log(err);
}
};
fetchData();
}, []);
const handleUserChange = (event: { target: { value: React.SetStateAction<string>; }; }) => {
setSelectedUser(event.target.value);
};
// const joinChannel = async () => {
// try {
// await api.post("/join", { convId: channel });
// } catch (err) {
// console.log(err);
// }
// };
// const handleCheckButtonClick = () => {
// // Perform your check action here
// console.log("Checking user:", selectedUser);
// };
const handleButtonClick = async () => {
// let path = `play?`;
let path = `http://` + process.env.REACT_APP_BASE_URL + `/pong/play?`;
const superpowerCheckbox = document.querySelector<HTMLInputElement>('input[value="superpower"]');
if (superpowerCheckbox && superpowerCheckbox.checked) {
path += 'superpower=true&';
}
const obstacleCheckbox = document.querySelector<HTMLInputElement>('input[value="obstacle"]');
if (obstacleCheckbox && obstacleCheckbox.checked) {
path += 'obstacle=true&';
}
const speedCheckbox = document.querySelector<HTMLInputElement>('input[value="speed"]');
if (speedCheckbox && speedCheckbox.checked) {
path += 'speed=true&';
}
if (selectedUser.length > 0)
path += 'username=' + selectedUser;//important here
// Remove the trailing '&' character
// path = path.slice(0, -1);
// console.log(path)
// await api.post("/partyInvite", {username: selectedUser, gameId})
// console.log("path= ", path)
// history(path, { replace: true });
// window.location.replace(path);
window.history.pushState({}, '', path);
window.location.reload();
// history(path);
};
return (
<Backdrop onClick={handleClose}>
<motion.div
onClick={(e) => e.stopPropagation()}
className="modal"
// variant={dropIn}
initial="hidden"
animate="visible"
exit="exit"
>
<div>
<select value={selectedUser} onChange={handleUserChange}>
<option value="">Select a user</option>
{users.map((user: User) => (
<option key={user.id} value={user.username}>
{user.username}
</option>
))}
</select>
</div>
<div className="notClicked" id="canvas_container">
{/* <button onClick={handleButtonClick}>Draw on Canvas</button> */}
<div className='checkbox'>
<p><input type="checkbox" value="superpower"/> Super Power </p>
<p><input type="checkbox" value="obstacle"/> Obstacle </p>
<p><input type="checkbox" value="speed"/> Faster and Faster </p>
</div>
<button className="submit" onClick={handleButtonClick} >Play</button>
{/* <button className="submit" onClick={handleClose}>Cancel</button> */}
</div>
{/* <div className="div_submit">
<button className="submit" onClick={handleCheckButtonClick}>
Invite to play
</button>
</div> */}
</motion.div>
</Backdrop>
);
};
export default GameModal;

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

@ -1,10 +1,24 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* PartyInvite.tsx :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/19 16:44:29 by apommier #+# #+# */
/* Updated: 2023/06/20 23:53:01 by apommier ### ########.fr */
/* */
/* ************************************************************************** */
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import api from '../../script/axiosApi.tsx'; import api from '../../script/axiosApi.tsx';
import DefaultPicture from '../../assets/profile.jpg' import DefaultPicture from '../../assets/profile.jpg'
import styled from "styled-components"; import styled from "styled-components";
import {User} from "../../../interfaces.tsx"
import { RxCheckCircled, RxCircleBackslash } from "react-icons/rx"; import { RxCheckCircled, RxCircleBackslash } from "react-icons/rx";
import React from "react";
const UserChat = styled.div ` const UserChat = styled.div `
padding: 5px; padding: 5px;
@ -25,10 +39,19 @@ const SideP = styled.p`
margin-left: 15px; margin-left: 15px;
` `
export default function Friend({currentUser}) interface InviteProps {
username: string,
gameId: string
}
interface UserProps {
currentInvite: {username: string, gameId: string}
}
export default function PartyInvite({currentInvite}: 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);
@ -37,11 +60,11 @@ export default function Friend({currentUser})
try { try {
// const user = await api.get("/profile");\ // const user = await api.get("/profile");\
// const tmpUser = await api.get("/profile") // const tmpUser = await api.get("/profile")
const pic = await api.post("/getPicture", {username: currentUser.username}) const pic = await api.post("/getPicture", {username: currentInvite.username})
const tmpRequest = await api.post("/user", {username: currentUser.username}) const tmpRequest = await api.post("/user", {username: currentInvite.username})
// setUser(tmpUser.data); // setUser(tmpUser.data);
setRequest(tmpRequest.data); setRequest(tmpRequest.data);
// console.log(`user naem profile pic222= ${currentUser.username}`) // console.log(`user naem profile pic222= ${currentInvite.username}`)
// console.log(` profile pic222= ${pic.data}`) // console.log(` profile pic222= ${pic.data}`)
setProfilePicture(pic.data); setProfilePicture(pic.data);
} catch (error) { } catch (error) {
@ -52,18 +75,25 @@ export default function Friend({currentUser})
fetchProfilePicture(); fetchProfilePicture();
}, [clickEvent]) }, [clickEvent])
const handleButtonClick = (user) => { const handleButtonClick = (user: InviteProps) => {
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}) //call canvas ??
setClickEvent(true); // await api.post("/friend", {username: request.username})
await api.post("/deleteInvite", {username: request.username})
let path = `http://` + process.env.REACT_APP_BASE_URL + `/pong/play?`
path += 'username=' + request.username;
path += '&gameId=' + currentInvite.gameId;
// setClickEvent(true);
window.history.pushState({}, '', path);
window.location.reload();
} catch(err) { } catch(err) {
console.log(err); console.log(err);
} }
@ -71,9 +101,10 @@ 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("/deleteInvite", {username: request.username})
// await api.post("/refuseInvite", {username: request.username})
setClickEvent(true); setClickEvent(true);
} catch(err) { } catch(err) {
console.log(err); console.log(err);
@ -84,6 +115,7 @@ export default function Friend({currentUser})
// Vérifier si le contenu doit être caché // Vérifier si le contenu doit être caché
if (clickEvent) { if (clickEvent) {
console.log("retrun true")
return null; // Rendre null pour ne pas afficher le contenu return null; // Rendre null pour ne pas afficher le contenu
} }
@ -94,13 +126,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(currentInvite)}>{request.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

@ -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

@ -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,46 +130,43 @@ 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); // 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) => { socket.on('pong:gameId', async (data) => {
console.log("gameId received"); console.log("gameId received");
@ -182,6 +210,7 @@ function DrawCanvas(option) {
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) => {
@ -214,9 +243,60 @@ function DrawCanvas(option) {
// 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; */
@ -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

@ -0,0 +1,111 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
"jsx": "react",
"noEmit": true,
"allowImportingTsExtensions": true,
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

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