Compare commits

..

3 Commits

Author SHA1 Message Date
Alexandre POMMIER
abd822dfc7 update master 2023-06-23 16:09:45 +02:00
Little Dipper
f9a26e8e0f longeur messages et logueur modal nouveau message 2023-06-18 23:04:49 +02:00
Little Dipper
263e3332a1 js to ts 2023-06-18 21:22:25 +02:00
59 changed files with 1043 additions and 1635 deletions

153
README.md
View File

@ -1,153 +0,0 @@
# 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 443 ssl;
# listen ${NGINX_PORT};
listen 8080;
listen 80;
location /{
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://react_app:8001;
proxy_pass http://react_app:8080;
}
location /api {
@ -20,18 +20,4 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
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> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/17 01:00:00 by apommier #+# #+# */
/* Updated: 2023/06/21 01:19:01 by apommier ### ########.fr */
/* Updated: 2023/06/18 13:30:50 by apommier ### ########.fr */
/* */
/* ************************************************************************** */
@ -27,7 +27,6 @@ import { generateOTP } from './users/2fa';
import { VerifyOTP } from './users/2fa';
import { ValidateOTP } from './users/2fa';
import { privateDecrypt } from 'crypto';
import { formatWithOptions } from 'util';
//2fa
@ -129,8 +128,6 @@ export class AppController {
async newBlocked(@Request() req, @Body() data: any) {
// return await this.userService.getFriends(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)
return await this.userService.addBlocked(user, data.username);
}
@ -139,8 +136,6 @@ export class AppController {
@Post('/invite')
async newInvite(@Request() req, @Body() data: any) {
console.log(`user= ${req.user.username}`)
if (data.username === req.user.username)
return (0);
const user = await this.userService.findOne(data.username)
if (!user)
return (0);
@ -220,10 +215,7 @@ export class AppController {
@UseGuards(JwtAuthGuard)
@Post('/win')
async addWin(@Request() req, @Body() data: any) {
console.log("WIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIN: ", 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++;
const Esp = 1 / (1 + Math.pow(10, (data.opRank - user.rank) / this.scaleFactor))
const newRank = user.rank + this.kFactor * (1 - Esp);
@ -237,16 +229,14 @@ export class AppController {
newMatch.opScore = data.opScore;
newMatch.opponent = data.opName;
newMatch.parent = user;
console.log(`newMatch WIIIN = ${newMatch}`);
await this.userService.saveChild(user, newMatch);
}
@UseGuards(JwtAuthGuard)
@Post('/loss')
async addLoss(@Request() req, @Body() data: any) {
console.log("LOOOOOOOOOOOOOOOSE: ", req.user.username)
const user = await this.userService.findOne(req.user.username);
console.log("User", user)
user.loss++;
const Esp = 1 / (1 + Math.pow(10, (data.opRank - user.rank) / this.scaleFactor))
@ -261,7 +251,7 @@ export class AppController {
newMatch.opScore = data.opScore;
newMatch.opponent = data.opName;
newMatch.parent = user;
console.log(`newMatch Loose= ${newMatch}`);
await this.userService.saveChild(user, newMatch);
}
@ -280,49 +270,6 @@ export class AppController {
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)
@Post('/history')
async getHistory(@Body() data: any)
@ -472,8 +419,6 @@ export class AppController {
if (!amIhere)
data.members.push(req.user.username)
// let test = {id: 2, members: "cc"};
data.admin = []
data.admin.push(req.user.username)
data.owner = req.user.username
data.group = true;
return await this.chatService.createConv(data);
@ -494,13 +439,6 @@ export class AppController {
return await this.chatService.findAll();
}
@UseGuards(JwtAuthGuard)
@Post('/convId')
async getConvById(@Body() data: any) {
return await this.chatService.findConv(data.convId);
}
@UseGuards(JwtAuthGuard)
@Post('/message')
async postMessage(@Request() req, @Body() data: any) {
@ -541,6 +479,12 @@ export class AppController {
// res.json(messages);
}
@UseGuards(JwtAuthGuard)
@Post('/ban')
async banUser(@Body() data: any) {
return await this.chatService.banUser(data.convId, data.username)
}
@UseGuards(JwtAuthGuard)
@Post('/name')
async setName(@Body() data: any) {
@ -549,6 +493,12 @@ export class AppController {
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)
@Post('/password')
async setPassword(@Body() data: any) {
@ -561,36 +511,12 @@ export class AppController {
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)
@Post('/admin')
async setAdmin(@Body() data: any) {
if (!data.username)
return ;
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)
@Post('/isAdmin')
async isAdmin(@Request() req, @Body() data: any) {
@ -598,6 +524,11 @@ export class AppController {
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)
@Post('/private')

View File

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

View File

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

View File

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

View File

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

View File

@ -65,12 +65,6 @@ export class User {
@Column('text', { array: true, nullable: true })
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 })
friends: string[];

View File

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

View File

@ -8,20 +8,16 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
@WebSocketServer()
server: Server;
private clients: Record<string, Socket> = {};
// private clientsNames: Record<string, Socket[]> = {};
private clientsNames: Map<string, string[]> = new Map();
// private games: Map<string, Socket[]> = new Map();// Chat en cours, identifiées par un ID
afterInit(server: Server)
{
console.log('ChatGateway initialized');
}
handleConnection(client: Socket, ...args: any[])
{
console.log(`Client connected: ${client.id}`);
@ -96,82 +92,93 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
console.log("create")
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('ban')
banUser(client: any, payload: any): void {
if (!this.clientsNames.has(payload.username))
{
console.log("No user found.");
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)
}
// @SubscribeMessage('sendMessage')
// handleMessage(user: any, payload: any): void {
// console.log(`message recceveid: ${payload}`)
// console.log(`message recceveid: ${payload.sender}`)
// console.log(`message recceveid: ${payload.convId}`)
// console.log(`message recceveid: ${payload.members}`)
@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")
}
// console.log(`client id: ${user.id}`)
// 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')
handleMessage(client: Socket, payload: any): void {
// console.log(`message received: ${payload}`);
// console.log(`message sender: ${payload.sender}`);
// console.log(`client id: ${client.id}`);
// console.log(`conversation ID: ${payload.convId}`);
// console.log(`members: ${payload.members}`);
console.log(`message sender: ${payload.sender}`);
console.log(`client id: ${client.id}`);
console.log(`conversation ID: ${payload.convId}`);
console.log(`members: ${payload.members}`);
this.clientsNames.forEach((clientArray, clientName) =>
{
// console.log(` 5Clients with name ${clientName}:`);
console.log(` 5Clients with name ${clientName}:`);
if (payload.members.includes(clientName))
{
clientArray.forEach((targetClient, index) =>
{
// console.log(`client id: ${client.id}`);
// console.log(`target: ${targetClient}`);
// console.log(`target id: ${targetClient}`);
console.log(`client id: ${client.id}`);
console.log(`target: ${targetClient}`);
console.log(`target id: ${targetClient}`);
if (targetClient && targetClient !== client.id)
{
// console.log("Sending to someone");
// console.log(`index= ${index}`);
// console.log(`target: ${targetClient}`); // Perform actions on each target client
console.log("Sending to someone");
console.log(`index= ${index}`);
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)
// 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 {
console.log("not sending");
@ -186,3 +193,41 @@ muteUser(client: any, payload: any): void {
// 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,26 +1,7 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* 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 { Server, Socket } from 'socket.io';
import { v4 as uuidv4 } from 'uuid';
//========================================================================================================
//========================================================================================================
// Connection/Disconnection
//========================================================================================================
//========================================================================================================
@WebSocketGateway({ cors: true })
export class PongGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer() server: Server;
@ -50,82 +31,50 @@ export class PongGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
handleDisconnect(client: Socket)
{
console.log(`Normal disconnected: ${client.id}`);
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);
console.log(`Client disconnected: ${client.id}`);
// this.waitingClients.delete(client);
this.waitingClients.forEach((waitingClient) => {
if (waitingClient.client === client) {
this.waitingClients.delete(waitingClient);
}})
delete this.clients[client.id];
}
})
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')
addMatchmaking(client: Socket, payload: any): void {
@ -142,7 +91,6 @@ addMatchmaking(client: Socket, payload: any): void {
waitingClient.option === payload.option && waitingClient.client !== client
);
if (matchingClients.length > 0) {
console.log("Creating new game...");
const players = [matchingClients[0].client, client]; // Add the current client to the players array
@ -169,76 +117,43 @@ addMatchmaking(client: Socket, payload: any): void {
player.emit('pong:gameId', gameId);
});
}
// 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);
//========================================================================================================
//========================================================================================================
// Private Match
//========================================================================================================
//========================================================================================================
// // if (Object.keys(this.clients).length === 2)
// // {
// // const clientIds = Object.keys(this.clients);
// // console.log(`id of 0= ${clientIds[0]}`);
// // payload.ballX
// // payload.ballY
// // payload.
// @SubscribeMessage('pong:invite')
// createPrivateGame(client: Socket, payload: any): void {
// //after invite accepted ?
// //set the two user in a game ?
// if (clientIds[0] === payload.id)
// {
// // console.log("client 0 true");
// if (payload.ballX <= payload.width / 2)
// this.clients[clientIds[1]].emit('pong:info', payload);
// }
// else if (clientIds[1] === payload.id)
// {
// if (payload.ballX < payload.width / 2)
// this.clients[clientIds[0]].emit('pong:info', payload);
// // console.log("client 0 true");
// }
// // }
// console.log("END OF HANDLE");
// }
@SubscribeMessage('pong: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')
sendPower(client: Socket, payload: any): void
@ -276,6 +191,29 @@ addPrivateParty(client: Socket, payload: any): void {
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')
forcedMessage(client: Socket, payload: any): void
{
@ -298,6 +236,29 @@ addPrivateParty(client: Socket, payload: any): void {
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')
handlePaddle(client: Socket, payload: any): void
{

View File

@ -1,5 +1,2 @@
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=localhost
# REACT_APP_BASE_URL=192.168.1.19

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,12 +3,11 @@ import React, { useState, useEffect, useRef } from "react";
import Rank from './Rank.tsx'
import defaultpic from '../../assets/profile.jpg'
import api from '../../script/axiosApi.tsx';
import {User} from "../../../interfaces.tsx"
function Ranking(){
const [isLoading, setIsLoading] = useState(true);
const [ranking, setRanking] = useState<User[]>([]);
const [ranking, setRanking] = useState([]);
useEffect(()=> {
@ -42,7 +41,16 @@ function Ranking(){
// <h1 className='title'>Ranking</h1>
<div className='scroll'>
{ranking.map((user, index) => (
<Rank user={user} index={index} key={user.username}/>
<Rank user={user} index={index}/>
// 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>
)}

View File

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

View File

@ -1,14 +1,13 @@
import React, { useState, useEffect, useRef } from "react";
import io, { Socket } from 'socket.io-client';
import io from 'socket.io-client';
import '../../styles/Messages.css'
import styled from "styled-components";
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 Modal from "./Modal.tsx";
import GameModal from "./GameModal.tsx";
import Message from "./Message.tsx"
import Message from './Message.tsx';
// import Input from "./Input";
//react icons
@ -18,15 +17,12 @@ import { MdOutlineGroupAdd } from 'react-icons/md';
import { GrAdd } from 'react-icons/gr';
import { RiListSettingsLine } from 'react-icons/ri'
// import { Rank } from "../../DataBase/DataRank";
import { Rank } from "../../DataBase/DataRank";
import GreenAlert from "../Alert/GreenAlert.tsx";
import RedAlert from "../Alert/RedAlert.tsx";
import YellowAlert from "../Alert/YellowAlert";
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`
margin-left: 10px;
@ -74,31 +70,20 @@ const SideP = styled.p`
//========================================================================================================
//========================================================================================================
interface MessageProps {
id: number;
convId: number;
sender: string;
text: string;
createdAt?: Date;
}
function Chats(){
const [isLoading, setIsLoading] = useState<boolean>(true);
const [isLoading, setIsLoading] = useState(true);
const [conversations, setConversation] = useState([]);
const [partyInvite, setPartyInvite] = useState([]);
const [user, setUser] = useState<User>();
const [currentChat, setCurrentChat] = useState<Conv>(); // false is good?
const [isAdmin, setIsAdmin] = useState<boolean>(false); // false is good?
const [user, setUser] = useState(null);
const [currentChat, setCurrentChat] = useState(false); // false is good?
const [isAdmin, setIsAdmin] = useState(false); // false is good?
// const [currentChat, setCurrentChat] = useState(false); // false is good?
const [messages, setMessage] = useState<MessageProps[]>([]);
const [messages, setMessage] = useState([]);
const [newMessages, setNewMessage] = useState("");
const [incomingMessage, setIncomingMessage] = useState<MessageProps>();
const [incomingMessage, setIncomingMessage] = useState("");
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(()=> {
@ -106,55 +91,29 @@ function Chats(){
const getConv = async ()=>{
try{
const convs = await api.get("/conv")
const tmpInvite = await api.get("/partyInvite")
const tmpUser = await api.get("/profile")
const tmpUsers = await api.get("/users");
console.log(convs);
// console.log("invite data use effect= ", tmpInvite.data);
setPartyInvite(tmpInvite.data);
setUser(tmpUser.data);
setConversation(convs.data);
setUsers(tmpUsers.data);
// console.log(`connection....`);
socket.current = io('http://' + process.env.REACT_APP_BASE_URL + ':4001', { transports: ['polling'] });
// console.log(`connection done`);
socket.current = io('http://' + process.env.REACT_APP_BASE_URL + ':4001');
console.log(`connection....`);
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);
});
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)
}
catch(err){
console.log("ERRORRRRR")
console.log(err);
}
};
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(()=> {
@ -174,12 +133,9 @@ function Chats(){
}
}
// console.log(`result1 = ${currentChat.id !== incomingMessage.convId}`)
if (currentChat && incomingMessage && currentChat.id === incomingMessage.convId)
if (currentChat !== null && 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]);
}
}
@ -190,13 +146,10 @@ function Chats(){
useEffect(()=> {
const getMessage = async ()=>
{
if (!currentChat)
return ;
const data = {convId: currentChat.id};
try {
const res = await api.post('/getMessage', data);
console.log("message of conv=", res.data)
setMessage(res.data);
} catch(err) {
@ -205,36 +158,30 @@ function Chats(){
getMessage();
}, [currentChat]);
const handleSubmit = async (e: { preventDefault: () => void; })=>{
const handleSubmit = async (e)=>{
e.preventDefault();
// console.log(`e= ${e.key}`)
// console.log(`name= ${user.username}`)
// let message;
if (!user || !currentChat)
return ;
const message = {
sender: user.username,
text: newMessages,
convId: currentChat.id,
members: null,
id: null,
members: null
};
try{
const allowed = await api.post('/allowed', {convId: currentChat.id});
console.log("convid:", currentChat.id);
console.log(`id= ${currentChat.id}`)
const allowed = await api.post('/allowed', {convId: message.convId, username: user.username});
console.log("allowed= ", allowed.data)
if (!allowed.data)
{
console.log("muted or banned");
return ;
}
console.log("not muted or banned");
const res = await api.post('/message', message);
const convMember = await api.post('/member', message);
message.members = convMember.data.members;
message.id = res.data.id
console.log(convMember);
// console.log(`currentChat= ${currentChat.id}`)
setMessage([...messages, res.data]);
setNewMessage("");
if (socket.current)
socket.current.emit('sendMessage', message);
}
catch(err){
@ -242,28 +189,27 @@ function Chats(){
}
}
const handleKeyPress = async (e: { key: string; })=> {
const handleKeyPress = async (e)=>{
// console.log(`e in press= ${e.key}`)
if (e.key !== "Enter")
return ;
// console.log(`name= ${user.username}`)
if (!user || !currentChat)
return ;
const message = {
sender: user.username,
text: newMessages,
convId: currentChat.id,
members: null,
id: null,
members: null
};
try{
console.log(`id= ${currentChat.id}`)
const res = await api.post('/message', message);
const convMember = await api.post('/member', message);
message.members = convMember.data.members;
message.id = res.data.id
console.log(convMember);
// console.log(`currentChat= ${currentChat.id}`)
setMessage([...messages, res.data]);
setNewMessage("");
if (socket.current)
socket.current.emit('sendMessage', message);
}
catch(err){
@ -274,7 +220,7 @@ function Chats(){
const [friend, setFriend] = useState("");
// const [modalOpen, setModalOpen] = useState(false);
const [modalOpen, setModalOpen] = useState(false);
const [addFriend, setAddFriend] = useState(false);
const [block, setBlock] = useState(false);
@ -282,32 +228,8 @@ function Chats(){
const [showBlockAlert, setShowBlockAlert] = useState(false);
const [setting, setSetting] = useState(false);
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 close = () => setModalOpen(false);
const open = () => setModalOpen(true);
// const closeAddFriend = () => setAddFriend(false);
// const closeBlock = () => setBlock(false);
const closeSetting = () => setSetting(false);
@ -317,7 +239,7 @@ function Chats(){
// const closeBlock = () => setBlock(false);
const handleFriend = (event: { target: { value: React.SetStateAction<string>; }; }) => {
const handleFriend = (event) => {
setFriend(event.target.value);
};
@ -369,15 +291,6 @@ function Chats(){
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
@ -391,7 +304,7 @@ function Chats(){
<div className='navbar'>
<img src={DefaultPic} alt="profile" className="pic"/>
<span>
{isLoading || !user ? (
{isLoading ? (
<h4>Loading...</h4>
) : (
<h4>{user.nickname}</h4>
@ -443,24 +356,7 @@ function Chats(){
</div> */}
<div className="end">
{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>
))}
<input className="lookForFriends" type="text" value={friend} onChange={handleFriend} />
<TouchDiv>
<motion.div onClick={handleAddFriend}>
<MdOutlineGroupAdd />
@ -497,7 +393,7 @@ function Chats(){
initial={false}
onExitComplete={() => null}
>
{setting && <ModalSetting handleClose={closeSetting} convId={currentChat.id.toString()} socket={socket.current}/>}
{setting && <ModalSetting handleClose={closeSetting} convId={currentChat.id}/>}
</AnimatePresence>
</motion.div>
</TouchDiv>
@ -508,37 +404,18 @@ function Chats(){
</div>
<div className="messages_box">
<div className="contact">
<UserChat>
<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}>
<motion.div className="newMessage"
onClick={() => (modalOpen ? close() : open())}
>
<GrAdd/>
<span>New Conversation</span>
</motion.div>
{newConversationModalOpen && (
<Modal handleClose={closeNewConversationModal} />
)}
{modalOpen && <Modal modalOpen={modalOpen} handleClose={close}/>}
</UserChat>
{/* {partyInvite.map((c) => {
return (
)})
} */}
{partyInvite.map( i =>(
<PartyInvite currentInvite={i}/>
))}
{conversations.map((c: Conv, index ) => {
{conversations.map((c, index ) => {
return (
<div key={index}
onClick={() => setCurrentChat(c)}>
@ -546,7 +423,7 @@ function Chats(){
<img className="pic-user" src={DefaultPic} alt="User" />
<div className="infoSideBar">
<span>{c.name}</span>
{/* <SideP>Desc?</SideP> */}
<SideP>Desc?</SideP>
</div>
</UserChat>
</div>
@ -555,12 +432,12 @@ function Chats(){
</div>
{
currentChat && user ? (
currentChat ? (
<>
<div className="messages">
<div className="scroll">
{messages.map(m=>(
<Message key={m.id} message= {m} own={m.sender === user.username}/>
<Message message = {m} own={m.sender === user.username} user={m}/>
))}
</div>
{/* <Input/> */}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,20 +1,14 @@
import React from 'react';
import { useEffect } from 'react';
// import { useState, useRef } from 'react';
import DrawCanvas from './canvas.tsx';
import DrawCanvas from './canvas';
import queryString from 'query-string';
import '../styles/field.css';
import { useParams } from "react-router-dom";
import React from 'react';
// import { withRouter } from 'react-router-dom';
interface GameProps {
privateParty: boolean,
username?: string
gameId?: number
}
function Field()
{
useEffect(() => {
@ -23,7 +17,6 @@ function Field()
console.log("launch canva hehe")
let Modifiers = 0;
let info: GameProps;
if (queryParams.superpower === 'true') {
Modifiers += 1;
@ -36,37 +29,81 @@ function Field()
if (queryParams.speed === 'true') {
Modifiers += 4;
}
// console.log(`modifiers= ${Modifiers}`)
// DrawCanvas(Modifiers);
// return () => {
// console.log("000000000000000000000000000000000")
// // socketRef.current.disconnect();
// };
info = {
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);
// console.log(`modifiers= ${Modifiers}`)
const cleanup = DrawCanvas(Modifiers);
return () => {
console.log("Cleanup");
// cleanup(); // Call the cleanup function to stop the ongoing process or perform necessary cleanup tasks
if (cleanup)
cleanup();
cleanup(); // Call the cleanup function to stop the ongoing process or perform necessary cleanup tasks
};
}, []);
// const [buttonClicked, setButtonClicked] = useState(false);
// const handleButtonClick = () => {
// drawCanvas();
// setButtonClicked(true);
// };
return (
<div className="field" id="canvas_container">
<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>
);
}
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 PlayButton from "../components/Game/PlayButton.tsx";
import Ranking from "../components/Game/Ranking.tsx";
import PlayButton from "../components/Game/PlayButton";
import Ranking from "../components/Game/Ranking";
import '../styles/Game.css'
function Game(){

View File

@ -9,7 +9,7 @@ function Head()
<title>BEST PONG EVER</title>
{/* <script src="./script/login.js"></script> */}
<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>
</div>
);

View File

@ -1,42 +1,37 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Home.tsx :+: :+: :+: */
/* Home.jsx :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/09 08:19:04 by apommier #+# #+# */
/* Updated: 2023/06/23 15:58:14 by apommier ### ########.fr */
/* Updated: 2023/06/09 08:19:05 by apommier ### ########.fr */
/* */
/* ************************************************************************** */
// import { React, useState } from "react";
import '../styles/Profile.css'
// import '../styles/App.css'
import DefaultPicture from "../assets/profile.jpg";
import WinLoss from "../components/Profile/Win_Loss.tsx";
// import DefaultPicture from "../assets/profile.jpg";
import WinLoss from "../components/Profile/Win_Loss";
import { motion, AnimatePresence } from 'framer-motion'
// import { AiFillEdit } from 'react-icons/ai'
// import { GrClose } from 'react-icons/gr'
import { Link } from "react-router-dom";
import ModalEdit from "../components/Profile/EditName.tsx";
import ModalEdit from "../components/Profile/EditName";
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 {UserProfile} from "../DataBase/DataUserProfile";
// import axios from "axios";
import api from '../script/axiosApi.tsx';
import api from '../script/axiosApi';
import { CgEditMarkup } from 'react-icons/cg'
import { IoCloseCircleOutline } from "react-icons/io5";
// import * as React from 'react';
// import { useState, useEffect, useParams} from "react";
import React, { useState, useEffect, useRef, ChangeEventHandler } from "react";
import React, { useState, useEffect, useRef } from "react";
import { useParams } from 'react-router-dom';
import {User, Conv} from "../../interfaces.tsx"
import YellowAlert from '../components/Alert/YellowAlert.tsx';
// axios.get("http://localhost/api")
// .then((response) => {
// response = response.json()
@ -48,70 +43,43 @@ import YellowAlert from '../components/Alert/YellowAlert.tsx';
function Profile () {
const [user, setUser] = useState<User>();
const [isLoading, setIsLoading] = useState<boolean>(true);
const [modalOpen, setModalOpen] = useState<boolean>(false);
const [mine, setMine] = useState<boolean>(false);
const DefaultPicture: string = "../assets/profile.jpg";
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [modalOpen, setModalOpen] = useState(false);
const [mine, setMine] = useState(false);
const close = () => setModalOpen(false);
const open = () => setModalOpen(true);
const { username } = useParams();
// const [selectedPhoto, setSelectedPhoto] = useState();
// const [selectedPhoto, setSelectedPhoto] = useState(null);
const [selectedPhoto, setSelectedPhoto] = useState(null);
const [profilePicture, setProfilePicture] = useState('');
const handleFileChange = async (event: { target: { files: any; }; }) => {
// const files = event.target.files;
// if (files && files.length > 0) {
const photo = (event.target.files[0]);
console.log("file selected")
if (photo) {
console.log("selected photo")
const formData = new FormData();
formData.append('photo', photo);
try {
await api.post('/picture', formData);
console.log('File uploaded successfully');
window.location.reload();
} catch (error) {
console.error('Error uploading file:', error);
}
}
const handleFileChange = (event) => {
// const file = event.target.files[0];
setSelectedPhoto(event.target.files[0]);
// try{
// api.post("/picture", {picture: URL.createObjectURL(file)})
// }
// catch(err){
// console.log(err);
// }
};
// 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');
// }
// };
const handleUpload = async () => {
const formData: FormData = new FormData();
if (selectedPhoto){
formData.append('photo', selectedPhoto);
}
try {
await api.post('/picture', formData);
console.log('File uploaded successfully');
window.location.reload(false);
} catch (error) {
console.error('Error uploading file:', error);
}
};
useEffect(()=> {
const getUser = async ()=>{
@ -157,10 +125,12 @@ function Profile () {
<img className="profile-pic" src={DefaultPicture} alt="Default Profile Picture" />
)}
<span>
{isLoading || !user ? (
{isLoading ? (
<h1>Loading...</h1>
) : (
user ? (
<h1>{user.nickname}</h1>
) : ("")
)}
</span>
@ -168,22 +138,16 @@ function Profile () {
{mine ? (
<div>
<motion.div >
<Link to="#" className="edit_name" onClick={() => (modalOpen ? close() : open())}>
<motion.div onClick={() => (modalOpen ? close() : open())}>
<Link to="#" className="edit_name">
{modalOpen === true ? <IoCloseCircleOutline/> : <CgEditMarkup/>}
</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>
{/* <div className="file-upload-container"> */}
{/* <button onClick={handleUpload} className="upload-button">Upload</button> */}
{/* <button onClick={handleUpload} className="upload-button">Upload</button> */}
{/* </div> */}
<div>
<input type="file" accept="image/*" onChange={handleFileChange} />
<button onClick={handleUpload}>Upload</button>
</div>
</div>
) : (
<></>
@ -202,49 +166,13 @@ function Profile () {
function Home () {
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 (
<motion.div className="page"
initial={{opacity: -1}}
animate={{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">
<motion.div animate={{x: move ? -200: 120}}
<motion.div animate={{x: move ? -200: 170}}
transition={{type: "tween", duration: 0.5}}>
<Profile/>
</motion.div>
@ -258,19 +186,6 @@ function Home () {
onClick={ () => setmove(!move)}>
<Link to="#" className="history"><AiOutlineHistory/> Match History</Link>
</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>
)
}

View File

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

View File

@ -1,31 +1,14 @@
import React from "react";
// import Sidebar from '../components/Messages/Sidebar'
import Chats from "../components/Messages/Chats.tsx"
import Chats from "../components/Messages/Chats"
import '../styles/Messages.css'
import { motion } from 'framer-motion'
// import {io} from 'socket.io-client'
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 (
<>
<div>
<motion.div className="home"
initial={{opacity: 0}}
animate={{opacity: 1}}
@ -35,7 +18,7 @@ function Messages() {
<Chats/>
</div>
</motion.div>
</>
</div>
);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -89,17 +89,6 @@ span {
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{
text-align: end;
/* id: right; */

View File

@ -204,18 +204,6 @@ p {
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 {
flex-direction: row;
align-items: center;
@ -288,12 +276,6 @@ p {
background-color: rgba(212, 175, 55, 0.7);
font-size: 25px;
color: rgba(255, 255, 255, 1);
flex-wrap: wrap;
}
.yellowAlert::p {
overflow-wrap: break-word;
max-width: 1000px;
}
.modalSetting{
@ -316,7 +298,7 @@ p {
.settingSecondPart{
margin-top: 10%;
margin-left: 5%;
margin-left: 10%;
/* margin-left: 20%; */
}
@ -334,20 +316,3 @@ input.in{
border-radius: 12px;
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,75 +1,11 @@
/* 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 {
text-align: center;
overflow-y: scroll;
/* height: 50vh; */
/* width: 50vh; */
/* background-color: black; */
}
.profile {
align-items: center;
text-align: center;
flex-direction: row;
color: white;
}
@ -82,7 +18,7 @@
border-color: red;
/* border-image: linear-gradient(90deg, #5843e4, #5a0760); */
/* margin-top: 20px; */
margin-top: 20px;
}
.home{
@ -109,7 +45,7 @@
.div_history {
flex-direction: row;
align-items: center;
/* margin-top: -80px; */
margin-top: -80px;
}
.edit_name {

View File

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

View File

@ -1,21 +1,16 @@
.playButton {
background-image: linear-gradient(90deg, #5843e4, #5a0760);
display: flex;
flex-wrap: wrap;
overflow: hidden;
background-image: linear-gradient(90deg, #5843e4, #5a0760);;
border-radius: 5vh;
color: white;
/* display: block; */
display: block;
margin: auto;
margin-top: 30vh;
padding: 2vh 4vw;
padding: 2vh 5vw;
height: 10vh;
width: 20vw;
font-size: 250%;
text-align: center;
font-size: 300%;
}
.field {
background-color: rgb(249, 249, 249);
/* border-radius: 5vh; */
@ -45,29 +40,6 @@
/* padding-top: 25; */
/* 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 {
background-color: rgb(124, 47, 47);

View File

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

View File

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

2
containers/test Normal file
View File

@ -0,0 +1,2 @@
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;'"
ports:
- 8080:8080
- 80:80
volumes:
- ./conf/nginx.conf:/etc/nginx/conf.d/default.conf
# volumes:
@ -34,7 +34,7 @@ services:
# depends_on:
# - nginx
ports:
- 8001:8001
- 8080:8080
volumes:
- ./containers/react:/app
# - ./containers/react:/app