From 030993f3261227aa2338035a7ab8d559b5709202 Mon Sep 17 00:00:00 2001 From: ismail Date: Tue, 24 Oct 2023 21:45:41 +0100 Subject: [PATCH 1/5] INIT --- .DS_Store | Bin 8196 -> 6148 bytes .../20231024181019_anme/migration.sql | 46 ++++++++++++++++++ .../code/src/profile/profile.controller.ts | 2 +- frontend/code/src/Components/Layout/index.tsx | 5 +- .../Components/Profile/assets/MessageB.tsx | 24 +++++---- .../code/src/Components/Profile/index.tsx | 9 +++- 6 files changed, 73 insertions(+), 13 deletions(-) create mode 100644 backend/code/prisma/migrations/20231024181019_anme/migration.sql diff --git a/.DS_Store b/.DS_Store index 7c1ae9c1b4b754c97b14cfb87bd6d02302443545..4ecb4d91ba0163eb62193ab32d1ed0ba471adbbf 100644 GIT binary patch delta 103 zcmZp1XfcprU|?W$DortDU=RQ@Ie-{Mvv5sJ6q~50$SAfkU^g?P*k&GqmCO@UgBG)M pa0oI36##(%H;`}zsoj|Poq009j3-DZ0~5p)kiiU_<9X&V0{~(D59$B_ delta 285 zcmZoMXmOBWU|?W$DortDU;r^WfEYvza8FDWo2aMAD77(QH}hr%jz7$c**Q2S7O*f% zZRTNF$z0FFkjIeEkiwA45YLbcW|cDJ0C~kZ>4w3{`MCu^Qy7@m-s%KWIMwFnySOCf z { return await this.profileService.getProfile(userId); } diff --git a/frontend/code/src/Components/Layout/index.tsx b/frontend/code/src/Components/Layout/index.tsx index 85c6139..ec59556 100644 --- a/frontend/code/src/Components/Layout/index.tsx +++ b/frontend/code/src/Components/Layout/index.tsx @@ -41,9 +41,12 @@ export const Layout: FC = (): JSX.Element => { try { await user.login(); } - catch(e){ + catch(e:any){ + if (e?.response?.status !== 403 && e?.response?.data?.message !== "Please complete your profile") + { navigate("/"); user.logout(); + } } diff --git a/frontend/code/src/Components/Profile/assets/MessageB.tsx b/frontend/code/src/Components/Profile/assets/MessageB.tsx index 7afb618..8a21b1c 100644 --- a/frontend/code/src/Components/Profile/assets/MessageB.tsx +++ b/frontend/code/src/Components/Profile/assets/MessageB.tsx @@ -1,14 +1,18 @@ +import { Link , useParams} from "react-router-dom" export const Message = () => { + const {id} = useParams() return ( - - - - - - - - - - + + + + + + + + + + + + ) } \ No newline at end of file diff --git a/frontend/code/src/Components/Profile/index.tsx b/frontend/code/src/Components/Profile/index.tsx index 6bcf321..3c58ca2 100644 --- a/frontend/code/src/Components/Profile/index.tsx +++ b/frontend/code/src/Components/Profile/index.tsx @@ -15,10 +15,12 @@ import { useUserStore } from '../../Stores/stores' import dots from './assets/svg/threedots.svg' 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) console.log(`params : ${params.id} type ${typeof(params.id)}`) const [users, setUsers] = useState(undefined); @@ -27,6 +29,10 @@ export const Profile = () =>{ try { const res = await api.get(`profile/${params.id}`) setUsers(res.data) + res?.data?.friendship?.accepted === undefined && setStatus('none'); + res?.data?.friendship?.accepted && setStatus('friend'); + !res?.data?.friendship?.accepted && res?.data?.friendship?.fromId === user.id && setStatus('sent') + !res?.data?.friendship?.accepted && res?.data?.friendship?.fromId !== user.id && setStatus('recive') } catch (error) { navigate("/NotFound") @@ -71,8 +77,9 @@ export const Profile = () =>{
{ - params.id !== "me" ? + params.id !== "me" && params.id !== user.id ? (<> + ) : ( From 226922a9f9517781cb5b9f256dad9b95bec61278 Mon Sep 17 00:00:00 2001 From: ismail Date: Tue, 24 Oct 2023 21:45:41 +0100 Subject: [PATCH 2/5] INIT --- .DS_Store | Bin 8196 -> 6148 bytes .../20231024181019_anme/migration.sql | 46 ++++++++++++++++++ .../code/src/profile/profile.controller.ts | 2 +- frontend/code/src/Components/Layout/index.tsx | 5 +- .../Components/Profile/assets/MessageB.tsx | 24 +++++---- .../code/src/Components/Profile/index.tsx | 9 +++- 6 files changed, 73 insertions(+), 13 deletions(-) create mode 100644 backend/code/prisma/migrations/20231024181019_anme/migration.sql diff --git a/.DS_Store b/.DS_Store index 7c1ae9c1b4b754c97b14cfb87bd6d02302443545..4ecb4d91ba0163eb62193ab32d1ed0ba471adbbf 100644 GIT binary patch delta 103 zcmZp1XfcprU|?W$DortDU=RQ@Ie-{Mvv5sJ6q~50$SAfkU^g?P*k&GqmCO@UgBG)M pa0oI36##(%H;`}zsoj|Poq009j3-DZ0~5p)kiiU_<9X&V0{~(D59$B_ delta 285 zcmZoMXmOBWU|?W$DortDU;r^WfEYvza8FDWo2aMAD77(QH}hr%jz7$c**Q2S7O*f% zZRTNF$z0FFkjIeEkiwA45YLbcW|cDJ0C~kZ>4w3{`MCu^Qy7@m-s%KWIMwFnySOCf z { return await this.profileService.getProfile(userId); } diff --git a/frontend/code/src/Components/Layout/index.tsx b/frontend/code/src/Components/Layout/index.tsx index 85c6139..ec59556 100644 --- a/frontend/code/src/Components/Layout/index.tsx +++ b/frontend/code/src/Components/Layout/index.tsx @@ -41,9 +41,12 @@ export const Layout: FC = (): JSX.Element => { try { await user.login(); } - catch(e){ + catch(e:any){ + if (e?.response?.status !== 403 && e?.response?.data?.message !== "Please complete your profile") + { navigate("/"); user.logout(); + } } diff --git a/frontend/code/src/Components/Profile/assets/MessageB.tsx b/frontend/code/src/Components/Profile/assets/MessageB.tsx index 7afb618..8a21b1c 100644 --- a/frontend/code/src/Components/Profile/assets/MessageB.tsx +++ b/frontend/code/src/Components/Profile/assets/MessageB.tsx @@ -1,14 +1,18 @@ +import { Link , useParams} from "react-router-dom" export const Message = () => { + const {id} = useParams() return ( - - - - - - - - - - + + + + + + + + + + + + ) } \ No newline at end of file diff --git a/frontend/code/src/Components/Profile/index.tsx b/frontend/code/src/Components/Profile/index.tsx index 6bcf321..3c58ca2 100644 --- a/frontend/code/src/Components/Profile/index.tsx +++ b/frontend/code/src/Components/Profile/index.tsx @@ -15,10 +15,12 @@ import { useUserStore } from '../../Stores/stores' import dots from './assets/svg/threedots.svg' 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) console.log(`params : ${params.id} type ${typeof(params.id)}`) const [users, setUsers] = useState(undefined); @@ -27,6 +29,10 @@ export const Profile = () =>{ try { const res = await api.get(`profile/${params.id}`) setUsers(res.data) + res?.data?.friendship?.accepted === undefined && setStatus('none'); + res?.data?.friendship?.accepted && setStatus('friend'); + !res?.data?.friendship?.accepted && res?.data?.friendship?.fromId === user.id && setStatus('sent') + !res?.data?.friendship?.accepted && res?.data?.friendship?.fromId !== user.id && setStatus('recive') } catch (error) { navigate("/NotFound") @@ -71,8 +77,9 @@ export const Profile = () =>{
{ - params.id !== "me" ? + params.id !== "me" && params.id !== user.id ? (<> + ) : ( From 594d1df2ccf3d5ae979dedeb3f5c82513cef67c9 Mon Sep 17 00:00:00 2001 From: ismail Date: Sun, 29 Oct 2023 22:57:25 +0100 Subject: [PATCH 3/5] ADD:FriendsButtons --- frontend/code/package-lock.json | 80 +++++++++++++ frontend/code/package.json | 1 + frontend/code/src/Components/Layout/index.tsx | 27 ++++- .../Components/Profile/assets/MessageB.tsx | 13 +- .../code/src/Components/Profile/index.tsx | 112 +++++++++++++----- 5 files changed, 201 insertions(+), 32 deletions(-) diff --git a/frontend/code/package-lock.json b/frontend/code/package-lock.json index 1a0aa2e..219bfb2 100644 --- a/frontend/code/package-lock.json +++ b/frontend/code/package-lock.json @@ -32,6 +32,7 @@ "react-icons": "^4.10.1", "react-konva": "^18.2.10", "react-scripts": "^5.0.1", + "socket.io-client": "^4.7.2", "typescript": "^4.9.5", "web-vitals": "^2.1.4", "zustand": "^4.4.1" @@ -4619,6 +4620,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -8043,6 +8049,46 @@ "node": ">= 0.8" } }, + "node_modules/engine.io-client": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.2.tgz", + "integrity": "sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -15598,6 +15644,32 @@ "node": ">=8" } }, + "node_modules/socket.io-client": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz", + "integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -17926,6 +17998,14 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/frontend/code/package.json b/frontend/code/package.json index dd76a8a..2d4953f 100644 --- a/frontend/code/package.json +++ b/frontend/code/package.json @@ -27,6 +27,7 @@ "react-icons": "^4.10.1", "react-konva": "^18.2.10", "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/Layout/index.tsx b/frontend/code/src/Components/Layout/index.tsx index ec59556..a7e2e2f 100644 --- a/frontend/code/src/Components/Layout/index.tsx +++ b/frontend/code/src/Components/Layout/index.tsx @@ -8,12 +8,13 @@ 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, useState , useEffect} from "react"; import { Outlet } from "react-router"; import { matchRoutes, useLocation } from "react-router-dom"; import { useUserStore } from "../../Stores/stores"; import { useNavigate } from "react-router-dom"; import { FirstLogin } from "../FirstLogin"; +import { io } from 'socket.io-client'; const routes = [ { path: "Profile/:id" }, @@ -32,7 +33,31 @@ const useCurrentPath = () => { return route.path; }; +export const socket = io("http://localhost:3004",{'transports': ['websocket', 'polling'],withCredentials:true}); export const Layout: FC = (): JSX.Element => { + const [isConnected, setIsConnected] = useState(socket.connected); + const [fooEvents, setFooEvents] = useState([]); + useEffect(() => { + function onConnect() { + console.log("hello") + setIsConnected(true); + } + + function onDisconnect() { + setIsConnected(false); + } + + + + socket.on('connect', onConnect); + socket.on('disconnect', onDisconnect); + + return () => { + socket.off('connect', onConnect); + socket.off('disconnect', onDisconnect); + }; + }, []); + const user = useUserStore(); const navigate = useNavigate(); diff --git a/frontend/code/src/Components/Profile/assets/MessageB.tsx b/frontend/code/src/Components/Profile/assets/MessageB.tsx index 8a21b1c..a287e48 100644 --- a/frontend/code/src/Components/Profile/assets/MessageB.tsx +++ b/frontend/code/src/Components/Profile/assets/MessageB.tsx @@ -1,9 +1,14 @@ import { Link , useParams} from "react-router-dom" -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"}) + } const {id} = useParams() return ( - - + // + @@ -13,6 +18,6 @@ export const Message = () => { - + // ) } \ No newline at end of file diff --git a/frontend/code/src/Components/Profile/index.tsx b/frontend/code/src/Components/Profile/index.tsx index 3c58ca2..df49fce 100644 --- a/frontend/code/src/Components/Profile/index.tsx +++ b/frontend/code/src/Components/Profile/index.tsx @@ -5,49 +5,74 @@ 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 { 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 { VscChromeClose , VscAdd , VscCheck , VscComment} from "react-icons/vsc"; +// import dots from './assets/svg/threedots.svg' import api from '../../Api/base' -// import toast from 'react-hot-toast' +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 [users, setUsers] = useState(undefined); + const [profile, setProfile] = useState(undefined); useEffect(() => { const fetchUser = async() => { try { const res = await api.get(`profile/${params.id}`) - setUsers(res.data) - res?.data?.friendship?.accepted === undefined && setStatus('none'); - res?.data?.friendship?.accepted && setStatus('friend'); - !res?.data?.friendship?.accepted && res?.data?.friendship?.fromId === user.id && setStatus('sent') - !res?.data?.friendship?.accepted && res?.data?.friendship?.fromId !== user.id && setStatus('recive') - + 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 - setUsers(user) + setProfile(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"}) - // } + 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 ( <>
@@ -61,7 +86,7 @@ export const Profile = () =>{
{ - users?.picture?.large ? profile avatar: + profile?.picture?.large ? profile avatar: }
@@ -69,29 +94,62 @@ export const Profile = () =>{
{ - users?.name?.first ?
{users?.name?.first} {users.name.last}
: + profile?.name?.first ?
{profile?.name?.first} {profile.name.last}
: }
- {users?.bio} + {profile?.bio}
{ - params.id !== "me" && params.id !== user.id ? - (<> - - ) - : - ( + 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 -
+ {/*
@@ -99,7 +157,7 @@ export const Profile = () =>{
  • Block
  • Send friend request
  • -
    +
    */}
    From 4639cda9860fa3cd6386ef8aa0b74b30ee07b85f Mon Sep 17 00:00:00 2001 From: ismail Date: Wed, 1 Nov 2023 19:05:58 +0100 Subject: [PATCH 4/5] SAVE --- frontend/Dockerfile.dev | 2 +- frontend/code/package-lock.json | 20 +++++++ frontend/code/package.json | 1 + .../code/src/Components/Home/LeaderBoard.tsx | 2 + .../code/src/Components/Home/assets/Table.tsx | 53 +++++++++++-------- .../src/Components/Layout/Assets/Profile.tsx | 5 +- .../code/src/Components/Profile/index.tsx | 12 ++--- 7 files changed, 65 insertions(+), 30 deletions(-) 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 2d4953f..00ad6bf 100644 --- a/frontend/code/package.json +++ b/frontend/code/package.json @@ -25,6 +25,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", diff --git a/frontend/code/src/Components/Home/LeaderBoard.tsx b/frontend/code/src/Components/Home/LeaderBoard.tsx index c13f38f..c9c025e 100644 --- a/frontend/code/src/Components/Home/LeaderBoard.tsx +++ b/frontend/code/src/Components/Home/LeaderBoard.tsx @@ -1,5 +1,6 @@ import { Chart } from './assets/Chart' import { Table } from './assets/Table' + export const LeaderBoard = () => { return (
    @@ -7,6 +8,7 @@ export const LeaderBoard = () => { Leader Board
    + ) diff --git a/frontend/code/src/Components/Home/assets/Table.tsx b/frontend/code/src/Components/Home/assets/Table.tsx index fcfc7ff..efb7d80 100644 --- a/frontend/code/src/Components/Home/assets/Table.tsx +++ b/frontend/code/src/Components/Home/assets/Table.tsx @@ -3,6 +3,9 @@ import { useState,useEffect } from 'react' import { Daimond } from './Daimond' import { Loading } from '../../Loading' import { Link } from 'react-router-dom' +import { NullPlaceHolder } from '../../Chat/Components/RoomChatHelpers' +import api from '../../../Api/base' + // import { useLoaderData } from 'react-router' // export const dataLoader = async() => { @@ -21,6 +24,7 @@ import { Link } from 'react-router-dom' // } + export const Table = () => { const [users, setUsers] = useState([]) @@ -55,30 +59,37 @@ export const Table = () => { !loading && users.map((x: any, index: number) => ( - + // + // + // + // + // + - - + + ))} {loading && ()} diff --git a/frontend/code/src/Components/Layout/Assets/Profile.tsx b/frontend/code/src/Components/Layout/Assets/Profile.tsx index 3d258bc..6104298 100644 --- a/frontend/code/src/Components/Layout/Assets/Profile.tsx +++ b/frontend/code/src/Components/Layout/Assets/Profile.tsx @@ -1,8 +1,9 @@ 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/Profile/index.tsx b/frontend/code/src/Components/Profile/index.tsx index df49fce..9241c17 100644 --- a/frontend/code/src/Components/Profile/index.tsx +++ b/frontend/code/src/Components/Profile/index.tsx @@ -103,14 +103,14 @@ export const Profile = () =>{
    { params.id !== "me" && params.id !== user.id && status === 'none' && -
    } { params.id !== "me" && params.id !== user.id && status === 'sent' && -
    @@ -118,21 +118,21 @@ export const Profile = () =>{ { params.id !== "me" && params.id !== user.id && status === 'recive' &&
    - -
    } { params.id !== "me" && params.id !== user.id && status === 'friend' && -
    - From 5cd995eb593aac97bc413051361295648e28c5f4 Mon Sep 17 00:00:00 2001 From: ismail Date: Sat, 4 Nov 2023 22:42:46 +0100 Subject: [PATCH 5/5] ADD:infinite scroll ADD:history ADD:leaderboard --- backend/code/src/game/game.controller.ts | 11 +- backend/code/src/game/game.service.ts | 4 + .../src/leaderboard/leaderboard.service.ts | 24 ++- .../code/src/Components/Home/LeaderBoard.tsx | 5 +- .../code/src/Components/Home/assets/Table.tsx | 176 +++++++++++------- frontend/code/src/Components/Home/index.tsx | 12 +- frontend/code/src/Components/Layout/index.tsx | 34 +--- .../code/src/Components/Profile/History.tsx | 2 +- .../Components/Profile/assets/MessageB.tsx | 3 +- .../src/Components/Profile/assets/Table.tsx | 170 ++++++++++------- .../code/src/Components/Profile/index.tsx | 8 +- frontend/code/src/index.css | 35 +++- 12 files changed, 297 insertions(+), 187 deletions(-) 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/code/src/Components/Home/LeaderBoard.tsx b/frontend/code/src/Components/Home/LeaderBoard.tsx index c9c025e..3a32832 100644 --- a/frontend/code/src/Components/Home/LeaderBoard.tsx +++ b/frontend/code/src/Components/Home/LeaderBoard.tsx @@ -1,9 +1,10 @@ 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 efb7d80..6ff5e5f 100644 --- a/frontend/code/src/Components/Home/assets/Table.tsx +++ b/frontend/code/src/Components/Home/assets/Table.tsx @@ -1,54 +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 { 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' -// import { useLoaderData } from 'react-router' +export const Table: FC = () => +{ + 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); -// 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; - + const fetchItems = useCallback( + async () => { + if (fetching) { + return; + } -// } + setFetching(true); -export const Table = () => -{ - const [users, setUsers] = useState([]) - // const users : any = useLoaderData(); + 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; + } - 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) + } - fetchdata().catch(console.error) - },[]) - + 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%"}} + >
    + //
    + //
    + // {index + 1} + //
    + //
    + //
    + // + //
    + //
    + // Avatar Tailwind CSS Component
    + //
    + + //
    {x.name.first}
    + //
    + // + //
    {index + 123}
    -
    -
    - {index + 1} -
    -
    -
    - -
    -
    - Avatar Tailwind CSS Component
    -
    + -
    {x.name.first}
    -
    - -
    {index + 123}
    @@ -57,43 +94,46 @@ export const Table = () => - - { !loading && users.map((x: any, index: number) => ( - // - // - // - //
    {x.name.first}
    - // - // - // - // - // - - + + + - - ))} + + + ))} {loading && ()}
    Score
    - //
    - //
    - // {index + 1} - //
    - //
    - //
    - // - //
    - //
    - // Avatar Tailwind CSS Component
    - //
    +
    {index + 123}
    - + {!loading && users.map((x: any, index: number) => ( +
    +
    +
    + {index + 1} +
    +
    + +
    +
    + Avatar Tailwind CSS Component
    +
    +
    {x?.Username}
    +
    + +
    {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..ec2d143 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 : FC = () : 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/index.tsx b/frontend/code/src/Components/Layout/index.tsx index a7e2e2f..e3e5a17 100644 --- a/frontend/code/src/Components/Layout/index.tsx +++ b/frontend/code/src/Components/Layout/index.tsx @@ -8,14 +8,12 @@ import { Message } from "./Assets/Message"; import { Profile } from "./Assets/Profile"; import { Settings } from "./Assets/Settings"; import { Out } from "./Assets/Out"; -import { FC, PropsWithChildren, useLayoutEffect, useState , useEffect} 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"; import { useNavigate } from "react-router-dom"; import { FirstLogin } from "../FirstLogin"; -import { io } from 'socket.io-client'; - const routes = [ { path: "Profile/:id" }, { path: "Settings" }, @@ -33,30 +31,7 @@ const useCurrentPath = () => { return route.path; }; -export const socket = io("http://localhost:3004",{'transports': ['websocket', 'polling'],withCredentials:true}); export const Layout: FC = (): JSX.Element => { - const [isConnected, setIsConnected] = useState(socket.connected); - const [fooEvents, setFooEvents] = useState([]); - useEffect(() => { - function onConnect() { - console.log("hello") - setIsConnected(true); - } - - function onDisconnect() { - setIsConnected(false); - } - - - - socket.on('connect', onConnect); - socket.on('disconnect', onDisconnect); - - return () => { - socket.off('connect', onConnect); - socket.off('disconnect', onDisconnect); - }; - }, []); const user = useUserStore(); const navigate = useNavigate(); @@ -131,8 +106,11 @@ 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 a287e48..04b910d 100644 --- a/frontend/code/src/Components/Profile/assets/MessageB.tsx +++ b/frontend/code/src/Components/Profile/assets/MessageB.tsx @@ -1,11 +1,10 @@ -import { Link , useParams} from "react-router-dom" 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"}) } - const {id} = useParams() + return ( // 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 9241c17..e7c58f4 100644 --- a/frontend/code/src/Components/Profile/index.tsx +++ b/frontend/code/src/Components/Profile/index.tsx @@ -1,7 +1,5 @@ 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' @@ -47,7 +45,7 @@ export const Profile = () =>{ setProfile(user) //eslint-disable-next-line - },[params, user]) + },[params.id, user]) console.log(status) const sendRequest = async() => { setDisbaled("btn-disabled") @@ -140,7 +138,7 @@ export const Profile = () =>{
    } { - params.id === "me" || params.id === user.id && + ((params.id === "me" ) || (params.id === user.id)) && } @@ -161,7 +159,7 @@ export const Profile = () =>{
    -
    +
    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 + } }