From 04f87107f5e55eda637be82019b2b5d25e42f0fd Mon Sep 17 00:00:00 2001 From: LeandraH Date: Wed, 27 Nov 2024 17:31:36 +0100 Subject: [PATCH 1/7] 2926: Add a fallback language in web --- web/src/routes/SearchPage.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/web/src/routes/SearchPage.tsx b/web/src/routes/SearchPage.tsx index e145501fd0..0e69357c1f 100644 --- a/web/src/routes/SearchPage.tsx +++ b/web/src/routes/SearchPage.tsx @@ -4,6 +4,7 @@ import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' import { parseHTML, pathnameFromRouteInformation, SEARCH_ROUTE, useSearch } from 'shared' +import { config } from 'translations' import { CityRouteProps } from '../CityContentSwitcher' import CityContentLayout, { CityContentLayoutProps } from '../components/CityContentLayout' @@ -45,8 +46,16 @@ const SearchPage = ({ city, cityCode, languageCode, pathname }: CityRouteProps): language: languageCode, cmsApiBaseUrl, }) + const mainResults = useSearch(allPossibleResults, query) - const results = useSearch(allPossibleResults, query) + const { data: allPossibleFallbackResults } = useAllPossibleSearchResults({ + city: cityCode, + language: config.sourceLanguage, + cmsApiBaseUrl, + }) + const fallbackResults = useSearch(allPossibleFallbackResults, query) + + const results = mainResults?.length === 0 ? fallbackResults : mainResults if (!city) { return null From f4cd441264feb4aba82ad3d616927522334b72e8 Mon Sep 17 00:00:00 2001 From: LeandraH Date: Wed, 27 Nov 2024 17:31:52 +0100 Subject: [PATCH 2/7] 2926: Refactoring --- web/src/hooks/useAllPossibleSearchResults.ts | 28 +++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/web/src/hooks/useAllPossibleSearchResults.ts b/web/src/hooks/useAllPossibleSearchResults.ts index be5a70dbe3..181690209b 100644 --- a/web/src/hooks/useAllPossibleSearchResults.ts +++ b/web/src/hooks/useAllPossibleSearchResults.ts @@ -1,10 +1,14 @@ import { useMemo } from 'react' +import { SearchResult } from 'shared' import { + CategoriesMapModel, createCategoriesEndpoint, createEventsEndpoint, createPOIsEndpoint, + EventModel, ExtendedPageModel, + PoiModel, useLoadFromEndpoint, } from 'shared/api' @@ -20,6 +24,16 @@ type UseAllPossibleSearchResultsReturn = { loading: boolean } +const combineResults = ( + categories: CategoriesMapModel | null, + events: EventModel[] | null, + pois: PoiModel[] | null, +): SearchResult[] => [ + ...(categories?.toArray().filter(category => !category.isRoot()) || []), + ...(events || []), + ...(pois || []), +] + const useAllPossibleSearchResults = ({ city, language, @@ -27,19 +41,19 @@ const useAllPossibleSearchResults = ({ }: UseAllPossibleSearchResultsProps): UseAllPossibleSearchResultsReturn => { const params = { city, language } - const { data: categories, ...categoriesReturn } = useLoadFromEndpoint(createCategoriesEndpoint, cmsApiBaseUrl, params) - const { data: events, ...eventsReturn } = useLoadFromEndpoint(createEventsEndpoint, cmsApiBaseUrl, params) - const { data: pois, ...poisReturn } = useLoadFromEndpoint(createPOIsEndpoint, cmsApiBaseUrl, params) + const categories = useLoadFromEndpoint(createCategoriesEndpoint, cmsApiBaseUrl, params) + const events = useLoadFromEndpoint(createEventsEndpoint, cmsApiBaseUrl, params) + const pois = useLoadFromEndpoint(createPOIsEndpoint, cmsApiBaseUrl, params) const allPossibleResults = useMemo( - () => [...(categories?.toArray().filter(category => !category.isRoot()) || []), ...(events || []), ...(pois || [])], - [categories, events, pois], + () => combineResults(categories.data, events.data, pois.data), + [categories.data, events.data, pois.data], ) return { data: allPossibleResults, - loading: categoriesReturn.loading || eventsReturn.loading || poisReturn.loading, - error: categoriesReturn.error || eventsReturn.error || poisReturn.error, + loading: categories.loading || events.loading || pois.loading, + error: categories.error || events.error || pois.error, } } From 78170acce2cc4ca0d7553aa55ec365f6032f2573 Mon Sep 17 00:00:00 2001 From: LeandraH Date: Wed, 27 Nov 2024 17:59:44 +0100 Subject: [PATCH 3/7] 2926: More refactoring --- native/src/routes/SearchModalContainer.tsx | 7 ++----- shared/hooks/useSearch.ts | 13 +++++++++++++ web/src/hooks/useAllPossibleSearchResults.ts | 17 ++--------------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/native/src/routes/SearchModalContainer.tsx b/native/src/routes/SearchModalContainer.tsx index abd6f9b950..3504c2be1f 100644 --- a/native/src/routes/SearchModalContainer.tsx +++ b/native/src/routes/SearchModalContainer.tsx @@ -1,6 +1,7 @@ import React, { ReactElement, useMemo } from 'react' import { SearchRouteType } from 'shared' +import { combinePossibleSearchResults } from 'shared/hooks/useSearch' import { NavigationProps, RouteProps } from '../constants/NavigationTypes' import useCityAppContext from '../hooks/useCityAppContext' @@ -19,11 +20,7 @@ const SearchModalContainer = ({ navigation, route }: SearchModalContainerProps): const { data, ...response } = useLoadCityContent({ cityCode, languageCode }) const allPossibleResults = useMemo( - () => [ - ...(data?.categories.toArray().filter(category => !category.isRoot()) || []), - ...(data?.events || []), - ...(data?.pois || []), - ], + () => combinePossibleSearchResults(data?.categories, data?.events, data?.pois), [data?.categories, data?.events, data?.pois], ) diff --git a/shared/hooks/useSearch.ts b/shared/hooks/useSearch.ts index 9299c5231c..c535a2b572 100644 --- a/shared/hooks/useSearch.ts +++ b/shared/hooks/useSearch.ts @@ -2,10 +2,23 @@ import MiniSearch from 'minisearch' import { useCallback } from 'react' import useLoadAsync from '../api/endpoints/hooks/useLoadAsync' +import CategoriesMapModel from '../api/models/CategoriesMapModel' +import EventModel from '../api/models/EventModel' import ExtendedPageModel from '../api/models/ExtendedPageModel' +import PoiModel from '../api/models/PoiModel' export type SearchResult = ExtendedPageModel +export const combinePossibleSearchResults = ( + categories?: CategoriesMapModel | null, + events?: EventModel[] | null, + pois?: PoiModel[] | null, +): SearchResult[] => [ + ...(categories?.toArray().filter(category => !category.isRoot()) || []), + ...(events || []), + ...(pois || []), +] + const useSearch = (allPossibleResults: SearchResult[], query: string): SearchResult[] | null => { const initializeMiniSearch = useCallback(async () => { const search = new MiniSearch({ diff --git a/web/src/hooks/useAllPossibleSearchResults.ts b/web/src/hooks/useAllPossibleSearchResults.ts index 181690209b..45f98606bf 100644 --- a/web/src/hooks/useAllPossibleSearchResults.ts +++ b/web/src/hooks/useAllPossibleSearchResults.ts @@ -1,16 +1,13 @@ import { useMemo } from 'react' -import { SearchResult } from 'shared' import { - CategoriesMapModel, createCategoriesEndpoint, createEventsEndpoint, createPOIsEndpoint, - EventModel, ExtendedPageModel, - PoiModel, useLoadFromEndpoint, } from 'shared/api' +import { combinePossibleSearchResults } from 'shared/hooks/useSearch' type UseAllPossibleSearchResultsProps = { city: string @@ -24,16 +21,6 @@ type UseAllPossibleSearchResultsReturn = { loading: boolean } -const combineResults = ( - categories: CategoriesMapModel | null, - events: EventModel[] | null, - pois: PoiModel[] | null, -): SearchResult[] => [ - ...(categories?.toArray().filter(category => !category.isRoot()) || []), - ...(events || []), - ...(pois || []), -] - const useAllPossibleSearchResults = ({ city, language, @@ -46,7 +33,7 @@ const useAllPossibleSearchResults = ({ const pois = useLoadFromEndpoint(createPOIsEndpoint, cmsApiBaseUrl, params) const allPossibleResults = useMemo( - () => combineResults(categories.data, events.data, pois.data), + () => combinePossibleSearchResults(categories.data, events.data, pois.data), [categories.data, events.data, pois.data], ) From d111ff7649e5c0cd0e665e4a8066685a443ccfb7 Mon Sep 17 00:00:00 2001 From: LeandraH Date: Wed, 27 Nov 2024 18:46:17 +0100 Subject: [PATCH 4/7] 2926: Even more refactoring --- native/src/routes/SearchModalContainer.tsx | 8 +++----- shared/hooks/useSearch.ts | 14 +++++++------- web/src/hooks/useAllPossibleSearchResults.ts | 9 ++------- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/native/src/routes/SearchModalContainer.tsx b/native/src/routes/SearchModalContainer.tsx index 3504c2be1f..1d175d20aa 100644 --- a/native/src/routes/SearchModalContainer.tsx +++ b/native/src/routes/SearchModalContainer.tsx @@ -1,7 +1,8 @@ import React, { ReactElement, useMemo } from 'react' import { SearchRouteType } from 'shared' -import { combinePossibleSearchResults } from 'shared/hooks/useSearch' +import { useFormatPossibleSearchResults } from 'shared/hooks/useSearch' +import { config } from 'translations' import { NavigationProps, RouteProps } from '../constants/NavigationTypes' import useCityAppContext from '../hooks/useCityAppContext' @@ -19,10 +20,7 @@ const SearchModalContainer = ({ navigation, route }: SearchModalContainerProps): const initialSearchText = route.params.searchText ?? '' const { data, ...response } = useLoadCityContent({ cityCode, languageCode }) - const allPossibleResults = useMemo( - () => combinePossibleSearchResults(data?.categories, data?.events, data?.pois), - [data?.categories, data?.events, data?.pois], - ) + const allPossibleResults = useFormatPossibleSearchResults(data?.categories, data?.events, data?.pois) return ( diff --git a/shared/hooks/useSearch.ts b/shared/hooks/useSearch.ts index c535a2b572..bbfde2bcff 100644 --- a/shared/hooks/useSearch.ts +++ b/shared/hooks/useSearch.ts @@ -1,5 +1,5 @@ import MiniSearch from 'minisearch' -import { useCallback } from 'react' +import { useCallback, useMemo } from 'react' import useLoadAsync from '../api/endpoints/hooks/useLoadAsync' import CategoriesMapModel from '../api/models/CategoriesMapModel' @@ -9,15 +9,15 @@ import PoiModel from '../api/models/PoiModel' export type SearchResult = ExtendedPageModel -export const combinePossibleSearchResults = ( +export const useFormatPossibleSearchResults = ( categories?: CategoriesMapModel | null, events?: EventModel[] | null, pois?: PoiModel[] | null, -): SearchResult[] => [ - ...(categories?.toArray().filter(category => !category.isRoot()) || []), - ...(events || []), - ...(pois || []), -] +): SearchResult[] => + useMemo( + () => [...(categories?.toArray().filter(category => !category.isRoot()) || []), ...(events || []), ...(pois || [])], + [categories, events, pois], + ) const useSearch = (allPossibleResults: SearchResult[], query: string): SearchResult[] | null => { const initializeMiniSearch = useCallback(async () => { diff --git a/web/src/hooks/useAllPossibleSearchResults.ts b/web/src/hooks/useAllPossibleSearchResults.ts index 45f98606bf..b895069418 100644 --- a/web/src/hooks/useAllPossibleSearchResults.ts +++ b/web/src/hooks/useAllPossibleSearchResults.ts @@ -1,5 +1,3 @@ -import { useMemo } from 'react' - import { createCategoriesEndpoint, createEventsEndpoint, @@ -7,7 +5,7 @@ import { ExtendedPageModel, useLoadFromEndpoint, } from 'shared/api' -import { combinePossibleSearchResults } from 'shared/hooks/useSearch' +import { useFormatPossibleSearchResults } from 'shared/hooks/useSearch' type UseAllPossibleSearchResultsProps = { city: string @@ -32,10 +30,7 @@ const useAllPossibleSearchResults = ({ const events = useLoadFromEndpoint(createEventsEndpoint, cmsApiBaseUrl, params) const pois = useLoadFromEndpoint(createPOIsEndpoint, cmsApiBaseUrl, params) - const allPossibleResults = useMemo( - () => combinePossibleSearchResults(categories.data, events.data, pois.data), - [categories.data, events.data, pois.data], - ) + const allPossibleResults = useFormatPossibleSearchResults(categories.data, events.data, pois.data) return { data: allPossibleResults, From eb6cf41d7981cf54926837c9a08ebd5a32539aca Mon Sep 17 00:00:00 2001 From: LeandraH Date: Wed, 27 Nov 2024 19:01:36 +0100 Subject: [PATCH 5/7] 2926: Add a fallback language in native --- native/src/routes/SearchModal.tsx | 7 ++++++- native/src/routes/SearchModalContainer.tsx | 10 +++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/native/src/routes/SearchModal.tsx b/native/src/routes/SearchModal.tsx index 9e7da908ea..c723e609a9 100644 --- a/native/src/routes/SearchModal.tsx +++ b/native/src/routes/SearchModal.tsx @@ -29,6 +29,7 @@ const SearchCounter = styled.Text` export type SearchModalProps = { allPossibleResults: SearchResult[] + allPossibleFallbackResults: SearchResult[] languageCode: string cityCode: string closeModal: (query: string) => void @@ -37,6 +38,7 @@ export type SearchModalProps = { const SearchModal = ({ allPossibleResults, + allPossibleFallbackResults, languageCode, cityCode, closeModal, @@ -46,7 +48,10 @@ const SearchModal = ({ const resourceCache = useResourceCache({ cityCode, languageCode }) const { t } = useTranslation('search') - const searchResults = useSearch(allPossibleResults, query) + const mainResults = useSearch(allPossibleResults, query) + const fallbackResults = useSearch(allPossibleFallbackResults, query) + + const searchResults = mainResults?.length === 0 ? fallbackResults : mainResults if (!searchResults) { return null diff --git a/native/src/routes/SearchModalContainer.tsx b/native/src/routes/SearchModalContainer.tsx index 1d175d20aa..8e4ae409dc 100644 --- a/native/src/routes/SearchModalContainer.tsx +++ b/native/src/routes/SearchModalContainer.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement, useMemo } from 'react' +import React, { ReactElement } from 'react' import { SearchRouteType } from 'shared' import { useFormatPossibleSearchResults } from 'shared/hooks/useSearch' @@ -19,9 +19,16 @@ const SearchModalContainer = ({ navigation, route }: SearchModalContainerProps): const { cityCode, languageCode } = useCityAppContext() const initialSearchText = route.params.searchText ?? '' const { data, ...response } = useLoadCityContent({ cityCode, languageCode }) + const { data: fallbackData } = useLoadCityContent({ cityCode, languageCode: config.sourceLanguage }) const allPossibleResults = useFormatPossibleSearchResults(data?.categories, data?.events, data?.pois) + const allPossibleFallbackResults = useFormatPossibleSearchResults( + fallbackData?.categories, + fallbackData?.events, + fallbackData?.pois, + ) + return ( {data && ( @@ -29,6 +36,7 @@ const SearchModalContainer = ({ navigation, route }: SearchModalContainerProps): cityCode={cityCode} closeModal={navigation.goBack} allPossibleResults={allPossibleResults} + allPossibleFallbackResults={allPossibleFallbackResults} languageCode={languageCode} initialSearchText={initialSearchText} /> From 0245b3f506892c7447cd9d481f3378c4d2b20698 Mon Sep 17 00:00:00 2001 From: LeandraH Date: Mon, 2 Dec 2024 19:15:44 +0100 Subject: [PATCH 6/7] 2926: Fix tests --- native/src/routes/SearchModalContainer.tsx | 15 +++++++++++---- .../src/routes/__tests__/SearchModal.spec.tsx | 1 + shared/hooks/useSearch.ts | 17 ++++++++++------- .../useAllPossibleSearchResults.spec.ts | 3 --- web/src/hooks/useAllPossibleSearchResults.ts | 9 +++++++-- 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/native/src/routes/SearchModalContainer.tsx b/native/src/routes/SearchModalContainer.tsx index 8e4ae409dc..7f1425aa73 100644 --- a/native/src/routes/SearchModalContainer.tsx +++ b/native/src/routes/SearchModalContainer.tsx @@ -1,7 +1,8 @@ -import React, { ReactElement } from 'react' +import React, { ReactElement, useMemo } from 'react' import { SearchRouteType } from 'shared' -import { useFormatPossibleSearchResults } from 'shared/hooks/useSearch' +import { CategoriesMapModel, EventModel, PoiModel } from 'shared/api' +import { formatPossibleSearchResults } from 'shared/hooks/useSearch' import { config } from 'translations' import { NavigationProps, RouteProps } from '../constants/NavigationTypes' @@ -15,15 +16,21 @@ export type SearchModalContainerProps = { route: RouteProps } +const useMemoizeResults = ( + categories?: CategoriesMapModel | null, + events?: EventModel[] | null, + pois?: PoiModel[] | null, +) => useMemo(() => formatPossibleSearchResults(categories, events, pois), [categories, events, pois]) + const SearchModalContainer = ({ navigation, route }: SearchModalContainerProps): ReactElement | null => { const { cityCode, languageCode } = useCityAppContext() const initialSearchText = route.params.searchText ?? '' const { data, ...response } = useLoadCityContent({ cityCode, languageCode }) const { data: fallbackData } = useLoadCityContent({ cityCode, languageCode: config.sourceLanguage }) - const allPossibleResults = useFormatPossibleSearchResults(data?.categories, data?.events, data?.pois) + const allPossibleResults = useMemoizeResults(data?.categories, data?.events, data?.pois) - const allPossibleFallbackResults = useFormatPossibleSearchResults( + const allPossibleFallbackResults = useMemoizeResults( fallbackData?.categories, fallbackData?.events, fallbackData?.pois, diff --git a/native/src/routes/__tests__/SearchModal.spec.tsx b/native/src/routes/__tests__/SearchModal.spec.tsx index 50385309ee..d8023d8f99 100644 --- a/native/src/routes/__tests__/SearchModal.spec.tsx +++ b/native/src/routes/__tests__/SearchModal.spec.tsx @@ -55,6 +55,7 @@ describe('SearchModal', () => { const props: SearchModalProps = { allPossibleResults, + allPossibleFallbackResults: [], languageCode, cityCode, closeModal: dummy, diff --git a/shared/hooks/useSearch.ts b/shared/hooks/useSearch.ts index bbfde2bcff..8a0e523b81 100644 --- a/shared/hooks/useSearch.ts +++ b/shared/hooks/useSearch.ts @@ -1,5 +1,5 @@ import MiniSearch from 'minisearch' -import { useCallback, useMemo } from 'react' +import { useCallback } from 'react' import useLoadAsync from '../api/endpoints/hooks/useLoadAsync' import CategoriesMapModel from '../api/models/CategoriesMapModel' @@ -9,15 +9,18 @@ import PoiModel from '../api/models/PoiModel' export type SearchResult = ExtendedPageModel -export const useFormatPossibleSearchResults = ( +// TODO: turn it back into a memoized function +// TODO: mock it in the web test +// TODO: maybe test this in shared +export const formatPossibleSearchResults = ( categories?: CategoriesMapModel | null, events?: EventModel[] | null, pois?: PoiModel[] | null, -): SearchResult[] => - useMemo( - () => [...(categories?.toArray().filter(category => !category.isRoot()) || []), ...(events || []), ...(pois || [])], - [categories, events, pois], - ) +): SearchResult[] => [ + ...(categories?.toArray().filter(category => !category.isRoot()) || []), + ...(events || []), + ...(pois || []), +] const useSearch = (allPossibleResults: SearchResult[], query: string): SearchResult[] | null => { const initializeMiniSearch = useCallback(async () => { diff --git a/web/src/hooks/__tests__/useAllPossibleSearchResults.spec.ts b/web/src/hooks/__tests__/useAllPossibleSearchResults.spec.ts index cfd38b34c1..e6a0af5333 100644 --- a/web/src/hooks/__tests__/useAllPossibleSearchResults.spec.ts +++ b/web/src/hooks/__tests__/useAllPossibleSearchResults.spec.ts @@ -27,11 +27,8 @@ describe('useAllPossibleSearchResults', () => { const city = new CityModelBuilder(1).build()[0]!.code const language = new LanguageModelBuilder(1).build()[0]!.code - const categories = new CategoriesMapModelBuilder(city, language).build() - const events = new EventModelBuilder('seed', 2, city, language).build() - const locations = new PoiModelBuilder(3).build() it('should return the correct results', () => { diff --git a/web/src/hooks/useAllPossibleSearchResults.ts b/web/src/hooks/useAllPossibleSearchResults.ts index b895069418..d08759802b 100644 --- a/web/src/hooks/useAllPossibleSearchResults.ts +++ b/web/src/hooks/useAllPossibleSearchResults.ts @@ -1,3 +1,5 @@ +import { useMemo } from 'react' + import { createCategoriesEndpoint, createEventsEndpoint, @@ -5,7 +7,7 @@ import { ExtendedPageModel, useLoadFromEndpoint, } from 'shared/api' -import { useFormatPossibleSearchResults } from 'shared/hooks/useSearch' +import { formatPossibleSearchResults } from 'shared/hooks/useSearch' type UseAllPossibleSearchResultsProps = { city: string @@ -30,7 +32,10 @@ const useAllPossibleSearchResults = ({ const events = useLoadFromEndpoint(createEventsEndpoint, cmsApiBaseUrl, params) const pois = useLoadFromEndpoint(createPOIsEndpoint, cmsApiBaseUrl, params) - const allPossibleResults = useFormatPossibleSearchResults(categories.data, events.data, pois.data) + const allPossibleResults = useMemo( + () => formatPossibleSearchResults(categories.data, events.data, pois.data), + [categories.data, events.data, pois.data], + ) return { data: allPossibleResults, From 0696cb9478307cfc15203d9bab5a6361b970ea32 Mon Sep 17 00:00:00 2001 From: LeandraH Date: Wed, 11 Dec 2024 11:43:36 +0100 Subject: [PATCH 7/7] 2926: Cleaning up --- shared/hooks/useSearch.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/shared/hooks/useSearch.ts b/shared/hooks/useSearch.ts index 8a0e523b81..f0c487508f 100644 --- a/shared/hooks/useSearch.ts +++ b/shared/hooks/useSearch.ts @@ -9,9 +9,6 @@ import PoiModel from '../api/models/PoiModel' export type SearchResult = ExtendedPageModel -// TODO: turn it back into a memoized function -// TODO: mock it in the web test -// TODO: maybe test this in shared export const formatPossibleSearchResults = ( categories?: CategoriesMapModel | null, events?: EventModel[] | null,