merge with master but css broken
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
CONFLICT srcs/requirements/nestjs/api_back/src/game/game.module.ts
|
||||
CONFLICT srcs/requirements/nestjs/api_back/src/users/users.service.ts
|
||||
|
||||
### Pour lancer le docker :
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ WORKDIR /usr/app
|
||||
|
||||
COPY ./game_back ./
|
||||
|
||||
RUN npm install typescript
|
||||
RUN npm install
|
||||
|
||||
RUN npx tsc
|
||||
|
||||
|
||||
@@ -80,6 +80,10 @@ export class GameSession {
|
||||
|
||||
s.pause();
|
||||
|
||||
if (!s.matchEnded) {
|
||||
s._matchEnd(en.PlayerSide.noSide, true);
|
||||
}
|
||||
|
||||
s.spectatorsMap.forEach((client) => {
|
||||
clientTerminate(client);
|
||||
});
|
||||
@@ -224,7 +228,6 @@ export class GameSession {
|
||||
{
|
||||
if (this.playersMap.size !== 2)
|
||||
{
|
||||
this.matchEnded = true;
|
||||
if (this.playersMap.size != 0) {
|
||||
this._forfeit();
|
||||
}
|
||||
@@ -237,7 +240,6 @@ export class GameSession {
|
||||
}
|
||||
private _forfeit()
|
||||
{
|
||||
this.matchEnded = true;
|
||||
console.log("Forfeit Ending");
|
||||
const gc = this.components;
|
||||
const luckyWinner: ClientPlayer = this.playersMap.values().next().value;
|
||||
@@ -307,8 +309,11 @@ export class GameSession {
|
||||
if (winner === en.PlayerSide.left) {
|
||||
console.log("Player Left WIN");
|
||||
}
|
||||
else {
|
||||
else if (winner === en.PlayerSide.right) {
|
||||
console.log("Player Right WIN");
|
||||
}
|
||||
else {
|
||||
console.log("Match end Draw");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1406
srcs/requirements/nestjs/api_back/package-lock.json
generated
1406
srcs/requirements/nestjs/api_back/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -24,10 +24,12 @@ export class AuthenticationService {
|
||||
if (!check_name)
|
||||
return await this.userService.create(createUsersDto);
|
||||
let createUsersDtoWithUsername : CreateUsersDto = createUsersDto;
|
||||
let i = 0;
|
||||
while (check_name === true)
|
||||
{
|
||||
createUsersDtoWithUsername = { ...createUsersDto, username: createUsersDto.username + randomUUID() };
|
||||
createUsersDtoWithUsername = { ...createUsersDto, username: createUsersDto.username + '_' + i};
|
||||
check_name = await this.userService.isUsernameExists(createUsersDtoWithUsername.username);
|
||||
i++;
|
||||
}
|
||||
return this.userService.create(createUsersDtoWithUsername);
|
||||
}
|
||||
|
||||
@@ -28,12 +28,8 @@ export class ChatService {
|
||||
private readonly chatroomRepository: Repository<Chatroom>,
|
||||
@InjectRepository(Friendship)
|
||||
private readonly friendshipRepository: Repository<Friendship>,
|
||||
)
|
||||
{
|
||||
// this.sockets = new Map<string, socketDto>();
|
||||
}
|
||||
) {}
|
||||
|
||||
// sockets = new mapSocket();
|
||||
|
||||
addSocket(key: string, socket: socketDto)
|
||||
{
|
||||
@@ -54,9 +50,9 @@ export class ChatService {
|
||||
}
|
||||
|
||||
// temp for test
|
||||
sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
// sleep(ms) {
|
||||
// return new Promise(resolve => setTimeout(resolve, ms));
|
||||
// }
|
||||
|
||||
|
||||
/* GETTERS ************************************************
|
||||
@@ -66,9 +62,6 @@ export class ChatService {
|
||||
{
|
||||
printCaller("-- in ");
|
||||
|
||||
console.log("---- username:", username)
|
||||
console.log("---- all rooms:", await this.getAllRooms());
|
||||
|
||||
//await this.sleep(1000);
|
||||
let rooms: Chatroom[];
|
||||
|
||||
@@ -237,7 +230,8 @@ export class ChatService {
|
||||
}
|
||||
|
||||
printCaller("-- out ");
|
||||
return user_db.currentRoom;
|
||||
if (user_db)
|
||||
return user_db.currentRoom;
|
||||
}
|
||||
|
||||
async getRoomByName(room_name: string, fieldsToReturn: string[] = null): Promise<Chatroom>
|
||||
@@ -351,7 +345,6 @@ export class ChatService {
|
||||
printCallerError(`ERROR in chat: room not found for ${username}`);
|
||||
return;
|
||||
}
|
||||
console.log("---- room_db:", room_db);
|
||||
|
||||
if (!room_db.admins.includes(username))
|
||||
{
|
||||
@@ -408,7 +401,6 @@ export class ChatService {
|
||||
console.log("throw error: error: true, code: 'NO_PASSWORD', message: 'this room has no password protection'");
|
||||
throw new HttpException({ error: true, code: 'NO_PASSWORD', message: `this room has no password protection` }, HttpStatus.FORBIDDEN);
|
||||
}
|
||||
console.log("---- room_db:", room_db);
|
||||
if (room_db.protection)
|
||||
{
|
||||
if (room.protection && !old_password)
|
||||
@@ -574,6 +566,7 @@ export class ChatService {
|
||||
let users: string[] = [];
|
||||
let friends_users: SendableFriendship[];
|
||||
let user = await this.getUserByName(username);
|
||||
|
||||
if (user)
|
||||
friends_users = await this.friendshipService.findAllBlockedFriends(user.id);
|
||||
if (friends_users)
|
||||
@@ -713,8 +706,6 @@ export class ChatService {
|
||||
async removeUserFromRoom(username: string, room_name: string): Promise<string[]>
|
||||
{
|
||||
printCaller("-- in ");
|
||||
console.log("---- username:", username);
|
||||
console.log("---- room_name:", room_name);
|
||||
|
||||
let messages = [`${username} left the room`];
|
||||
|
||||
@@ -755,11 +746,8 @@ export class ChatService {
|
||||
else
|
||||
room.owner = "";
|
||||
}
|
||||
console.log("---- room:", room);
|
||||
await this.chatroomRepository.save(room);
|
||||
|
||||
console.log("---- all rooms:", await this.getAllRooms());
|
||||
|
||||
printCaller("-- out ");
|
||||
return messages;
|
||||
}
|
||||
@@ -911,11 +899,6 @@ export class ChatService {
|
||||
}
|
||||
}
|
||||
|
||||
console.log("---- socket.username", socket.username);
|
||||
console.log("---- room_name", room_name);
|
||||
console.log("---- socket.room", socket.room);
|
||||
console.log("---- socket.username", socket.username);
|
||||
|
||||
let socket_name = `${socket.username}_not_emit`;
|
||||
await this.addMessageToRoom(room_name, socket.username, message);
|
||||
await socket.to(room_name).except(socket_name).emit('message', socket.username, message);
|
||||
@@ -965,10 +948,6 @@ export class ChatService {
|
||||
{
|
||||
printCaller("-- in ");
|
||||
|
||||
console.log("-- old_name:", old_name);
|
||||
console.log("-- new_name:", new_name);
|
||||
|
||||
|
||||
let rooms: Chatroom[] = await this.getAllRooms();
|
||||
if(!rooms)
|
||||
{
|
||||
|
||||
@@ -17,10 +17,10 @@ export class Friendship {
|
||||
@CreateDateColumn()
|
||||
date : Date;
|
||||
|
||||
@ManyToOne(type => User, user => user.username)
|
||||
@ManyToOne(type => User, user => user.username, {onDelete: 'CASCADE'})
|
||||
sender: User;
|
||||
|
||||
@ManyToOne(type => User, user => user.username)
|
||||
@ManyToOne(type => User, user => user.username, {onDelete: 'CASCADE'})
|
||||
receiver: User;
|
||||
|
||||
@Column({ type: 'enum', enum: FriendshipStatus, default: FriendshipStatus.REQUESTED})
|
||||
|
||||
@@ -8,17 +8,13 @@ import { FriendshipService } from './friendship.service';
|
||||
export class FriendshipController {
|
||||
constructor(private readonly friendshipService: FriendshipService) { }
|
||||
|
||||
|
||||
// new and improved finder of people
|
||||
// GET http://transcendance:8080/api/v2/network/myfriends
|
||||
@Get('myfriends')
|
||||
@UseGuards(AuthenticateGuard)
|
||||
@UseGuards(TwoFactorGuard)
|
||||
findOne(@Query('username') otherUsername: string, @Req() req) {
|
||||
// console.log('GET myfriends')
|
||||
const user = req.user;
|
||||
if (otherUsername !== undefined) {
|
||||
// console.log('my friend: ' + otherUsername)
|
||||
return this.friendshipService.findOneRelationshipByUsername(otherUsername, user.username);
|
||||
}
|
||||
return this.friendshipService.findAllFriendships(user.id);
|
||||
@@ -30,10 +26,8 @@ export class FriendshipController {
|
||||
@UseGuards(AuthenticateGuard)
|
||||
@UseGuards(TwoFactorGuard)
|
||||
create(@Body() createFriendshipDto: CreateFriendshipDto, @Req() req) {
|
||||
// console.log('friendship.service create')
|
||||
const user = req.user;
|
||||
if (user.username !== createFriendshipDto.receiverUsername) {
|
||||
// console.log('friendship.service create have a receiver name')
|
||||
return this.friendshipService.create(createFriendshipDto, user);
|
||||
}
|
||||
return new HttpException('You can\'t request a frienship to yourself', HttpStatus.BAD_REQUEST);
|
||||
@@ -72,7 +66,6 @@ export class FriendshipController {
|
||||
@UseGuards(TwoFactorGuard)
|
||||
remove(@Param('relationshipId') relationshipId: number, @Req() req) {
|
||||
const user : User = req.user;
|
||||
// console.log('deleting a friendship')
|
||||
return this.friendshipService.removeFriendship(relationshipId, user);
|
||||
}
|
||||
|
||||
@@ -99,8 +92,6 @@ export class FriendshipController {
|
||||
@UseGuards(AuthenticateGuard)
|
||||
@UseGuards(TwoFactorGuard)
|
||||
findBlocked(@Query('relationshipId') relationshipId: number, @Req() req) {
|
||||
// console.log('friendship.controller fetching blocked users')
|
||||
// console.log(relationshipId)
|
||||
const user = req.user;
|
||||
if (Number.isNaN(relationshipId))
|
||||
return this.friendshipService.findAllBlockedFriends(user.id);
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { User } from 'src/users/entities/user.entity';
|
||||
|
||||
import { SendableUser } from 'src/users/sendableUsers';
|
||||
|
||||
import { Repository, Brackets } from 'typeorm';
|
||||
import { CreateFriendshipDto } from './dto/create-friendship.dto';
|
||||
import { Friendship, FriendshipStatus } from './entities/friendship.entity';
|
||||
@@ -19,7 +16,6 @@ export class FriendshipService {
|
||||
private readonly userRepository: Repository<User>,
|
||||
) { }
|
||||
|
||||
//kinda useless but someone else might use it
|
||||
async findOneRelationshipById(friendId : number, userId : number) {
|
||||
const friendship = await this.friendshipRepository
|
||||
.createQueryBuilder('friendship')
|
||||
@@ -42,12 +38,8 @@ export class FriendshipService {
|
||||
)
|
||||
}),
|
||||
)
|
||||
// .andWhere('friendship.status != :status', {status : FriendshipStatus.BLOCKED})
|
||||
.getOne()
|
||||
|
||||
// console.log('END Find one friend by ID: ')
|
||||
// console.log({...friendship})
|
||||
|
||||
if (!friendship) {
|
||||
throw new HttpException(`There is no such friendship`, HttpStatus.NOT_FOUND);
|
||||
}
|
||||
@@ -55,7 +47,6 @@ export class FriendshipService {
|
||||
}
|
||||
|
||||
async findOneRelationshipByUsername(friendUsername : string, username : string) {
|
||||
// This seems to work, finding friendships by username but checking the actual users not the friendship
|
||||
const friendship = await this.friendshipRepository
|
||||
.createQueryBuilder('friendship')
|
||||
.leftJoinAndSelect('friendship.sender', 'sender')
|
||||
@@ -77,12 +68,8 @@ export class FriendshipService {
|
||||
)
|
||||
}),
|
||||
)
|
||||
// .andWhere('friendship.status != :status', {status : FriendshipStatus.BLOCKED})
|
||||
.getOne()
|
||||
|
||||
// console.log('END Find one friend by username: ')
|
||||
// console.log({...friendship})
|
||||
|
||||
if (!friendship) {
|
||||
throw new HttpException(`There is no such friendship`, HttpStatus.NOT_FOUND);
|
||||
}
|
||||
@@ -104,8 +91,6 @@ export class FriendshipService {
|
||||
for (const friendship of friendships) {
|
||||
sendFrienships.push(new SendableFriendship(friendship));
|
||||
}
|
||||
// console.log('friendship.service my friends:')
|
||||
// console.log({...sendFrienships})
|
||||
return sendFrienships;
|
||||
}
|
||||
|
||||
@@ -146,16 +131,6 @@ export class FriendshipService {
|
||||
.where('sender.id = :requestee', { requestee: userId })
|
||||
.andWhere('friendship.status = :status', { status: FriendshipStatus.BLOCKED })
|
||||
.getMany();
|
||||
|
||||
// let partialFriendship : Partial<Friendship>[] = [];
|
||||
// for (const friendship of friendships) {
|
||||
// partialFriendship.push({id: friendship.id, date: friendship.date, senderUsername: friendship.senderUsername, receiverUsername: friendship.receiverUsername, status: friendship.status});
|
||||
// }
|
||||
// console.log('friendship.service findAllBlockedFriends, friendships:')
|
||||
// console.log({...friendships})
|
||||
// console.log('friendship.service findAllBlockedFriends, partial friendship:')
|
||||
// return partialFriendship;
|
||||
|
||||
let sendFrienships: SendableFriendship[] = []
|
||||
for (const friendship of friendships) {
|
||||
sendFrienships.push(new SendableFriendship(friendship));
|
||||
@@ -171,10 +146,6 @@ export class FriendshipService {
|
||||
.where('sender.id = :requestee', { requestee: userId })
|
||||
.andWhere('friendship.status = :status', { status: FriendshipStatus.REQUESTED })
|
||||
.getMany();
|
||||
|
||||
// console.log('friendships services pendant friendships:')
|
||||
// console.log({...friendships})
|
||||
|
||||
let sendFrienships: SendableFriendship[] = []
|
||||
for (const friendship of friendships) {
|
||||
sendFrienships.push(new SendableFriendship(friendship));
|
||||
@@ -190,10 +161,6 @@ export class FriendshipService {
|
||||
.where('receiver.id = :addressee', { addressee: userId })
|
||||
.andWhere('friendship.status = :status', { status: FriendshipStatus.REQUESTED })
|
||||
.getMany();
|
||||
|
||||
// console.log('friendship service received requests')
|
||||
// console.log({...friendships})
|
||||
|
||||
let sendFrienships: SendableFriendship[] = []
|
||||
for (const friendship of friendships) {
|
||||
sendFrienships.push(new SendableFriendship(friendship));
|
||||
@@ -202,15 +169,11 @@ export class FriendshipService {
|
||||
}
|
||||
|
||||
async create(createFriendshipDto: CreateFriendshipDto, creator : User) {
|
||||
// console.log("DTO : \n")
|
||||
// console.log({...createFriendshipDto})
|
||||
|
||||
const receiver = await this.userRepository.findOneBy({username: createFriendshipDto.receiverUsername});
|
||||
if (!receiver)
|
||||
throw new HttpException(`The addressee does not exist.`, HttpStatus.NOT_FOUND);
|
||||
if (createFriendshipDto.status !== FriendshipStatus.REQUESTED && createFriendshipDto.status !== FriendshipStatus.BLOCKED)
|
||||
throw new HttpException(`The status is not valid.`, HttpStatus.NOT_FOUND);
|
||||
|
||||
|
||||
const friendship = await this.friendshipRepository
|
||||
.createQueryBuilder('friendship')
|
||||
@@ -222,21 +185,13 @@ export class FriendshipService {
|
||||
.andWhere('receiver.id = :receiverId2', {receiverId2: creator.id})
|
||||
.getOne();
|
||||
|
||||
// console.log('friendship.service create, friendship: \n\n\n')
|
||||
// console.log({...friendship})
|
||||
|
||||
if (friendship) {
|
||||
// console.log('there is already a friend request')
|
||||
|
||||
if (friendship.status && friendship.status === FriendshipStatus.ACCEPTED)
|
||||
throw new HttpException(`The friendship request has already been accepted.`, HttpStatus.OK);
|
||||
else if (friendship.status && friendship.status === FriendshipStatus.REQUESTED) {
|
||||
if (friendship && friendship.sender && friendship.sender.id === creator.id) {
|
||||
throw new HttpException(`The friendship request has already been sent the ${friendship.date}.`, HttpStatus.OK);
|
||||
} else {
|
||||
|
||||
// console.log('the friend request is being updated to Accepted')
|
||||
|
||||
friendship.status = FriendshipStatus.ACCEPTED;
|
||||
const saveAFriendship = await this.friendshipRepository.save(friendship);
|
||||
return new SendableFriendship(saveAFriendship);
|
||||
@@ -246,7 +201,6 @@ export class FriendshipService {
|
||||
else if (friendship.status && friendship.status === FriendshipStatus.DECLINED)
|
||||
throw new HttpException(`The request has been declined.`, HttpStatus.OK);
|
||||
}
|
||||
// console.log('friendship.service create, we are still saving a new friendship')
|
||||
|
||||
const newFriendship = new Friendship();
|
||||
newFriendship.sender = creator;
|
||||
@@ -263,10 +217,6 @@ export class FriendshipService {
|
||||
.leftJoinAndSelect('friendship.receiver', 'receiver')
|
||||
.where('friendship.id = :friendshipId', { friendshipId: relationshipId })
|
||||
.getOne();
|
||||
|
||||
// console.log('.service accept friendship')
|
||||
// console.log({...relation})
|
||||
|
||||
if (!relation)
|
||||
throw new HttpException(`The requested relationship not found.`, HttpStatus.NOT_FOUND);
|
||||
if (relation.sender.id === user.id) {
|
||||
@@ -308,8 +258,6 @@ export class FriendshipService {
|
||||
|
||||
// in the case where you RECEIVED the friendship but now want to block that person
|
||||
if (relation.receiver && relation.receiver.id === user.id) {
|
||||
// console.log('friendship.service blockFriendship trying to delete and recreate a friendship with block')
|
||||
// console.log({...relation})
|
||||
const newFriendshipDto = {"receiverUsername": relation.sender.username, "status": FriendshipStatus.BLOCKED};
|
||||
await this.removeFriendship(relationshipId, user);
|
||||
return await this.create(newFriendshipDto, user);
|
||||
@@ -332,17 +280,10 @@ export class FriendshipService {
|
||||
if (friendship.sender.id !== user.id && friendship.receiver.id !== user.id) {
|
||||
throw new HttpException(`You can't do that.`, HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
// console.log('.service deleted a friendship')
|
||||
|
||||
return this.friendshipRepository.remove(friendship);
|
||||
}
|
||||
|
||||
async findIfUserIsBlockedOrHasBlocked(userConnectedId: number, userToFindId: number) {
|
||||
// console.log("finding if user is blocked")
|
||||
// console.log('user connected: ' + userConnectedId)
|
||||
// console.log('user to find: ' + userToFindId)
|
||||
|
||||
const friendship = await this.friendshipRepository
|
||||
.createQueryBuilder('friendship')
|
||||
.leftJoinAndSelect('friendship.sender', 'sender')
|
||||
@@ -366,17 +307,11 @@ export class FriendshipService {
|
||||
.andWhere('friendship.status = :status', {status : FriendshipStatus.BLOCKED})
|
||||
.getOne()
|
||||
|
||||
|
||||
// console.log('printing friendship queried')
|
||||
// console.log({...friendship})
|
||||
|
||||
if (friendship) {
|
||||
console.log('we are blocked in friendship.service')
|
||||
return true;
|
||||
}
|
||||
|
||||
// console.log('we are not blocked in friendship service')
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
|
||||
import { Friendship, FriendshipStatus } from "./entities/friendship.entity";
|
||||
|
||||
|
||||
// Ok i don't really know what i'm doing but i want a thing that is typeset that i can use to send info back to the Front when they create a friendship or ask for a friendship
|
||||
// Ok it doesn't seem like i really need an interface, that just enfoces the types
|
||||
|
||||
// export interface SendableFriendship {
|
||||
export class SendableFriendship {
|
||||
id: number;
|
||||
date: Date;
|
||||
@@ -13,7 +8,6 @@ export class SendableFriendship {
|
||||
receiverUsername: string;
|
||||
status: FriendshipStatus;
|
||||
|
||||
// if my constructor is here won't it get sent all over the place too?
|
||||
constructor(friendship: Friendship) {
|
||||
this.id = friendship.id;
|
||||
this.date = friendship.date
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Body, Controller, Get, HttpException, HttpStatus, Post, Req, Res, UseGuards } from '@nestjs/common';
|
||||
import { Body, Controller, Get, HttpException, HttpStatus, Param, Post, Query, Req, Res, UseGuards } from '@nestjs/common';
|
||||
import { AuthenticateGuard, TwoFactorGuard } from 'src/auth/42/guards/42guards';
|
||||
import { User } from 'src/users/entities/user.entity';
|
||||
import { Response } from 'express';
|
||||
@@ -21,6 +21,17 @@ export class GameController {
|
||||
return this.gameService.getMatchesForSpectator();
|
||||
}
|
||||
|
||||
@Get('match/history')
|
||||
@UseGuards(AuthenticateGuard)
|
||||
@UseGuards(TwoFactorGuard)
|
||||
async getMatchHistory(@Req() req, @Query('username') username: string, @Res() res)
|
||||
{
|
||||
if (!username) {
|
||||
username = req.user.username;
|
||||
}
|
||||
return this.gameService.getMatchHistory(username, res);
|
||||
}
|
||||
|
||||
@Get('ranking')
|
||||
@UseGuards(AuthenticateGuard)
|
||||
@UseGuards(TwoFactorGuard)
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Module, forwardRef } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Friendship } from 'src/friendship/entities/friendship.entity';
|
||||
import { FriendshipService } from 'src/friendship/friendship.service';
|
||||
import { MatchHistory } from 'src/users/entities/matchHistory.entity';
|
||||
import { User } from 'src/users/entities/user.entity';
|
||||
import { UsersService } from 'src/users/users.service';
|
||||
import { Game } from './entity/game.entity';
|
||||
@@ -14,7 +15,7 @@ import { Chatroom } from 'src/chat/entities/chatroom.entity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([TokenGame, User, Game, Friendship, Chatroom]),
|
||||
TypeOrmModule.forFeature([TokenGame, User, Game, Friendship, Chatroom, MatchHistory]),
|
||||
ChatModule,
|
||||
],
|
||||
controllers: [GameController],
|
||||
|
||||
@@ -14,6 +14,8 @@ import { CreateGameDto } from './dto/createGame.dto';
|
||||
import { UpdateGameDto } from './dto/updateGame.dto';
|
||||
import { FriendshipService } from 'src/friendship/friendship.service';
|
||||
import { STATUS } from 'src/common/constants/constants';
|
||||
import { MatchHistory } from 'src/users/entities/matchHistory.entity';
|
||||
import { SendableMatchHistory } from 'src/users/class/matchHistory.class';
|
||||
|
||||
@Injectable()
|
||||
export class GameService {
|
||||
@@ -22,6 +24,8 @@ export class GameService {
|
||||
private readonly gameRepository : Repository<Game>,
|
||||
@InjectRepository(User)
|
||||
private readonly userRepository : Repository<User>,
|
||||
@InjectRepository(MatchHistory)
|
||||
private readonly matchHistory : Repository<MatchHistory>,
|
||||
@InjectRepository(TokenGame)
|
||||
private readonly tokenGameRepository : Repository<TokenGame>,
|
||||
private readonly userService : UsersService,
|
||||
@@ -43,6 +47,31 @@ export class GameService {
|
||||
return gamesToReturn;
|
||||
}
|
||||
|
||||
async getMatchHistory(username : string, @Res() res : Response)
|
||||
{
|
||||
const user = await this.userRepository.createQueryBuilder("user")
|
||||
.where('user.username = :username', {username: username})
|
||||
.getOne();
|
||||
if (!user)
|
||||
return res.status(HttpStatus.NOT_FOUND).json({message : "History for " + username + " not found"});
|
||||
console.log(user.username + 'OK USERNAME')
|
||||
const gameHistory = await this.matchHistory.
|
||||
createQueryBuilder('history')
|
||||
.leftJoinAndSelect('history.playerOne', 'playerOne')
|
||||
.leftJoinAndSelect('history.playerTwo', 'playerTwo')
|
||||
.where('history.playerOne.id = :userOne', {userOne : user.id})
|
||||
.orWhere('history.playerTwo.id = :userTwo', {userTwo: user.id})
|
||||
.getMany()
|
||||
let sendableHistory : SendableMatchHistory[] = []
|
||||
for (const history of gameHistory)
|
||||
{
|
||||
sendableHistory.push(new SendableMatchHistory(history))
|
||||
}
|
||||
console.log("sendable history")
|
||||
console.log(sendableHistory);
|
||||
return res.status(HttpStatus.OK).json(sendableHistory);
|
||||
}
|
||||
|
||||
async getRankingForAllUsers(currentUser : User) {
|
||||
const users = await this.userRepository.createQueryBuilder("user")
|
||||
.leftJoinAndSelect("user.stats", "stats")
|
||||
@@ -293,6 +322,14 @@ export class GameService {
|
||||
this.userService.incrementVictories(playerOne.id)
|
||||
this.userService.incrementDefeats(playerTwo.id)
|
||||
}
|
||||
const matchHistory = new MatchHistory();
|
||||
matchHistory.playerOne = playerOne;
|
||||
matchHistory.playerTwo = playerTwo;
|
||||
matchHistory.playerOneResult = game.playerOneUsernameResult;
|
||||
matchHistory.playerTwoResult = game.playerTwoUsernameResult;
|
||||
const savedHistory = await this.matchHistory.save(matchHistory);
|
||||
console.log("MATCH HISTORYsssssssssss")
|
||||
console.log(savedHistory);
|
||||
return HttpStatus.OK
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
import { match } from "assert";
|
||||
import { MatchHistory } from "../entities/matchHistory.entity";
|
||||
|
||||
export class SendableMatchHistory {
|
||||
id: number;
|
||||
date: Date;
|
||||
playerOneUsername: string;
|
||||
playerTwoUsername: string;
|
||||
playerTwoResult : number;
|
||||
playerOneResult : number;
|
||||
|
||||
constructor(matchHistory: MatchHistory) {
|
||||
this.id = matchHistory.id;
|
||||
this.date = matchHistory.date
|
||||
this.playerOneUsername = matchHistory.playerOne.username;
|
||||
this.playerTwoUsername = matchHistory.playerTwo.username;
|
||||
this.playerOneResult = matchHistory.playerOneResult;
|
||||
this.playerTwoResult = matchHistory.playerTwoResult;
|
||||
};
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { IsBoolean, Matches, IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
import { IsBoolean, Matches, MaxLength, IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
import { isSet } from 'util/types';
|
||||
|
||||
export class CreateUsersDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Matches(/^[a-zA-Z0-9'-_]+$/)
|
||||
@MaxLength(50)
|
||||
readonly username: string;
|
||||
readonly fortyTwoId: string;
|
||||
@IsEmail()
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { User } from "./user.entity";
|
||||
|
||||
@Entity('matchHistory')
|
||||
export class MatchHistory {
|
||||
@PrimaryGeneratedColumn()
|
||||
id : number;
|
||||
@ManyToOne(type => User, user => user.username, {onDelete: 'CASCADE'})
|
||||
playerOne: User;
|
||||
@ManyToOne(type => User, user => user.username, {onDelete: 'CASCADE'})
|
||||
playerTwo: User;
|
||||
@Column()
|
||||
playerOneResult : number;
|
||||
@Column()
|
||||
playerTwoResult : number;
|
||||
@CreateDateColumn()
|
||||
date : Date;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { Exclude } from "class-transformer";
|
||||
import { IsEmail, Length } from "class-validator";
|
||||
import { Column, Entity, JoinColumn, JoinTable, ManyToMany, OneToMany, OneToOne, PrimaryGeneratedColumn, Unique } from "typeorm";
|
||||
import { Friendship } from "../../friendship/entities/friendship.entity";
|
||||
import { MatchHistory } from "./matchHistory.entity";
|
||||
import { UserStats } from "./userStat.entities";
|
||||
|
||||
|
||||
@@ -43,18 +44,26 @@ export class User {
|
||||
@Column({ nullable: true })
|
||||
secretTwoFactorAuth: string;
|
||||
|
||||
@OneToMany(type => Friendship , (friendship) => friendship.sender)
|
||||
@OneToMany(type => Friendship , (friendship) => friendship.sender, {onDelete: 'CASCADE'})
|
||||
sentFriendRequest: Friendship[];
|
||||
|
||||
@OneToMany(type => Friendship , (friendship) => friendship.receiver)
|
||||
@OneToMany(type => Friendship , (friendship) => friendship.receiver, {onDelete: 'CASCADE'})
|
||||
receivedFriendRequest: Friendship[];
|
||||
|
||||
@OneToMany(type => MatchHistory , (matchHistory) => matchHistory.playerOne, {onDelete: 'CASCADE'})
|
||||
playerOneMatch: MatchHistory[];
|
||||
|
||||
@OneToMany(type => MatchHistory , (matchHistory) => matchHistory.playerTwo, {onDelete: 'CASCADE'})
|
||||
playerTwoMatch: MatchHistory[];
|
||||
|
||||
@JoinColumn()
|
||||
@OneToOne(() => UserStats, { cascade: true })
|
||||
@OneToOne(() => UserStats, { cascade: true, onDelete: 'CASCADE' })
|
||||
stats: UserStats;
|
||||
|
||||
|
||||
// ROOMS :
|
||||
|
||||
@Column({ nullable: true })
|
||||
currentRoom: string; // chatroom name
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ export class SendableUser {
|
||||
status: string;
|
||||
stats: UserStats;
|
||||
|
||||
// if my constructor is here won't it get sent all over the place too?
|
||||
constructor(user: User) {
|
||||
this.username = user.username;
|
||||
this.image_url = user.image_url;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {
|
||||
Body, Controller, Delete, Get, NotFoundException,HttpStatus, Param, Patch, Post, Query, Redirect, Req, Res, UploadedFile, UseGuards, UseInterceptors
|
||||
Body, Controller, Delete, Get, NotFoundException,HttpStatus, Next, Patch, Post, Query, Redirect, Req, Res, UploadedFile, UseGuards, UseInterceptors
|
||||
} from '@nestjs/common';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { Response } from 'express';
|
||||
@@ -73,8 +73,14 @@ export class UsersController {
|
||||
@UseGuards(AuthenticateGuard)
|
||||
@UseGuards(TwoFactorGuard)
|
||||
@Delete()
|
||||
remove(@Req() req) {
|
||||
return this.usersService.remove(req.user.id);
|
||||
remove(@Req() req, @Res() response, @Next() next) {
|
||||
this.usersService.remove(req.user.id);
|
||||
req.logout(function(err) {
|
||||
if (err) { return next(err); }
|
||||
response.redirect('/');
|
||||
});
|
||||
req.session.cookie.maxAge = 0;
|
||||
return {msg : 'Your account has been deleted'};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,12 +4,11 @@ import { User } from './entities/user.entity';
|
||||
import { Repository, Not } from 'typeorm';
|
||||
import { CreateUsersDto } from './dto/create-users.dto';
|
||||
import { UpdateUsersDto } from './dto/update-users.dto';
|
||||
import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto';
|
||||
import { UserStats } from './entities/userStat.entities';
|
||||
import { FriendshipService } from 'src/friendship/friendship.service';
|
||||
import { ChatService } from 'src/chat/chat.service';
|
||||
// On va devoir sûrement trouver un moyen plus simple pour passer l'id, sûrement via des pipes
|
||||
// ou des interceptors, mais pour l'instant on va faire comme ça.
|
||||
import { MatchHistory } from './entities/matchHistory.entity';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
|
||||
@@ -25,16 +24,12 @@ export class UsersService {
|
||||
const user = await this.userRepository.findOneBy({fortyTwoId: fortytwo_id});
|
||||
if (!user)
|
||||
{
|
||||
// console.log(`The requested user not found.`);
|
||||
return null;
|
||||
}
|
||||
// console.log(`The requested user found.`);
|
||||
return user;
|
||||
}
|
||||
|
||||
async findOne(username: string) {
|
||||
// console.log(`FIND ONE USER SERVICE Find user ${username}`);
|
||||
|
||||
const user = await this.userRepository.createQueryBuilder('user')
|
||||
.leftJoinAndSelect('user.stats', 'stats')
|
||||
.where('user.username = :username', { username: username })
|
||||
@@ -42,8 +37,6 @@ export class UsersService {
|
||||
if (!user)
|
||||
throw new NotFoundException(`The requested user not found.`);
|
||||
|
||||
// console.log(`FIND ONE USER SERVICE The requested user found. ` + user.username + " " + user.stats.id + user.stats.winGame + user.stats.loseGame + user.stats.drawGame + user.stats.totalGame);
|
||||
|
||||
const partialUser : Partial<User> = {
|
||||
username: user.username,
|
||||
image_url: user.image_url,
|
||||
@@ -51,9 +44,6 @@ export class UsersService {
|
||||
status: user.status,
|
||||
stats: user.stats,
|
||||
};
|
||||
|
||||
// console.log(`Returned Partial User.` + partialUser.username + user.username);
|
||||
|
||||
return partialUser;
|
||||
}
|
||||
|
||||
@@ -72,26 +62,14 @@ export class UsersService {
|
||||
let partialUsers : Partial<User>[] = [];
|
||||
|
||||
for (const otherUser of otherUsers) {
|
||||
// console.log('other user: ')
|
||||
// console.log({...otherUser})
|
||||
// let tmp = await this.friendshipService.findIfUserIsBlockedOrHasBlocked(currentUser.id, otherUser.id);
|
||||
// console.log('user.services findIF Blocked... : ')
|
||||
// console.log(tmp)
|
||||
// if (tmp === false) {
|
||||
if (await this.friendshipService.findIfUserIsBlockedOrHasBlocked(currentUser.id, otherUser.id) === false) {
|
||||
partialUsers.push({username: otherUser.username, image_url: otherUser.image_url, status: otherUser.status, stats: otherUser.stats});
|
||||
}
|
||||
}
|
||||
|
||||
// console.log('user.services findAll, partialUsers:')
|
||||
// console.log({...partialUsers})
|
||||
|
||||
return partialUsers;
|
||||
}
|
||||
|
||||
async create(createUserDto: CreateUsersDto) {
|
||||
// console.log('\nuser.services create a new user, createUserDto: ')
|
||||
// console.log({...createUserDto})
|
||||
if (await this.userRepository.findOneBy({fortyTwoId: createUserDto.fortyTwoId}))
|
||||
throw new HttpException(`The user already exists.`,HttpStatus.CONFLICT);
|
||||
const user = this.userRepository.create(createUserDto);
|
||||
@@ -103,8 +81,7 @@ export class UsersService {
|
||||
|
||||
async update(id: number, updateUserDto: UpdateUsersDto, username : string) {
|
||||
console.log("Maj user username : " + username + " updateuser dto " + updateUserDto.username )
|
||||
if (await this.isUsernameExists(updateUserDto.username) === true && updateUserDto.username !== username) {
|
||||
// console.log('updating username ' + updateUserDto.username + ' but it already is in use')
|
||||
if (await this.isUsernameExists(updateUserDto.username) === true && updateUserDto.username.toLowerCase() !== username.toLowerCase()) {
|
||||
throw new HttpException(`The username is already in use.`,HttpStatus.CONFLICT);
|
||||
}
|
||||
const user = await this.userRepository.preload(
|
||||
@@ -149,15 +126,12 @@ export class UsersService {
|
||||
return this.userRepository.update(id, {image_url: avatar});
|
||||
}
|
||||
|
||||
// doing search with username not id because userService.findOne doesn't return an Id anymore, just username... fuck this architecture is big trash...
|
||||
async getAvatarUrl(username: string) {
|
||||
const user = await this.userRepository.findOneBy({username: username});
|
||||
if (!user)
|
||||
throw new HttpException(`The user could not be found.`,HttpStatus.NOT_FOUND);
|
||||
// console.log('user.service getAvatarUrl of ' + user.username)
|
||||
if (!user.image_url)
|
||||
throw new HttpException(`The user has no avatar.`,HttpStatus.NOT_FOUND);
|
||||
// console.log('User.service Avatar URL ' + user.image_url);
|
||||
return user.image_url;
|
||||
}
|
||||
|
||||
|
||||
42
srcs/requirements/svelte/api_front/package-lock.json
generated
42
srcs/requirements/svelte/api_front/package-lock.json
generated
@@ -380,9 +380,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
@@ -622,9 +622,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
|
||||
"integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==",
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz",
|
||||
"integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
@@ -666,9 +666,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
|
||||
"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
|
||||
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
@@ -1556,18 +1556,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "3.53.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.53.1.tgz",
|
||||
"integrity": "sha512-Q4/hHkktZogGhN5iqxqSi9sjEVoe/NbIxX4hXEHoasTxj+TxEQVAq66LnDMdAZxjmsodkoI5F3slqsS68U7FNw==",
|
||||
"version": "3.55.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz",
|
||||
"integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-check": {
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-2.9.2.tgz",
|
||||
"integrity": "sha512-DRi8HhnCiqiGR2YF9ervPGvtoYrheE09cXieCTEqeTPOTJzfoa54Py8rovIBv4bH4n5HgZYIyTQ3DDLHQLl2uQ==",
|
||||
"version": "2.10.3",
|
||||
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-2.10.3.tgz",
|
||||
"integrity": "sha512-Nt1aWHTOKFReBpmJ1vPug0aGysqPwJh2seM1OvICfM2oeyaA62mOiy5EvkXhltGfhCcIQcq2LoE0l1CwcWPjlw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.9",
|
||||
@@ -1664,9 +1664,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.15.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
|
||||
"integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
|
||||
"version": "5.16.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz",
|
||||
"integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
@@ -1716,9 +1716,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
|
||||
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
|
||||
@@ -26,6 +26,16 @@ body {
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Bit5x3";
|
||||
src:
|
||||
url("/fonts/Bit5x3.woff2") format("woff2"),
|
||||
local("Bit5x3"),
|
||||
url("/fonts/Bit5x3.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgb(0,100,200);
|
||||
|
||||
@@ -4,18 +4,17 @@
|
||||
import { location } from 'svelte-spa-router';
|
||||
import Chat from './pieces/chat/Chat.svelte';
|
||||
import Header from './pieces/Header.svelte';
|
||||
import { showHeader } from './pieces/store_showHeader';
|
||||
|
||||
const conditionsFailed = (event) => {
|
||||
console.error('conditionsFailed event', event.detail);
|
||||
// i mean i guess i can just leave this in there permanently?
|
||||
|
||||
// replace('/unauthorized-access');
|
||||
replace('/');
|
||||
};
|
||||
const conditionsFailed = (event) => {
|
||||
console.error('conditionsFailed event', event.detail);
|
||||
// replace('/unauthorized-access');
|
||||
replace('/');
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
{#if ($location !== '/')}
|
||||
{#if ($location !== '/' && $showHeader)}
|
||||
<Header/>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { push } from "svelte-spa-router";
|
||||
|
||||
let qrCodeImg;
|
||||
@@ -57,21 +56,18 @@
|
||||
|
||||
</script>
|
||||
|
||||
<!-- ask Hugo but for Reactive page might want to put main in a div or something? -->
|
||||
<main>
|
||||
<h1>2FA Sign In</h1>
|
||||
<p>use google authenticator</p>
|
||||
{#await fetchQrCodeImg}
|
||||
<p>Please Wait...</p>
|
||||
{:then data}
|
||||
<!-- What the hell is data? ask Cherif -->
|
||||
<img src={qrCodeImg} alt="A QRCodeImg you must scan with google authenticator" id="qrcodeImg" />
|
||||
<form on:submit|preventDefault={submitCode}>
|
||||
<input id="code" bind:value={qrCode} type="text" placeholder="Input Code"/>
|
||||
<button type="submit">Send</button>
|
||||
</form>
|
||||
{#if wrongCode}
|
||||
<!-- shoudl i do this in the Net Ninja way? like it's "" until an error happens? -->
|
||||
<div class="error">
|
||||
{wrongCode}
|
||||
</div>
|
||||
@@ -83,18 +79,13 @@
|
||||
|
||||
<style>
|
||||
main {
|
||||
/* display: flex; */
|
||||
/* align-items: center; */
|
||||
text-align: center;
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
/* max-width: 500px; */
|
||||
}
|
||||
|
||||
form {
|
||||
/* max-width: 330px; */
|
||||
padding-top: 15px;
|
||||
|
||||
}
|
||||
|
||||
form input {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { gameState } from "./client/ws";
|
||||
|
||||
import { invited_username } from '../../pieces/store_invitation';
|
||||
import { showHeader } from '../../pieces/store_showHeader';
|
||||
|
||||
//user's stuff
|
||||
let user;
|
||||
@@ -61,6 +62,7 @@
|
||||
clearTimeout(value);
|
||||
});
|
||||
pong.destroy();
|
||||
setHiddenGame(true);
|
||||
})
|
||||
|
||||
function resetPage() {
|
||||
@@ -73,6 +75,12 @@
|
||||
pong.destroy();
|
||||
};
|
||||
|
||||
function leaveMatch() {
|
||||
clearInterval(watchMatchStartInterval);
|
||||
clearInterval(watchGameStateInterval);
|
||||
resetPage();
|
||||
};
|
||||
|
||||
const initGame = async() =>
|
||||
{
|
||||
showWaitPage = true;
|
||||
@@ -239,20 +247,18 @@
|
||||
});
|
||||
}
|
||||
|
||||
function leaveMatch() {
|
||||
clearInterval(watchMatchStartInterval);
|
||||
clearInterval(watchGameStateInterval);
|
||||
resetPage();
|
||||
};
|
||||
|
||||
let game_page_class = "";
|
||||
function setHiddenGame(value: boolean)
|
||||
{
|
||||
if (value) {
|
||||
if (value === true) {
|
||||
game_page_class = "";
|
||||
window.document.body.classList.remove('dim_background');
|
||||
showHeader.set(true);
|
||||
}
|
||||
else {
|
||||
game_page_class = "dim_background";
|
||||
window.document.body.classList.add('dim_background');
|
||||
showHeader.set(false);
|
||||
}
|
||||
hiddenGame = value;
|
||||
}
|
||||
@@ -299,11 +305,11 @@
|
||||
|
||||
{#if gameState.matchStarted && !gameState.matchEnded}
|
||||
<div class="div_game">
|
||||
<button class="pong_button" on:click={leaveMatch}>forfeit</button>
|
||||
<button class="pong_button margin_top" on:click={leaveMatch}>forfeit</button>
|
||||
</div>
|
||||
{:else if !gameState.matchStarted}
|
||||
<div class="div_game">
|
||||
<button class="pong_button" on:click={leaveMatch}>leave matchmaking</button>
|
||||
<button class="pong_button margin_top" on:click={leaveMatch}>leave matchmaking</button>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -323,7 +329,7 @@
|
||||
{#if hiddenGame}
|
||||
{#if showGameOptions}
|
||||
<div class="div_game" id="game_options">
|
||||
<button class="pong_button" on:click={fetchInvitations}>Show invitations</button>
|
||||
<button class="pong_button game_options_button" on:click={fetchInvitations}>Show invitations</button>
|
||||
<fieldset in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<legend>game options</legend>
|
||||
|
||||
@@ -370,7 +376,7 @@
|
||||
|
||||
{#if showInvitations}
|
||||
<div class="div_game" id="game_invitations">
|
||||
<button class="pong_button" on:click={switchToGameOptions}>Play a Game</button>
|
||||
<button class="pong_button game_options_button" on:click={switchToGameOptions}>Play a Game</button>
|
||||
<fieldset in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<legend>invitations</legend>
|
||||
<button class="pong_button" on:click={fetchInvitations}>Reload</button>
|
||||
@@ -390,23 +396,28 @@
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<div id="preload_font">.</div>
|
||||
|
||||
</div> <!-- div "game_page" -->
|
||||
|
||||
<style>
|
||||
@font-face {
|
||||
|
||||
#preload_font {
|
||||
font-family: "Bit5x3";
|
||||
src:
|
||||
url("/fonts/Bit5x3.woff2") format("woff2"),
|
||||
local("Bit5x3"),
|
||||
url("/fonts/Bit5x3.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
opacity:0;
|
||||
height:0;
|
||||
width:0;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.dim_background {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
:global(body.dim_background) {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
#game_page {
|
||||
margin: 0;
|
||||
position: relative;
|
||||
@@ -417,8 +428,9 @@
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#game_options, #game_invitations {
|
||||
margin-top: 20px;
|
||||
.game_options_button {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#canvas_container {
|
||||
@@ -429,11 +441,9 @@
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
canvas {
|
||||
/* background-color: #ff0000; */
|
||||
background-color: #333;
|
||||
max-width: 75vw;
|
||||
font-family: "Bit5x3";
|
||||
/* max-height: 100vh; */
|
||||
background-color: #333;
|
||||
max-width: 65vw;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
@@ -459,10 +469,12 @@ canvas {
|
||||
padding: 10px;
|
||||
}
|
||||
.avatar {
|
||||
min-height: 100px;
|
||||
min-width: 100px;
|
||||
|
||||
max-width: 100px;
|
||||
max-height: 100px;
|
||||
height: 5vw;
|
||||
width: 5vw;
|
||||
margin-top: 2vw;
|
||||
margin-bottom: -2vw;
|
||||
}
|
||||
button.margin_top {
|
||||
margin-top: 1vw;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { fade, fly } from 'svelte/transition';
|
||||
|
||||
import MatchListElem from "../../pieces/MatchListElem.svelte";
|
||||
import type { Match } from "../../pieces/Match";
|
||||
import MatchOngoingElem from "../../pieces/MatchOngoingElem.svelte";
|
||||
import type { MatchOngoing } from "../../pieces/Match";
|
||||
import { fetchAvatar } from "../../pieces/utils";
|
||||
|
||||
import * as pongSpectator from "./client/pongSpectator";
|
||||
import { gameState } from "./client/ws";
|
||||
|
||||
import { showHeader } from '../../pieces/store_showHeader';
|
||||
|
||||
let playerOneAvatar = "";
|
||||
let playerTwoAvatar = "";
|
||||
|
||||
@@ -20,13 +22,13 @@
|
||||
//html boolean for pages
|
||||
let hiddenGame = true;
|
||||
|
||||
let matchList: Match[] = [];
|
||||
let matchList: MatchOngoing[] = [];
|
||||
let watchGameStateInterval;
|
||||
const watchGameStateIntervalRate = 142;
|
||||
let timeoutArr = [];
|
||||
|
||||
onMount( async() => {
|
||||
matchList = await fetchMatchList();
|
||||
await fetchMatchList();
|
||||
})
|
||||
|
||||
onDestroy( async() => {
|
||||
@@ -35,9 +37,21 @@
|
||||
clearTimeout(value);
|
||||
});
|
||||
pongSpectator.destroy();
|
||||
setHiddenGame(true);
|
||||
})
|
||||
|
||||
async function initGameSpectator(match: Match)
|
||||
async function resetPage() {
|
||||
setHiddenGame(true);
|
||||
pongSpectator.destroy();
|
||||
await fetchMatchList();
|
||||
};
|
||||
|
||||
function leaveMatch() {
|
||||
clearInterval(watchGameStateInterval);
|
||||
resetPage();
|
||||
};
|
||||
|
||||
async function initGameSpectator(match: MatchOngoing)
|
||||
{
|
||||
watchGameStateInterval = setInterval(watchGameState, watchGameStateIntervalRate);
|
||||
pongSpectator.init(match.gameOptions, sound, gameAreaId, match.gameServerIdOfTheMatch);
|
||||
@@ -68,20 +82,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
function leaveMatch() {
|
||||
clearInterval(watchGameStateInterval);
|
||||
resetPage();
|
||||
};
|
||||
|
||||
async function resetPage() {
|
||||
setHiddenGame(true);
|
||||
pongSpectator.destroy();
|
||||
matchList = await fetchMatchList();
|
||||
};
|
||||
|
||||
async function fetchMatchList()
|
||||
{
|
||||
return fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`)
|
||||
matchList = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("All matchs not retrieved");
|
||||
@@ -100,11 +103,15 @@
|
||||
let game_page_class = "";
|
||||
function setHiddenGame(value: boolean)
|
||||
{
|
||||
if (value) {
|
||||
if (value === true) {
|
||||
game_page_class = "";
|
||||
window.document.body.classList.remove('dim_background');
|
||||
showHeader.set(true);
|
||||
}
|
||||
else {
|
||||
game_page_class = "dim_background";
|
||||
window.document.body.classList.add('dim_background');
|
||||
showHeader.set(false);
|
||||
}
|
||||
hiddenGame = value;
|
||||
}
|
||||
@@ -137,7 +144,7 @@
|
||||
|
||||
{#if !gameState.matchEnded}
|
||||
<div class="div_game">
|
||||
<button class="pong_button" on:click={leaveMatch}>leave</button>
|
||||
<button class="pong_button margin_top" on:click={leaveMatch}>leave</button>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -145,7 +152,7 @@
|
||||
<!-- -->
|
||||
|
||||
{#if hiddenGame}
|
||||
<div class="div_game" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<div class="div_game game_options" in:fly="{{ y: 10, duration: 1000 }}">
|
||||
<fieldset>
|
||||
<legend>options</legend>
|
||||
<button class="pong_button" on:click={fetchMatchList}>Reload</button>
|
||||
@@ -164,44 +171,65 @@
|
||||
|
||||
</fieldset>
|
||||
{#if matchList.length !== 0}
|
||||
<menu id="match_list">
|
||||
<!-- <menu id="match_list"> -->
|
||||
{#each matchList as match}
|
||||
<MatchListElem match={match} on:click={(e) => initGameSpectator(match)} />
|
||||
<MatchOngoingElem match={match} on:click={(e) => initGameSpectator(match)} />
|
||||
<br>
|
||||
{/each}
|
||||
</menu>
|
||||
<!-- </menu> -->
|
||||
{:else}
|
||||
<p>no match ongoing</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div id="preload_font">.</div>
|
||||
|
||||
</div> <!-- div "game_page" -->
|
||||
|
||||
<!-- -->
|
||||
<style>
|
||||
@font-face {
|
||||
|
||||
#preload_font {
|
||||
font-family: "Bit5x3";
|
||||
src:
|
||||
url("/fonts/Bit5x3.woff2") format("woff2"),
|
||||
local("Bit5x3"),
|
||||
url("/fonts/Bit5x3.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
opacity:0;
|
||||
height:0;
|
||||
width:0;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.dim_background {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
#game_page {
|
||||
:global(body.dim_background) {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
/* Possible rollback, demander à Hugo */
|
||||
/* #game_page {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
} */
|
||||
#game_page {
|
||||
margin: 0;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.game_options {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#canvas_container {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
@@ -210,14 +238,11 @@
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
canvas {
|
||||
/* background-color: #ff0000; */
|
||||
background-color: #333333;
|
||||
max-width: 75vw;
|
||||
font-family: "Bit5x3";
|
||||
/* max-height: 100vh; */
|
||||
background-color: #333;
|
||||
max-width: 65vw;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.div_game {
|
||||
text-align: center;
|
||||
font-family: "PressStart2P";
|
||||
@@ -239,15 +264,16 @@ canvas {
|
||||
font-size: 1vw;
|
||||
padding: 10px;
|
||||
}
|
||||
#match_list {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 1vw;
|
||||
}
|
||||
/* #match_list {
|
||||
text-align: center;
|
||||
} */
|
||||
.avatar {
|
||||
min-height: 100px;
|
||||
min-width: 100px;
|
||||
|
||||
max-width: 100px;
|
||||
max-height: 100px;
|
||||
height: 5vw;
|
||||
width: 5vw;
|
||||
margin-top: 2vw;
|
||||
margin-bottom: -2vw;
|
||||
}
|
||||
button.margin_top {
|
||||
margin-top: 1vw;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import GenerateUserDisplay from '../../pieces/GenerateUserDisplay.svelte';
|
||||
import MatchHistory from '../../pieces/MatchHistory.svelte';
|
||||
import { push } from 'svelte-spa-router';
|
||||
import { fetchUser } from "../../pieces/utils";
|
||||
|
||||
@@ -16,8 +17,9 @@
|
||||
<div class="background-pages">
|
||||
<div class="outer">
|
||||
{#if user !== undefined}
|
||||
<button id="setting_button" on:click={() => (push('/profile/settings'))}>Profile Settings</button>
|
||||
<GenerateUserDisplay user={user}/>
|
||||
<button on:click={() => (push('/profile/settings'))}>Profile Settings</button>
|
||||
<MatchHistory user={user}/>
|
||||
{:else}
|
||||
<h2>Sorry</h2>
|
||||
<div>Failed to load current</div>
|
||||
@@ -27,10 +29,20 @@
|
||||
|
||||
<style>
|
||||
|
||||
div.outer{
|
||||
max-width: 100%;
|
||||
text-align: center;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
div.outer{
|
||||
max-width: 100%;
|
||||
text-align: center;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
#setting_button {
|
||||
margin-top: 1vw;
|
||||
font-family: "PressStart2P";
|
||||
background-color: #618174;
|
||||
border-color: #071013;
|
||||
color: white;
|
||||
border-width: 2px;
|
||||
font-size: 1vw;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import GenerateUserDisplay from '../../pieces/GenerateUserDisplay.svelte';
|
||||
import MatchHistory from '../../pieces/MatchHistory.svelte';
|
||||
import { fetchUser } from "../../pieces/utils";
|
||||
|
||||
export let params;
|
||||
@@ -18,13 +19,20 @@
|
||||
</script>
|
||||
|
||||
<div class="background-pages">
|
||||
{#if oneUser}
|
||||
<GenerateUserDisplay user={oneUser}/>
|
||||
{:else}
|
||||
<h2>Sorry</h2>
|
||||
<div>Failed to load user {params.username}</div>
|
||||
{/if}
|
||||
|
||||
<!-- This is where i might toss in an Invite to Game button ? -->
|
||||
|
||||
{#if oneUser}
|
||||
<GenerateUserDisplay user={oneUser}/>
|
||||
<MatchHistory user={oneUser}/>
|
||||
{:else}
|
||||
<h2>Sorry</h2>
|
||||
<div>Failed to load user {params.username}</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
div {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
let avatar, newAvatar;
|
||||
let uploadAvatarSuccess = false;
|
||||
|
||||
// tbh i might not need this...
|
||||
let set = { username: '', tfa: false };
|
||||
let nameTmp; // annoying...
|
||||
let nameTmp;
|
||||
const errors = { username: '', checkbox: '', avatar: ''};
|
||||
let success = {username: '', avatar: '' };
|
||||
|
||||
@@ -24,7 +23,6 @@
|
||||
if (!user) {
|
||||
console.log('User did not load, something more official should prolly happen')
|
||||
}
|
||||
// i don't unerstand why this is necessary but it really doesn't like it otherwise
|
||||
nameTmp = user.username;
|
||||
|
||||
set.tfa = user.isEnabledTwoFactorAuth;
|
||||
@@ -51,7 +49,7 @@
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
success.username = ''
|
||||
errors.username = "Use [a-zA-Z0-9] and - _ .";
|
||||
errors.username = "Max length : 50 . Use [a-zA-Z0-9] and - _ .";
|
||||
if (response.status === 409) {
|
||||
errors.username = `${set.username} is already in use, pick a different one.`;
|
||||
}
|
||||
@@ -82,8 +80,6 @@
|
||||
const data = new FormData();
|
||||
data.append("file", newAvatar[0]);
|
||||
|
||||
// tmp
|
||||
console.log(data);
|
||||
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/avatar`,
|
||||
{
|
||||
@@ -93,9 +89,11 @@
|
||||
)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
success.avatar = ''
|
||||
errors.avatar = response.statusText;
|
||||
throw new Error("HTTP " + response.status);
|
||||
}
|
||||
errors.avatar = ''
|
||||
uploadAvatarSuccess = true;
|
||||
success.avatar = 'Your avatar has been updated';
|
||||
})
|
||||
@@ -106,6 +104,23 @@
|
||||
avatar = await fetchAvatar(user.username);
|
||||
}
|
||||
|
||||
const deleteAccount = async() => {
|
||||
console.log("deleting account")
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("HTTP " + response.status);
|
||||
}
|
||||
console.log("account deleted")
|
||||
push('/');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("catch unable to delete: ", error);
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -117,8 +132,6 @@
|
||||
<form on:submit|preventDefault={settingsHandler}>
|
||||
<div class="form-field">
|
||||
<div class="label">New Username</div>
|
||||
<!-- it really hates {user.username} and ${user.username} -->
|
||||
<!-- <input type="text" placeholder="current username: ${user.username}" bind:value={set.username}> -->
|
||||
<input type="text" placeholder="{nameTmp}" bind:value={set.username}>
|
||||
<div class="success">{success.username}</div>
|
||||
<div class="error">{errors.username}</div>
|
||||
@@ -133,62 +146,48 @@
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<!-- ok so it can't actually show us the file we upload until we've uploaded it... -->
|
||||
{#if avatar}
|
||||
<img class="avatar" src={avatar} alt="your avatar"/>
|
||||
{/if}
|
||||
<form on:submit|preventDefault={uploadAvatar}>
|
||||
<!-- not convinced this has any utility. -->
|
||||
<!-- <input type="text" bind:value={postVar} placeholder={"choose your file"}/> -->
|
||||
<div class="form-field">
|
||||
<div class="label">Pick a new Avatar</div>
|
||||
<input type="file" bind:files={newAvatar}/>
|
||||
<div class="error">{errors.avatar}</div>
|
||||
<div class="success">{success.avatar}</div>
|
||||
</div>
|
||||
<Button type={!newAvatar ? "primary" : "secondary"}>Upload Avatar</Button>
|
||||
</form>
|
||||
</Card>
|
||||
</div>
|
||||
<Button type="primary" on:click={() => deleteAccount()}>Delete Account</Button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
main {
|
||||
text-align: center;
|
||||
/* display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 20px; */
|
||||
}
|
||||
|
||||
|
||||
|
||||
div.cards{
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 20px;
|
||||
/* text-align: center; */
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
img {
|
||||
/* we want the image to display as the same size no matter the resolution */
|
||||
/* max-width: fit-content; */
|
||||
/* max-width: 100px; */
|
||||
width: 60px;
|
||||
/* ok this works but i'm guessing i'll need to add some @media ... */
|
||||
/* padding: 10px; */
|
||||
/* this doesn't work either when nothing loads and it's just the words... don't love that */
|
||||
}
|
||||
|
||||
form {
|
||||
/* max-width: 500px; */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
padding: 10px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.label {
|
||||
@@ -200,7 +199,6 @@
|
||||
color: #333;
|
||||
}
|
||||
|
||||
|
||||
.error{
|
||||
font-size: 1vw;
|
||||
font-weight: bold;
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
|
||||
import { onMount } from "svelte";
|
||||
import Button from "../../pieces/Button.svelte";
|
||||
import DisplayAUser from "../../pieces/DisplayAUser.svelte";
|
||||
import Tabs from "../../pieces/Tabs.svelte";
|
||||
import { fetchUser, fetchAllUsers, fetchAvatar } from "../../pieces/utils";
|
||||
|
||||
let user;
|
||||
import { clickOutside } from '../../pieces/clickOutside'
|
||||
import { push } from "svelte-spa-router";
|
||||
|
||||
let user;
|
||||
let allUsers = [];
|
||||
let myFriendships = [];
|
||||
let requestsMade, requestsRecieved;
|
||||
@@ -21,6 +25,11 @@
|
||||
"Blocked Users",
|
||||
];
|
||||
let activeTabItem: string = "All Users";
|
||||
let loadedUser;
|
||||
|
||||
let showModal = false;
|
||||
|
||||
|
||||
|
||||
onMount(async () => {
|
||||
user = await fetchUser();
|
||||
@@ -28,7 +37,7 @@
|
||||
fetchAll();
|
||||
});
|
||||
|
||||
const fetchAll = async () => {
|
||||
const fetchAll = async () => {
|
||||
// no need to await i think it can load in the background
|
||||
fetchAllUsers_Wrapper();
|
||||
fetchMyFriendships();
|
||||
@@ -51,12 +60,14 @@
|
||||
}
|
||||
};
|
||||
|
||||
// it's more like fetch friendships
|
||||
// it's more like fetch friendships
|
||||
// then i need to extract the users
|
||||
const fetchMyFriendships = async () => {
|
||||
myFriendships = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/myfriends`)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
if (response.status === 404)
|
||||
return []
|
||||
throw new Error("HTTP " + response.status);
|
||||
}
|
||||
return response.json();
|
||||
@@ -71,6 +82,8 @@
|
||||
requestsMade = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/pending`)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
if (response.status === 404)
|
||||
return []
|
||||
throw new Error("HTTP " + response.status);
|
||||
}
|
||||
return response.json();
|
||||
@@ -81,10 +94,12 @@
|
||||
});
|
||||
};
|
||||
|
||||
const fetchRequestsReceived = async () => {
|
||||
const fetchRequestsReceived = async () => {
|
||||
requestsRecieved = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/received`)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
if (response.status === 404)
|
||||
return []
|
||||
throw new Error("HTTP " + response.status);
|
||||
}
|
||||
return response.json();
|
||||
@@ -99,6 +114,8 @@
|
||||
blockedUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/blocked`)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
if (response.status === 404)
|
||||
return []
|
||||
throw new Error("HTTP " + response.status);
|
||||
}
|
||||
return response.json();
|
||||
@@ -110,22 +127,24 @@
|
||||
};
|
||||
/**** END OF MAIN FETCH ****/
|
||||
|
||||
// returns everything but BLOCKED
|
||||
const fetchFriendshipFull = async (aUsername) => {
|
||||
// returns everything but BLOCKED
|
||||
const fetchFriendshipFull = async (aUsername) => {
|
||||
friendshipStatusFull = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/myfriends?username=${aUsername}`)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
if (response.status === 404)
|
||||
return []
|
||||
throw new Error("HTTP " + response.status);
|
||||
}
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch((error) => {
|
||||
console.log("catch fetchFriendshipFull: ", error);
|
||||
return [];
|
||||
});
|
||||
};
|
||||
|
||||
const sendFriendRequest = async (aUsername) => {
|
||||
const sendFriendRequest = async (aUsername) => {
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations`,
|
||||
{
|
||||
method: "POST",
|
||||
@@ -152,9 +171,14 @@
|
||||
const viewAUser = async (aUsername) => {
|
||||
usernameBeingViewed = aUsername;
|
||||
await fetchFriendshipFull(aUsername);
|
||||
showModal = true;
|
||||
};
|
||||
|
||||
const acceptFriendRequest = async (relationshipId) => {
|
||||
const unViewAUser = () => {
|
||||
showModal = false;
|
||||
}
|
||||
|
||||
const acceptFriendRequest = async (relationshipId) => {
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations/${relationshipId}/accept`,
|
||||
{
|
||||
method: "PATCH",
|
||||
@@ -174,7 +198,7 @@
|
||||
activeTabItem = activeTabItem;
|
||||
};
|
||||
|
||||
const declineFriendRequest = async (relationshipId) => {
|
||||
const declineFriendRequest = async (relationshipId) => {
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations/${relationshipId}/decline`,
|
||||
{
|
||||
method: "PATCH",
|
||||
@@ -194,7 +218,7 @@
|
||||
activeTabItem = activeTabItem;
|
||||
};
|
||||
|
||||
const unfriend = async (relationshipId) => {
|
||||
const unfriend = async (relationshipId) => {
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations/${relationshipId}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
@@ -218,7 +242,7 @@
|
||||
activeTabItem = activeTabItem;
|
||||
};
|
||||
|
||||
const blockANonFriendUser = async (aUsername) => {
|
||||
const blockANonFriendUser = async (aUsername) => {
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations`,
|
||||
{
|
||||
method: "POST",
|
||||
@@ -245,7 +269,7 @@
|
||||
activeTabItem = activeTabItem;
|
||||
};
|
||||
|
||||
const blockAFriend = async (relationshipId) => {
|
||||
const blockAFriend = async (relationshipId) => {
|
||||
await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/network/relations/${relationshipId}/block`,
|
||||
{
|
||||
method: "PATCH"
|
||||
@@ -268,13 +292,14 @@
|
||||
activeTabItem = activeTabItem;
|
||||
};
|
||||
|
||||
const unblockAUser = async (relationshipId) => {
|
||||
const unblockAUser = async (relationshipId) => {
|
||||
// it's basically the same as unfriending someone cuz unfriending them means the relationship is deleted
|
||||
await unfriend(relationshipId);
|
||||
await fetchAll();
|
||||
activeTabItem = activeTabItem;
|
||||
};
|
||||
|
||||
const switchTab = async (e) => {
|
||||
const switchTab = async (e) => {
|
||||
activeTabItem = e.detail;
|
||||
if (activeTabItem === "All Users") {
|
||||
await fetchAllUsers_Wrapper();
|
||||
@@ -286,79 +311,104 @@
|
||||
await fetchBlockedUsers();
|
||||
console.log("fetching blocked users");
|
||||
}
|
||||
if (usernameBeingViewed)
|
||||
if (usernameBeingViewed) {
|
||||
await fetchAllUsers_Wrapper();
|
||||
fetchFriendshipFull(usernameBeingViewed);
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="background-pages">
|
||||
<div class="top-grid">
|
||||
|
||||
<!-- let tabItems: string[] = ['All Users', 'My Friends', 'Friend Requests']
|
||||
let activeTabItem: string = 'All Users'; -->
|
||||
|
||||
<div class="sidebar-list">
|
||||
<Tabs items={tabItems} activeItem={activeTabItem} size="small" on:tabChange={switchTab}/>
|
||||
<div class="sidebar-list background-pages">
|
||||
<Tabs items={tabItems} activeItem={activeTabItem} size="default" on:tabChange={switchTab}/>
|
||||
{#if activeTabItem === 'All Users' && allUsers}
|
||||
<h3>{activeTabItem}</h3>
|
||||
{#if Object.keys(allUsers).length === 0}
|
||||
<div class="tip">You are alone on this platform...</div>
|
||||
{/if}
|
||||
<!-- does this work? -->
|
||||
<!-- {#each allUsers as aUser (aUser.username)} -->
|
||||
<!-- {#each allUsers as aUser (aUser.id)} -->
|
||||
{#each allUsers as aUser}
|
||||
<div class="sidebar-item" on:click={() => viewAUser(aUser.username)}>{aUser.username}</div>
|
||||
<!-- i could make an indicator component? like green for connected or something?
|
||||
i could use words but color them?
|
||||
i could make it so if they're in a game -->
|
||||
<div class="status sidebar-item">{aUser.status}</div>
|
||||
<div class="user" on:click={() => viewAUser(aUser.username)}>
|
||||
{#await fetchAvatar(aUser.username) then avatar}
|
||||
<img class="avatar sidebar-item" src="{avatar}" alt="user avatar">
|
||||
{:catch error}
|
||||
<p class="error">Avatar was unable to load</p>
|
||||
{/await}
|
||||
<div class="sidebar-item">{aUser.username}</div>
|
||||
<div class="status sidebar-item">{aUser.status}</div>
|
||||
</div>
|
||||
<br>
|
||||
{/each}
|
||||
{:else if activeTabItem === 'My Friends' && myFriendships}
|
||||
<h3>{activeTabItem}</h3>
|
||||
{#if Object.keys(myFriendships).length === 0}
|
||||
<div class="tip">You don't have any Friends... Yet!</div>
|
||||
{/if}
|
||||
{#each myFriendships as aFriendship}
|
||||
{#if aFriendship.senderUsername !== user.username}
|
||||
<div class="sidebar-item" on:click={() => viewAUser(aFriendship.senderUsername)}>{aFriendship.senderUsername}</div>
|
||||
{:else if aFriendship.receiverUsername !== user.username}
|
||||
<div class="sidebar-item" on:click={() => viewAUser(aFriendship.receiverUsername)}>{aFriendship.receiverUsername}</div>
|
||||
{/if}
|
||||
{#if aFriendship.senderUsername !== user.username}
|
||||
<div class="user" on:click={() => viewAUser(aFriendship.senderUsername)}>
|
||||
{#await fetchAvatar(aFriendship.senderUsername) then avatar}
|
||||
<img class="avatar sidebar-item" src="{avatar}" alt="user avatar">
|
||||
{:catch error}
|
||||
<p class="error">Avatar was unable to load</p>
|
||||
{/await}
|
||||
<div class="sidebar-item">{aFriendship.senderUsername}</div>
|
||||
</div>
|
||||
{:else if aFriendship.receiverUsername !== user.username}
|
||||
<div class="user" on:click={() => viewAUser(aFriendship.receiverUsername)}>
|
||||
{#await fetchAvatar(aFriendship.receiverUsername) then avatar}
|
||||
<img class="avatar sidebar-item" src="{avatar}" alt="user avatar">
|
||||
{:catch error}
|
||||
<p class="error">Avatar was unable to load</p>
|
||||
{/await}
|
||||
<div class="sidebar-item">{aFriendship.receiverUsername}</div>
|
||||
</div>
|
||||
{/if}
|
||||
<br>
|
||||
{/each}
|
||||
{:else if activeTabItem === 'Friend Requests' && requestsRecieved}
|
||||
<h3>{activeTabItem}</h3>
|
||||
{#if Object.keys(requestsRecieved).length === 0}
|
||||
<div class="tip">You don't have any Friend Requests</div>
|
||||
{/if}
|
||||
{#each requestsRecieved as aUser}
|
||||
<div class="sidebar-item" on:click={() => viewAUser(aUser.senderUsername)}>{aUser.senderUsername}</div>
|
||||
<div class="status sidebar-item">{aUser.status}</div>
|
||||
<div class="user" on:click={() => viewAUser(aUser.senderUsername)}>
|
||||
{#await fetchAvatar(aUser.senderUsername) then avatar}
|
||||
<img class="avatar sidebar-item" src="{avatar}" alt="user avatar">
|
||||
{:catch error}
|
||||
<p class="error">Avatar was unable to load</p>
|
||||
{/await}
|
||||
<div class="sidebar-item">{aUser.senderUsername}</div>
|
||||
<div class="status sidebar-item">{aUser.status}</div>
|
||||
</div>
|
||||
<br>
|
||||
{/each}
|
||||
{:else if activeTabItem === 'Blocked Users' && blockedUsers}
|
||||
<h3>{activeTabItem}</h3>
|
||||
<!-- seems a little excessive... maybe a lighter way of doing this? doesn't seem like it, i hate it but at least only happens sometimes.default... -->
|
||||
{#if Object.keys(blockedUsers).length === 0}
|
||||
<div class="tip">You have not Blocked any Users</div>
|
||||
{/if}
|
||||
{#each blockedUsers as aUser}
|
||||
<div class="sidebar-item" on:click={() => viewAUser(aUser.receiverUsername)}>{aUser.receiverUsername}</div>
|
||||
<div class="status sidebar-item">{aUser.status}</div>
|
||||
<div class="user" on:click={() => viewAUser(aUser.receiverUsername)}>
|
||||
{#await fetchAvatar(aUser.receiverUsername) then avatar}
|
||||
<img class="avatar sidebar-item" src="{avatar}" alt="user avatar">
|
||||
{:catch error}
|
||||
<p class="error">Avatar was unable to load</p>
|
||||
{/await}
|
||||
<div class="sidebar-item">{aUser.receiverUsername}</div>
|
||||
<div class="status sidebar-item">{aUser.status}</div>
|
||||
</div>
|
||||
<br>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="main-display">
|
||||
{#if usernameBeingViewed}
|
||||
<DisplayAUser aUsername={usernameBeingViewed}/>
|
||||
{#if showModal && usernameBeingViewed}
|
||||
<div class="backdrop"></div>
|
||||
<div class="box background-pages" use:clickOutside on:outclick={() => unViewAUser()}>
|
||||
<DisplayAUser aUsername={usernameBeingViewed} bind:loaded={loadedUser}/>
|
||||
|
||||
{#if loadedUser === true}
|
||||
<div class="buttons-area">
|
||||
{#if friendshipStatusFull && friendshipStatusFull.id}
|
||||
{#if friendshipStatusFull.status === 'R'}
|
||||
@@ -391,32 +441,17 @@
|
||||
<Button on:click={() => blockANonFriendUser(usernameBeingViewed)}>Block User</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="placeholder">
|
||||
<h1>Click on a user!</h1>
|
||||
<h4>You'll see them displayed here</h4>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{/if}
|
||||
<button on:click={() => push(`/profile/users/${usernameBeingViewed}`)}>Profile Page</button>
|
||||
<button on:click={() => unViewAUser()}>Close</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
/* ok so i want a 12 column grid with a sidebar */
|
||||
|
||||
div.top-grid{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
/* max-height: calc(100vh - 30vh); */
|
||||
height: 85vh;
|
||||
/* margin: 0; */
|
||||
}
|
||||
|
||||
div.sidebar-list{
|
||||
grid-column: 1 / span 2;
|
||||
div.sidebar-list{
|
||||
background: #FB8B24;
|
||||
padding: 1vw;
|
||||
font-size: smaller;
|
||||
@@ -428,17 +463,19 @@
|
||||
border-right: 4px solid #071013;
|
||||
border-bottom: 4px solid #071013;
|
||||
overflow-wrap: break-word;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.sidebar-item{
|
||||
/* yea i mean that seems fine... */
|
||||
display: inline-block;
|
||||
/* somehting about the buttons too, smaller ? */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.status{
|
||||
font-size: 0.6em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* selector attributes to get only divs with .a-user */
|
||||
/* you gotta be careful with Svelte cuz it tacks classes on to the end of basically all elems! */
|
||||
div[class^="sidebar-item"]:hover{
|
||||
@@ -447,14 +484,33 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.main-display{
|
||||
grid-column: 3 / span 10;
|
||||
/* Modal Stuff */
|
||||
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
background: rgba(0,0,0,0.50)
|
||||
}
|
||||
|
||||
div.placeholder{
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
.box {
|
||||
--width: 70vw;
|
||||
--height: 60vh;
|
||||
position: absolute;
|
||||
width: var(--width);
|
||||
/* height: var(--height); */
|
||||
height: auto;
|
||||
left: calc(50% - var(--width) / 2);
|
||||
top: calc(50% - var(--height) / 2);
|
||||
align-items: center;
|
||||
padding: 8px;
|
||||
border: 2px solid white;
|
||||
border-radius: 7px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.buttons-area{
|
||||
text-align: center;
|
||||
@@ -466,10 +522,22 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
img.avatar {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.error{
|
||||
font-size: 0.8em;
|
||||
div.user {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.tile {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.error{
|
||||
font-size: 0.5em;
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
color: rgb(152, 20, 20);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 6px;
|
||||
/* this softens the corners */
|
||||
|
||||
box-shadow: 0px 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
</style>
|
||||
@@ -4,14 +4,28 @@
|
||||
import GenerateUserDisplay from './GenerateUserDisplay.svelte';
|
||||
import { fetchUser } from "../pieces/utils";
|
||||
|
||||
export let aUsername;
|
||||
let aUser;
|
||||
export let aUsername;
|
||||
export let loaded = false;
|
||||
let aUser;
|
||||
|
||||
onMount( async() => {
|
||||
loaded = false;
|
||||
aUser = await fetchUser(aUsername);
|
||||
})
|
||||
|
||||
$: aUsername, fetchUser(aUsername);
|
||||
const updateUser = async(aUsername) => {
|
||||
loaded = false;
|
||||
aUser = await fetchUser(aUsername);
|
||||
};
|
||||
|
||||
$: aUsername, updateUser(aUsername);
|
||||
|
||||
$: {
|
||||
if (!aUser)
|
||||
loaded = false;
|
||||
else
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -23,3 +37,13 @@
|
||||
<div>Failed to load user {aUsername}</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
div {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -1,35 +1,17 @@
|
||||
<script lang="ts">
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
import { fetchAvatar } from "./utils";
|
||||
import { fetchAvatar } from "./utils.js";
|
||||
|
||||
export let user;
|
||||
let rank = '';
|
||||
let avatar;
|
||||
// avatar needs to be updated!!!
|
||||
console.log('Generate User Display, BEFORE on mount ' + avatar)
|
||||
console.log('user:')
|
||||
console.log({...user})
|
||||
// console.log(user)
|
||||
let errors = {avatar: ''};
|
||||
|
||||
onMount( async() => {
|
||||
avatar = await fetchAvatar(user.username);
|
||||
})
|
||||
|
||||
|
||||
/**** THIS IS BASICALLY ALL THE RANK LOGIC ERIC HAS MADE ****/
|
||||
|
||||
if (user.loseGame > user.winGame) {
|
||||
rank = 'Bitch Ass Loser!'
|
||||
} else if (user.loseGame === user.winGame) {
|
||||
if (user.stats.loseGame > user.stats.winGame) {
|
||||
rank = "Come on, you can do better"
|
||||
} else if (user.stats.loseGame === user.stats.winGame) {
|
||||
rank = 'Fine i guess...'
|
||||
} else {
|
||||
rank = 'Yea you da Boss!'
|
||||
rank = 'You da Boss!'
|
||||
}
|
||||
|
||||
|
||||
// Glittery Stars and such for Rank
|
||||
|
||||
let index = 0, interval = 1000;
|
||||
@@ -60,14 +42,15 @@
|
||||
|
||||
</script>
|
||||
|
||||
<!-- is this if excessive? -->
|
||||
|
||||
<div class="outer">
|
||||
{#if user}
|
||||
<main>
|
||||
<!-- <img class="icon" src="img/default_user_icon.png" alt="default user icon"> -->
|
||||
<!-- <img class="icon" src="{user.image_url}" alt="default user icon"> -->
|
||||
<img class="avatar" src="{avatar}" alt="user avatar">
|
||||
<div class="error">{errors.avatar}</div>
|
||||
{#await fetchAvatar(user.username) then avatar}
|
||||
<img class="avatar" src="{avatar}" alt="user avatar">
|
||||
{:catch error}
|
||||
<p class="errorA">Avatar was unable to load</p>
|
||||
{/await}
|
||||
<div class="username">{user.username}</div>
|
||||
<div class="rank">Rank:
|
||||
<span class="glitter">
|
||||
@@ -91,7 +74,7 @@
|
||||
</div>
|
||||
<section class="main-stats">
|
||||
<h4>Match Statistics</h4>
|
||||
<p>Victories: {user.stats.winGame}</p>
|
||||
<p>Wins: {user.stats.winGame}</p>
|
||||
<p>Losses: {user.stats.loseGame}</p>
|
||||
<p>Draws: {user.stats.drawGame}</p>
|
||||
<p class="highlight">Total: {user.stats.totalGame}</p>
|
||||
@@ -107,19 +90,23 @@
|
||||
div.outer{
|
||||
max-width: 960px;
|
||||
margin: 40px auto;
|
||||
margin-top: 1vw;
|
||||
}
|
||||
|
||||
/* The main part */
|
||||
main{
|
||||
max-width: 960px;
|
||||
margin: 40px auto;
|
||||
margin-top: 1vw;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Normal CSS stuff */
|
||||
.avatar{
|
||||
max-width: 150px;
|
||||
/* padding: 5px; */
|
||||
width: 15vw;
|
||||
height: 15vw;
|
||||
max-width: 130px;
|
||||
max-height: 130px;
|
||||
margin-bottom: 1vw;
|
||||
}
|
||||
|
||||
/* The variable rich section */
|
||||
@@ -127,10 +114,8 @@
|
||||
max-width: 600px;
|
||||
margin: 40px auto;
|
||||
text-align: center;
|
||||
/* i think i want to use a grid? */
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
/* not sure about this, maybe top should be larger? */
|
||||
grid-template-rows: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
@@ -152,10 +137,10 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error{
|
||||
font-size: 1vw;
|
||||
.errorA{
|
||||
font-size: 0.5em;
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
color: rgb(152, 20, 20);
|
||||
}
|
||||
|
||||
.highlight {
|
||||
@@ -180,8 +165,6 @@
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Glittery Star Stuff */
|
||||
|
||||
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
import { push } from "svelte-spa-router";
|
||||
import { location } from 'svelte-spa-router';
|
||||
|
||||
|
||||
// no need, it's just for links
|
||||
import active from 'svelte-spa-router/active'
|
||||
// or i could leave them all and not display if they're active?
|
||||
|
||||
$: current = $location;
|
||||
|
||||
let handleClickLogout = async () =>
|
||||
@@ -100,4 +95,10 @@
|
||||
max-width: 10%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
button:hover{
|
||||
cursor: pointer;
|
||||
background-color: rgb(0, 166, 255) !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -2,9 +2,18 @@
|
||||
import type { MatchOptions } from "../pages/game/client/pongSpectator";
|
||||
export { MatchOptions } from "../pages/game/client/pongSpectator";
|
||||
|
||||
export class Match {
|
||||
export class MatchOngoing {
|
||||
gameServerIdOfTheMatch: string;
|
||||
gameOptions: MatchOptions;
|
||||
playerOneUsername: string;
|
||||
playerTwoUsername: string;
|
||||
}
|
||||
|
||||
export class MatchHistory {
|
||||
id: number;
|
||||
date: Date;
|
||||
playerOneUsername: string;
|
||||
playerTwoUsername: string;
|
||||
playerOneResult: number;
|
||||
playerTwoResult: number;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { fetchAvatar } from "./utils.js";
|
||||
import type { MatchHistory } from "./Match";
|
||||
|
||||
export let user;
|
||||
let matchList: MatchHistory[] = [];
|
||||
|
||||
onMount( async() => {
|
||||
matchList = await fetchMatchList(user.username);
|
||||
console.log(matchList);
|
||||
// matchList = await mockMatchList(user.username);
|
||||
})
|
||||
|
||||
async function fetchMatchList(username: string)
|
||||
{
|
||||
let url = `http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/history?username=${username}`;
|
||||
return fetch(url)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("HTTP " + response.status);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("catch fetchMatchList: ", error);
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
async function mockMatchList(username?: string)
|
||||
{
|
||||
return [
|
||||
{
|
||||
playerOneUsername: "hulamy",
|
||||
playerTwoUsername: "lperrey",
|
||||
playerOneResult: 5,
|
||||
playerTwoResult: 11,
|
||||
date: new Date()
|
||||
},
|
||||
{
|
||||
playerOneUsername: "hulamy",
|
||||
playerTwoUsername: "lperrey",
|
||||
playerOneResult: 14,
|
||||
playerTwoResult: 12,
|
||||
date: new Date()
|
||||
},
|
||||
{
|
||||
playerOneUsername: "hulamy",
|
||||
playerTwoUsername: "lperrey",
|
||||
playerOneResult: 0,
|
||||
playerTwoResult: 0,
|
||||
date: new Date()
|
||||
},
|
||||
{
|
||||
playerOneUsername: "hulamy",
|
||||
playerTwoUsername: "lperrey",
|
||||
playerOneResult: 3,
|
||||
playerTwoResult: 0,
|
||||
date: new Date()
|
||||
},
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<br />
|
||||
<div class="background-pages">
|
||||
<div class="principal-div">
|
||||
<table class="stats-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Player One</th>
|
||||
<th></th>
|
||||
<th>VS</th>
|
||||
<th></th>
|
||||
<th>Player Two</th>
|
||||
<th>Result</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each matchList as match, i}
|
||||
<tr>
|
||||
<th>{i + 1}</th>
|
||||
{#await fetchAvatar(match.playerOneUsername) then avatar}
|
||||
<td>
|
||||
<img class="avatar" src="{avatar}" alt="avatarOne">
|
||||
<br>
|
||||
{match.playerOneUsername}
|
||||
</td>
|
||||
{/await}
|
||||
<td>{match.playerOneResult}</td>
|
||||
<td>VS</td>
|
||||
<td>{match.playerTwoResult}</td>
|
||||
{#await fetchAvatar(match.playerTwoUsername) then avatar}
|
||||
<td>
|
||||
<img class="avatar" src="{avatar}" alt="avatarTwo">
|
||||
<br>
|
||||
{match.playerTwoUsername}
|
||||
</td>
|
||||
{/await}
|
||||
{#if match.playerOneResult === match.playerTwoResult}
|
||||
<th>DRAW</th>
|
||||
{:else if (user.username === match.playerOneUsername && match.playerOneResult > match.playerTwoResult)
|
||||
|| (user.username === match.playerTwoUsername && match.playerTwoResult > match.playerOneResult)}
|
||||
<th>WIN</th>
|
||||
{:else}
|
||||
<th>LOSE</th>
|
||||
{/if}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
.avatar {
|
||||
width: 3vw;
|
||||
height: 3vw;
|
||||
}
|
||||
|
||||
.principal-div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.stats-table {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
font-size: 1vw;
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
.stats-table thead tr {
|
||||
background-color: #618174;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.stats-table th,
|
||||
.stats-table td {
|
||||
padding: 12px 15px;
|
||||
size: 10vw;
|
||||
max-width: 10vw;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.stats-table tbody tr {
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
|
||||
.stats-table tbody tr:nth-of-type(even) {
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
.stats-table tbody tr:last-of-type {
|
||||
border-bottom: 2px solid #618174;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { Match, MatchOptions} from "./Match";
|
||||
|
||||
export let match: Match;
|
||||
|
||||
let matchOptionsString = "";
|
||||
|
||||
onMount( async() => {
|
||||
if (match.gameOptions === MatchOptions.noOption) {
|
||||
matchOptionsString = "standard";
|
||||
}
|
||||
else {
|
||||
if (match.gameOptions & MatchOptions.multiBalls) {
|
||||
matchOptionsString += "multi balls";
|
||||
}
|
||||
if (match.gameOptions & MatchOptions.movingWalls) {
|
||||
if (matchOptionsString) { matchOptionsString += ", "; }
|
||||
matchOptionsString += "moving walls";
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
<!-- -->
|
||||
|
||||
<li>
|
||||
<button on:click>
|
||||
'{match.playerOneUsername}' VS '{match.playerTwoUsername}'
|
||||
<br/> [{matchOptionsString}]
|
||||
</button>
|
||||
</li>
|
||||
|
||||
|
||||
<!-- -->
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { MatchOngoing, MatchOptions} from "./Match";
|
||||
import { fetchAvatar } from "./utils.js";
|
||||
|
||||
export let match: MatchOngoing;
|
||||
|
||||
let matchOptionsString = "";
|
||||
|
||||
onMount( async() => {
|
||||
if (match.gameOptions === MatchOptions.noOption) {
|
||||
matchOptionsString = "standard";
|
||||
}
|
||||
else {
|
||||
if (match.gameOptions & MatchOptions.multiBalls) {
|
||||
matchOptionsString += "multi balls";
|
||||
}
|
||||
if (match.gameOptions & MatchOptions.movingWalls) {
|
||||
if (matchOptionsString) { matchOptionsString += ", "; }
|
||||
matchOptionsString += "moving walls";
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
<!-- -->
|
||||
|
||||
<div>
|
||||
{#await fetchAvatar(match.playerOneUsername) then avatar}
|
||||
<img class="avatar" src="{avatar}" alt="avatarOne">
|
||||
{/await}
|
||||
<button on:click class="match_elem">
|
||||
'{match.playerOneUsername}' VS '{match.playerTwoUsername}'
|
||||
<br/>
|
||||
[{matchOptionsString}]
|
||||
</button>
|
||||
{#await fetchAvatar(match.playerTwoUsername) then avatar}
|
||||
<img class="avatar" src="{avatar}" alt="avatarTwo">
|
||||
{/await}
|
||||
</div>
|
||||
|
||||
<!-- -->
|
||||
<style>
|
||||
|
||||
.avatar {
|
||||
width: 3vw;
|
||||
height: 3vw;
|
||||
margin-bottom: -1vw;
|
||||
}
|
||||
|
||||
.match_elem {
|
||||
margin: 1vw;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,40 +0,0 @@
|
||||
<script lang="ts">
|
||||
|
||||
// trying something...
|
||||
// const element = document.getElementById("bottom-half");
|
||||
export let element;
|
||||
|
||||
// is this too JS for me?
|
||||
const scrollTo= () => {
|
||||
element.scrollIntoView({behavior: "smooth"});
|
||||
console.log('supposed to scroll');
|
||||
// Maybe eventually i can do this in a more svelte way, on:scroll={someEvent} ... whatever
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<!-- <svelte:window bind:scrollY={y}/> -->
|
||||
|
||||
|
||||
<!-- wait actually i don't want this... -->
|
||||
<!-- <svelte:window on:scroll={handleOnScroll} /> -->
|
||||
|
||||
<!-- a div instead? -->
|
||||
<!-- <button>Login</button> -->
|
||||
<div class="to-login" on:click={scrollTo}>Login</div>
|
||||
|
||||
|
||||
<style>
|
||||
.to-login{
|
||||
display: inline-block;
|
||||
/* color: bisque; */
|
||||
color: blue;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.to-login:hover{
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@
|
||||
export let items;
|
||||
export let activeItem;
|
||||
export let size = 'medium';
|
||||
// big, medium, small
|
||||
// big, medium, small, default (responsive)
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
@@ -11,11 +11,9 @@
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="tabs" class:size={size}>
|
||||
<!-- creates a list, can be done other ways -->
|
||||
<ul>
|
||||
{#each items as item}
|
||||
<li class={size} on:click={() => dispatch('tabChange', item)}>
|
||||
<!-- the class active is attributed if the condition is met -->
|
||||
<div class:active={activeItem === item}>{item}</div>
|
||||
</li>
|
||||
{/each}
|
||||
@@ -23,12 +21,9 @@
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* .tabs{
|
||||
margin-bottom: 40px;
|
||||
} */
|
||||
|
||||
.tab.big{
|
||||
margin-bottom: 50px;
|
||||
/* guessing at size */
|
||||
}
|
||||
.tab.medium{
|
||||
margin-bottom: 40px;
|
||||
@@ -36,7 +31,6 @@
|
||||
}
|
||||
.tab.small{
|
||||
margin-bottom: 10px;
|
||||
/* need it small */
|
||||
}
|
||||
ul{
|
||||
display: flex;
|
||||
@@ -45,15 +39,12 @@
|
||||
list-style-type: none;
|
||||
}
|
||||
li{
|
||||
/* margin: 0 16px; */
|
||||
/* font-size: 18px; */
|
||||
color: #555;
|
||||
cursor: pointer;
|
||||
}
|
||||
li.big{
|
||||
margin: 0 20px;
|
||||
font-size: 22px;
|
||||
/* guessing at size */
|
||||
}
|
||||
li.medium{
|
||||
margin: 0 16px;
|
||||
@@ -63,13 +54,36 @@
|
||||
li.small{
|
||||
margin: 8px;
|
||||
font-size: 10px;
|
||||
/* need it small */
|
||||
}
|
||||
|
||||
|
||||
.active{
|
||||
color: #d91b42;
|
||||
border-bottom: 2px solid #d91b42;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
li div:hover {
|
||||
color: rgb(0, 166, 255) !important;
|
||||
}
|
||||
|
||||
/* default is medium */
|
||||
.tab.default {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
li.default{
|
||||
margin: 0 16px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
/* they turn .small */
|
||||
.tab.default {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
li.default {
|
||||
margin: 8px;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,15 @@
|
||||
export function clickOutside(node) {
|
||||
const handleClick = (event) => {
|
||||
if (!node.contains(event.target)) {
|
||||
node.dispatchEvent(new CustomEvent("outclick"));
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("click", handleClick, true);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
document.removeEventListener("click", handleClick, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const showHeader = writable(true);
|
||||
@@ -47,6 +47,8 @@ export async function fetchAllUsers()
|
||||
return fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
if (response.status === 404)
|
||||
return [];
|
||||
throw new Error("HTTP " + response.status);
|
||||
}
|
||||
return response.json();
|
||||
|
||||
@@ -5,8 +5,6 @@ import ProfileSettings from '../pages/profile/ProfileSettings.svelte';
|
||||
import ProfileUsers from '../pages/profile/ProfileUsers.svelte';
|
||||
import ProfileDisplayOneUser from "../pages/profile/ProfileDisplayOneUser.svelte";
|
||||
|
||||
import DisplayAUser from '../pieces/DisplayAUser.svelte';
|
||||
|
||||
// establishing the prefix here very clearly so we can have a coherent repeatable structure
|
||||
export const prefix = '/profile';
|
||||
|
||||
@@ -15,6 +13,5 @@ export const profileRoutes = {
|
||||
'/settings': ProfileSettings,
|
||||
'/users': ProfileUsers,
|
||||
'/users/:username': ProfileDisplayOneUser,
|
||||
// '/users/:username': DisplayAUser,
|
||||
'*': NotFound
|
||||
};
|
||||
Reference in New Issue
Block a user