-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
finishes rebasing chat app modified: package.json new file: src/assets/Ellipse 33.png new file: src/components/chat/Chat.tsx modified: src/components/footer/Footer.tsx new file: src/hooks/customHooks.ts new file: src/services/chatApi.ts modified: src/types/Types.ts modified: src/utils/schemas.ts
- Loading branch information
Showing
8 changed files
with
335 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
import React, { useEffect, useRef, useState } from 'react'; | ||
import { FaPaperPlane, FaComments, FaTimes } from 'react-icons/fa'; | ||
import chatAvatar from '../../assets/Ellipse 33.png'; | ||
import { chatSchema, ChatData } from '../../utils/schemas'; | ||
import { zodResolver } from '@hookform/resolvers/zod'; | ||
import { useForm, SubmitHandler } from 'react-hook-form'; | ||
import { useNavigate } from 'react-router-dom'; | ||
import { useGetChatsQuery } from '../../services/chatApi'; | ||
import { useAppSelector } from '../../hooks/customHooks'; | ||
import { ChatMessage } from '../../types/Types'; | ||
import { io, Socket } from 'socket.io-client'; | ||
|
||
const Chat: React.FC = () => { | ||
// selectors and hooks | ||
const [showChat, setShowChat] = useState(false); | ||
const userToken: string | undefined = useAppSelector(state => state.user.token)?.replace(/"/g, '') as string; | ||
const userId = useAppSelector(state => state.user.userId)?.replace(/"/g, '') as string; | ||
const [messageList, setMessageList] = useState<ChatMessage[] | undefined>([]); | ||
const [welcomeMessage, setWelcomeMessage] = useState< | ||
{ fistName: string; lastName: string; photo: string } | undefined | ||
>(undefined); | ||
const navigate = useNavigate(); | ||
const scrollDown = useRef<HTMLDivElement>(null); | ||
const { data, isError, isSuccess, isLoading } = useGetChatsQuery(userToken, { skip: !userToken }); | ||
let socket: Socket | null = null; | ||
console.log(isLoading, data); | ||
|
||
useEffect(() => { | ||
if (userToken) { | ||
socket = io.connect('http://localhost:8080', { auth: { token: userToken } }); | ||
Check failure on line 30 in src/components/chat/Chat.tsx GitHub Actions / Build (20)
|
||
|
||
if (isSuccess) { | ||
setMessageList(data.chat); | ||
} | ||
|
||
if (isError) { | ||
setError('content', { message: 'Error fetching messages' }); | ||
} | ||
|
||
socket.on('welcome', data => { | ||
setWelcomeMessage(data); | ||
}); | ||
|
||
socket.on('returnMessage', (msg: ChatMessage) => { | ||
setMessageList(currentMessages => [...(currentMessages || []), msg]); | ||
if (scrollDown.current) { | ||
scrollDown.current.scrollIntoView({ behavior: 'smooth' }); | ||
} | ||
}); | ||
|
||
socket.on('receiveMessage', (msg: ChatMessage) => { | ||
setMessageList(currentMessages => [...(currentMessages || []), msg]); | ||
if (scrollDown.current) { | ||
scrollDown.current.scrollIntoView({ behavior: 'smooth' }); | ||
} | ||
}); | ||
|
||
return () => { | ||
socket.off('returnMessage'); | ||
socket.off('receiveMessage'); | ||
}; | ||
} | ||
}, [userToken, isSuccess, isError, data]); | ||
|
||
// Form validations | ||
const { | ||
register, | ||
handleSubmit, | ||
setError, | ||
reset, | ||
formState: { errors }, | ||
} = useForm<ChatData>({ resolver: zodResolver(chatSchema) }); | ||
|
||
// Handlers | ||
const handleOpenChat = () => { | ||
userToken ? setShowChat(true) : (setShowChat(false), navigate('/login')); | ||
}; | ||
|
||
const handleCloseChat = () => setShowChat(false); | ||
|
||
const handleMessageSubmit: SubmitHandler<ChatData> = async (data: ChatData) => { | ||
const sendChatData = { ...data, socketId: String(Date.now()), senderId: userId }; | ||
if (socket) { | ||
await socket.emit('sentMessage', sendChatData); | ||
} | ||
setMessageList(currentMessage => [...(currentMessage || []), sendChatData]); | ||
Check failure on line 86 in src/components/chat/Chat.tsx GitHub Actions / Build (20)
|
||
reset(); | ||
if (scrollDown.current) scrollDown.current.scrollIntoView({ behavior: 'smooth' }); | ||
}; | ||
|
||
return ( | ||
<> | ||
<div className='fixed bottom-5 right-5 z-50'> | ||
<button onClick={handleOpenChat} className='text-greenColor text-4xl z-50'> | ||
<FaComments className={showChat ? 'hidden' : ''} /> | ||
</button> | ||
</div> | ||
|
||
{showChat && ( | ||
<div className='fixed inset-0 z-40 flex items-center justify-end mx-2 sm:mr-6'> | ||
<div className='bg-white w-9/10 max-w-md h-5/6 rounded-2xl shadow-xl relative bg-whiteColor'> | ||
<div className='header bg-darkGreen flex justify-between items-center px-6 py-4 rounded-t-2xl text-white'> | ||
<div className='flex items-center gap-4'> | ||
<img | ||
src={welcomeMessage === undefined ? chatAvatar : welcomeMessage.photo} | ||
alt='Mavericks' | ||
className='w-14 h-14 rounded-full object-cover' | ||
/> | ||
<div className='ml-4 text-whiteColor'> | ||
<p>Mavericks Public {welcomeMessage?.fistName && <span>{'|' + welcomeMessage.fistName}</span>}</p> | ||
<span className='flex items-center'> | ||
<svg width='8' height='8' viewBox='0 0 8 8' fill='none' xmlns='http://www.w3.org/2000/svg'> | ||
<circle cx='4' cy='4' r='4' fill='#0E9F6E' /> | ||
</svg> | ||
<span className='ml-2'>online</span> | ||
</span> | ||
</div> | ||
</div> | ||
<div className='flex items-center'> | ||
<button | ||
className='text-2xl text-whiteColor transition-all hover:text-grayColor mr-2' | ||
onClick={handleCloseChat} | ||
> | ||
<FaTimes size={24} /> | ||
</button> | ||
</div> | ||
</div> | ||
<div className='messages-container h-2/3 p-6 flex-1 overflow-y-auto'> | ||
<div className='introduction bg-grayColor text-center rounded-2xl p-4 mb-6'> | ||
{errors.root ? ( | ||
<h1 className='font-semibold text-[#ff0000]'>{errors.root.message}</h1> | ||
) : ( | ||
<> | ||
<h1 className='font-semibold text-lg'>Welcome to Mavericks E-commerce website!</h1> | ||
<p className='text-sm'> | ||
We’re excited to help you with exclusive services we have, let’s know how we can help you! | ||
</p> | ||
</> | ||
)} | ||
</div> | ||
<ul className='messages-list flex flex-col gap-4 text-whiteColor'> | ||
{isLoading ? ( | ||
<div className='flex flex-col gap-4'> | ||
<div className='relative flex flex-col w-full animate-pulse gap-3 p-4'> | ||
<div className='flex-1 w-[80%]'> | ||
<div className='h-6 rounded-lg bg-[gray] text-sm'></div> | ||
</div> | ||
<div className='flex-1 w-[80%] self-end'> | ||
<div className='h-6 rounded-lg bg-[#aaa5a5] text-sm'></div> | ||
</div> | ||
<div className='flex-1 w-[80%]'> | ||
<div className='h-6 rounded-lg bg-grayColor text-sm'></div> | ||
</div> | ||
</div> | ||
<div className='relative flex flex-col w-full animate-pulse gap-3 p-4 self-end'> | ||
<div className='flex-1 w-[80%] self-end'> | ||
<div className='h-6 rounded-lg bg-[gray] text-sm'></div> | ||
</div> | ||
<div className='flex-1 w-[80%]'> | ||
<div className='h-6 rounded-lg bg-[#aaa5a5] text-sm'></div> | ||
</div> | ||
<div className='flex-1 w-[80%] self-end'> | ||
<div className='h-6 rounded-lg bg-grayColor text-sm'></div> | ||
</div> | ||
</div> | ||
</div> | ||
) : ( | ||
messageList?.map(msg => ( | ||
<li | ||
key={msg.id} | ||
className={`py-2 px-4 rounded-2xl text-sm max-w-chat ${userId === msg.senderId ? 'bg-greenColor text-white self-end' : 'bg-[#9d9494] text-white self-start'}`} | ||
> | ||
<span className='text-sm text-[#333354]'> | ||
{msg.senderId === userId ? 'Me: ' : msg.User.firstName + ': '} | ||
</span>{' '} | ||
<span>{msg.content}</span> | ||
</li> | ||
)) | ||
)} | ||
{isError && <p className='tex-sm text-redColor'>Error getting the messages!</p>} | ||
<div ref={scrollDown}></div> | ||
</ul> | ||
</div> | ||
<form | ||
className='flex gap-4 p-4 border-t border-grayColor relative' | ||
onSubmit={handleSubmit(handleMessageSubmit)} | ||
> | ||
<textarea | ||
{...register('content')} | ||
className={`flex-1 p-2 border rounded-2xl outline-none ${errors.content ? 'border-redColor' : 'border-darkGreen'}`} | ||
></textarea> | ||
|
||
<button type='submit' className='text-greenColor cursor-pointer'> | ||
<FaPaperPlane size={32} className='rotate-45' /> | ||
</button> | ||
</form> | ||
</div> | ||
</div> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
export default Chat; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,81 @@ | ||
import FooterTitle from "../../containers/footer/FooterTitle" | ||
import SocialIcon from "../../containers/footer/SocialIcon" | ||
import FooterLink from "../../containers/footer/FooterLink" | ||
|
||
import FooterTitle from '../../containers/footer/FooterTitle'; | ||
import SocialIcon from '../../containers/footer/SocialIcon'; | ||
import FooterLink from '../../containers/footer/FooterLink'; | ||
import Chat from '../chat/Chat'; | ||
function Footer() { | ||
return ( | ||
<> | ||
<div className="w-full flex flex-col gap-2 bg-grayColor font-roboto 2xl:items-center "> | ||
<div className="p-3 md:p-4 xl:px-10 2xl:w-[1440px] grid grid-cols-2 md:grid-cols-5 xl:grid-cols-6 gap-x-20 gap-y-5 sm:gap-5 md:gap-2"> | ||
<div className="flex flex-col md:row-span-1 md:col-start-1 md:col-end-3 gap-3"> | ||
<FooterTitle title={"mavericks"} /> | ||
<div className="leading-none text-xs md:text-base break-words flex flex-col gap-3 font-light flex-grow"> | ||
<p> | ||
K309 St , Makuza plaza, Nyarugenge , | ||
Kigali, Rwanda | ||
</p> | ||
<p> | ||
[email protected] | ||
</p> | ||
<p>+250 788888888</p> | ||
</div> | ||
<div className="flex gap-2"> | ||
<a href="#" target="_blank"> | ||
<SocialIcon name="instagram" /> | ||
</a> | ||
<a href="#" target="_blank"> | ||
<SocialIcon name="facebook" /> | ||
</a> | ||
<a href="#" target="_blank"> | ||
<SocialIcon name="twitter" /> | ||
</a> | ||
</div> | ||
</div> | ||
<div className="flex flex-col gap-3"> | ||
<FooterTitle title={"company"} /> | ||
<div className="flex flex-col gap-1 font-light"> | ||
<FooterLink name={"about us"} /> | ||
<FooterLink name={"contact us"} /> | ||
</div> | ||
</div> | ||
<div className="flex flex-col gap-3"> | ||
<FooterTitle title={"shop"} /> | ||
<div className="flex flex-col gap-1 font-light"> | ||
<FooterLink name={"new arrival"} /> | ||
<FooterLink name={"all products"} /> | ||
<FooterLink name={"babies"} /> | ||
<FooterLink name={"father"} /> | ||
<FooterLink name={"electronics"} /> | ||
</div> | ||
</div> | ||
<div className="flex flex-col gap-3"> | ||
<FooterTitle title={"help"} /> | ||
<div className="flex flex-col gap-2 md:gap-1 font-light"> | ||
<FooterLink name={"customer services"} /> | ||
<FooterLink name={"my account"} /> | ||
<FooterLink name={"find store"} /> | ||
<FooterLink name={"legal & privacy"} /> | ||
</div> | ||
</div> | ||
<div className="flex flex-col items-center gap-3 row-span-1 md:row-span-1 col-span-2 md:col-start-3 md:col-end-6 lg:col-start-4 xl:col-start-6 xl:auto-cols-max"> | ||
<FooterTitle title={"subscribe"} /> | ||
<p className="hidden xl:flex leading-none text-base font-light">Be the first to get latest news about trends,Promotions and many more.</p> | ||
<form className="w-full flex flex-col"> | ||
<div className=" w-full flex"> | ||
<label htmlFor="email"></label> | ||
<input type="text" id="email" placeholder="Email address" className="p-2 flex-grow xl:w-3/4" /> | ||
<button type="submit" className="leading-none bg-greenColor text-whiteColor w-20">Join</button> | ||
</div> | ||
<span className="text-xs text-redColor text-left">Email is not valid</span> | ||
</form> | ||
</div> | ||
</div> | ||
<p className="p-3 md:p-4 xl:px-10 2xl:w-[1440px] text-xs text-center xl:text-left ">© 2024 Mavericks Shop. All rights reserved.</p> | ||
return ( | ||
<> | ||
<div className='w-full flex flex-col gap-2 bg-grayColor font-roboto 2xl:items-center '> | ||
<Chat /> | ||
<div className='p-3 md:p-4 xl:px-10 2xl:w-[1440px] grid grid-cols-2 md:grid-cols-5 xl:grid-cols-6 gap-x-20 gap-y-5 sm:gap-5 md:gap-2'> | ||
<div className='flex flex-col md:row-span-1 md:col-start-1 md:col-end-3 gap-3'> | ||
<FooterTitle title={'mavericks'} /> | ||
<div className='leading-none text-xs md:text-base break-words flex flex-col gap-3 font-light flex-grow'> | ||
<p>K309 St , Makuza plaza, Nyarugenge , Kigali, Rwanda</p> | ||
<p>[email protected]</p> | ||
<p>+250 788888888</p> | ||
</div> | ||
<div className='flex gap-2'> | ||
<a href='#' target='_blank'> | ||
<SocialIcon name='instagram' /> | ||
</a> | ||
<a href='#' target='_blank'> | ||
<SocialIcon name='facebook' /> | ||
</a> | ||
<a href='#' target='_blank'> | ||
<SocialIcon name='twitter' /> | ||
</a> | ||
</div> | ||
</div> | ||
<div className='flex flex-col gap-3'> | ||
<FooterTitle title={'company'} /> | ||
<div className='flex flex-col gap-1 font-light'> | ||
<FooterLink name={'about us'} /> | ||
<FooterLink name={'contact us'} /> | ||
</div> | ||
</div> | ||
<div className='flex flex-col gap-3'> | ||
<FooterTitle title={'shop'} /> | ||
<div className='flex flex-col gap-1 font-light'> | ||
<FooterLink name={'new arrival'} /> | ||
<FooterLink name={'all products'} /> | ||
<FooterLink name={'babies'} /> | ||
<FooterLink name={'father'} /> | ||
<FooterLink name={'electronics'} /> | ||
</div> | ||
</div> | ||
<div className='flex flex-col gap-3'> | ||
<FooterTitle title={'help'} /> | ||
<div className='flex flex-col gap-2 md:gap-1 font-light'> | ||
<FooterLink name={'customer services'} /> | ||
<FooterLink name={'my account'} /> | ||
<FooterLink name={'find store'} /> | ||
<FooterLink name={'legal & privacy'} /> | ||
</div> | ||
</> | ||
) | ||
</div> | ||
<div className='flex flex-col items-center gap-3 row-span-1 md:row-span-1 col-span-2 md:col-start-3 md:col-end-6 lg:col-start-4 xl:col-start-6 xl:auto-cols-max'> | ||
<FooterTitle title={'subscribe'} /> | ||
<p className='hidden xl:flex leading-none text-base font-light'> | ||
Be the first to get latest news about trends,Promotions and many more. | ||
</p> | ||
<form className='w-full flex flex-col'> | ||
<div className=' w-full flex'> | ||
<label htmlFor='email'></label> | ||
<input type='text' id='email' placeholder='Email address' className='p-2 flex-grow xl:w-3/4' /> | ||
<button type='submit' className='leading-none bg-greenColor text-whiteColor w-20'> | ||
Join | ||
</button> | ||
</div> | ||
<span className='text-xs text-redColor text-left'>Email is not valid</span> | ||
</form> | ||
</div> | ||
</div> | ||
<p className='p-3 md:p-4 xl:px-10 2xl:w-[1440px] text-xs text-center xl:text-left '> | ||
© 2024 Mavericks Shop. All rights reserved. | ||
</p> | ||
</div> | ||
</> | ||
); | ||
} | ||
|
||
export default Footer | ||
export default Footer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; | ||
import { RootState, AppDispatch } from '../redux/store'; | ||
|
||
export const useAppDispatch = () => useDispatch<AppDispatch>(); | ||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { mavericksApi } from '.'; | ||
import { ChatDataResponse } from '../types/Types'; | ||
|
||
const chatApi = mavericksApi.injectEndpoints({ | ||
endpoints: builder => ({ | ||
getChats: builder.query<{ chat: ChatDataResponse[] }, undefined>({ | ||
query: token => ({ | ||
url: '/chats', | ||
headers: { | ||
Authorization: token, | ||
}, | ||
}), | ||
}), | ||
}), | ||
// overrideExisting: false, | ||
}); | ||
|
||
export const { useGetChatsQuery } = chatApi; |
Oops, something went wrong.