Skip to content

Commit

Permalink
FRIENDSHIP - LEADERBOARD - HISTORY - INFINITE_SCROLL (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
automerge-pingpong[bot] authored Nov 5, 2023
2 parents b912392 + 9e425af commit abf4178
Show file tree
Hide file tree
Showing 18 changed files with 553 additions and 359 deletions.
Binary file modified .DS_Store
Binary file not shown.
46 changes: 46 additions & 0 deletions backend/code/prisma/migrations/20231024181019_anme/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
Warnings:
- A unique constraint covering the columns `[createdAt]` on the table `messages` will be added. If there are existing duplicate values, this will fail.
- A unique constraint covering the columns `[Username]` on the table `users` will be added. If there are existing duplicate values, this will fail.
*/
-- CreateEnum
CREATE TYPE "NotifType" AS ENUM ('addFriend');

-- DropForeignKey
ALTER TABLE "messages" DROP CONSTRAINT "messages_roomId_fkey";

-- AlterTable
ALTER TABLE "blocked_friends" ADD COLUMN "dmRoomId" TEXT;

-- AlterTable
ALTER TABLE "room_members" ADD COLUMN "bannedAt" TIMESTAMP(3);

-- AlterTable
ALTER TABLE "users" ADD COLUMN "tfaSecret" TEXT,
ADD COLUMN "tfaStatus" BOOLEAN NOT NULL DEFAULT false;

-- CreateTable
CREATE TABLE "notifications" (
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"id" TEXT NOT NULL,
"recipientId" TEXT NOT NULL,
"content" "NotifType" NOT NULL,
"is_read" BOOLEAN NOT NULL DEFAULT false,
"readAt" TIMESTAMP(3),

CONSTRAINT "notifications_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "notifications_createdAt_key" ON "notifications"("createdAt");

-- CreateIndex
CREATE UNIQUE INDEX "messages_createdAt_key" ON "messages"("createdAt");

-- CreateIndex
CREATE UNIQUE INDEX "users_Username_key" ON "users"("Username");

-- AddForeignKey
ALTER TABLE "messages" ADD CONSTRAINT "messages_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "rooms"("id") ON DELETE CASCADE ON UPDATE CASCADE;
11 changes: 10 additions & 1 deletion backend/code/src/game/game.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Controller, Get, Post, Query, UseGuards } from '@nestjs/common';
import { Controller, Get, Post, Query, UseGuards, Param } from '@nestjs/common';
import { GameService } from './game.service';
import { AtGuard } from 'src/auth/guards/at.guard';
import { GetCurrentUser } from 'src/auth/decorator/get_current_user.decorator';
Expand All @@ -21,4 +21,13 @@ export class GameController {
) {
return this.gameService.getHistory(userId, offset, limit);
}

@Get('history/:id')
@UseGuards(AtGuard)
getHistoryId(
@Param('id') userId: string,
@Query() { offset, limit }: QueryOffsetDto,
) {
return this.gameService.getHistory(userId, offset, limit);
}
}
4 changes: 4 additions & 0 deletions backend/code/src/game/game.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,14 @@ export class GameService {
score2: true,
participant1: {
select: {
userId:true,
Username: true,
avatar: true,
},
},
participant2: {
select: {
userId:true,
Username: true,
avatar: true,
},
Expand All @@ -78,11 +80,13 @@ export class GameService {
match: {
createdAt: match.createdAt,
Player1: {
id:match.participant1.userId,
username: match.participant1.Username,
score: match.score1,
avatar: avatar1,
},
Player2: {
id:match.participant2.userId,
username: match.participant2.Username,
score: match.score2,
avatar: avatar2,
Expand Down
24 changes: 20 additions & 4 deletions backend/code/src/leaderboard/leaderboard.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,25 @@ export class LeaderBoardService {
},
},
});
return leaderboard.map((user) => ({
userId: user.winner_id,
wins: user._count.id,
}));
const leaderboardsPromises = leaderboard.map(async (user ) => {
const lead = await this.prisma.user.findUnique({
where: {
userId: user.winner_id,
},
select: {
Username: true,
firstName: true,
lastName: true,
avatar: true,
userId: true,
},
},
)
return {
...lead,
wins: user._count.id
}
});
return await Promise.all(leaderboardsPromises);
}
}
2 changes: 1 addition & 1 deletion frontend/Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:20
FROM node:18

WORKDIR /app

Expand Down
20 changes: 20 additions & 0 deletions frontend/code/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frontend/code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
"react-hook-form": "^7.46.1",
"react-hot-toast": "^2.4.1",
"react-icons": "^4.10.1",
"react-infinite-scroll-component": "^6.1.0",
"react-konva": "^18.2.10",
"socket.io-client": "^4.7.2",
"react-scripts": "^5.0.1",
"socket.io-client": "^4.7.2",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4",
"zustand": "^4.4.1"
Expand Down
7 changes: 5 additions & 2 deletions frontend/code/src/Components/Home/LeaderBoard.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Chart } from './assets/Chart'
import { Table } from './assets/Table'
export const LeaderBoard = () => {


export const LeaderBoard = () => {
return (
<div className='flex flex-col rounded-2xl justify-start items-start mt-6 sm:h-full h-full w-full bg-base-200 overflow-auto no-scrollbar'>
<div className='flex flex-col rounded-2xl justify-start items-start mt-6 sm:h-full h-full w-full bg-base-200'>
<div className="flex justify-start items-start pl-2 pt-2 sm:pl-12 sm:pt-12 gap-x-4 py-4">
<Chart/> <span className='font-montserrat'>Leader Board </span>

</div>

<Table />
</div>
)
Expand Down
147 changes: 99 additions & 48 deletions frontend/code/src/Components/Home/assets/Table.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,91 @@
import { Trophy } from './Trophy'
import { useState,useEffect } from 'react'
import { useState,useEffect, useCallback, useRef, FC } from 'react'
import { Daimond } from './Daimond'
import { Loading } from '../../Loading'
import { Link } from 'react-router-dom'
// import { useLoaderData } from 'react-router'
import { NullPlaceHolder } from '../../Chat/Components/RoomChatHelpers'
import { Logo } from '../../Layout/Assets/Logo'
import InfiniteScroll from 'react-infinite-scroll-component';
import api from '../../../Api/base'
import toast from 'react-hot-toast'

// export const dataLoader = async() => {

// var users :any[] = [];
// for (let i = 0 ; i < 10 ; ++i)
// {
// let response = await fetch(`https://randomuser.me/api/`)
// let data = await response.json()
// if (data.results && data.results.length > 0) {
// const newUser = data.results[0];
// users.push(newUser)
// }
// }
// return users;


// }
export const Table = () =>
export const Table: FC = () =>
{
const [users, setUsers] = useState<any | undefined>([])
// const users : any = useLoaderData();
const [users, setUsers] = useState<any>([])
const [loading , setLoading] = useState<boolean>(true)
const page = useRef(0);
const hasMoreItems = useRef(true)
const [nextPageUrl, setNextPageUrl] : any = useState(
"/leaderboard?offset=0&limit=20"

);

const [fetching, setFetching] = useState(false);

const [loading , setLoading] = useState<boolean>(true)
useEffect( () => {
const fetchdata = async() =>{
for (let i = 0 ; i < 10 ; ++i)
{
let response = await fetch(`https://randomuser.me/api/`)
let data = await response.json()
if (data.results && data.results.length > 0) {
const newUser = data.results[0];
setUsers((oldUsers : any) => [...oldUsers, newUser]);
}
}
setLoading(false)
const fetchItems = useCallback(
async () => {
if (fetching) {
return;
}
fetchdata().catch(console.error)
},[])


setFetching(true);

try {
const newdata : any = await api.get(nextPageUrl);
if (newdata.data.length < 20) {
setUsers([...users, ...newdata.data]);
setNextPageUrl(null);
return;
}
console.log(newdata.data.length)
if (!newdata.data || newdata.data.length === 0)
{
setNextPageUrl(null)
return ;
}
else
{
console.log(newdata.data)
console.log("here")
setUsers([...users, ...newdata.data]);
setNextPageUrl(`/leaderboard?offset=${page.current}&limit=20`);
page.current += 20;
}


}
catch(e)
{
toast.error("Can't get leadeboard");
}
finally {
setLoading(false)

setFetching(false);
}
},
[users, fetching, nextPageUrl]
);

useEffect(() => {
fetchItems()
page.current += 20;
//eslint-disable-next-line
},[])
hasMoreItems.current = !!nextPageUrl;

return (
<div className="overflow-x-auto no-scrollbar w-full">
users?.length > 0 || loading ? (
<div className='w-full h-full' >
<InfiniteScroll
dataLength={users.length}
next={fetchItems}
loader={<div className='flex items-center justify-center w-full '><Logo x={"6"} y={"6"}/></div>}
endMessage={<div className='p-4 flex justify-center items-center font-montserrat text-neutral'>No more results!</div>}
hasMore={hasMoreItems.current}
scrollableTarget="scrollTarget"
style={{overflow:"auto", height:"100%"}}
>
<table className="table w-full">
<thead>
<tr className='w-[80vw] flex justify-between px-10 items-center'>
Expand All @@ -53,11 +94,13 @@ export const Table = () =>
<th>Score</th>
</tr>
</thead>
<tbody className='flex flex-col justify-between items-center gap-2 sm:gap-4'>
{ !loading && users.map((x: any, index: number) => (
<tbody className='flex flex-col justify-between no-scrollbar items-center gap-2 sm:gap-4'>


{!loading && users.map((x: any, index: number) => (
<tr
key={index}
className='bg-accent border-base-200 rounded-xl w-11/12 flex justify-between sm:justify-between px-4 h-16 xl:h-20 sm:px-10 items-center'
className='bg-accent border-base-200 rounded-xl w-11/12 flex justify-between sm:justify-between px-4 h-16 xl:h-28 sm:px-10 items-center'
>
<td>
<div className="flex items-center space-x-3">
Expand All @@ -67,22 +110,30 @@ export const Table = () =>
</div>
</td>
<td className='flex justify-start items-center gap-x-2 hover:cursor-pointer'>
<Link to={`/Profile/${x.id.value}`}>
<div className="flex flex justify-start items-center gap-x-2 hover:cursor-pointer"><div className="avatar">
<Link to={`/Profile/${x.userId}`}>
<div className="flex justify-start items-center gap-x-2 hover:cursor-pointer"><div className="avatar">
<div className="w-10 rounded-full ring ring-primary ring-offset-base-100 ring-offset-2">
<img src={x.picture.thumbnail} alt="Avatar Tailwind CSS Component" /> </div>
<img src={`https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/${x?.avatar}`} alt="Avatar Tailwind CSS Component" /> </div>
</div>

<div className="flex font-montserrat w-12 text-neutral font-semibold">{x.name.first}</div>
<div className="flex font-montserrat w-12 text-neutral font-semibold">{x?.Username}</div>
</div>
</Link>
</td>
<td className='flex justify-start items-center gap-x-1 w-18'><Daimond/> <div>{index + 123}</div></td>
<td className='flex justify-start items-center gap-x-1 w-18'><Daimond/> <div>{x?.wins}</div></td>
</tr>
))}


))}
{loading && (<Loading size={"lg"}/>)}
</tbody>
</table>
</InfiniteScroll>
</div>
):(
<div className='h-full w-full flex items-center justify-center'>
<NullPlaceHolder message='No leaderboard available'/>
</div>
)

);
}
Loading

0 comments on commit abf4178

Please sign in to comment.