diff --git a/.DS_Store b/.DS_Store index b370c47..4ecb4d9 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/backend/code/prisma/migrations/20231024181019_anme/migration.sql b/backend/code/prisma/migrations/20231024181019_anme/migration.sql new file mode 100644 index 0000000..8b5fd5f --- /dev/null +++ b/backend/code/prisma/migrations/20231024181019_anme/migration.sql @@ -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; diff --git a/backend/code/src/game/game.controller.ts b/backend/code/src/game/game.controller.ts index 4387ede..26dab0b 100644 --- a/backend/code/src/game/game.controller.ts +++ b/backend/code/src/game/game.controller.ts @@ -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'; @@ -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); + } } diff --git a/backend/code/src/game/game.service.ts b/backend/code/src/game/game.service.ts index 28a3c4a..06b3cc2 100644 --- a/backend/code/src/game/game.service.ts +++ b/backend/code/src/game/game.service.ts @@ -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, }, @@ -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, diff --git a/backend/code/src/leaderboard/leaderboard.service.ts b/backend/code/src/leaderboard/leaderboard.service.ts index aaa2f75..cf9dfb4 100644 --- a/backend/code/src/leaderboard/leaderboard.service.ts +++ b/backend/code/src/leaderboard/leaderboard.service.ts @@ -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); } } diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev index 0789330..a8ebb4c 100644 --- a/frontend/Dockerfile.dev +++ b/frontend/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM node:20 +FROM node:18 WORKDIR /app diff --git a/frontend/code/package-lock.json b/frontend/code/package-lock.json index 219bfb2..3534222 100644 --- a/frontend/code/package-lock.json +++ b/frontend/code/package-lock.json @@ -30,6 +30,7 @@ "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", "react-scripts": "^5.0.1", "socket.io-client": "^4.7.2", @@ -14606,6 +14607,17 @@ "react": "*" } }, + "node_modules/react-infinite-scroll-component": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz", + "integrity": "sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==", + "dependencies": { + "throttle-debounce": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -16560,6 +16572,14 @@ "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==" }, + "node_modules/throttle-debounce": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz", + "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", diff --git a/frontend/code/package.json b/frontend/code/package.json index 11c8b68..395a475 100644 --- a/frontend/code/package.json +++ b/frontend/code/package.json @@ -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" diff --git a/frontend/code/src/Components/Home/LeaderBoard.tsx b/frontend/code/src/Components/Home/LeaderBoard.tsx index c13f38f..3a32832 100644 --- a/frontend/code/src/Components/Home/LeaderBoard.tsx +++ b/frontend/code/src/Components/Home/LeaderBoard.tsx @@ -1,12 +1,15 @@ import { Chart } from './assets/Chart' import { Table } from './assets/Table' -export const LeaderBoard = () => { + + +export const LeaderBoard = () => { return ( -
+
Leader Board
+ ) diff --git a/frontend/code/src/Components/Home/assets/Table.tsx b/frontend/code/src/Components/Home/assets/Table.tsx index fcfc7ff..6ff5e5f 100644 --- a/frontend/code/src/Components/Home/assets/Table.tsx +++ b/frontend/code/src/Components/Home/assets/Table.tsx @@ -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([]) - // const users : any = useLoaderData(); + const [users, setUsers] = useState([]) + const [loading , setLoading] = useState(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(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 ( -
+ users?.length > 0 || loading ? ( +
+
} + endMessage={
No more results!
} + hasMore={hasMoreItems.current} + scrollableTarget="scrollTarget" + style={{overflow:"auto", height:"100%"}} + >
@@ -53,11 +94,13 @@ export const Table = () => - - { !loading && users.map((x: any, index: number) => ( + + + + {!loading && users.map((x: any, index: number) => ( - + - ))} + + + ))} {loading && ()}
Score
@@ -67,22 +110,30 @@ export const Table = () =>
- -
+ +
- Avatar Tailwind CSS Component
+ Avatar Tailwind CSS Component
- -
{x.name.first}
+
{x?.Username}
{index + 123}
{x?.wins}
+ +
+ ):( +
+
+ ) + ); } \ No newline at end of file diff --git a/frontend/code/src/Components/Home/index.tsx b/frontend/code/src/Components/Home/index.tsx index de90d59..808709d 100644 --- a/frontend/code/src/Components/Home/index.tsx +++ b/frontend/code/src/Components/Home/index.tsx @@ -1,10 +1,10 @@ -import { FC } from 'react' import { Button } from './assets/Button' import { LeaderBoard } from './LeaderBoard' import { Link } from 'react-router-dom' import herosvg from './assets/Hero.png' -export const Home : FC = () : JSX.Element =>{ +export const Home = () : JSX.Element =>{ + return ( <> @@ -16,10 +16,12 @@ export const Home : FC = () : JSX.Element =>{
READY TO PLAY A GAME?
-
- + +
+ + +
-
diff --git a/frontend/code/src/Components/Layout/Assets/Profile.tsx b/frontend/code/src/Components/Layout/Assets/Profile.tsx index 459f37f..ea2e64b 100644 --- a/frontend/code/src/Components/Layout/Assets/Profile.tsx +++ b/frontend/code/src/Components/Layout/Assets/Profile.tsx @@ -1,34 +1,14 @@ -import { Link } from "react-router-dom"; -import { classNames } from "../../../Utils/helpers"; - -type ProfileProps = React.HTMLAttributes & { - selected?: boolean; -}; - -export const Profile = ({ selected, className, ...props }: ProfileProps) => { - return ( - -
- - - -
+import {Link} from 'react-router-dom' +import { useUserStore } from '../../../Stores/stores' +export const Profile = ({selected} : boolean | any) => { + const userStore = useUserStore(); + return ( + +
+ + + +
); }; diff --git a/frontend/code/src/Components/Layout/index.tsx b/frontend/code/src/Components/Layout/index.tsx index ae9767e..494f263 100644 --- a/frontend/code/src/Components/Layout/index.tsx +++ b/frontend/code/src/Components/Layout/index.tsx @@ -8,7 +8,7 @@ import { Message } from "./Assets/Message"; import { Profile } from "./Assets/Profile"; import { Settings } from "./Assets/Settings"; import { Out } from "./Assets/Out"; -import { FC, PropsWithChildren, useLayoutEffect } from "react"; +import { FC, PropsWithChildren, useLayoutEffect} from "react"; import { Outlet } from "react-router"; import { matchRoutes, useLocation } from "react-router-dom"; import { useUserStore } from "../../Stores/stores"; @@ -37,6 +37,7 @@ function onConnect() { console.log("hello"); } export const Layout: FC = (): JSX.Element => { + const user = useUserStore(); const navigate = useNavigate(); @@ -44,10 +45,16 @@ export const Layout: FC = (): JSX.Element => { const log = async () => { try { await user.login(); - } catch (e) { - navigate("/"); - user.logout(); + } + catch(e:any){ + if (e?.response?.status !== 403 && e?.response?.data?.message !== "Please complete your profile") + { + navigate("/"); + user.logout(); + } } + + }; socket.on("connect", onConnect); @@ -111,8 +118,9 @@ export const Layout: FC = (): JSX.Element => { -
- +
+ +
diff --git a/frontend/code/src/Components/Profile/History.tsx b/frontend/code/src/Components/Profile/History.tsx index be724bc..5c39112 100644 --- a/frontend/code/src/Components/Profile/History.tsx +++ b/frontend/code/src/Components/Profile/History.tsx @@ -2,7 +2,7 @@ import { Chart } from './assets/Chart' import { Table } from './assets/Table' export const History = (props:any) => { return ( -
+
Matches History diff --git a/frontend/code/src/Components/Profile/assets/MessageB.tsx b/frontend/code/src/Components/Profile/assets/MessageB.tsx index 7afb618..04b910d 100644 --- a/frontend/code/src/Components/Profile/assets/MessageB.tsx +++ b/frontend/code/src/Components/Profile/assets/MessageB.tsx @@ -1,14 +1,22 @@ -export const Message = () => { +import { toast } from "react-hot-toast" +import api from "../../../Api/base" +export const Message = (props:any) => { + const handleSendReq = async() => { + toast.promise(api.post("friends/add",{friendId:props.profile.id}),{loading:`Sending friend request`,success:`request sent to ${props.profile.name.first}`,error:"could not send friend request"}) + } + return ( - - - - - - - - - - + // + + + + + + + + + + + // ) } \ No newline at end of file diff --git a/frontend/code/src/Components/Profile/assets/Table.tsx b/frontend/code/src/Components/Profile/assets/Table.tsx index 389a5ec..7634253 100644 --- a/frontend/code/src/Components/Profile/assets/Table.tsx +++ b/frontend/code/src/Components/Profile/assets/Table.tsx @@ -1,80 +1,114 @@ -import { useState,useEffect } from 'react' +import { useState,useEffect, useRef } from 'react' import { Link } from 'react-router-dom' import { Load , Loading} from '../../Loading/'; -import { useParams } from 'react-router-dom'; +import { Logo } from '../../Layout/Assets/Logo'; +import InfiniteScroll from 'react-infinite-scroll-component'; +import api from '../../../Api/base'; +import toast from 'react-hot-toast'; + +const getColor = (v1 : number , v2:number) => { + if (v1 > v2) return "text-lime-400"; + if (v1 < v2) return "text-red-400"; + return "text-gray-400"; + +} + +const getTime = (value: string) => { + const date = new Date(value); + const today = new Date() + console.log(date); + if (date.getDate() === today.getDate() && date.getFullYear() === today.getFullYear() && date.getMonth() === today.getMonth()) + return `Today ${date.getHours()}:${date.getMinutes()}` + else if (date.getDate() === today.getDate() - 1 && date.getFullYear() === today.getFullYear() && date.getMonth() === today.getMonth()) + return `Yestrday ${date.getHours()}:${date.getMinutes()}` + return `${date.getDate()}-${date.getMonth()}-${date.getFullYear()}` +} export const Table = (props:any) => { - const param = useParams(); - const [users, setUsers] = useState([]); - const [enemys , setEnemys] = useState([]); - const [loading , setLoading] = useState(true); - useEffect( () => { - setLoading(true) - setUsers([]) - setEnemys([]) - const fetchdata = async() =>{ - for (let i = 0 ; i < 5 ; ++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]; - newUser.seed = data.info.seed; - setEnemys((oldUsers : any) => [...oldUsers, newUser]); - } - } - for (let i = 0 ; i < 5 ; ++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]; - newUser.seed = data.info.seed; - setUsers((oldUsers : any) => [...oldUsers, newUser]); - } - } - setLoading(false) + const [history, setHistory] = useState([]); + const [loading, setLoading] = useState(true); + const [hasMore, setHasMore] = useState(true); + // const [nextPage , setNextPage] = useState(`/game/history/${param.id}?offset=${0}&limit=20`) + const offset = useRef(0); + + const fetchData = async() => { + try{ + + const history:any = await api.get(`/game/history/${props.props.props}`,{params:{offset:offset.current,limit:20}}); + offset.current += 20; + setHistory((prev:any) => [...prev , ...history.data]); + setLoading(false); + if (history.data.length < 20) + setHasMore(false) + }catch{ + toast.error("Error on loading match history") } - fetchdata().catch(console.error) - },[param]) + + + } + // useEffect(() => { + // fetchData(); + // offset.current += 20; + // },[]) + useEffect(() => { + offset.current = 0; + setHistory([]); + setLoading(true); + setHasMore(true) + fetchData(); offset.current += 20; + //eslint-disable-next-line + },[props.props.props]) + console.log(props) return ( -
- - - - {!loading && users.map((x: any ,index:number) => ( - - - -
- -
- today 17:45 - -
-
-
- {x?.name?.first ?
{x.name.first}
: } - {x?.seed ? Avatar Tailwind CSS Component : } -
- 1 : 1 -
+
+
} + dataLength={history.length} + next={fetchData} + className='overflow-auto' + scrollableTarget="scrollTarget" + endMessage={No more history} + > + + + + {!loading && history.map((x: any ,index:number) => ( + + - - - ))} - { loading && ( - - ) +
+ {getTime(x.match.createdAt)} +
+ +
+ + + ))} + { loading && ( + + ) + + } + +
- {enemys[index]?.seed ? Avatar Tailwind CSS Component : } -
{enemys[index]?.name?.first ? enemys[index].name.first : }
- -
+ 1
+
+ {x?.match?.Player1?.username ?
{x.match.Player1.username}
: } + {x?.match ? Avatar : } +
+ {x.match.Player1.score} : {x.match.Player2.score} +
+ + {x?.match?.Player2?.id ? Avatar Tailwind CSS Component : } +
{x?.match?.Player2?.username ? x.match.Player2.username : }
+
+
{x.match.Player1.score > x.match.Player2.score && "+1"} {x.match.Player1.score < x.match.Player2.score && "-1"}{x.match.Player1.score === x.match.Player2.score && "0"}
- } -
+
) diff --git a/frontend/code/src/Components/Profile/index.tsx b/frontend/code/src/Components/Profile/index.tsx index fa844c4..f9cdfd6 100644 --- a/frontend/code/src/Components/Profile/index.tsx +++ b/frontend/code/src/Components/Profile/index.tsx @@ -1,186 +1,168 @@ -import { Pong } from "./assets/Pong"; -import { File } from "./assets/File"; -import { Share } from "./assets/ShareB"; -import { Message } from "./assets/MessageB"; -import { History } from "./History"; -import Hero from "./assets/Hero.gif"; -import { useState, useEffect } from "react"; -import { Link, useNavigate, useParams } from "react-router-dom"; -import { Load } from "../Loading/"; -import Newbie from "../Badges/Newbie.svg"; -import Master from "../Badges/Master.svg"; -import Ultimate from "../Badges/Ultimate.svg"; -import { Edit } from "./assets/Edit"; -import { useUserStore } from "../../Stores/stores"; -import dots from "./assets/svg/threedots.svg"; -import api from "../../Api/base"; -import { BlockedUsersModal } from "../Chat/Components/RoomChatHelpers"; -import { useModalStore } from "../Chat/Controllers/LayoutControllers"; -import { createNewRoomCall } from "../Chat/Services/ChatServices"; -import toast from "react-hot-toast"; -import { - ChatType, - useChatStore, -} from "../Chat/Controllers/RoomChatControllers"; -// import toast from 'react-hot-toast' -export const Profile = () => { - const user = useUserStore(); - const params = useParams(); - const LayoutState = useModalStore((state) => state); - const navigate = useNavigate(); - const ChatState = useChatStore((state) => state); - console.log(`params : ${params.id} type ${typeof params.id}`); - const [users, setUsers] = useState(undefined); +import { Pong } from './assets/Pong' +import { File } from './assets/File' - useEffect(() => { - const fetchUser = async () => { - try { - const res = await api.get(`profile/${params.id}`); - setUsers(res.data); - } catch (error) { - navigate("/NotFound"); - } - }; - if (params.id !== user.id || params.id !== "me") fetchUser(); - else setUsers(user); - //eslint-disable-next-line - }, [params, user]); - // console.log(users) - // const handleSendReq = async() => { - // toast.promise(api.post("friend/add",{friendId:users.id}),{loading:`Sending friend request`,success:`request sent to ${users.name.first}`,error:"could not send friend request"}) - // } - return ( - <> - -
-
-
- bg hero -
- -
+import { History } from './History' +import Hero from './assets/Hero.gif' +import { useState , useEffect } from 'react' +import { Link, useNavigate, useParams} from 'react-router-dom' +import { Load } from '../Loading/' +import Newbie from '../Badges/Newbie.svg' +import Master from '../Badges/Master.svg' +import Ultimate from '../Badges/Ultimate.svg' +import { Edit } from './assets/Edit' +import { useUserStore } from '../../Stores/stores' +import { VscChromeClose , VscAdd , VscCheck , VscComment} from "react-icons/vsc"; +import api from '../../Api/base' +import toast from 'react-hot-toast' +type FRIENDSHIP = 'none' | 'friend' | 'sent' | 'recive' | 'blocked' | undefined; +export const Profile = () =>{ + const user = useUserStore(); + const params = useParams(); + const navigate = useNavigate(); + const [status , setStatus] = useState(undefined) + const [disabled , setDisbaled] = useState(""); + console.log(`params : ${params.id} type ${typeof(params.id)}`) + const [profile, setProfile] = useState(undefined); + + useEffect(() => { + const fetchUser = async() => { + try { + const res = await api.get(`profile/${params.id}`) + setProfile(res.data) + res.data.friendship.length === 0 && setStatus('none'); + res.data.friendship.length > 0 && res.data.friendship[0].accepted && setStatus('friend'); + res.data.friendship.length > 0 && !res.data.friendship[0].accepted && res.data.friendship[0].fromId === user.id && setStatus('sent') + res.data.friendship.length > 0 && !res.data.friendship[0].accepted && res.data.friendship[0].fromId !== user.id && setStatus('recive') + console.log(res.data) + } catch (error) { + + navigate("/NotFound") + } + } + if (params.id !== user.id || params.id !== "me") + fetchUser(); + else + setProfile(user) -
-
- {users?.picture?.large ? ( - profile avatar - ) : ( - - )} + //eslint-disable-next-line + },[params, user]) + console.log(status) + const sendRequest = async() => { + setDisbaled("btn-disabled") + const fetchFunc = async () => {await api.post("friends/add",{friendId:profile.id}) ; setStatus('sent'); setDisbaled("")} + toast.promise(fetchFunc(),{loading:`Sending friend request`,success:`request sent to ${profile.name.first}`,error:"could not send friend request"}) + // setDisbaled("") + + + } + const cancelRequest = async() => { + setDisbaled("btn-disabled") + const fetchFunc = async () => {await api.post("friends/unfriend",{friendId:profile.id}) ; setStatus('none');setDisbaled("")} + toast.promise(fetchFunc(),{loading:`Cancling friend request`,success:`cancel ${profile.name.first} friend request`,error:"could not cancel friend request"}) + } + const acceptRequest = async() => { + setDisbaled("btn-disabled") + const fetchFunc = async () => {await api.post("/friends/accept",{friendId:profile.id}) ; setStatus('none');setDisbaled("")} + toast.promise(fetchFunc(),{loading:`Accepting friend request`,success:`${profile.name.first} friend request accepted`,error:"could not accept friend request"}) + } + const rejectRequest = async() => { + setDisbaled("btn-disabled") + const fetchFunc = async () => {await api.post("/friends/reject",{friendId:profile.id}) ; setStatus('none');setDisbaled("")} + toast.promise(fetchFunc(),{loading:`rejecting friend request`,success:`${profile.name.first} friend request rejected`,error:"could not reject friend request"}) + } + + return ( + <> +
+
+ +
bg hero +
+ +
+ +
+
+ { + profile?.picture?.large ? profile avatar: + } + +
+
-
-
-
- {users?.name?.first ? ( -
- {users?.name?.first} {users.name.last}{" "} -
- ) : ( - - )}{" "} -
- - {users?.bio} -
-
-
- {params.id !== "me" ? ( - <> -
{ - await createNewRoomCall( - "", - "dm", - undefined, - params.id - ).then((res) => { - if (res?.status === 200 || res?.status === 201) { - ChatState.changeChatType(ChatType.Chat); - ChatState.selectNewChatID(res?.data?.id); - ChatState.setCurrentDmUser({ - id: users?.id, - firstname: users?.name.first, - lastname: users?.name.last, - avatar: { - thumbnail: users?.picture.thumbnail, - medium: users?.picture.medium, - large: users?.picture.large, - }, - bio: users?.bio, - }); - navigate(`/Dm/${params.id}`); - } else { - toast.error( - "You Can't Send Message To this User For Now, try Again later" - ); +
+ { + profile?.name?.first ?
{profile?.name?.first} {profile.name.last}
: + }
+ + {profile?.bio} +
+
+
+ { + params.id !== "me" && params.id !== user.id && status === 'none' && +
+ } + { + params.id !== "me" && params.id !== user.id && status === 'sent' && +
} - }); - }} - > - -
- - - ) : ( - - - - )} + { + params.id !== "me" && params.id !== user.id && status === 'recive' && +
+ + +
+ } + { + params.id !== "me" && params.id !== user.id && status === 'friend' && +
+ + +
+ } + { + (params.id === "me" || params.id === user.id) && + + + } +
+
+ newbie badge + Master badge + Ultimate badge + {/*
+ +
    +
  • Block
  • +
  • Send friend request
  • +
+
*/} +
+
-
- newbie badge - Master badge - Ultimate badge -
- -
    -
  • -
    Block
    -
  • -
  • -
    Send friend request
    -
  • -
  • { - LayoutState.setShowBlockedList( - !LayoutState.showBlockedLIstModal - ); - }} - > - -
    See Blocked List
    -
    -
  • -
-
+
+
-
-
-
-
-
- ); -}; + ) +} \ No newline at end of file diff --git a/frontend/code/src/index.css b/frontend/code/src/index.css index dec01b2..2b6bc93 100644 --- a/frontend/code/src/index.css +++ b/frontend/code/src/index.css @@ -3,8 +3,8 @@ @tailwind utilities; @layer utilities { - @variants responsive { - /* Hide scrollbar for Chrome, Safari and Opera */ + @variants responsive { + /* Hide scrollbar for Chrome, Safari and Opera */ .no-scrollbar::-webkit-scrollbar { display: none; } @@ -13,8 +13,34 @@ .no-scrollbar { -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ - } + } + .customScroll { + --sb-track-color: #353135; + --sb-thumb-color: #7940CF; + --sb-size: 8px; + + scrollbar-color: var(--sb-thumb-color) + var(--sb-track-color); + } + + .customScroll::-webkit-scrollbar { + width: var(--sb-size) + } + + .customScroll::-webkit-scrollbar-track { + background: var(--sb-track-color); + border-radius: 8px; + } + + .customScroll::-webkit-scrollbar-thumb { + background: var(--sb-thumb-color); + border-radius: 8px; + border: 2px solid #7940CF; + } } + + + } @import url('https://fonts.googleapis.com/css2?family=Lexend+Peta:wght@400;700;800&family=Montserrat:ital,wght@0,700;1,500&family=Poppins:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap'); @layer base { @@ -22,4 +48,7 @@ @apply no-scrollbar; @apply font-montserrat; } + *{ + @apply customScroll + } }