Skip to content

Commit a456679

Browse files
committed
chore: refactor useScroll
1 parent ef19ec6 commit a456679

File tree

6 files changed

+225
-131
lines changed

6 files changed

+225
-131
lines changed

apps/masterbots.ai/components/routes/chat/chat.tsx

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,40 +35,40 @@
3535
import { ChatList } from '@/components/routes/chat/chat-list'
3636
import { ChatPanel } from '@/components/routes/chat/chat-panel'
3737
import { ChatScrollAnchor } from '@/components/routes/chat/chat-scroll-anchor'
38-
import { useAtBottom } from '@/lib/hooks/use-at-bottom'
3938
import { useMBChat } from '@/lib/hooks/use-mb-chat'
4039
import { useSidebar } from '@/lib/hooks/use-sidebar'
4140
import { useThread } from '@/lib/hooks/use-thread'
4241
import { useThreadVisibility } from '@/lib/hooks/use-thread-visibility'
43-
import { cn, scrollToBottomOfElement } from '@/lib/utils'
42+
import { cn } from '@/lib/utils'
4443
import type { ChatProps } from '@/types/types'
4544
import type { Message as UiUtilsMessage } from '@ai-sdk/ui-utils'
46-
import { useScroll } from 'framer-motion'
4745
import type { Chatbot } from 'mb-genql'
4846
import { useParams, usePathname } from 'next/navigation'
4947
import React, { useEffect } from 'react'
48+
import { useScroll } from '@/lib/hooks/use-scroll'
5049

5150
export function Chat({
5251
chatbot: chatbotProps,
5352
className,
5453
chatPanelClassName,
5554
isPopup,
56-
scrollToBottom: scrollToBottomOfPopup,
55+
scrollToBottomOfPopup,
5756
isAtBottom: isAtBottomOfPopup,
5857
}: ChatProps) {
5958
const {
6059
activeThread,
6160
loadingState,
6261
isOpenPopup,
6362
sectionRef,
64-
isAtBottom: isAtBottomOfSection,
63+
isAtBottomOfSection,
6564
setActiveThread,
6665
setIsOpenPopup,
6766
setLoadingState,
6867
} = useThread()
6968
const { activeChatbot } = useSidebar()
7069
const { isContinuousThread } = useThreadVisibility()
71-
const containerRef = React.useRef<HTMLDivElement>()
70+
const containerRef = React.useRef<HTMLDivElement>(null)
71+
const threadRef = React.useRef<HTMLDivElement>(null)
7272
const params = useParams<{ chatbot: string; threadId: string }>()
7373
const chatbot = chatbotProps || activeThread?.chatbot || (activeChatbot as Chatbot)
7474
const [
@@ -78,31 +78,35 @@ export function Chat({
7878
const pathname = usePathname()
7979
const prevPathname = React.useRef(pathname)
8080

81-
const { scrollY } = useScroll({
82-
container: containerRef as React.RefObject<HTMLElement>,
83-
})
84-
85-
const { isAtBottom } = useAtBottom({
86-
ref: containerRef,
87-
scrollY,
81+
const { isNearBottom, smoothScrollToBottom, scrollToTop } = useScroll({
82+
containerRef,
83+
threadRef,
84+
isNewContent: isLoading,
85+
hasMore: false, //* true for implementing infinite scroll
86+
isLast: true,
87+
loading: isLoading,
88+
loadMore: () => {}, //* Implement if adding infinite scroll
8889
})
8990

9091
// ? safer way to debounce scroll to bottom
92+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
9193
let timeoutId: any
9294
const debounceScrollToBottom = (element: HTMLElement | undefined) => {
9395
clearTimeout(timeoutId)
9496
timeoutId = setTimeout(() => {
95-
scrollToBottomOfElement(element)
97+
if (element) {
98+
smoothScrollToBottom()
99+
}
96100
clearTimeout(timeoutId)
97-
}, 150) //? Adjustable delay as necessary
101+
}, 150)
98102
}
99103

100104
const scrollToBottom = () => {
101105
if ((params.threadId && containerRef.current) || (!params.threadId && sectionRef.current)) {
102-
let element: any
106+
let element: HTMLElement | undefined = undefined
103107
if (sectionRef.current) {
104108
element = sectionRef.current
105-
} else {
109+
} else if (containerRef.current) {
106110
element = containerRef.current
107111
}
108112
debounceScrollToBottom(element)
@@ -156,14 +160,16 @@ export function Chat({
156160
<>
157161
{params.threadId && (
158162
<div
159-
ref={containerRef as React.Ref<HTMLDivElement>}
163+
ref={containerRef}
160164
className={cn('pb-[200px] pt-4 md:pt-10 h-full overflow-auto', className)}
161165
>
162-
<ChatList />
166+
<div ref={threadRef}>
167+
<ChatList />
168+
</div>
163169
<ChatScrollAnchor
164170
isAtBottom={
165171
params.threadId
166-
? isAtBottom
172+
? isNearBottom
167173
: isPopup
168174
? Boolean(isAtBottomOfPopup)
169175
: isAtBottomOfSection
@@ -189,7 +195,11 @@ export function Chat({
189195
placeholder={chatbot ? chatSearchMessage(isNewChat, isContinuousThread, allMessages) : ''}
190196
showReload={!isNewChat}
191197
isAtBottom={
192-
params.threadId ? isAtBottom : isPopup ? Boolean(isAtBottomOfPopup) : isAtBottomOfSection
198+
params.threadId
199+
? isNearBottom
200+
: isPopup
201+
? Boolean(isAtBottomOfPopup)
202+
: isAtBottomOfSection
193203
}
194204
/>
195205
</>

apps/masterbots.ai/components/routes/thread/thread-popup.tsx

Lines changed: 52 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,51 +6,51 @@ import { ChatList } from '@/components/routes/chat/chat-list'
66
import { Button } from '@/components/ui/button'
77
import { IconClose } from '@/components/ui/icons'
88
import { Skeleton } from '@/components/ui/skeleton'
9-
import { useAtBottom } from '@/lib/hooks/use-at-bottom'
109
import { useMBChat } from '@/lib/hooks/use-mb-chat'
1110
import { useSidebar } from '@/lib/hooks/use-sidebar'
1211
import { useThread } from '@/lib/hooks/use-thread'
13-
import { cn, getRouteType, scrollToBottomOfElement } from '@/lib/utils'
12+
import { cn, getRouteType } from '@/lib/utils'
1413
import { getMessages } from '@/services/hasura'
1514
import type { Message as AiMessage } from 'ai'
16-
import { useScroll } from 'framer-motion'
1715
import type { Chatbot, Message } from 'mb-genql'
1816
import { usePathname } from 'next/navigation'
1917
import { useEffect, useRef, useState } from 'react'
18+
import { useScroll } from '@/lib/hooks/use-scroll'
2019

2120
export function ThreadPopup({ className }: { className?: string }) {
2221
const { activeChatbot } = useSidebar()
2322
const { isOpenPopup, activeThread } = useThread()
2423
const [{ allMessages, isLoading }, { sendMessageFromResponse }] = useMBChat()
2524
const [browseMessages, setBrowseMessages] = useState<Message[]>([])
26-
const popupContentRef = useRef<HTMLDivElement>()
25+
const popupContentRef = useRef<HTMLDivElement>(null)
26+
const threadRef = useRef<HTMLDivElement>(null)
2727
const pathname = usePathname()
2828

29-
const { scrollY } = useScroll({
30-
container: popupContentRef as React.RefObject<HTMLElement>,
31-
})
32-
33-
const { isAtBottom } = useAtBottom({
34-
ref: popupContentRef,
35-
scrollY,
29+
const { isNearBottom, smoothScrollToBottom } = useScroll({
30+
containerRef: popupContentRef,
31+
threadRef,
32+
isNewContent: isLoading,
33+
hasMore: false,
34+
isLast: true,
35+
loading: isLoading,
36+
loadMore: () => {},
3637
})
3738

3839
const scrollToBottom = () => {
3940
if (popupContentRef.current) {
40-
const element = popupContentRef.current
41-
scrollToBottomOfElement(element)
41+
smoothScrollToBottom()
4242
}
4343
}
4444

45-
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
45+
// Update effect to use smoothScrollToBottom from custom hook
4646
useEffect(() => {
4747
if (isLoading && isOpenPopup) {
4848
const timeout = setTimeout(() => {
49-
scrollToBottom()
49+
smoothScrollToBottom()
5050
clearTimeout(timeout)
5151
}, 150)
5252
}
53-
}, [isLoading, isOpenPopup])
53+
}, [isLoading, isOpenPopup, smoothScrollToBottom])
5454

5555
// Fetch browse messages when activeThread changes
5656
useEffect(() => {
@@ -95,46 +95,48 @@ export function ThreadPopup({ className }: { className?: string }) {
9595
/>
9696

9797
<div
98+
ref={popupContentRef}
9899
className={cn(
99100
'flex flex-col dark:bg-[#18181b] bg-white grow rounded-b-[8px] scrollbar h-full',
100101
isBrowseView ? 'pb-2 md:pb-4' : 'pb-[120px] md:pb-[180px]',
101-
isBrowseView ? '' :'max-h-[calc(100%-240px)] md:max-h-[calc(100%-220px)]',
102+
isBrowseView ? '' : 'max-h-[calc(100%-240px)] md:max-h-[calc(100%-220px)]',
102103
className,
103104
)}
104-
ref={popupContentRef as React.Ref<HTMLDivElement>}
105105
>
106-
{isBrowseView ? (
107-
// Browse view
108-
<div className="px-8 py-4">
109-
<BrowseChatMessageList
110-
chatbot={activeThread?.chatbot}
111-
user={activeThread?.user || undefined}
112-
messages={browseMessages}
113-
threadId={activeThread?.threadId}
114-
/>
115-
</div>
116-
) : (
117-
// Chat view
118-
<>
119-
<ChatList
120-
isThread={false}
121-
messages={allMessages}
122-
sendMessageFn={sendMessageFromResponse}
123-
chatbot={activeThread?.chatbot || (activeChatbot as Chatbot)}
124-
chatContentClass="!border-x-gray-300 !px-[16px] !mx-0 max-h-[none] dark:!border-x-mirage"
125-
className="max-w-full !px-[32px] !mx-0"
126-
chatArrowClass="!right-0 !mr-0"
127-
chatTitleClass="!px-[11px]"
128-
/>
129-
130-
<Chat
131-
isPopup
132-
chatPanelClassName="!pl-0 rounded-b-[8px] overflow-hidden !absolute"
133-
scrollToBottom={scrollToBottom}
134-
isAtBottom={isAtBottom}
135-
/>
136-
</>
137-
)}
106+
<div ref={threadRef}>
107+
{isBrowseView ? (
108+
// Browse view
109+
<div className="px-8 py-4">
110+
<BrowseChatMessageList
111+
chatbot={activeThread?.chatbot}
112+
user={activeThread?.user || undefined}
113+
messages={browseMessages}
114+
threadId={activeThread?.threadId}
115+
/>
116+
</div>
117+
) : (
118+
// Chat view
119+
<>
120+
<ChatList
121+
isThread={false}
122+
messages={allMessages}
123+
sendMessageFn={sendMessageFromResponse}
124+
chatbot={activeThread?.chatbot || (activeChatbot as Chatbot)}
125+
chatContentClass="!border-x-gray-300 !px-[16px] !mx-0 max-h-[none] dark:!border-x-mirage"
126+
className="max-w-full !px-[32px] !mx-0"
127+
chatArrowClass="!right-0 !mr-0"
128+
chatTitleClass="!px-[11px]"
129+
/>
130+
131+
<Chat
132+
isPopup
133+
chatPanelClassName="!pl-0 rounded-b-[8px] overflow-hidden !absolute"
134+
scrollToBottom={scrollToBottom}
135+
isAtBottom={isNearBottom}
136+
/>
137+
</>
138+
)}
139+
</div>
138140
</div>
139141
</div>
140142
</div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)