Skip to content

Commit f63220b

Browse files
authored
fix(search): remember the last selected view (#9633)
* fix(search): remember the last selected view * fix(search): target the search field in the focus test
1 parent 2fc9739 commit f63220b

3 files changed

Lines changed: 60 additions & 32 deletions

File tree

‎apps/sim/app/o/[organizationId]/search/search.tsx‎

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import { Chip, ComposerActionButton, cn } from '@sim/emcn'
55
import { ArrowUp, Plus, Search, Sparkles, StopFilled } from '@sim/emcn/icons'
66
import { generateId } from '@sim/utils/id'
77
import { useIsFetching, useQueryClient } from '@tanstack/react-query'
8-
import { usePathname, useRouter } from 'next/navigation'
8+
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
99
import { useQueryStates } from 'nuqs'
1010
import { HEADER_ACTION_CLUSTER } from '@/components/page-header-bar'
1111
import type { WorkspaceSearchFilters } from '@/lib/api/contracts/knowledge'
1212
import { resourceScopeKey } from '@/lib/core/resource-scope'
13+
import { BrowserStorage } from '@/lib/core/utils/browser-storage'
1314
import { organizationRoutes } from '@/lib/navigation/paths'
1415
import type { OrganizationSummaryRequest } from '@/app/o/[organizationId]/components/organization-conversation'
1516
import { SearchInputBar } from '@/app/o/[organizationId]/components/search-input-bar'
@@ -166,17 +167,17 @@ function OrganizationSearchContent({ userId, chatId }: OrganizationSearchProps)
166167
const { recordQuery } = useSearchHistoryActions()
167168
const router = useRouter()
168169
const pathname = usePathname()
170+
const hasSearchParams = useSearchParams().size > 0
169171
const routes = organizationRoutes(organization.id)
170172
const hasConversation = Boolean(chatId) || pathname.startsWith(`${routes.search}/chat/`)
171173
const queryClient = useQueryClient()
172174
const [{ q, mode }, setParams] = useQueryStates(
173175
organizationSearchParsers,
174176
organizationSearchUrlKeys
175177
)
176-
const activeMode =
177-
mothershipAvailable && (mode ?? (hasConversation ? 'assistant' : 'search')) === 'assistant'
178-
? 'assistant'
179-
: 'search'
178+
const routeMode = mode ?? (hasConversation ? 'assistant' : hasSearchParams ? 'search' : null)
179+
const activeMode = mothershipAvailable ? routeMode : 'search'
180+
const preferenceKey = `organization-search-mode:${userId}:${organization.id}`
180181
const [assistantOpened, setAssistantOpened] = useState(activeMode === 'assistant')
181182
const [conversationInstance, setConversationInstance] = useState(0)
182183
const [isStartingNew, startNewTransition] = useTransition()
@@ -185,8 +186,20 @@ function OrganizationSearchContent({ userId, chatId }: OrganizationSearchProps)
185186
(id: string) => setSummaries((current) => current.filter((request) => request.id !== id)),
186187
[]
187188
)
189+
useEffect(() => {
190+
if (!mothershipAvailable) return
191+
if (routeMode) {
192+
BrowserStorage.setItem(preferenceKey, routeMode)
193+
return
194+
}
195+
const savedMode = BrowserStorage.getItem<unknown>(preferenceKey, null)
196+
// Pin the mode to this history entry so Back survives a later preference change.
197+
void setParams({ mode: savedMode === 'assistant' ? 'assistant' : 'search' })
198+
}, [mothershipAvailable, preferenceKey, routeMode, setParams])
199+
200+
if (activeMode === 'assistant' && !assistantOpened) setAssistantOpened(true)
201+
188202
const changeMode = (next: 'search' | 'assistant') => {
189-
if (next === 'assistant') setAssistantOpened(true)
190203
void setParams({ mode: next }, { history: 'push' })
191204
}
192205
const startNew = () => {
@@ -201,7 +214,7 @@ function OrganizationSearchContent({ userId, chatId }: OrganizationSearchProps)
201214
}
202215
startNewTransition(() => {
203216
setConversationInstance((current) => current + 1)
204-
router.push(activeMode === 'assistant' ? `${routes.search}?mode=assistant` : routes.search, {
217+
router.push(mothershipAvailable ? `${routes.search}?mode=${activeMode}` : routes.search, {
205218
scroll: false,
206219
})
207220
})
@@ -263,28 +276,28 @@ function OrganizationSearchContent({ userId, chatId }: OrganizationSearchProps)
263276
return (
264277
<div className='h-full min-h-0 min-w-0 bg-[var(--bg)] pt-[var(--workspace-content-title-bar-inset)]'>
265278
<div className='flex h-full min-h-0 min-w-0 flex-col [--workspace-content-title-bar-inset:0px]'>
266-
{(assistantOpened || hasConversation || activeMode === 'assistant') &&
267-
mothershipAvailable && (
268-
<div
269-
inert={activeMode !== 'assistant'}
270-
className={cn(
271-
'min-h-0 min-w-0 flex-1 flex-col',
272-
activeMode === 'assistant' ? 'flex' : 'hidden'
273-
)}
274-
>
275-
<Suspense fallback={<HomeFallback header={header} />}>
276-
<OrganizationConversation
277-
key={conversationInstance}
278-
requestMode='assistant'
279-
chatId={chatId}
280-
header={header}
281-
active={activeMode === 'assistant'}
282-
summaryRequest={summaries[0]}
283-
onSummaryConsumed={consumeSummary}
284-
/>
285-
</Suspense>
286-
</div>
287-
)}
279+
{(assistantOpened || hasConversation) && mothershipAvailable && (
280+
<div
281+
inert={activeMode !== 'assistant'}
282+
className={cn(
283+
'min-h-0 min-w-0 flex-1 flex-col',
284+
activeMode === 'assistant' ? 'flex' : 'hidden'
285+
)}
286+
>
287+
<Suspense fallback={<HomeFallback header={header} />}>
288+
<OrganizationConversation
289+
key={conversationInstance}
290+
requestMode='assistant'
291+
chatId={chatId}
292+
header={header}
293+
active={activeMode === 'assistant'}
294+
summaryRequest={summaries[0]}
295+
onSummaryConsumed={consumeSummary}
296+
/>
297+
</Suspense>
298+
</div>
299+
)}
300+
{!activeMode && <HomeFallback />}
288301
{activeMode === 'search' && (
289302
<>
290303
<div className={cn(RESOURCE_HEADER_CLASSES.layout, RESOURCE_HEADER_CLASSES.content)}>

‎apps/sim/scripts/test-assistant-actions-e2e.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1488,7 +1488,7 @@ try {
14881488
await reviewCards.getByRole('button', { name: 'Cancel', exact: true }).focus()
14891489
await page.keyboard.press('Enter')
14901490
await expect.poll(() => decisionPersisted).toBe(true)
1491-
await page.goto(`${baseUrl.origin}${root}/search`)
1491+
await page.goto(`${baseUrl.origin}${root}/search?mode=search`)
14921492
const search = page.getByRole('textbox', { name: 'Search your sources', exact: true })
14931493
await search.fill('Continue searching')
14941494
releaseResponse.resolve()

‎apps/sim/scripts/test-organization-search-e2e.ts‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,21 @@ try {
412412
await expect(assistantInput).toHaveValue('Summarize launch readiness')
413413
}
414414
)
415+
await check('Returning to Search restores the last selected view', async () => {
416+
await page.goto(`${baseUrl.origin}${root}/search`)
417+
const views = page.getByRole('navigation', { name: 'Search views' })
418+
for (const mode of ['Assistant', 'Search'] as const) {
419+
const tab = views.getByRole('button', { name: mode, exact: true })
420+
await tab.click()
421+
await expect(tab).toHaveAttribute('aria-current', 'page')
422+
await page.getByRole('link', { name: 'Home', exact: true }).click()
423+
await expect(page).toHaveURL(`${baseUrl.origin}${root}/home`)
424+
await page.getByRole('link', { name: 'Search', exact: true }).click()
425+
await expect(tab).toHaveAttribute('aria-current', 'page')
426+
await page.goto(`${baseUrl.origin}${root}/search`)
427+
await expect(tab).toHaveAttribute('aria-current', 'page')
428+
}
429+
})
415430
await check(
416431
'Traditional search submits the organization query and keeps results in Search',
417432
async () => {
@@ -750,7 +765,7 @@ try {
750765
await input.fill('Edited launch query')
751766
await expect(input).toHaveValue('Edited launch query')
752767
await page.getByRole('button', { name: 'New search', exact: true }).click()
753-
await expect(page).toHaveURL(`${baseUrl.origin}${root}/search`)
768+
await expect(page).toHaveURL(`${baseUrl.origin}${root}/search?mode=search`)
754769
await expect(input).toHaveValue('')
755770
await page.goBack()
756771
await expect(input).toHaveValue('Edited launch query')
@@ -1001,7 +1016,7 @@ try {
10011016
values (${generateId()}, ${organizationId}, 'Search only',
10021017
${sql.json({ disableWorkspaceCreation: true })}, ${userId}, true)`
10031018
await page.goto(`${baseUrl.origin}${root}/home`)
1004-
await expect(page).toHaveURL(`${baseUrl.origin}${root}/search`)
1019+
await expect(page).toHaveURL((url) => url.pathname === `${root}/search`)
10051020
await page.goto(`${baseUrl.origin}${root}/search/chat/${assistantId}`)
10061021
await expect(page.getByText(answer, { exact: true })).toBeVisible()
10071022
}

0 commit comments

Comments
 (0)