vvandenb 2 years ago
parent
commit
b01afbb5ec
  1. 177
      back/volume/src/chat/chat.controller.ts
  2. 2
      back/volume/src/chat/chat.gateway.ts
  3. 13
      back/volume/src/chat/chat.module.ts
  4. 147
      back/volume/src/chat/chat.service.ts
  5. 13
      back/volume/src/chat/dto/connection.dto.ts
  6. 28
      back/volume/src/chat/dto/create-channel.dto.ts
  7. 12
      back/volume/src/chat/dto/create-message.dto.ts
  8. 13
      back/volume/src/chat/dto/createChannel.dto.ts
  9. 30
      back/volume/src/chat/dto/update-channel.dto.ts
  10. 24
      back/volume/src/chat/dto/updateChannel.dto.ts
  11. 16
      back/volume/src/chat/dto/updateUser.dto.ts
  12. 54
      back/volume/src/chat/entity/channel.entity.ts
  13. 16
      back/volume/src/chat/entity/connection.entity.ts
  14. 15
      back/volume/src/chat/entity/dm.entity.ts
  15. 15
      back/volume/src/chat/entity/message.entity.ts
  16. 16
      back/volume/src/pong/game/Ball.ts
  17. 32
      back/volume/src/pong/game/Game.ts
  18. 33
      back/volume/src/pong/pong.controller.ts
  19. 2
      back/volume/src/pong/pong.gateway.ts
  20. 2
      back/volume/src/pong/pong.module.ts
  21. 15
      back/volume/src/pong/pong.service.ts
  22. 6
      back/volume/src/users/entity/user.entity.ts
  23. 33
      back/volume/src/users/users.controller.ts
  24. 27
      back/volume/src/users/users.service.ts
  25. 13
      front/volume/src/App.svelte
  26. 4
      front/volume/src/Auth.ts
  27. 2
      front/volume/src/FakeLogin.svelte
  28. 151
      front/volume/src/components/Channels.svelte
  29. 64
      front/volume/src/components/Chat.svelte
  30. 7
      front/volume/src/components/Friends.svelte
  31. 2
      front/volume/src/components/Leaderboard.svelte
  32. 8
      front/volume/src/components/MatchHistory.svelte
  33. 2
      front/volume/src/components/NavBar.svelte
  34. 2
      front/volume/src/components/Pong/Paddle.ts
  35. 1
      front/volume/src/components/Pong/Pong.svelte
  36. 20
      front/volume/src/components/Profile.svelte

177
back/volume/src/chat/chat.controller.ts

@ -0,0 +1,177 @@
import {
BadRequestException,
Body,
Controller,
Delete,
Get,
NotFoundException,
Param,
ParseIntPipe,
Post,
UseGuards
} from '@nestjs/common'
import { AuthenticatedGuard } from 'src/auth/42-auth.guard'
import { UsersService } from 'src/users/users.service'
import { ChannelService } from './chat.service'
import { CreateChannelDto } from './dto/create-channel.dto'
import { IdDto, PasswordDto, MuteDto } from './dto/updateUser.dto'
import type User from 'src/users/entity/user.entity'
import type Channel from './entity/channel.entity'
import { Profile42 } from 'src/auth/42.decorator'
import { Profile } from 'passport-42'
@Controller('channels')
export class ChatController {
constructor (
private readonly channelService: ChannelService,
private readonly usersService: UsersService
) {}
@Post(':id/invite')
@UseGuards(AuthenticatedGuard)
async addUser (
@Param('id') id: number, @Body() target: IdDto,
@Profile42() profile: Profile) {
const channel = await this.channelService.getFullChannel(id)
const user: User | null = await this.usersService.findUser(target.id)
if (user == null) throw new NotFoundException(`User #${target.id} not found`)
if (!await this.channelService.isUser(channel.id, +profile.id))
throw new BadRequestException('You are not allowed to invite users to this channel')
if (await this.channelService.isUser(channel.id, target.id))
throw new BadRequestException('User is already in this channel')
if (await this.channelService.isBanned(channel.id, target.id))
throw new BadRequestException('User is banned from this channel')
channel.users.push(user)
this.channelService.save(channel)
}
@Delete(':id/kick')
@UseGuards(AuthenticatedGuard)
async removeUser (
@Param('id') id: number, @Body() target: IdDto,
@Profile42() profile: Profile) {
const channel = await this.channelService.getFullChannel(id)
if (!await this.channelService.isAdmin(channel.id, +profile.id))
throw new BadRequestException('You are not allowed to kick users from this channel')
if (!await this.channelService.isUser(channel.id, target.id))
throw new BadRequestException('User is not in this channel')
if (await this.channelService.isOwner(channel.id, target.id))
throw new BadRequestException('You cannot kick the owner of the channel')
channel.users = channel.users.filter((usr: User) => {
return usr.ftId !== target.id
})
this.channelService.save(channel)
}
@Post(':id/admin')
@UseGuards(AuthenticatedGuard)
async addAdmin (
@Param('id') id: number,
@Body() target: IdDto,
@Profile42() profile: Profile) {
const channel = await this.channelService.getFullChannel(id)
const user: User | null = await this.usersService.findUser(target.id)
if (user == null) throw new NotFoundException(`User #${target.id} not found`)
if (!await this.channelService.isOwner(channel.id, +profile.id))
throw new BadRequestException('You are not the owner of this channel')
if (!await this.channelService.isUser(channel.id, target.id))
throw new BadRequestException('User is not in this channel')
if (await this.channelService.isAdmin(channel.id, target.id))
throw new BadRequestException('User is already an admin of this channel')
channel.admins.push(user)
this.channelService.save(channel)
}
@Delete(':id/admin')
@UseGuards(AuthenticatedGuard)
async removeAdmin (
@Param('id') id: number,
@Body() target: IdDto,
@Profile42() profile: Profile) {
const channel = await this.channelService.getFullChannel(id)
if (!await this.channelService.isOwner(channel.id, +profile.id))
throw new BadRequestException('You are not the owner of this channel')
if (!await this.channelService.isAdmin(channel.id, target.id))
throw new BadRequestException('User is not an admin of this channel')
channel.admins = channel.admins.filter((usr: User) => {
return usr.ftId !== target.id
})
this.channelService.save(channel)
}
@Post(':id/ban')
@UseGuards(AuthenticatedGuard)
async addBan (
@Param('id') id: number,
@Body() target: IdDto,
@Profile42() profile: Profile) {
const channel = await this.channelService.getFullChannel(id)
const user: User | null = await this.usersService.findUser(target.id)
if (user == null) throw new NotFoundException(`User #${target.id} not found`)
if (!await this.channelService.isAdmin(channel.id, +profile.id))
throw new BadRequestException('You are not allowed to ban users from this channel')
if (await this.channelService.isOwner(channel.id, target.id))
throw new BadRequestException('You cannot ban the owner of the channel')
if (await this.channelService.isBanned(channel.id, target.id))
throw new BadRequestException('User is already banned from this channel')
channel.banned.push(user)
this.channelService.save(channel)
}
@Post(':id/mute')
@UseGuards(AuthenticatedGuard)
async addMute (
@Param('id') id: number,
@Body() mute: MuteDto, // [userId, duration]
@Profile42() profile: Profile) {
const channel = await this.channelService.getFullChannel(id)
const user: User | null = await this.usersService.findUser(mute.data[0])
if (user == null) throw new NotFoundException(`User #${mute.data[0]} not found`)
if (!await this.channelService.isAdmin(channel.id, +profile.id))
throw new BadRequestException('You are not allowed to mute users from this channel')
if (await this.channelService.isOwner(channel.id, mute.data[0]))
throw new BadRequestException('You cannot mute the owner of the channel')
if (await this.channelService.getMuteDuration(channel.id, mute.data[0]) > 0)
throw new BadRequestException('User is already muted from this channel')
channel.muted.push(mute.data)
this.channelService.save(channel)
}
@Delete(':id')
@UseGuards(AuthenticatedGuard)
async deleteChannel (
@Profile42() profile: Profile,
@Param('id') id: number
) {
if (!await this.channelService.isOwner(id, +profile.id))
throw new BadRequestException('You are not the owner of this channel')
await this.channelService.removeChannel(id)
return
}
@Post(':id/password')
@UseGuards(AuthenticatedGuard)
async updatePassword (
@Profile42() profile: Profile,
@Param('id') id: number,
@Body() data: PasswordDto
) {
if (await this.channelService.isOwner(id, +profile.id))
throw new BadRequestException('You are not the owner of this channel')
await this.channelService.updatePassword(id, data.password)
}
@Get()
@UseGuards(AuthenticatedGuard)
async getChannelsForUser (@Profile42() profile: Profile): Promise<Channel[]> {
return await this.channelService.getChannelsForUser(+profile.id)
}
@Post()
async createChannel (@Body() channel: CreateChannelDto) {
return await this.channelService.createChannel(channel)
}
}

2
back/volume/src/chat/chat.gateway.ts

@ -1,3 +1,4 @@
/*
import { UnauthorizedException, UseGuards } from '@nestjs/common'
import {
type OnGatewayConnection,
@ -99,3 +100,4 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
/// TODO: Send message to users
}
}
*/

13
back/volume/src/chat/chat.module.ts

@ -3,10 +3,12 @@ import { TypeOrmModule } from '@nestjs/typeorm'
import { AuthModule } from 'src/auth/auth.module'
import { UsersModule } from 'src/users/users.module'
import { ChatGateway } from './chat.gateway'
import { ChatService } from './chat.service'
import { Channel } from './entity/channel.entity'
import { Message } from './entity/message.entity'
// import { ChatGateway } from './chat.gateway'
import { ChatController } from './chat.controller'
import { ChannelService } from './chat.service'
import Channel from './entity/channel.entity'
import Message from './entity/message.entity'
@Module({
imports: [
@ -15,6 +17,7 @@ import { Message } from './entity/message.entity'
TypeOrmModule.forFeature([Channel]),
TypeOrmModule.forFeature([Message])
],
providers: [ChatGateway, ChatService]
controllers: [ChatController],
providers: [ChannelService]
})
export class ChatModule {}

147
back/volume/src/chat/chat.service.ts

@ -1,56 +1,139 @@
import { Injectable } from '@nestjs/common'
import { Injectable, NotFoundException } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { type User } from 'src/users/entity/user.entity'
import { Channel } from './entity/channel.entity'
import { Message } from './entity/message.entity'
import { type CreateChannelDto } from './dto/create-channel.dto'
import { UsersService } from 'src/users/users.service'
import type User from 'src/users/entity/user.entity'
import Channel from './entity/channel.entity'
@Injectable()
export class ChatService {
export class ChannelService {
constructor (
@InjectRepository(Channel)
private readonly ChannelRepository: Repository<Channel>,
@InjectRepository(Message)
private readonly MessageRepository: Repository<Message>
private readonly usersService: UsersService
) {}
async createChannel (Channel: Channel, creator: User): Promise<Channel> {
const newChannel = await this.addCreatorToChannel(Channel, creator)
async createChannel (channel: CreateChannelDto): Promise<Channel> {
const user: User | null = await this.usersService.findUser(channel.owner)
if (user == null)
throw new NotFoundException(`User #${channel.owner} not found`)
const newChannel = new Channel()
newChannel.owner = user
newChannel.users = [user]
newChannel.admins = [user]
newChannel.name = channel.name
newChannel.isPrivate = channel.isPrivate
newChannel.password = channel.password
return await this.ChannelRepository.save(newChannel)
}
async getChannelsForUser (userId: number): Promise<Channel[]> {
return await this.ChannelRepository.find({}) // where userId is in User[] of channel?
async updatePassword (id: number, password: string) {
let channel: Channel | null = await this.ChannelRepository.findOneBy({id})
if (channel === null) { throw new NotFoundException(`Channel #${id} not found`) }
channel.password = password
await this.ChannelRepository.save(channel)
}
async getChannelsForUser (ftId: number): Promise<Channel[]> {
let rooms: Channel[] = []
rooms = [
...(await this.ChannelRepository.createQueryBuilder('room')
.where('room.isPrivate = false')
.getMany())
]
rooms = [
...rooms,
...(await this.ChannelRepository.createQueryBuilder('room')
.innerJoin('room.users', 'users')
.where('room.isPrivate = true')
.andWhere('users.ftId = :ftId', { ftId })
.getMany())
]
return rooms
}
async addUserToChannel (channel: Channel, user: User): Promise<Channel> {
channel.owner = user
return await this.ChannelRepository.save(channel)
}
async addCreatorToChannel (Channel: Channel, creator: User): Promise<Channel> {
Channel.users.push(creator)
return Channel
async getChannel (id: number): Promise<Channel> {
const channel = await this.ChannelRepository.findOneBy({ id })
if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) }
return channel
}
async createMessage (message: Message): Promise<Message> {
return await this.MessageRepository.save(
this.MessageRepository.create(message)
)
async getFullChannel (id: number): Promise<Channel> {
const channel = await this.ChannelRepository.findOne({
where: { id },
relations: ['users', 'admins', 'banned', 'muted', 'owner']
})
if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) }
return channel
}
async deleteBySocketId (socketId: string) {
return await this.ChannelRepository.delete({}) // for disconnect
async update (channel: Channel) {
await this.ChannelRepository.update(channel.id, channel)
}
async getChannel (id: number): Promise<Channel | null> {
return await this.ChannelRepository.findOneBy({ id })
async save (channel: Channel) {
await this.ChannelRepository.save(channel)
}
async findMessagesInChannelForUser (
channel: Channel,
user: User
): Promise<Message[]> {
return await this.MessageRepository.createQueryBuilder('message')
.where('message.channel = :chan', { chan: channel })
.andWhere('message.author NOT IN (:...blocked)', {
blocked: user.blocked
})
.getMany()
async removeChannel (channelId: number) {
await this.ChannelRepository.delete(channelId)
}
async isOwner (id: number, userId: number): Promise<boolean> {
const channel = await this.ChannelRepository.findOne({
where: { id },
relations: { owner: true }
})
if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) }
return channel.owner.ftId === userId
}
async isAdmin (id: number, userId: number): Promise<boolean> {
const channel = await this.ChannelRepository.findOne({
where: { id },
relations: { admins: true }
})
if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) }
return channel.admins.findIndex((user) => user.ftId === userId) != -1
}
async isUser (id: number, userId: number): Promise<boolean> {
const channel = await this.ChannelRepository.findOne({
where: { id },
relations: { users: true }
})
if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) }
return channel.users.findIndex((user) => user.ftId === userId) != -1
}
async isBanned (id: number, userId: number): Promise<boolean> {
const channel = await this.ChannelRepository.findOne({
where: { id },
relations: { banned: true }
})
if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) }
return channel.banned.findIndex((user) => user.ftId === userId) != -1
}
async getMuteDuration (id: number, userId: number): Promise<number> {
const channel = await this.ChannelRepository.findOne({
where: { id },
relations: { muted: true }
})
if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) }
const mutation: Array<number> | undefined = channel.muted.find((mutation) => mutation[0] === userId)
if (mutation == null) { return 0 }
return mutation[1]
}
}

13
back/volume/src/chat/dto/connection.dto.ts

@ -0,0 +1,13 @@
import { IsNumber, IsOptional, IsString } from 'class-validator'
export class ConnectionDto {
@IsNumber()
UserId: number
@IsNumber()
ChannelId: number
@IsString()
@IsOptional()
pwd: string
}

28
back/volume/src/chat/dto/create-channel.dto.ts

@ -0,0 +1,28 @@
import { Transform } from 'class-transformer'
import {
IsPositive,
IsAlpha,
IsString,
IsOptional,
IsNumber,
IsBoolean
} from 'class-validator'
export class CreateChannelDto {
@IsOptional()
@IsPositive()
id: number
@IsString()
name: string
@IsNumber()
owner: number
@IsOptional()
password: string
@IsBoolean()
@Transform(({ value }) => value === 'true')
isPrivate: boolean
}

12
back/volume/src/chat/dto/create-message.dto.ts

@ -0,0 +1,12 @@
import { IsNumber, IsString } from 'class-validator'
export class CreateMessageDto {
@IsString()
text: string
@IsNumber()
UserId: number
@IsNumber()
ChannelId: number
}

13
back/volume/src/chat/dto/createChannel.dto.ts

@ -1,13 +0,0 @@
import { IsPositive, IsAlpha, IsString, IsOptional } from 'class-validator'
export class CreateChannelDto {
@IsString()
@IsAlpha()
name: string
@IsPositive()
owner: number
@IsOptional()
password: string
}

30
back/volume/src/chat/dto/update-channel.dto.ts

@ -0,0 +1,30 @@
import { PartialType } from '@nestjs/mapped-types'
import { CreateChannelDto } from './create-channel.dto'
import { IsNumber, IsOptional, IsString } from 'class-validator'
export class UpdateChannelDto extends PartialType(CreateChannelDto) {
id: number
@IsOptional()
@IsNumber()
users: [number]
@IsOptional()
@IsNumber()
messages: [number]
@IsOptional()
@IsNumber()
owners: [number] // user id
@IsOptional()
@IsNumber()
banned: [number] // user id
@IsOptional()
@IsNumber()
muted: [number] // user id
@IsString()
@IsOptional()
password: string
}

24
back/volume/src/chat/dto/updateChannel.dto.ts

@ -1,24 +0,0 @@
import { PartialType } from '@nestjs/mapped-types'
import { CreateChannelDto } from './createChannel.dto'
import { type Message } from '../entity/message.entity'
import { type User } from 'src/users/entity/user.entity'
import { IsString } from 'class-validator'
export class UpdateChannelDto extends PartialType(CreateChannelDto) {
id: number
users: [User]
messages: [Message]
owners: [number] // ftId
admins: [number]
banned: [number] // ftId
muted: [number] // ftId
@IsString()
password: string
}

16
back/volume/src/chat/dto/updateUser.dto.ts

@ -0,0 +1,16 @@
import { IsNumber, IsString} from 'class-validator'
export class IdDto {
@IsNumber()
id: number
}
export class PasswordDto {
@IsString()
password: string
}
export class MuteDto {
data: Array<number>
}

54
back/volume/src/chat/entity/channel.entity.ts

@ -2,27 +2,40 @@ import {
BeforeInsert,
Column,
Entity,
JoinColumn,
JoinTable,
ManyToMany,
ManyToOne,
OneToMany,
OneToOne,
PrimaryGeneratedColumn
} from 'typeorm'
import User from 'src/users/entity/user.entity'
import Message from './message.entity'
import * as bcrypt from 'bcrypt'
import { User } from 'src/users/entity/user.entity'
import { Message } from './message.entity'
@Entity()
export class Channel {
export default class Channel {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@ManyToMany(() => User)
@JoinTable()
owner: User
@Column({ default: false })
isPrivate: boolean
@Column({ select: false, default: '' })
password: string
@BeforeInsert()
async hashPassword () {
if (this.password === '') return
this.password = await bcrypt.hash(
this.password,
Number(process.env.HASH_SALT)
)
}
@ManyToMany(() => User)
@JoinTable()
@ -31,22 +44,19 @@ export class Channel {
@OneToMany(() => Message, (message: Message) => message.channel)
messages: Message[]
@OneToMany(() => User, (user: User) => user.id) // refuse connection
banned: User[]
@ManyToOne(() => User)
@JoinColumn()
owner: User
@OneToMany(() => User, (user: User) => user.id) // refuse post
muted: User[]
@ManyToMany(() => User)
@JoinTable()
admins: User[]
@Column({ select: false })
password: string
@ManyToMany(() => User) // refuse connection
@JoinTable()
banned: User[]
@BeforeInsert()
async hashPassword () {
this.password = await bcrypt.hash(
this.password,
Number(process.env.HASH_SALT)
)
}
@ManyToMany(() => User) // refuse post
@JoinTable()
muted: Array<Array<number>>
}
export default Channel

16
back/volume/src/chat/entity/connection.entity.ts

@ -0,0 +1,16 @@
import { Column, Entity, OneToOne } from 'typeorm'
import Channel from './channel.entity'
import User from 'src/users/entity/user.entity'
@Entity()
export class connectedUser {
@OneToOne(() => User)
user: User
@OneToOne(() => Channel, (channel) => channel.id)
channel: Channel
@Column()
socket: string
}

15
back/volume/src/chat/entity/dm.entity.ts

@ -0,0 +1,15 @@
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'
import Message from './message.entity'
import type User from 'src/users/entity/user.entity'
@Entity()
export class Channel {
@PrimaryGeneratedColumn()
id: number
@Column()
users: User[]
@OneToMany(() => Message, (message) => message.channel)
messages: Message[]
}

15
back/volume/src/chat/entity/message.entity.ts

@ -7,28 +7,25 @@ import {
ManyToOne,
PrimaryGeneratedColumn
} from 'typeorm'
import { User } from 'src/users/entity/user.entity'
import { Channel } from './channel.entity'
import User from 'src/users/entity/user.entity'
import Channel from './channel.entity'
@Entity()
export class Message {
export default class Message {
@PrimaryGeneratedColumn()
id: number
@Column()
text: string
@ManyToOne(() => User, (author: User) => author.messages)
@ManyToOne(() => User)
@JoinColumn()
author: User
@ManyToOne(() => Channel, (channel: Channel) => channel.messages)
@ManyToOne(() => Channel, (channel) => channel.messages, { cascade: true })
@JoinTable()
channel: Channel
@CreateDateColumn()
createdAt: Date
created_at: Date
}
export default Message

16
back/volume/src/pong/game/Ball.ts

@ -20,7 +20,7 @@ export class Ball {
constructor (
spawn: Point,
size: Point = DEFAULT_BALL_SIZE,
speed: Point = DEFAULT_BALL_INITIAL_SPEED
speed: Point = DEFAULT_BALL_INITIAL_SPEED.clone()
) {
this.rect = new Rect(spawn, size)
this.speed = speed
@ -93,21 +93,23 @@ export class Ball {
playerScored (): number {
let indexPlayerScored: number
if (this.rect.center.x <= this.spawn.x) {
indexPlayerScored = 1
this.speed.x = this.initial_speed.x
} else {
indexPlayerScored = 0
this.speed.x = -this.initial_speed.x
}
this.rect.center = this.spawn.clone()
if (this.speed.x < 0) {
this.speed.x = this.initial_speed.x
this.speed.y = -this.initial_speed.y
} else {
this.speed.x = -this.initial_speed.x
if (this.speed.y < 0) {
this.speed.y = this.initial_speed.y
} else {
this.speed.y = -this.initial_speed.y
}
this.rect.center = this.spawn.clone()
return indexPlayerScored
}
}

32
back/volume/src/pong/game/Game.ts

@ -21,7 +21,6 @@ export class Game {
map: MapDtoValidated
ball: Ball
players: Player[] = []
playing: boolean
ranked: boolean
waitingForTimeout: boolean
gameStoppedCallback: (name: string) => void
@ -37,7 +36,6 @@ export class Game {
) {
this.id = randomUUID()
this.timer = null
this.playing = false
this.ranked = ranked
this.waitingForTimeout = false
this.map = map
@ -99,7 +97,6 @@ export class Game {
void this.pongService.setInGame(p.name)
p.newGame()
})
this.playing = true
this.broadcastGame(GAME_EVENTS.START_GAME)
this.timer = setInterval(this.gameLoop.bind(this), 1000 / GAME_TICKS)
console.log(`Game ${this.id} starting in 3 seconds`)
@ -111,21 +108,20 @@ export class Game {
}
stop (): void {
if (this.timer !== null && this.playing) {
this.playing = false
if (this.timer !== null) {
clearInterval(this.timer)
this.timer = null
this.pongService
.saveResult(this.players, this.ranked)
.then(() => {
this.gameStoppedCallback(this.players[0].name)
this.players = []
})
.catch(() => {
this.gameStoppedCallback(this.players[0].name)
this.players = []
})
}
this.timer = null
this.pongService
.saveResult(this.players, this.ranked, DEFAULT_WIN_SCORE)
.then(() => {
this.gameStoppedCallback(this.players[0].name)
this.players = []
})
.catch(() => {
this.gameStoppedCallback(this.players[0].name)
this.players = []
})
}
movePaddle (name: string | undefined, position: Point): void {
@ -142,10 +138,6 @@ export class Game {
})
}
isPlaying (): boolean {
return this.playing
}
private gameLoop (): void {
if (this.waitingForTimeout) {
return

33
back/volume/src/pong/pong.controller.ts

@ -0,0 +1,33 @@
import {
Controller,
Get,
Param,
ParseIntPipe,
UseGuards
} from '@nestjs/common'
import { Paginate, type Paginated, PaginateQuery } from 'nestjs-paginate'
import { AuthenticatedGuard } from 'src/auth/42-auth.guard'
import type Result from './entity/result.entity'
import { PongService } from './pong.service'
@Controller('results')
export class PongController {
constructor (private readonly pongService: PongService) {}
@Get('global')
@UseGuards(AuthenticatedGuard)
async getGlobalHistory (
@Paginate() query: PaginateQuery
): Promise<Paginated<Result>> {
return await this.pongService.getHistory(query, 0)
}
@Get(':id')
@UseGuards(AuthenticatedGuard)
async getHistoryById (
@Param('id', ParseIntPipe) id: number,
@Paginate() query: PaginateQuery
): Promise<Paginated<Result>> {
return await this.pongService.getHistory(query, id)
}
}

2
back/volume/src/pong/pong.gateway.ts

@ -46,7 +46,7 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
): void {
const name: string | undefined = this.socketToPlayerName.get(client)
const game: Game | undefined = this.games.playerGame(name)
if (game?.isPlaying() !== undefined) {
if (game !== undefined) {
game.stop()
}
if (name !== undefined) {

2
back/volume/src/pong/pong.module.ts

@ -4,10 +4,12 @@ import Result from './entity/result.entity'
import { TypeOrmModule } from '@nestjs/typeorm'
import { PongService } from './pong.service'
import { UsersModule } from 'src/users/users.module'
import { PongController } from './pong.controller'
@Module({
imports: [forwardRef(() => UsersModule), TypeOrmModule.forFeature([Result])],
providers: [PongGateway, PongService],
controllers: [PongController],
exports: [PongService]
})
export class PongModule {}

15
back/volume/src/pong/pong.service.ts

@ -15,18 +15,17 @@ export class PongService {
private readonly usersService: UsersService
) {}
async updateStats (player: User, i: number, result: Result): Promise<void> {
async updateStats (player: User, i: number, result: Result, maxScore: number): Promise<void> {
player.matchs++
if (result.score[i] > result.score[Math.abs(i - 1)]) player.wins++
if (result.score[i] === maxScore) player.wins++
else player.looses++
player.winrate = (100 * player.wins) / player.matchs
player.rank = (await this.usersService.getRank(player.ftId)) + 1
}
async updatePlayer (i: number, result: Result): Promise<void> {
async updatePlayer (i: number, result: Result, maxScore: number): Promise<void> {
const player: User | null = result.players[i]
if (player == null) return
if (result.ranked) await this.updateStats(player, i, result)
if (result.ranked) await this.updateStats(player, i, result, maxScore)
player.results.push(result)
player.status = 'online'
await this.usersService.save(player)
@ -38,7 +37,7 @@ export class PongService {
await this.usersService.save(player)
}
async saveResult (players: Player[], ranked: boolean): Promise<void> {
async saveResult (players: Player[], ranked: boolean, maxScore: number): Promise<void> {
const result = new Result()
const ply = new Array<User | null>()
ply.push(await this.usersService.findUserByName(players[0].name))
@ -47,8 +46,8 @@ export class PongService {
result.players = ply
result.score = [players[0].score, players[1].score]
await this.resultsRepository.save(result)
await this.updatePlayer(0, result)
await this.updatePlayer(1, result)
await this.updatePlayer(0, result, maxScore)
await this.updatePlayer(1, result, maxScore)
}
async getHistory (

6
back/volume/src/users/entity/user.entity.ts

@ -65,12 +65,6 @@ export class User {
@JoinTable()
results: Result[]
@OneToMany(() => Message, (message: Message) => message.author)
messages: Message[]
@ManyToMany(() => Channel, (channel: Channel) => channel.users)
rooms: Channel[]
@ManyToMany(() => User)
@JoinTable()
blocked: User[]

33
back/volume/src/users/users.controller.ts

@ -13,7 +13,6 @@ import {
BadRequestException,
Redirect
} from '@nestjs/common'
import { PaginateQuery, type Paginated, Paginate } from 'nestjs-paginate'
import { FileInterceptor } from '@nestjs/platform-express'
import { diskStorage } from 'multer'
@ -31,13 +30,11 @@ import { ApiBody, ApiConsumes } from '@nestjs/swagger'
import { type Request, Response } from 'express'
import { createReadStream } from 'fs'
import { join } from 'path'
import type Result from 'src/pong/entity/result.entity'
@Controller()
@Controller("users")
export class UsersController {
constructor (
private readonly usersService: UsersService,
private readonly pongService: PongService
) {}
@Get('all')
@ -68,29 +65,11 @@ export class UsersController {
return await this.usersService.getLeaderboard()
}
@Get('rank/:id')
@Get(':id/rank')
@UseGuards(AuthenticatedGuard)
async getRank (@Param('id', ParseIntPipe) id: number): Promise<number> {
return await this.usersService.getRank(id)
}
@Get('globalHistory')
@UseGuards(AuthenticatedGuard)
async getGlobalHistory (
@Paginate() query: PaginateQuery
): Promise<Paginated<Result>> {
return await this.pongService.getHistory(query, 0)
}
@Get('history/:id')
@UseGuards(AuthenticatedGuard)
async getHistoryById (
@Param('id', ParseIntPipe) id: number,
@Paginate() query: PaginateQuery
): Promise<Paginated<Result>> {
return await this.pongService.getHistory(query, id)
}
@Post('avatar')
@UseGuards(AuthenticatedGuard)
@Redirect('http://localhost')
@ -130,7 +109,7 @@ export class UsersController {
return await this.getAvatarById(profile.id, response)
}
@Get('user/:name')
@Get(':name/byname')
async getUserByName (@Param('name') username: string): Promise<User> {
const user = await this.usersService.findUserByName(username)
user.socketKey = ''
@ -140,7 +119,7 @@ export class UsersController {
@Get('invit/:username')
@UseGuards(AuthenticatedGuard)
async invitUser (
@Profile42() profile: Profile,
@Profile42() profile: Profile,
@Param('username') username: string
): Promise<void> {
const target: User | null = await this.usersService.findUserByName(
@ -149,14 +128,14 @@ export class UsersController {
if (target === null) {
throw new BadRequestException(`User ${username} not found.`)
}
if (+profile.id === target.ftId) {
if (+profile.id === +target.ftId) {
throw new BadRequestException("You can't invite yourself.")
}
const ret: string = await this.usersService.invit(profile.id, target.ftId)
if (ret !== 'OK') throw new BadRequestException(ret)
}
@Get('avatar/:id')
@Get(':id/avatar')
async getAvatarById (
@Param('id', ParseIntPipe) ftId: number,
@Res({ passthrough: true }) response: Response

27
back/volume/src/users/users.service.ts

@ -1,12 +1,13 @@
import { BadRequestException, Catch, Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { EntityNotFoundError, QueryFailedError, Repository } from 'typeorm'
import { User } from './entity/user.entity'
import { type UserDto } from './dto/user.dto'
import { type Channel } from 'src/chat/entity/channel.entity'
import { Cron } from '@nestjs/schedule'
import { randomUUID } from 'crypto'
import { type UserDto } from './dto/user.dto'
import type Channel from 'src/chat/entity/channel.entity'
import User from './entity/user.entity'
@Injectable()
@Catch(QueryFailedError, EntityNotFoundError)
export class UsersService {
@ -20,9 +21,7 @@ export class UsersService {
async findUsers (): Promise<User[]> {
const users = await this.usersRepository.find({})
users.forEach((usr) => {
usr.socketKey = ''
})
users.forEach((usr) => usr.socketKey = '')
return users
}
@ -32,6 +31,7 @@ export class UsersService {
relations: { results: true }
})
if (user == null) throw new BadRequestException('User not found.')
user.rank = (await this.getRank(user.ftId)) + 1;
return user
}
@ -42,9 +42,7 @@ export class UsersService {
if (Date.now() - usr.lastAccess > 60000) {
usr.isVerified = false
usr.status = 'offline'
this.usersRepository.save(usr).catch((err) => {
console.log(err)
})
this.usersRepository.save(usr).catch((err) => console.log(err))
}
})
}
@ -52,6 +50,7 @@ export class UsersService {
async findUser (ftId: number): Promise<User | null> {
const user = await this.usersRepository.findOneBy({ ftId })
if (user == null) return null
user.rank = (await this.getRank(user.ftId)) + 1;
user.lastAccess = Date.now()
if (user.status === 'offline') user.status = 'online'
await this.usersRepository.save(user)
@ -62,9 +61,7 @@ export class UsersService {
const users = await this.usersRepository.find({
where: { status: 'online' }
})
users.forEach((usr) => {
usr.socketKey = ''
})
users.forEach((usr) => usr.socketKey = '')
return users
}
@ -101,6 +98,7 @@ export class UsersService {
relations: { friends: true }
})
if (user == null) throw new BadRequestException('User not found.')
user.friends.forEach((friend) => friend.socketKey = '')
return user.friends
}
@ -112,6 +110,7 @@ export class UsersService {
}
})
if (user == null) throw new BadRequestException('User not found.')
user.followers.forEach((follower) => follower.socketKey = '')
return user.followers
}
@ -121,7 +120,9 @@ export class UsersService {
winrate: 'DESC'
}
})
return leaderboard.filter((user) => user.rank !== 0)
let ret = leaderboard.filter((user) => user.rank !== 0)
ret.forEach((follower) => follower.socketKey = '')
return ret
}
async getRank (ftId: number): Promise<number> {

13
front/volume/src/App.svelte

@ -61,7 +61,7 @@
setAppState(APPSTATE.PROFILE);
}
let profileUsername: string;
let profileUsername: string = "";
async function openIdProfile(event: CustomEvent<string>) {
profileUsername = event.detail;
setAppState(APPSTATE.PROFILE_ID);
@ -71,10 +71,6 @@
setAppState(APPSTATE.HISTORY);
}
async function openIdHistory() {
setAppState(APPSTATE.HISTORY_ID);
}
async function clickFriends() {
setAppState(APPSTATE.FRIENDS);
}
@ -86,7 +82,6 @@
function clickChannels() {
setAppState(APPSTATE.CHANNELS);
}
let channels: Array<ChannelsType> = [];
let selectedChannel: ChannelsType;
const handleSelectChannel = (channel: ChannelsType) => {
selectedChannel = channel;
@ -148,7 +143,7 @@
</div>
{:else}
<div on:click={resetAppState} on:keydown={resetAppState}>
<Channels {channels} onSelectChannel={handleSelectChannel} />
<Channels onSelectChannel={handleSelectChannel} />
</div>
{/if}
{/if}
@ -177,7 +172,7 @@
{/if}
{#if appState === APPSTATE.PROFILE}
<div on:click={resetAppState} on:keydown={resetAppState}>
<Profile on:view-history={openIdHistory} />
<Profile on:view-history={() => setAppState(APPSTATE.HISTORY_ID)} />
</div>
{/if}
{#if appState === APPSTATE.PROFILE_ID}
@ -187,7 +182,7 @@
on:keydown={() =>
setAppState(APPSTATE.CHANNELS + "#" + selectedChannel.name)}
>
<Profile username={profileUsername} on:view-history={openIdHistory} />
<Profile username={profileUsername} on:view-history={() => setAppState(APPSTATE.HISTORY_ID)} />
</div>
{/if}

4
front/volume/src/Auth.ts

@ -11,8 +11,8 @@ export const API_URL = `http://${import.meta.env.VITE_HOST}:${
import.meta.env.VITE_BACK_PORT
}`;
export async function getUser() {
const res = await fetch(API_URL, {
export async function getUser(bypass: boolean = false) {
const res = await fetch(API_URL + "/users", {
method: "get",
mode: "cors",
cache: "no-cache",

2
front/volume/src/FakeLogin.svelte

@ -5,7 +5,7 @@
export let ftId;
async function doPost() {
await fetch(API_URL + "/" + ftId, {
await fetch(API_URL + "/users/" + ftId, {
method: "POST",
credentials: "include",
mode: "cors",

151
front/volume/src/components/Channels.svelte

@ -1,34 +1,31 @@
<script lang="ts" context="module">
import type { chatMessagesType } from "./Chat.svelte";
export interface ChannelsType {
id: string;
id: number;
name: string;
privacy: string;
isPrivate: boolean;
password: string;
owner: string;
owner: number;
}
import { onMount } from "svelte";
import { API_URL, store } from "../Auth";
import { dataset_dev } from "svelte/internal";
</script>
<script lang="ts">
//--------------------------------------------------------------------------------/
export let channels: Array<ChannelsType> = [];
// onMount(async () => {
// const res = await fetch(API_URL + "/channels" + $store.ftId, {
// method: "GET",
// mode: "cors",
// });
// const data = await res.json();
// channels = data;
// });
let channels: Array<ChannelsType> = [];
onMount(async () => {
const res = await fetch(API_URL + "/channels", {
credentials: "include",
mode: "cors",
});
if (res.ok) channels = await res.json();
});
//--------------------------------------------------------------------------------/
export let onSelectChannel: (channel: ChannelsType) => void;
const selectChat = (id: string) => {
const selectChat = (id: number) => {
const channel = channels.find((c) => c.id === id);
if (channel) {
onSelectChannel(channel);
@ -45,56 +42,99 @@
);
if (privacy !== "public" && privacy !== "private") {
alert("Invalid privacy setting");
return;
}
let password = "";
if (privacy === "private") {
password = prompt("Enter a password for the new channel:");
if (!password) {
alert("Invalid password");
}
}
if (privacy === "public" || password) {
const newChannel: ChannelsType = {
id: Math.random().toString(),
name,
owner: $store.username,
password,
privacy,
};
// const response = await fetch(API_URL + "/channels" + $store.ftId , {
// method: "POST",
// mode: "cors",
// body: JSON.stringify(newChannel),
// });
// const data = await response.json();
// if (data.ok) {
// channels = [newChannel, ...channels];
// } else {
// alert("Error creating channel");
// }
channels = [newChannel, ...channels];
password = prompt("Enter a password for the new channel:");
const response = await fetch(API_URL + "/channels", {
credentials: "include",
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: name,
owner: $store.ftId,
password: password,
isPrivate: privacy === "private",
}),
});
if (response.ok) {
channels.push(await response.json());
} else {
alert("Error creating channel");
}
}
};
//--------------------------------------------------------------------------------/
const removeChannel = async (id: string) => {
const removeChannel = async (id: number) => {
let string = prompt("type 'delete' to delete this channel");
if (string === "delete") {
// const response = await fetch(API_URL + "/channels" + $store.ftId + "/" + id, {
// method: "DELETE",
// mode: "cors",
// });
// const data = await response.json();
// if (data.ok) {
// channels = channels.filter((c) => c.id !== id);
// } else {
// alert("Error deleting channel");
// }
channels = channels.filter((c) => c.id !== id);
const response = await fetch(API_URL + "/channels/" + id, {
credentials: "include",
method: "DELETE",
mode: "cors",
});
if (response.ok) channels = channels.filter((c) => c.id !== id);
else alert("Error deleting channel");
}
};
//--------------------------------------------------------------------------------/
const inviteChannel = async (id: number) => {
let string = prompt("Enter the username of the user you want to invite");
const response = await fetch(API_URL + "/users/" + string + "/byname", {
credentials: "include",
method: "GET",
mode: "cors",
});
if (response.ok) {
const user = await response.json();
const response2 = await fetch(API_URL + "/channels/" + id + "/invite", {
credentials: "include",
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
userId: user.ftId,
}),
});
if (response2.ok) {
channels.push(await response2.json());
} else {
alert("Error inviting user");
}
} else {
alert("Error getting user infos");
}
};
//--------------------------------------------------------------------------------/
const changePassword = async (id: number) => {
let string = prompt("Enter the new password for this channel (leave empty to remove password) :");
const response = await fetch(API_URL + "/channels/" + id + "/password", {
credentials: "include",
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
password: string,
}),
});
if (response.ok) {
channels.push(await response.json());
} else {
alert("Error changing password");
}
// TODO: save to database
};
//--------------------------------------------------------------------------------/
@ -113,6 +153,8 @@
on:click={() => removeChannel(_channels.id)}
on:keydown={() => removeChannel(_channels.id)}>delete</button
>
<button on:click={() => inviteChannel(_channels.id)}>invite</button>
<button on:click={() => changePassword(_channels.id)}>Set - Change - Remove Password</button>
</li>{/each}
{:else}
<p>No channels available</p>
@ -134,7 +176,6 @@
justify-content: center;
align-items: center;
}
.channels {
background-color: #fff;
border: 1px solid #ccc;

64
front/volume/src/components/Chat.svelte

@ -34,7 +34,6 @@
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
}
// TODO: save to database
};
//--------------------------------------------------------------------------------/
@ -46,7 +45,6 @@
showProfileMenu = true;
selectedUser = username;
showChatMembers = false;
showChannelSettings = false;
}
function closeProfileMenu() {
showProfileMenu = false;
@ -59,7 +57,6 @@
let showChatMembers = false;
function toggleChatMembers() {
showChatMembers = !showChatMembers;
showChannelSettings = false;
}
let chatMembers: Array<User> = [
{ username: "user1" },
@ -75,7 +72,7 @@
// let chatMembers: Array<Player> = [];
// async function getChatMembers() {
// console.log("Getting chat members");
// const res = await fetch(API_URL + "/chat/members", {
// const res = await fetch(API_URL + "/channels/members", {
// mode: "cors",
// });
// chatMembers = await res.json();
@ -83,25 +80,17 @@
//--------------------------------------------------------------------------------/
let showChannelSettings = false;
function toggleChannelSettings() {
showChannelSettings = !showChannelSettings;
showChatMembers = false;
}
//--------------------------------------------------------------------------------/
const blockUser = async (username: string) => {};
//--------------------------------------------------------------------------------/
const banUser = async (username: string) => {
// const prompt = window.prompt("Enter ban duration in seconds");
// const res1 = await fetch(API_URL + "/user/" + username, {
// const res1 = await fetch(API_URL + "/users/" + username + "/byname", {
// mode: "cors",
// });
// const data1 = await res1.json();
// const res2 = await fetch(API_URL + "/chat/channels/" + data1.ftId + "/ban", {
// const res2 = await fetch(API_URL + "/channels/" + data1.ftId + "/ban", {
// method: "POST",
// mode: "cors",
// });
@ -117,11 +106,11 @@
const kickUser = async (username: string) => {
// set-up channel joining and kicking
// const res1 = await fetch(API_URL + "/user/" + username, {
// const res1 = await fetch(API_URL + "/users/" + username + "/byname", {
// mode: "cors",
// });
// const data1 = await res1.json();
// const res2 = await fetch(API_URL + "/chat/channels/" + data1.ftId + "/kick", {
// const res2 = await fetch(API_URL + "/channels/" + data1.ftId + "/kick", {
// method: "POST",
// mode: "cors",
// });
@ -138,11 +127,11 @@
const muteUser = async (username: string) => {
// use minutes prompt to determine mute duration
// const prompt = window.prompt("Enter mute duration in seconds");
// const res1 = await fetch(API_URL + "/user/" + username, {
// const res1 = await fetch(API_URL + "/users/" + username + "/byname", {
// mode: "cors",
// });
// const data1 = await res1.json();
// const res2 = await fetch(API_URL + "/chat/channels/" + data1.ftId + "/mute", {
// const res2 = await fetch(API_URL + "/channels/" + data1.ftId + "/mute", {
// method: "POST",
// mode: "cors",
// });
@ -157,11 +146,11 @@
//--------------------------------------------------------------------------------/
const adminUser = async (username: string) => {
// const res1 = await fetch(API_URL + "/user/" + username, {
// const res1 = await fetch(API_URL + "/users/" + username + "/byname", {
// mode: "cors",
// });
// const data1 = await res1.json();
// const res2 = await fetch(API_URL + "/chat/channels/" + data1.ftId + "/admin", {
// const res2 = await fetch(API_URL + "/channels/" + data1.ftId + "/admin", {
// method: "POST",
// mode: "cors",
// });
@ -176,11 +165,11 @@
//--------------------------------------------------------------------------------/
const removeAdminUser = async (username: string) => {
// const res1 = await fetch(API_URL + "/user/" + username, {
// const res1 = await fetch(API_URL + "/users/" + username + "/byname", {
// mode: "cors",
// });
// const data1 = await res1.json();
// const res2 = await fetch(API_URL + "/chat/channels/" + data1.ftId + "/admin", {
// const res2 = await fetch(API_URL + "/channels/" + data1.ftId + "/admin", {
// method: "DELETE",
// mode: "cors",
// });
@ -291,23 +280,6 @@
</div>
</div>
{/if}
<button
on:click|stopPropagation={toggleChannelSettings}
on:keydown|stopPropagation>Channel Settings</button
>
{#if showChannelSettings}
<div
class="channelSettings"
on:click|stopPropagation
on:keydown|stopPropagation
>
<div>
<button>Change Password</button>
<button>Set Password</button>
<button>Remove Password</button>
</div>
</div>
{/if}
</div>
</div>
@ -356,18 +328,4 @@
.chatMembers button {
width: 6rem;
}
.channelSettings {
position: absolute;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
padding: 1rem;
max-height: 100px;
overflow-y: scroll;
}
.channelSettings button {
width: 5rem;
}
</style>

7
front/volume/src/components/Friends.svelte

@ -12,7 +12,7 @@
? event.target.querySelector('input[type="text"]').value
: event.detail;
const response = await fetch(API_URL + "/invit/" + username, {
const response = await fetch(API_URL + "/users/invit/" + username, {
credentials: "include",
mode: "cors",
});
@ -34,14 +34,14 @@
let friendsInterval: ReturnType<typeof setInterval>;
async function getFriends(): Promise<void> {
let response = await fetch(API_URL + "/friends", {
let response = await fetch(API_URL + "/users/friends", {
credentials: "include",
mode: "cors",
});
friends = await response.json();
}
async function getInvits(): Promise<void> {
let response = await fetch(API_URL + "/invits", {
let response = await fetch(API_URL + "/users/invits", {
credentials: "include",
mode: "cors",
});
@ -73,7 +73,6 @@
<span>{friend.username} is {friend.status}</span>
</li>
{/each}
/>
</div>
{:else}
<p>No friends to display</p>

2
front/volume/src/components/Leaderboard.svelte

@ -6,7 +6,7 @@
let leaderboard: Array<Player> = [];
async function getLeader(): Promise<void> {
let response = await fetch(API_URL + "/leaderboard", {
let response = await fetch(API_URL + "/users/leaderboard", {
credentials: "include",
mode: "cors",
});

8
front/volume/src/components/MatchHistory.svelte

@ -25,17 +25,17 @@
let newBatch: Array<Match> = [];
async function fetchData() {
let response;
let response: Response;
if (username === "Global") {
response = await fetch(`${API_URL}/globalHistory?page=${page}`, {
response = await fetch(`${API_URL}/results/global?page=${page}`, {
credentials: "include",
mode: "cors",
});
} else {
response = await fetch(`${API_URL}/user/${username}`);
response = await fetch(`${API_URL}/users/${username}/byname`);
if (response.ok) {
let user = await response.json();
response = await fetch(`${API_URL}/history/${user.ftId}?page=${page}`, {
response = await fetch(`${API_URL}/results/${user.ftId}?page=${page}`, {
credentials: "include",
mode: "cors",
});

2
front/volume/src/components/NavBar.svelte

@ -50,7 +50,7 @@
{#if link.text === "Profile"}
<li>
<button on:click={clickProfile}>
<img src={api + "/avatar"} alt="avatar" />
<img src={api + "/users/avatar"} alt="avatar" />
</button>
</li>
{/if}

2
front/volume/src/components/Pong/Paddle.ts

@ -19,7 +19,7 @@ export class Paddle {
new Point(this.rect.center.x + offset, this.rect.center.y),
new Point(this.rect.size.x / 3, this.rect.size.y)
);
render_rect.draw(context, "yellow");
render_rect.draw(context, color);
}
move(e: MouseEvent) {

1
front/volume/src/components/Pong/Pong.svelte

@ -70,6 +70,7 @@
}
if (data.yourPaddleIndex !== -2) {
gamePlaying = true;
game.ranked = data.ranked;
game.setInfo(data);
}
}

20
front/volume/src/components/Profile.svelte

@ -24,7 +24,7 @@
async function getUser() {
if (username !== $store.username) {
edit = false;
const res = await fetch(API_URL + "/user/" + username, {
const res = await fetch(API_URL + "/users/" + username + "/byname", {
mode: "cors",
});
user = res.json();
@ -37,7 +37,7 @@
const dispatch = createEventDispatcher();
async function handleSubmit() {
let response = await fetch(API_URL, {
let response = await fetch(API_URL + "/users", {
headers: { "content-type": "application/json" },
method: "POST",
body: JSON.stringify({ username: user.username }),
@ -52,7 +52,7 @@
async function handle2fa(event: Event) {
event.preventDefault();
user.twoFA = !user.twoFA;
let response = await fetch(API_URL, {
let response = await fetch(API_URL + "/users", {
headers: { "content-type": "application/json" },
method: "POST",
body: JSON.stringify(user),
@ -68,11 +68,11 @@
<div class="profile" on:click|stopPropagation on:keydown|stopPropagation>
<h3>===| <mark>{user.username}'s Profile</mark> |===</h3>
<div class="profile-header">
{#if edit}
<img src={API_URL + "/avatar"} alt="avatar" class="profile-img" />
{#if !edit}
<img src={API_URL + "/users/avatar"} alt="avatar" class="profile-img" />
{:else}
<form
action={`${API_URL}/avatar`}
action={`${API_URL}/users/avatar`}
method="post"
enctype="multipart/form-data"
bind:this={avatarForm}
@ -86,13 +86,17 @@
/>
</form>
<label class="img-class" for="avatar-input">
<img src={API_URL + "/avatar"} alt="avatar" class="profile-img" />
<img
src={API_URL + "/users/avatar"}
alt="avatar"
class="profile-img"
/>
</label>
{/if}
</div>
<div class="profile-body">
<p>
<button on:click={() => dispatch("view-history", user.ftId)}
<button on:click={() => dispatch("view-history", user.username)}
>View History</button
>
</p>

Loading…
Cancel
Save