From 0972cb2d14d5debb2490f93cbf1e6d4a882d0ce1 Mon Sep 17 00:00:00 2001 From: devxprite Date: Mon, 20 Jan 2025 20:31:26 +0530 Subject: [PATCH] chrore: Improve Chat Area --- src/components/chat-area.tsx | 91 ++++++++++++++++++-------------- src/components/loader.tsx | 29 +++++----- src/components/message-input.tsx | 3 +- src/hooks/useAuth.ts | 9 +++- src/main.tsx | 10 ++-- src/pages/home.tsx | 10 +++- 6 files changed, 85 insertions(+), 67 deletions(-) diff --git a/src/components/chat-area.tsx b/src/components/chat-area.tsx index c8f437a..991e7bd 100644 --- a/src/components/chat-area.tsx +++ b/src/components/chat-area.tsx @@ -1,33 +1,35 @@ +import { Fragment, useEffect, useRef, useMemo, memo } from 'react'; +import { LoaderCircle } from 'lucide-react'; +import { useScrolling } from 'react-use'; +import { ScrollArea } from './ui/scroll-area'; +import MessageBubble from './ui/message-bubble'; import moment from 'moment'; -import useChat from '@/hooks/useChat'; import LoadMore from './load-more-btn'; -import { Fragment, useEffect, useRef } from 'react'; +import useChat from '@/hooks/useChat'; import useTyping from '@/hooks/useTyping'; import MessageInput from './message-input'; import TypingBubble from './typing-bubble'; -import { LoaderCircle } from 'lucide-react'; -import { ScrollArea } from './ui/scroll-area'; -import MessageBubble from './ui/message-bubble'; -// import ScrollProgress from './ui/scroll-progress'; -import { cn, formatDateCalendar } from '@/lib/utils'; import { useAuthContext } from '@/context/auth-context'; import { useSettingsContext } from '@/context/settings-context'; +import { cn, formatDateCalendar } from '@/lib/utils'; +import ScrollProgress from './ui/scroll-progress'; import type { Message } from '@/types'; -import { useScrolling } from 'react-use'; -const DateDivider = ({ date }: { date: Message['timestamp'] }) => ( +const DateDivider = memo(({ date }: { date: Message['timestamp'] }) => (
{formatDateCalendar(date?.toDate())}
-); +)); +DateDivider.displayName = 'DateDivider'; -const MessageLoader = () => ( +const MessageLoader = memo(() => (

Loading Messages...

-); +)); +MessageLoader.displayName = 'MessageLoader'; const ChatArea = () => { const { user } = useAuthContext(); @@ -37,48 +39,57 @@ const ChatArea = () => { const isScrolling = useScrolling(chatContainerRef); const { messages, sendMessage, isSending, isLoading, hasMore, loadMore } = useChat(user); - const shouldShowDate = (msg: Message, lastMsg: Message) => { - if (!lastMsg) return true; - return !moment(msg.timestamp?.toDate()).isSame(lastMsg.timestamp?.toDate(), 'day'); - }; + const shouldShowDate = useMemo(() => { + return (msg: Message, lastMsg: Message) => { + // console.log('Show Date?'); + if (!lastMsg?.timestamp) return true; + return !moment(msg.timestamp?.toDate()).isSame(lastMsg.timestamp?.toDate(), 'day'); + }; + }, []); useEffect(() => { - if (messages.length > 0 && chatContainerRef.current && settings.autoScroll && !isScrolling) { + const shouldAutoScroll = messages.length > 0 && chatContainerRef.current && settings.autoScroll && !isScrolling; + + if (shouldAutoScroll) { const chatContainer = chatContainerRef.current; - chatContainer.scrollTo({ - top: chatContainer.scrollHeight, - behavior: 'smooth', + requestAnimationFrame(() => { + chatContainer.scrollTo({ + top: chatContainer.scrollHeight, + behavior: 'smooth', + }); }); } - }, [messages[messages.length - 1]]); + }, [messages[messages.length - 1]?.id, settings.autoScroll]); + + const messageList = useMemo(() => { + return messages.map((msg, i) => ( + + {shouldShowDate(msg, messages[i - 1]) && } + + + )); + }, [messages.length, user?.id, shouldShowDate]); + + // console.log('Render...'); + + if (isLoading && messages.length < 2) return ; return (
- {/* {settings.scrollIndicator && messages.length > 0 && ( + {settings.scrollIndicator && messages.length > 0 && ( - )} */} - - {isLoading && messages.length < 2 && } - + )}
{hasMore && } - - {messages.map((msg, i) => ( - - {shouldShowDate(msg, messages[i - 1]) && } - - - - ))} - + {messageList}
@@ -88,4 +99,4 @@ const ChatArea = () => { ); }; -export default ChatArea; +export default memo(ChatArea); diff --git a/src/components/loader.tsx b/src/components/loader.tsx index 107eb5f..ea9dde1 100644 --- a/src/components/loader.tsx +++ b/src/components/loader.tsx @@ -1,19 +1,18 @@ - const Loader = () => { return (
- - - {/* - - */} + + + {/* + + */} - - + + { r="82" fill="none" stroke="url(#pl-grad1)" - stroke-width="30" - stroke-dasharray="0 257 1 257" - stroke-dashoffset="0.01" - stroke-linecap="round" + strokeWidth="30" + strokeDasharray="0 257 1 257" + strokeDashoffset="0.01" + strokeLinecap="round" transform="rotate(-90,100,100)" /> { y1="18" x2="100.01" y2="182" - stroke-width="30" - stroke-dasharray="1 165" - stroke-linecap="round" + strokeWidth="30" + strokeDasharray="1 165" + strokeLinecap="round" /> diff --git a/src/components/message-input.tsx b/src/components/message-input.tsx index 9934005..9ae676f 100644 --- a/src/components/message-input.tsx +++ b/src/components/message-input.tsx @@ -60,8 +60,7 @@ const MessageInput = ({ onSubmit, isSending }: Props) => { placeholder="Write your message here..." className="bg-muted/75" minLength={1} - max={160} - maxLength={160} + maxLength={250} required disabled={isSending} onChange={handleInputChange} diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts index 30d2feb..ff1ec2c 100644 --- a/src/hooks/useAuth.ts +++ b/src/hooks/useAuth.ts @@ -4,6 +4,7 @@ import { collection, doc, getDocs, query, serverTimestamp, setDoc, where } from import { ref, set, onDisconnect, serverTimestamp as serverTimestampRtdb } from 'firebase/database'; import { User } from '../types'; import { db, rtdb } from '../lib/firebase'; +import toast from 'react-hot-toast'; const COOKIE_EXPIRY_DAYS = 30; @@ -12,7 +13,11 @@ const getUserAgent = () => encodeURIComponent(navigator.userAgent); const fetchCountryCode = async () => { try { const response = await fetch(`https://ipinfo.io/json?token=${import.meta.env.VITE_IPINFO_TOKEN}`); - if (!response.ok) throw new Error('Failed to fetch country info'); + + if (!response.ok) { + toast.error('Unable to determine your location. This might be due to browser settings or permissions. Please try again'); + throw new Error('Failed to fetch country info'); + } const data: { country: string } = await response.json(); return data.country as User['country']; @@ -154,4 +159,4 @@ const useAuth = () => { return { user, isLoading, isInitializing, error, initializeUser, logout }; }; -export default useAuth; +export default useAuth; \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index b66cefb..e04b6b4 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -4,7 +4,6 @@ import Loader from './components/loader.tsx'; import { createRoot } from 'react-dom/client'; import { lazy, StrictMode, Suspense } from 'react'; import { SettingsProvider } from './context/settings-context.tsx'; -import { AnimatePresence } from 'motion/react'; import { Toaster } from 'react-hot-toast'; import { Analytics } from '@vercel/analytics/react'; import { SpeedInsights } from '@vercel/speed-insights/react'; @@ -22,13 +21,10 @@ createRoot(document.getElementById('root')!).render( }> - - - - - + + -); +); \ No newline at end of file diff --git a/src/pages/home.tsx b/src/pages/home.tsx index 0c9e0d4..bf6e6ed 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -31,7 +31,15 @@ const Home = ({}: Props) => { Enter your name to join the chat - +