mirror of
https://github.com/LucasVbr/meeting-app.git
synced 2026-07-09 15:08:06 +00:00
Send messages with sockets ❤️
Took 3 hours 51 minutes
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormLabel,
|
||||
Input,
|
||||
useToast,
|
||||
} from '@chakra-ui/react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
export default function settings() {
|
||||
|
||||
const router = useRouter();
|
||||
const toast = useToast();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
formState: { errors, isSubmitting },
|
||||
} = useForm()
|
||||
|
||||
const [userData, setUserData] = useState({});
|
||||
|
||||
const { data: session, status } = useSession();
|
||||
if (status === "unauthenticated") router.push("/login");
|
||||
|
||||
if (status === "authenticated") {
|
||||
const { user } = session as unknown as Session;
|
||||
|
||||
if (user.role !== "ADMIN") router.push("/login");
|
||||
|
||||
const savePassion = (passion: any) => {
|
||||
const options = {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(passion),
|
||||
};
|
||||
|
||||
fetch(`/api/passions`, options)
|
||||
.then((res) => {
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
position:'top',
|
||||
title: `Passion ajoutée`,
|
||||
status: "success",
|
||||
isClosable: true,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
title: `Erreur lors de l'ajout`,
|
||||
position :'top',
|
||||
status: "error",
|
||||
isClosable: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box as="form" id='form_passion' width={"80%"} onSubmit={handleSubmit(savePassion)}>
|
||||
<FormControl isInvalid={errors.name}>
|
||||
<FormLabel htmlFor='passion'>Passion</FormLabel>
|
||||
<Input
|
||||
id='passion'
|
||||
placeholder='passion'
|
||||
{...register('name', {
|
||||
required: 'This is required',
|
||||
})}
|
||||
/>
|
||||
<FormErrorMessage>
|
||||
{errors.name && errors.name.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
<Button mt={4} colorScheme='purple' isLoading={isSubmitting} type='submit'>
|
||||
Submit
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import {Server} from 'socket.io';
|
||||
import prismaClient from '@/lib/prismaClient';
|
||||
|
||||
export default async function SocketHandler(req: any, res: any) {
|
||||
const {id: chatId} = req.query;
|
||||
|
||||
// It means that socket server was already initialised
|
||||
if (res.socket.server.io) {
|
||||
console.log('Already set up');
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const io = new Server(res.socket.server);
|
||||
res.socket.server.io = io;
|
||||
|
||||
// Define actions inside
|
||||
io.on('connection', async (socket) => {
|
||||
console.log(socket.id);
|
||||
|
||||
await prismaClient.chat.findFirst({
|
||||
where: {id: chatId},
|
||||
include: {Message: true},
|
||||
}).then(chat => {
|
||||
// @ts-ignore
|
||||
return socket.emit('allOldMessages', chat.Message);
|
||||
});
|
||||
|
||||
socket.on('createdMessage', async (msgInput) => {
|
||||
await prismaClient.message.create({
|
||||
data: {
|
||||
text: msgInput.text,
|
||||
User: {connect: {id: msgInput.sender}},
|
||||
Chat: {connect: {id: chatId}},
|
||||
},
|
||||
}).then((newMessage) => socket.emit('newIncomingMessage', newMessage));
|
||||
});
|
||||
});
|
||||
|
||||
console.log('Setting up socket');
|
||||
res.end();
|
||||
}
|
||||
+48
-11
@@ -1,36 +1,73 @@
|
||||
import {Container, Flex, Input, Text} from '@chakra-ui/react';
|
||||
import {Button, Container, Flex, Input, Text} from '@chakra-ui/react';
|
||||
import Head from 'next/head';
|
||||
import {websiteName} from '@/lib/constants';
|
||||
import {useSession} from 'next-auth/react';
|
||||
import {useRouter} from 'next/router';
|
||||
import {useEffect, useState} from 'react';
|
||||
import {useCallback, useEffect, useState} from 'react';
|
||||
|
||||
import MessageList from '@/components/chat/MessageList';
|
||||
import FormMessage from '@/components/form/FormMessage';
|
||||
|
||||
import {io, Socket} from 'socket.io-client';
|
||||
import {Message} from '@prisma/client';
|
||||
|
||||
export default function ChatId() {
|
||||
const router = useRouter();
|
||||
const {data: session, status} = useSession();
|
||||
const {id} = router.query;
|
||||
const [messages, setMessages] = useState([])
|
||||
const {id: chatId} = router.query;
|
||||
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [text, setText] = useState('');
|
||||
const [socket, setSocket] = useState<Socket>()
|
||||
|
||||
useEffect(() => {
|
||||
if (status === "authenticated")
|
||||
fetch(`/api/messages?where={"ChatID": "${id}"}`)
|
||||
.then(res => res.json())
|
||||
.then(msgs => setMessages(msgs))
|
||||
if (status === 'authenticated') socketInitializer()
|
||||
}, [status]);
|
||||
|
||||
|
||||
const socketInitializer = async () => {
|
||||
await fetch(`/api/socket/chat/${chatId}`)
|
||||
const soc = io()
|
||||
|
||||
soc.on('connect', () => {
|
||||
console.log('connected')
|
||||
})
|
||||
|
||||
soc.on("allOldMessages", (allOldMessages: Message[]) => {
|
||||
console.log("allOldMessages", allOldMessages);
|
||||
setMessages(allOldMessages);
|
||||
})
|
||||
|
||||
soc.on("newIncomingMessage", (newIncomingMessage: Message) => {
|
||||
console.log("newIncomingMessage", newIncomingMessage);
|
||||
console.log("messages", messages);
|
||||
setMessages(messages => ([...messages, newIncomingMessage]));
|
||||
});
|
||||
|
||||
setSocket(soc);
|
||||
}
|
||||
|
||||
if (status === 'loading') return <Text>Loading...</Text>;
|
||||
|
||||
if (session && messages)
|
||||
const handleSubmit = () => {
|
||||
if (text !== "" && socket) {
|
||||
// @ts-ignore
|
||||
socket.emit("createdMessage", {text, sender: session.user.id})
|
||||
setText("");
|
||||
}
|
||||
}
|
||||
|
||||
if (session && session.user && messages)
|
||||
return (
|
||||
<>
|
||||
<Head><title>{websiteName}</title></Head>
|
||||
|
||||
<Container>
|
||||
<MessageList user={session.user} messages={messages}/>
|
||||
<FormMessage user={session.user} chatId={id} />
|
||||
|
||||
<Flex gap={5} mt={5}>
|
||||
<Input type={"text"} colorScheme={'purple'} onChange={(evt) => setText(evt.target.value) } value={text} />
|
||||
<Button colorScheme={'purple'} onClick={handleSubmit}>Envoyer</Button>
|
||||
</Flex>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function Dashboard() {
|
||||
minH={"100vh"}
|
||||
>
|
||||
<GridItem area={"1 / 1 / 3 / 2"}>
|
||||
<LeftPanel user={refinedUser} />
|
||||
<LeftPanel user={user} />
|
||||
</GridItem>
|
||||
<GridItem area={"1 / 2 / 3 / 4"}>
|
||||
<Box py={3}>
|
||||
|
||||
@@ -25,7 +25,9 @@ import {
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
|
||||
import ModalModifyImages from "@/components/ModalModifyImages";
|
||||
import ModalModifyImages from "@/components/layout/user_profile/ModalModifyImages";
|
||||
import ModalChoosePassion from "@/components/layout/user_profile/ModalChoosePassion";
|
||||
import ProfileBadgeList from "@/components/layout/user_profile/ProfileBadgeList";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
@@ -33,6 +35,8 @@ import { useForm, Controller } from "react-hook-form";
|
||||
export default function UserProfile() {
|
||||
const router = useRouter();
|
||||
const toast = useToast();
|
||||
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const {
|
||||
@@ -82,6 +86,7 @@ export default function UserProfile() {
|
||||
.then((res) => {
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
position:'top',
|
||||
title: `Modifications effectuées`,
|
||||
status: "success",
|
||||
isClosable: true,
|
||||
@@ -92,6 +97,7 @@ export default function UserProfile() {
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
title: `Erreur lors de l'envoi des modifications`,
|
||||
position :'top',
|
||||
status: "error",
|
||||
isClosable: true,
|
||||
});
|
||||
@@ -299,6 +305,24 @@ export default function UserProfile() {
|
||||
</FormControl>
|
||||
</Box>
|
||||
<Divider colorScheme={"purple"} />
|
||||
<Box my={"1rem"}>
|
||||
<Box>
|
||||
<FormLabel as={"legend"} htmlFor={"passion"}>
|
||||
Centre d'intéret :
|
||||
</FormLabel>
|
||||
<Controller
|
||||
name={"passion"}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ProfileBadgeList passions={user.passion !== undefined ? user.passion : []}/>
|
||||
<ModalChoosePassion user={user}/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Divider colorScheme={"purple"} />
|
||||
<Box my={"1rem"}>
|
||||
<Box>
|
||||
<FormLabel as={"legend"} htmlFor={"gender"}>
|
||||
@@ -316,9 +340,6 @@ export default function UserProfile() {
|
||||
defaultValue={
|
||||
user.gender === null ? Gender.UNKNOWN : user.gender
|
||||
}
|
||||
// onChange={(value) => {
|
||||
// setUserData({ ...userData, gender: value });
|
||||
// }}
|
||||
>
|
||||
<HStack spacing={"0.5rem"}>
|
||||
<Radio value={Gender.MALE}>
|
||||
@@ -372,7 +393,7 @@ export default function UserProfile() {
|
||||
</Flex> */}
|
||||
</Box>
|
||||
<Divider colorScheme={"purple"} />
|
||||
<Center my={"1rem"}>
|
||||
<Center gap={"1rem"} my={"1rem"}>
|
||||
<Button
|
||||
colorScheme={"purple"}
|
||||
isLoading={isLoading}
|
||||
@@ -380,6 +401,13 @@ export default function UserProfile() {
|
||||
>
|
||||
Sauvegarder les modifications
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme={"purple"}
|
||||
variant='outline'
|
||||
onClick={() => router.push("/dashboard")}
|
||||
>
|
||||
Retour
|
||||
</Button>
|
||||
</Center>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
Reference in New Issue
Block a user