Display Chats in dashboard + autoscroll chat to bottom

Took 1 hour 11 minutes
This commit is contained in:
Lucàs
2023-05-20 22:10:27 +02:00
parent 6cb47c25e9
commit 57a0c057ab
9 changed files with 150 additions and 126 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ const Carousel = ({images, borderRadius: bRadius}: Props) => {
return ( return (
<Flex px={2} align="center" borderRadius={bRadius} overflow={'hidden'} <Flex px={2} align="center" borderRadius={bRadius} overflow={'hidden'}
justify={'space-between'} bgColor={'purple.50'} height={500} justify={'space-between'} bgColor={'purple.50'} height={500}
bgImage={images[currentIndex]} bgSize={'contain'} bgImage={images[currentIndex]} bgSize={'cover'}
bgRepeat={'no-repeat'} bgPosition={'center'} width={'100%'}> bgRepeat={'no-repeat'} bgPosition={'center'} width={'100%'}>
<IconButton aria-label="left-arrow" borderRadius="full" <IconButton aria-label="left-arrow" borderRadius="full"
onClick={handleClickPrevious}> onClick={handleClickPrevious}>
+3 -2
View File
@@ -1,6 +1,7 @@
import {Chat, User} from '@prisma/client'; import {Chat, User} from '@prisma/client';
import ChatListItem from '@/components/chat/ChatListItem'; import ChatListItem from '@/components/chat/ChatListItem';
import {useEffect, useState} from 'react'; import {useEffect, useState} from 'react';
import {Stack, StackDivider} from '@chakra-ui/react';
type Props = { type Props = {
user: User user: User
@@ -18,11 +19,11 @@ export default function ChatList({user}: Props) {
}, []); }, []);
return ( return (
<> <Stack divider={<StackDivider/>} spacing={2}>
{chats.map( {chats.map(
(chat: Chat & { User: User[] }, index: number) => (chat: Chat & { User: User[] }, index: number) =>
<ChatListItem key={index} chat={chat} user={user}/>) <ChatListItem key={index} chat={chat} user={user}/>)
} }
</> </Stack>
); );
} }
+9 -11
View File
@@ -1,26 +1,24 @@
import {Chat, User} from '@prisma/client'; import {Chat, User} from '@prisma/client';
import {Box, Flex, Image, Text} from '@chakra-ui/react'; import {Box, Button, Flex, Image, Text} from '@chakra-ui/react';
import {useRouter} from 'next/router'; import {useRouter} from 'next/router';
type Props = { type Props = {
user: User, user: User,
chat: Chat & {User: User[]} chat: Chat & { User: User[] }
}; };
export default function ChatListItem({user, chat}: Props) { export default function ChatListItem({user, chat}: Props) {
const router = useRouter(); const router = useRouter();
const otherUser = chat.User.find((u: User) => u.id !== user.id) as User const otherUser = chat.User.find((u: User) => u.id !== user.id) as User;
console.log(chat); console.log(chat);
return ( return (
<> <Flex alignItems={"center"} gap={5} cursor={'pointer'} onClick={() => router.push(`/chat/${chat.id}`)}>
<Flex cursor={"pointer"} onClick={() => router.push(`/chat/${chat.id}`)}> <Image borderRadius={'full'} boxSize="50px" src={otherUser.images[0] ?? '/blank_profile_picture.webp'}/>
<Image borderRadius={'full'} boxSize='50px' src={otherUser.images[0] ?? "/blank_profile_picture.webp"}/> <Box>
<Box> <Text>{otherUser.firstName} {otherUser.lastName}</Text>
<Text>{otherUser.firstName} {otherUser.lastName}</Text> </Box>
</Box> </Flex>
</Flex>
</>
); );
} }
+9 -7
View File
@@ -1,6 +1,6 @@
import type {Message as MessageType, User} from '@prisma/client'; import type {Message as MessageType, User} from '@prisma/client';
import Message from '@/components/chat/Message'; import Message from '@/components/chat/Message';
import {Flex} from '@chakra-ui/react'; import {Flex, Box} from '@chakra-ui/react';
type Props = { type Props = {
user: User, user: User,
@@ -9,12 +9,14 @@ type Props = {
const MessageList = ({messages, user}: Props) => { const MessageList = ({messages, user}: Props) => {
return ( return (
<Flex direction={'column'} gap={1}> <Box w={'70vh'} overflow={'scroll'}>
{messages.map((message, index) => <Message <Flex direction={'column'} gap={1} justifyContent={'flex-end'}>
align={user.id === message.UserID ? 'right' : 'left'} key={index} {messages.map((message, index) => <Message
message={message}/> align={user.id === message.UserID ? 'right' : 'left'} key={index}
)} message={message}/>,
</Flex> )}
</Flex>
</Box>
); );
}; };
@@ -17,13 +17,16 @@ import PassionTagList from "@/components/layout/dashboard/card_user/PassionTagLi
import { useState } from "react"; import { useState } from "react";
import SearchFailCard from "./SearchFailCard"; import SearchFailCard from "./SearchFailCard";
import LoadingPage from "@/components/LoadingPage"; import LoadingPage from "@/components/LoadingPage";
import {User} from '@prisma/client';
export default function CardUser({ type Props = {
users, users: User[]
loggedUser, loggedUser: User
setMatch, setMatch: any
refetchLoggedUser, refetchLoggedUser: any
}) { }
export default function CardUser({users, loggedUser, setMatch, refetchLoggedUser}: Props) {
const toast = useToast({ const toast = useToast({
position: "top", position: "top",
duration: 2000, duration: 2000,
@@ -68,13 +68,6 @@ export default function LeftPanel(props) {
Carte Carte
</LeftPanelButton> </LeftPanelButton>
<LeftPanelButton
leftIcon={<AiFillMessage />}
onClickHandler={() => router.push("/chat")}
>
Messages
</LeftPanelButton>
<LeftPanelButton <LeftPanelButton
leftIcon={<BsFillPersonFill />} leftIcon={<BsFillPersonFill />}
onClickHandler={() => router.push("/profile")} onClickHandler={() => router.push("/profile")}
+27 -11
View File
@@ -1,9 +1,9 @@
import {Button, Container, Flex, Input} from '@chakra-ui/react'; import {Button, Container, Flex, FormControl, Input} from '@chakra-ui/react';
import Head from 'next/head'; import Head from 'next/head';
import {websiteName} from '@/lib/constants'; import {websiteName} from '@/lib/constants';
import {useSession} from 'next-auth/react'; import {useSession} from 'next-auth/react';
import {useRouter} from 'next/router'; import {useRouter} from 'next/router';
import {useEffect, useState} from 'react'; import {useEffect, useRef, useState} from 'react';
import MessageList from '@/components/chat/MessageList'; import MessageList from '@/components/chat/MessageList';
@@ -30,19 +30,29 @@ export default function ChatId() {
const soc = io(); const soc = io();
soc.on('connect', () => console.log('connected')); soc.on('connect', () => console.log('connected'));
soc.on('allOldMessages', (allOldMessages: Message[]) => setMessages(allOldMessages)); soc.on('allOldMessages',
soc.on('newIncomingMessage', (newIncomingMessage: Message) => setMessages(messages => [...messages, newIncomingMessage])); (allOldMessages: Message[]) => setMessages(allOldMessages));
soc.on('newIncomingMessage',
(newIncomingMessage: Message) => setMessages(
messages => [...messages, newIncomingMessage]));
setSocket(soc); setSocket(soc);
}); });
}; };
const handleSubmit = () => { const handleSubmit = (evt) => {
evt.preventDefault()
if (text !== '' && socket && session?.user) { if (text !== '' && socket && session?.user) {
socket.emit('createdMessage', {text, sender: session.user.id}); socket.emit('createdMessage', {text, sender: session.user.id});
setText(''); setText('');
} }
}; };
const AlwaysScrollToBottom = () => {
const elementRef = useRef();
useEffect(() => elementRef.current.scrollIntoView());
return <div ref={elementRef}/>;
};
if (status === 'loading') return <LoadingPage/>; if (status === 'loading') return <LoadingPage/>;
return ( return (
<> <>
@@ -51,12 +61,18 @@ export default function ChatId() {
<Container> <Container>
<MessageList user={session.user as User} messages={messages}/> <MessageList user={session.user as User} messages={messages}/>
<Flex gap={5} mt={5}>
<Input type={'text'} colorScheme={'purple'} <form onSubmit={handleSubmit}>
onChange={evt => setText(evt.target.value)} value={text}/> <FormControl>
<Button colorScheme={'purple'} <Flex gap={5} py={5}>
onClick={handleSubmit}>Envoyer</Button> <Input type={'text'}
</Flex> onChange={evt => setText(evt.target.value)}
value={text}/>
<Button type={'submit'} colorScheme={'purple'}>Envoyer</Button>
</Flex>
</FormControl>
</form>
<AlwaysScrollToBottom/>
</Container> </Container>
</> </>
); );
+1 -4
View File
@@ -7,8 +7,5 @@ export default function Chat() {
const {data: session, status} = useSession({required: true}); const {data: session, status} = useSession({required: true});
if (status === 'loading') return <LoadingPage/>; if (status === 'loading') return <LoadingPage/>;
if (session) return <ChatList user={session.user as User}/>;
return (
<ChatList user={session.user as User}/>
);
} }
+91 -77
View File
@@ -1,25 +1,34 @@
import { Grid, GridItem, Box, useToast, useDisclosure } from "@chakra-ui/react"; import {
import { useSession } from "next-auth/react"; Grid,
import { useRouter } from "next/router"; GridItem,
import { useState } from "react"; Box,
useToast,
useDisclosure,
Card, CardBody, CardHeader, Heading,
} from '@chakra-ui/react';
import {useSession} from 'next-auth/react';
import {useRouter} from 'next/router';
import type { Session } from "@/models/auth/Session"; import type {Session} from '@/models/auth/Session';
import CardUser from "../components/layout/dashboard/card_user/CardUser"; import CardUser from '../components/layout/dashboard/card_user/CardUser';
import SearchFailCard from "../components/layout/dashboard/card_user/SearchFailCard"; import SearchFailCard
from '../components/layout/dashboard/card_user/SearchFailCard';
import LeftPanel from "../components/layout/dashboard/left_panel/LeftPanel"; import LeftPanel from '../components/layout/dashboard/left_panel/LeftPanel';
import ModalMatch from "@/components/layout/dashboard/match_notification/ModalMatch"; import ModalMatch
import Head from "next/head"; from '@/components/layout/dashboard/match_notification/ModalMatch';
import { websiteName } from "@/lib/constants"; import Head from 'next/head';
import { useQuery } from "@tanstack/react-query"; import {websiteName} from '@/lib/constants';
import LoadingPage from "@/components/LoadingPage"; import {useQuery} from '@tanstack/react-query';
import { Notification, NotificationType } from "@prisma/client"; import LoadingPage from '@/components/LoadingPage';
import {Notification, NotificationType, User} from '@prisma/client';
import ChatList from '@/components/chat/ChatList';
export default function Dashboard() { export default function Dashboard() {
const router = useRouter(); const router = useRouter();
const toast = useToast({ position: "top", isClosable: true }); const toast = useToast({position: 'top', isClosable: true});
const { data: session, status } = useSession(); const {data: session, status} = useSession({required: true});
const { const {
isLoading, isLoading,
@@ -28,18 +37,18 @@ export default function Dashboard() {
error, error,
refetch: refetchLoggedUser, refetch: refetchLoggedUser,
} = useQuery({ } = useQuery({
queryKey: ["LoggedUser"], queryKey: ['LoggedUser'],
enabled: status === "authenticated", enabled: status === 'authenticated',
queryFn: async () => { queryFn: async () => {
const { user } = session as unknown as Session; const {user} = session as unknown as Session;
return fetch(`/api/users/${user.id}?include=Notification`) return fetch(`/api/users/${user.id}?include=Notification`)
.then((res) => { .then((res) => {
return res.json(); return res.json();
}) })
.catch((err) => { .catch((err) => {
return err; return err;
}); });
}, },
}); });
@@ -49,84 +58,89 @@ export default function Dashboard() {
isLoading: isLoadingListUsers, isLoading: isLoadingListUsers,
error: errorListUsers, error: errorListUsers,
} = useQuery({ } = useQuery({
queryKey: ["ListUsers"], queryKey: ['ListUsers'],
enabled: status === "authenticated" && !isLoading, enabled: status === 'authenticated' && !isLoading,
queryFn: async () => { queryFn: async () => {
return fetch(`/api/user/userDashboard?userID=${loggedUser.id}`) //exclure les profils déjà like ou dislike return fetch(
.then((res) => res.json()) `/api/user/userDashboard?userID=${loggedUser.id}`) //exclure les profils déjà like ou dislike
.catch((err) => { .then((res) => res.json())
return err; .catch((err) => {
}); return err;
});
}, },
}); });
if (isLoading) { if (isLoading) {
if (status === "unauthenticated") router.push("/login"); if (status === 'unauthenticated') router.push('/login');
return <LoadingPage />; return <LoadingPage/>;
} }
if (isError) { if (isError) {
toast({ toast({
title: `Erreur lors de la récupération des données du profil`, title: `Erreur lors de la récupération des données du profil`,
status: "error", status: 'error',
position: "top", position: 'top',
}); });
if (status === "unauthenticated") router.push("/"); if (status === 'unauthenticated') router.push('/');
} }
let modal; let modal;
if (loggedUser?.Notification?.length > 0) { if (loggedUser?.Notification?.length > 0) {
const matchNotification = loggedUser.Notification.filter( const matchNotification = loggedUser.Notification.filter(
(notif: Notification) => (notif: Notification) =>
notif.type === NotificationType.NEW_MATCH && notif.type === NotificationType.NEW_MATCH &&
notif.hasBeenConsulted === false notif.hasBeenConsulted === false,
); );
modal = matchNotification.map((notif: Notification) => { modal = matchNotification.map((notif: Notification) => {
return ( return (
<ModalMatch notif={notif} key={notif.id} loggedUser={loggedUser} /> <ModalMatch notif={notif} key={notif.id} loggedUser={loggedUser}/>
); );
}); });
} }
if (status === 'loading') return <LoadingPage/>;
return ( return (
<> <>
{modal} {modal}
<Head> <Head>
<title>{websiteName} | Dashboard</title> <title>{websiteName} | Dashboard</title>
</Head> </Head>
<Grid <Grid templateColumns={'repeat(5, 1fr)'}
templateColumns={"repeat(5, 1fr)"} templateRows={'repeat(2, 1fr)'}
templateRows={"repeat(2, 1fr)"} gap={5}
gap={5} minH={'100vh'}>
minH={"100vh"} <GridItem area={'1 / 1 / 3 / 2'}>
> <LeftPanel user={loggedUser}/>
<GridItem area={"1 / 1 / 3 / 2"}> </GridItem>
<LeftPanel user={loggedUser} /> <GridItem area={'1 / 2 / 3 / 4'}>
</GridItem> <Box py={3}>
<GridItem area={"1 / 2 / 3 / 4"}> {isLoadingListUsers
<Box py={3}> ? (<LoadingPage/>)
{isLoadingListUsers ? ( : (isErrorListUsers || !listUsers || !listUsers.users ||
<LoadingPage /> listUsers.users.length === 0)
) : isErrorListUsers || ? <SearchFailCard/>
listUsers === undefined || : <CardUser users={listUsers.users} loggedUser={loggedUser}
listUsers.users === undefined || refetchLoggedUser={refetchLoggedUser}/>
listUsers.users.length === 0 ? ( }
<SearchFailCard /> </Box>
) : ( </GridItem>
<CardUser <GridItem area={'1 / 4 / 2 / 6'}>
users={listUsers.users} <Box py={3} pr={3}>
loggedUser={loggedUser} <Card w={"100%"} h={"100%"} borderRadius={"1rem"}>
refetchLoggedUser={refetchLoggedUser} <CardHeader>
/> <Heading cursor={"pointer"} size='md' onClick={() => {router.push('/chat')}}>Conversations</Heading>
)} </CardHeader>
</Box> <CardBody>
</GridItem> <ChatList user={session?.user as User}/>
<GridItem area={"1 / 4 / 2 / 6"}>{/*Right top*/}</GridItem> </CardBody>
<GridItem area={"2 / 4 / 3 / 6"}>{/*Right Bottom*/}</GridItem> </Card>
</Grid> </Box>
</> </GridItem>
<GridItem area={'2 / 4 / 3 / 6'}>{/*Right Bottom*/}</GridItem>
</Grid>
</>
); );
} }