Skip to content

Commit 1dbd575

Browse files
authored
[masterbots.ai] fix: shallow routing for category & chatbots for chat & profile page (#313)
* update * feat: added shallow link to the sidebar link * update' * fix: routing and content fetching * fix:update on routing * fix:update * update * update * update * fix: clean up * update * update
1 parent 0185325 commit 1dbd575

File tree

5 files changed

+202
-33
lines changed

5 files changed

+202
-33
lines changed

apps/masterbots.ai/components/layout/profile/profile-page-sidebar.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ export const ProfileSidebar = ({ children }: any) => {
1919
const [isThreadsOpen, setIsThreadsOpen] = useState(openSidebar);
2020
const location = useLocation();
2121
const { slug } = useParams()
22-
const { isSidebarOpen, toggleSidebar } = useSidebar();
22+
const { isSidebarOpen, toggleSidebar, setActiveCategory,
23+
setActiveChatbot, } = useSidebar();
2324
const { currentUser, isSameUser } = useProfile()
2425
const { data: session } = useSession()
2526
const { value: user } = useAsync(async () => {
@@ -28,7 +29,13 @@ export const ProfileSidebar = ({ children }: any) => {
2829
}, [slug, currentUser]);
2930

3031
const sameUser = isSameUser(user?.userId)
31-
32+
33+
const handleToggleThreads = () => {
34+
setIsThreadsOpen(!isThreadsOpen);
35+
setActiveCategory(null);
36+
setActiveChatbot(null);
37+
}
38+
3239
return (
3340
<div className="relative h-screen w-full">
3441
{/* Overlay for mobile */}
@@ -64,12 +71,20 @@ export const ProfileSidebar = ({ children }: any) => {
6471
<div className="rounded-lg">
6572
<Link
6673
href={`/u/${slug}/t`}
67-
onClick={() => setIsThreadsOpen(!isThreadsOpen)}
74+
onClick={handleToggleThreads}
6875
className={cn(
6976
"flex w-full items-center justify-between px-4 py-3",
7077
"hover:bg-gray-200 dark:hover:bg-mirage transition-colors duration-200",
7178
isThreadsOpen || location.pathname?.includes('/t/') ? 'bg-gray-200 dark:bg-mirage' : ''
7279
)}
80+
role="button"
81+
aria-expanded={isThreadsOpen}
82+
aria-controls="threads-panel"
83+
onKeyDown={(e) => {
84+
if (e.key === 'Enter' || e.key === 'Space') {
85+
handleToggleThreads();
86+
}
87+
}}
7388
>
7489
<div className="flex items-center space-x-2">
7590
<MessagesSquare className="w-5 h-5" />
@@ -127,9 +142,8 @@ export const ProfileSidebar = ({ children }: any) => {
127142
</div>
128143

129144
{/* Main content */}
130-
<div className="flex-1 w-full overflow-auto">
145+
<div className="flex-1 w-full overflow-auto" id="threads-panel">
131146
<main className="h-full w-full">
132-
133147
{children}
134148
</main>
135149
</div>

apps/masterbots.ai/components/layout/sidebar/sidebar-link.tsx

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { toSlug } from 'mb-lib'
1010
import Image from 'next/image'
1111
import Link from 'next/link'
1212
import { usePathname } from "next/navigation"
13-
import React, { useCallback, useState } from 'react'
13+
import React, { useCallback } from 'react'
1414

1515
interface SidebarLinkProps {
1616
category: Category
@@ -35,6 +35,7 @@ export default function SidebarLink({ category, isFilterMode, page }: SidebarLin
3535
setSelectedChatbots,
3636
expandedCategories,
3737
setExpandedCategories,
38+
navigateTo
3839
} = useSidebar()
3940
const isExpanded = expandedCategories.includes(category.categoryId)
4041

@@ -48,10 +49,20 @@ export default function SidebarLink({ category, isFilterMode, page }: SidebarLin
4849
)
4950
setActiveCategory(prev => {
5051
const newCategory = prev === category.categoryId ? null : category.categoryId
51-
if (newCategory && isBrowse) {
52+
if (newCategory) {
5253
setActiveChatbot(null)
53-
router.push( page === 'profile' ? `/u/${slug}/t/${toSlug(category.name.toLowerCase())}` :`/c/${toSlug(category.name.toLowerCase())}`)
54-
}
54+
navigateTo({
55+
page,
56+
slug: typeof slug === 'string' ? slug : undefined,
57+
categoryName: toSlug(category.name.toLowerCase())
58+
})
59+
60+
}else{
61+
navigateTo({
62+
page,
63+
slug: typeof slug === 'string' ? slug : undefined,
64+
})
65+
}
5566
return newCategory
5667
})
5768
}
@@ -128,17 +139,21 @@ export default function SidebarLink({ category, isFilterMode, page }: SidebarLin
128139

129140
return (
130141
<div className={cn('flex flex-col mb-2')}>
131-
<Link
132-
href={page === 'profile' ? `/u/${slug}/t/${toSlug(category.name)}` :`/c/${toSlug(category.name)}`}
142+
<a
143+
href="#"
144+
// href={page === 'profile' ? `/u/${slug}/t/${toSlug(category.name)}` :`/c/${toSlug(category.name)}`}
133145
className={cn(
134146
'flex items-center p-2 cursor-pointer',
135147
isActive && 'bg-gray-200 dark:bg-mirage',
136148
page === 'profile' && 'pl-6'
137149
)}
138-
onClick={handleClickCategory}
150+
onClick={(e) => {
151+
e.preventDefault();
152+
handleClickCategory(e);
153+
}}
139154
>
140155
{categoryContent}
141-
</Link>
156+
</a>
142157
{childrenContent}
143158
</div>
144159
)
@@ -161,14 +176,22 @@ const ChatbotComponent: React.FC<ChatbotComponentProps> = React.memo(function Ch
161176
isFilterMode,
162177
page
163178
}) {
164-
const { selectedChatbots, toggleChatbotSelection } = useSidebar()
179+
const { selectedChatbots, toggleChatbotSelection,navigateTo } = useSidebar()
165180
const pathname = usePathname()
166181
const isBrowse = !pathname.includes('/c') && !pathname.includes('/u')
167182
const { slug } = useParams()
168183

169184
const handleChatbotClick = useCallback((e: React.MouseEvent) => {
170-
if (isFilterMode) e.preventDefault()
171-
else setActiveChatbot(chatbot)
185+
e.preventDefault()
186+
setActiveChatbot(chatbot)
187+
if(chatbot){
188+
navigateTo({
189+
page,
190+
slug: slug as string,
191+
categoryName: toSlug(category.name.toLowerCase()),
192+
chatbotName: chatbot.name.toLowerCase()
193+
})
194+
}
172195
}, [chatbot, setActiveChatbot, isFilterMode])
173196

174197
const isSelected = selectedChatbots.includes(chatbot.chatbotId)
@@ -206,7 +229,7 @@ const ChatbotComponent: React.FC<ChatbotComponentProps> = React.memo(function Ch
206229
</div>
207230
) : (
208231
<Link
209-
href={ page === 'profile' ? `/u/${slug}/t/${toSlug(category.name)}/${chatbot.name.toLowerCase()}`: `/c/${toSlug(category.name)}/${chatbot.name.toLowerCase()}`}
232+
href={page === 'profile' ? `/u/${slug}/t/${toSlug(category.name)}/${chatbot.name.toLowerCase()}`: `/c/${toSlug(category.name)}/${chatbot.name.toLowerCase()}`}
210233
className={cn(
211234
'flex items-center p-2 w-full',
212235
isActive && 'bg-blue-100 dark:bg-blue-900',

apps/masterbots.ai/components/routes/browse/browse-list.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,12 @@ export default function BrowseList() {
108108
categoriesId: selectedCategories,
109109
chatbotsId: selectedChatbots
110110
})
111-
}, [selectedCategories.length, selectedChatbots.length])
111+
112+
console.log({
113+
selectedCategories,
114+
selectedChatbots
115+
})
116+
}, [selectedCategories, selectedChatbots])
112117

113118
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
114119
React.useEffect(() => {

apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,13 @@ import { NoResults } from '@/components/shared/no-results-card'
3232
import { useSidebar } from '@/lib/hooks/use-sidebar'
3333
import { useThread } from '@/lib/hooks/use-thread'
3434
import { useThreadVisibility } from '@/lib/hooks/use-thread-visibility'
35-
import { getThreads } from '@/services/hasura'
35+
import { getThreads, getBrowseThreads, getUserBySlug } from '@/services/hasura'
3636
import type { Thread } from 'mb-genql'
3737
import { useSession } from 'next-auth/react'
3838
import { useParams } from 'next/navigation'
39-
import { useEffect, useMemo, useRef, useState } from 'react'
39+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
40+
import { useAsync } from 'react-use'
41+
4042

4143
const PAGE_SIZE = 20
4244

@@ -52,53 +54,101 @@ export default function UserThreadPanel({
5254
search?: { [key: string]: string | string[] | undefined }
5355
page?: string
5456
}) {
55-
const params = useParams<{ chatbot: string; threadId: string }>()
57+
const params = useParams<{ chatbot: string; threadId: string, slug?: string }>()
5658
const { data: session } = useSession()
5759
const { activeCategory, activeChatbot } = useSidebar()
58-
const { isOpenPopup, activeThread, setActiveThread, setIsOpenPopup } =
59-
useThread()
60+
const { isOpenPopup, activeThread, setActiveThread, setIsOpenPopup } = useThread()
6061
const [loading, setLoading] = useState<boolean>(false)
6162
const { threads: hookThreads } = useThreadVisibility()
6263
const [searchTerm, setSearchTerm] = useState<string>('')
63-
64+
const { slug } = params;
65+
66+
const userWithSlug = useAsync(async () => {
67+
if (!slug) return { user: null }
68+
const result = await getUserBySlug({
69+
slug,
70+
isSameUser: session?.user?.slug === slug
71+
})
72+
return result
73+
}, [slug])
74+
6475
const finalThreads = useMemo(
6576
() => initialThreads ?? hookThreads,
6677
[initialThreads, hookThreads]
6778
)
68-
6979
const [threads, setThreads] = useState<Thread[]>(finalThreads ?? [])
7080
const [count, setCount] = useState<number>(finalThreads?.length ?? 0)
7181
const [totalThreads, setTotalThreads] = useState<number>(0)
82+
const prevCategoryRef = useRef(activeCategory);
83+
const prevChatbotRef = useRef(activeChatbot);
7284

7385
useEffect(() => {
7486
setThreads(finalThreads)
7587
setCount(finalThreads?.length ?? 0)
7688
setTotalThreads(finalThreads?.length ?? 0)
7789
}, [finalThreads])
7890
const fetchIdRef = useRef(0) // Store the fetchId in a ref
91+
92+
const fetchBrowseThreads = async () => {
93+
try {
94+
if(!slug) return []
95+
const user = userWithSlug.value?.user
96+
if (!user) return []
97+
return await getBrowseThreads({
98+
userId: user.userId,
99+
categoryId: activeCategory,
100+
chatbotName: activeChatbot?.name,
101+
limit: PAGE_SIZE,
102+
});
103+
104+
} catch (error) {
105+
console.error('Failed to fetch threads:', error);
106+
return [];
107+
}
108+
};
109+
79110
const loadMore = async () => {
80111
console.log('🟡 Loading More Content')
81112
setLoading(true)
113+
let moreThreads: Thread[] = []
82114

83-
const moreThreads = await getThreads({
115+
if(page === 'profile' && !session?.user) {
116+
moreThreads = await fetchBrowseThreads();
117+
}else{
118+
moreThreads = await getThreads({
84119
jwt: session!.user?.hasuraJwt,
85120
userId: session!.user.id,
86121
offset: threads.length,
87122
limit: PAGE_SIZE,
88123
categoryId: activeCategory,
89124
chatbotName: activeChatbot?.name
90125
})
126+
}
91127
if (moreThreads) setThreads(prevState => [...prevState, ...moreThreads])
92128
setCount(_prev => moreThreads.length ?? 0)
93129
setLoading(false)
94130
}
95131

96132
const handleThreadsChange = async () => {
97-
if (!session?.user) return
133+
let threads: Thread[] = []
134+
135+
console.log('🟡 Fetching Threads')
136+
setLoading(true)
137+
const isOwnProfile = session?.user?.slug === slug;
138+
if (!session?.user || !isOwnProfile) {
139+
if(page === 'profile') {
140+
threads = await fetchBrowseThreads();
141+
setThreads(_prev => threads ?? [])
142+
setCount(_prev => threads.length ?? 0)
143+
setTotalThreads(threads?.length ?? 0)
144+
}
145+
setLoading(false)
146+
return;
147+
}
98148
const currentFetchId = Date.now() // Generate a unique identifier for the current fetch
99149
fetchIdRef.current = currentFetchId
100-
setLoading(true)
101-
const threads = await getThreads({
150+
151+
threads = await getThreads({
102152
jwt: session!.user?.hasuraJwt,
103153
userId: session!.user.id,
104154
limit: PAGE_SIZE,
@@ -116,11 +166,30 @@ export default function UserThreadPanel({
116166
setLoading(false)
117167
}
118168

169+
170+
const shouldFetchThreads = useCallback(() => {
171+
if (isOpenPopup) return false;
172+
173+
const shouldFetch =
174+
activeCategory ||
175+
activeChatbot ||
176+
(prevCategoryRef.current && !activeCategory) ||
177+
(prevChatbotRef.current && !activeChatbot);
178+
179+
// Update refs after checking
180+
prevCategoryRef.current = activeCategory;
181+
prevChatbotRef.current = activeChatbot;
182+
183+
return shouldFetch;
184+
}, [isOpenPopup, activeCategory, activeChatbot]);
185+
186+
119187
useEffect(() => {
120-
if(page !== 'profile' && (!isOpenPopup || activeCategory || activeChatbot)) {
121-
handleThreadsChange()
122-
} }
123-
, [activeCategory, activeChatbot, isOpenPopup, page])
188+
if (shouldFetchThreads()) {
189+
handleThreadsChange();
190+
}
191+
}, [activeCategory, activeChatbot, isOpenPopup, shouldFetchThreads]);
192+
124193

125194

126195
useEffect(() => {
@@ -134,6 +203,7 @@ export default function UserThreadPanel({
134203
// eslint-disable-next-line react-hooks/exhaustive-deps
135204
}, [threads])
136205

206+
137207
const handleSearch = (term: string) => {
138208
setSearchTerm(term)
139209
}

0 commit comments

Comments
 (0)