Skip to content

Commit 0498699

Browse files
author
John Rogers
committed
Minor fix to back navigation handling - making the new mobile appraoch smoother
1 parent bd2d125 commit 0498699

2 files changed

Lines changed: 123 additions & 16 deletions

File tree

src/app/discover/page.tsx

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import {
4242
} from "@/services/api";
4343
import { submitFeedback } from "@/services/feedback";
4444
import { useAuth } from "@/contexts/AuthContext";
45-
import { useSearch } from "@/contexts/SearchContext";
45+
import { useSearch, generateSearchKey } from "@/contexts/SearchContext";
4646
import { trackSearch } from "@/lib/analytics";
4747
import { collection, addDoc, serverTimestamp } from "firebase/firestore/lite";
4848
import { db } from "../../firebase";
@@ -68,7 +68,7 @@ function DiscoverPageContent() {
6868
const theme = useTheme();
6969
const isMobile = useMediaQuery(theme.breakpoints.down("sm")); // 600px breakpoint
7070
const isTablet = useMediaQuery(theme.breakpoints.down("lg")); // 1200px breakpoint
71-
const { searchSettings, updateQuery, updateSearchSettings } = useSearch();
71+
const { searchSettings, updateQuery, updateSearchSettings, setCachedResults, getCachedResults, clearCachedResults } = useSearch();
7272
const [results, setResults] = useState<SearchResult[]>([]);
7373
const [filters, setFilters] = useState<AggregateFilter[]>([]);
7474
const [loading, setLoading] = useState(false);
@@ -461,19 +461,49 @@ function DiscoverPageContent() {
461461
searchSettings.debouncedQuery
462462
: searchSettings.debouncedQuery;
463463

464-
// Only clear results and reset for search parameter changes, NOT page changes
465-
if (isSearchParameterChange) {
466-
console.log(
467-
"🔍 Search parameters changed, clearing results and resetting to page 1"
468-
);
464+
// Check if we have search params but no results (e.g., navigating back from another page)
465+
const hasSearchParams =
466+
(queryToUse && queryToUse.trim().length > 0) ||
467+
(searchSettings.selectedFilters && Object.keys(searchSettings.selectedFilters).length > 0) ||
468+
!!resourceType ||
469+
!!searchSettings.similarUid;
470+
const hasNoResults = results.length === 0 && !loading && !loadingMore;
469471

470-
// Set loading immediately to prevent "No results" message from showing
471-
console.log("⏳ Setting loading to true BEFORE clearing results");
472-
setLoading(true);
472+
// Generate search key for caching
473+
const currentSearchKey = generateSearchKey(searchSettings, resourceType);
474+
475+
// Check for cached results first (only when restoring, not when parameters changed)
476+
if (!isSearchParameterChange && hasSearchParams && hasNoResults) {
477+
const cached = getCachedResults(currentSearchKey);
478+
if (cached) {
479+
console.log("✅ Restoring search results from cache");
480+
setResults(cached.results);
481+
setTotalHits(cached.totalHits);
482+
// Note: filters are fetched separately and don't need to be restored from cache
483+
setLoading(false);
484+
return; // Don't trigger a new search if we have cached results
485+
}
486+
}
473487

474-
// Clear results immediately when search parameters change
475-
console.log("🗑️ Clearing results array");
476-
setResults([]);
488+
// Trigger search if parameters changed OR if we have search params but no results (and no cache)
489+
if (isSearchParameterChange || (hasSearchParams && hasNoResults)) {
490+
if (isSearchParameterChange) {
491+
console.log(
492+
"🔍 Search parameters changed, clearing results and resetting to page 1"
493+
);
494+
// Clear cached results when search parameters change
495+
clearCachedResults();
496+
// Set loading immediately to prevent "No results" message from showing
497+
console.log("⏳ Setting loading to true BEFORE clearing results");
498+
setLoading(true);
499+
// Clear results immediately when search parameters change
500+
console.log("🗑️ Clearing results array");
501+
setResults([]);
502+
} else {
503+
// Restoring search from URL - just trigger the search without clearing
504+
console.log("🔍 Restoring search from URL - triggering search (no cache found)");
505+
setLoading(true);
506+
}
477507
// Reset pagination state for new search
478508
// Also reset if pagination strategy changed
479509
if (searchSettings.paginationStrategy !== paginationStrategyRef.current) {
@@ -1118,15 +1148,19 @@ function DiscoverPageContent() {
11181148
setTotalHits(res.num_hits || 0);
11191149
setIsResultCountLowerBound(res.is_result_count_lower_bound || false);
11201150

1151+
// Cache the results for instant restoration when navigating back
1152+
const cacheKey = generateSearchKey(searchSettings, resourceType);
1153+
setCachedResults(res, cacheKey);
1154+
11211155
// Track search analytics (only for first page, reliable calls, and only once per unique search)
11221156
// Create a unique key for this search to prevent duplicate tracking
11231157
// Include timestamp to ensure we track each actual API response
1124-
const searchKey = `${query || ""}_${JSON.stringify(combinedFilters)}_${searchSettings.useSearch2}_${searchSettings.hybridWeight}_${adjustedMaxDistance}_${apiDuration}`;
1158+
const analyticsKey = `${query || ""}_${JSON.stringify(combinedFilters)}_${searchSettings.useSearch2}_${searchSettings.hybridWeight}_${adjustedMaxDistance}_${apiDuration}`;
11251159

11261160
// Only track if this is a different search than the last one we tracked
11271161
// This ensures we only track once per actual API response, not on every render
1128-
if (lastTrackedSearchRef.current !== searchKey && res.num_hits !== undefined) {
1129-
lastTrackedSearchRef.current = searchKey;
1162+
if (lastTrackedSearchRef.current !== analyticsKey && res.num_hits !== undefined) {
1163+
lastTrackedSearchRef.current = analyticsKey;
11301164

11311165
console.log("📊 Tracking search analytics - API response received:", {
11321166
query: query || "",

src/contexts/SearchContext.tsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import React, {
1010
} from "react";
1111
import { useRouter, useSearchParams, usePathname } from "next/navigation";
1212
import { startTransition } from "react";
13+
import { SearchResult, SearchResponse } from "@/services/api";
1314

1415
export interface SearchSettings {
1516
query: string; // Immediate query for UI binding
@@ -26,8 +27,16 @@ export interface SearchSettings {
2627
similarUid: string | null;
2728
}
2829

30+
interface CachedSearchResults {
31+
results: SearchResult[];
32+
totalHits: number;
33+
aggregations: Record<string, any>;
34+
searchKey: string; // Key that identifies this search
35+
}
36+
2937
interface SearchContextType {
3038
searchSettings: SearchSettings;
39+
cachedResults: CachedSearchResults | null;
3140
updateQuery: (query: string) => void; // For immediate input updates
3241
updateSearchSettings: (
3342
settings: Partial<Omit<SearchSettings, "query" | "debouncedQuery">>
@@ -44,6 +53,9 @@ interface SearchContextType {
4453
paginationStrategy?: "filter" | "offset" | "trust_estimate";
4554
selectedCategory?: string | null;
4655
}) => void;
56+
setCachedResults: (results: SearchResponse | null, searchKey: string) => void;
57+
getCachedResults: (searchKey: string) => CachedSearchResults | null;
58+
clearCachedResults: () => void;
4759
}
4860

4961
const defaultSearchSettings: SearchSettings = {
@@ -64,6 +76,7 @@ const defaultSearchSettings: SearchSettings = {
6476
// Default context value for SSR - no-op functions that don't throw errors
6577
const defaultSearchContext: SearchContextType = {
6678
searchSettings: defaultSearchSettings,
79+
cachedResults: null,
6780
updateQuery: () => {
6881
console.warn("Search not available on server");
6982
},
@@ -76,6 +89,13 @@ const defaultSearchContext: SearchContextType = {
7689
loadSearchFromSaved: () => {
7790
console.warn("Search not available on server");
7891
},
92+
setCachedResults: () => {
93+
console.warn("Search not available on server");
94+
},
95+
getCachedResults: () => null,
96+
clearCachedResults: () => {
97+
console.warn("Search not available on server");
98+
},
7999
};
80100

81101
const SearchContext = createContext<SearchContextType>(defaultSearchContext);
@@ -318,6 +338,24 @@ function urlToSearchSettings(
318338
};
319339
}
320340

341+
// Helper function to generate a search key from search settings
342+
function generateSearchKey(settings: SearchSettings, resourceType?: string | null): string {
343+
const parts = [
344+
settings.debouncedQuery || "",
345+
JSON.stringify(settings.selectedFilters),
346+
settings.useSearch2 ? "1" : "0",
347+
settings.hybridWeight.toString(),
348+
settings.maxDistance.toString(),
349+
settings.maxDistanceMode,
350+
settings.directMatchWeight.toString(),
351+
settings.paginationStrategy,
352+
settings.selectedCategory || "",
353+
resourceType || "",
354+
settings.similarUid || "",
355+
];
356+
return parts.join("|");
357+
}
358+
321359
export function SearchProvider({ children }: { children: React.ReactNode }) {
322360
const router = useRouter();
323361
const pathname = usePathname();
@@ -336,6 +374,7 @@ export function SearchProvider({ children }: { children: React.ReactNode }) {
336374
}
337375
return defaultSearchSettings;
338376
});
377+
const [cachedResults, setCachedResultsState] = useState<CachedSearchResults | null>(null);
339378
const [isRestoringFromNavigation, setIsRestoringFromNavigation] =
340379
useState(false);
341380
const isInitialMount = useRef(true);
@@ -577,21 +616,55 @@ export function SearchProvider({ children }: { children: React.ReactNode }) {
577616
[]
578617
);
579618

619+
// Store cached results
620+
const setCachedResults = useCallback((results: SearchResponse | null, searchKey: string) => {
621+
if (results) {
622+
setCachedResultsState({
623+
results: results.results || [],
624+
totalHits: results.num_hits || 0,
625+
aggregations: results.aggregations || {},
626+
searchKey,
627+
});
628+
} else {
629+
setCachedResultsState(null);
630+
}
631+
}, []);
632+
633+
// Get cached results if they match the current search
634+
const getCachedResults = useCallback((searchKey: string): CachedSearchResults | null => {
635+
if (cachedResults && cachedResults.searchKey === searchKey) {
636+
return cachedResults;
637+
}
638+
return null;
639+
}, [cachedResults]);
640+
641+
// Clear cached results
642+
const clearCachedResults = useCallback(() => {
643+
setCachedResultsState(null);
644+
}, []);
645+
580646
return (
581647
<SearchContext.Provider
582648
value={{
583649
searchSettings,
650+
cachedResults,
584651
updateQuery,
585652
updateSearchSettings,
586653
resetSearchSettings,
587654
loadSearchFromSaved,
655+
setCachedResults,
656+
getCachedResults,
657+
clearCachedResults,
588658
}}
589659
>
590660
{children}
591661
</SearchContext.Provider>
592662
);
593663
}
594664

665+
// Export the helper function for use in components
666+
export { generateSearchKey };
667+
595668
export function useSearch() {
596669
const context = useContext(SearchContext);
597670
return context;

0 commit comments

Comments
 (0)