diff --git a/native/src/routes/SearchModal.tsx b/native/src/routes/SearchModal.tsx index 9e7da908ea..99c4fa7a05 100644 --- a/native/src/routes/SearchModal.tsx +++ b/native/src/routes/SearchModal.tsx @@ -4,9 +4,11 @@ import { KeyboardAvoidingView, Platform } from 'react-native' import styled from 'styled-components/native' import { parseHTML, SEARCH_FINISHED_SIGNAL_NAME, SEARCH_ROUTE, SearchResult, useSearch } from 'shared' +import { config } from 'translations' import FeedbackContainer from '../components/FeedbackContainer' import List from '../components/List' +import Loader from '../components/LoadingSpinner' import SearchHeader from '../components/SearchHeader' import SearchListItem from '../components/SearchListItem' import useResourceCache from '../hooks/useResourceCache' @@ -28,7 +30,8 @@ const SearchCounter = styled.Text` ` export type SearchModalProps = { - allPossibleResults: SearchResult[] + allPossibleContentLanguageResults: SearchResult[] + allPossibleFallbackLanguageResults: SearchResult[] languageCode: string cityCode: string closeModal: (query: string) => void @@ -36,7 +39,8 @@ export type SearchModalProps = { } const SearchModal = ({ - allPossibleResults, + allPossibleContentLanguageResults, + allPossibleFallbackLanguageResults, languageCode, cityCode, closeModal, @@ -46,11 +50,13 @@ const SearchModal = ({ const resourceCache = useResourceCache({ cityCode, languageCode }) const { t } = useTranslation('search') - const searchResults = useSearch(allPossibleResults, query) + const contentLanguageResults = useSearch(allPossibleContentLanguageResults, query) + const fallbackLanguageResults = useSearch(allPossibleFallbackLanguageResults, query) - if (!searchResults) { - return null - } + const searchResults = + languageCode === config.sourceLanguage + ? contentLanguageResults + : contentLanguageResults?.concat(fallbackLanguageResults ?? []) const onClose = (): void => { sendTrackingSignal({ @@ -63,6 +69,15 @@ const SearchModal = ({ closeModal(query) } + if (!searchResults) { + return ( + + + {!!query.length && } + + ) + } + const renderItem = ({ item }: { item: SearchResult }) => ( [ - ...(data?.categories.toArray().filter(category => !category.isRoot()) || []), - ...(data?.events || []), - ...(data?.pois || []), - ], - [data?.categories, data?.events, data?.pois], + const allPossibleContentLanguageResults = formatPossibleSearchResults(data?.categories, data?.events, data?.pois) + + const allPossibleFallbackLanguageResults = formatPossibleSearchResults( + fallbackData?.categories, + fallbackData?.events, + fallbackData?.pois, ) return ( @@ -33,7 +35,8 @@ const SearchModalContainer = ({ navigation, route }: SearchModalContainerProps): diff --git a/native/src/routes/__tests__/SearchModal.spec.tsx b/native/src/routes/__tests__/SearchModal.spec.tsx index 50385309ee..c48a319772 100644 --- a/native/src/routes/__tests__/SearchModal.spec.tsx +++ b/native/src/routes/__tests__/SearchModal.spec.tsx @@ -47,14 +47,15 @@ describe('SearchModal', () => { const eventModels = new EventModelBuilder('testseed', 5, cityCode, languageCode).build() const poiModels = new PoiModelBuilder(3).build() - const allPossibleResults = [ + const allPossibleContentLanguageResults = [ ...categoriesMapModel.toArray().filter(category => !category.isRoot()), ...eventModels, ...poiModels, ] const props: SearchModalProps = { - allPossibleResults, + allPossibleContentLanguageResults, + allPossibleFallbackLanguageResults: [], languageCode, cityCode, closeModal: dummy, diff --git a/shared/hooks/useSearch.ts b/shared/hooks/useSearch.ts index 9299c5231c..f0c487508f 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 formatPossibleSearchResults = ( + 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/__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 be5a70dbe3..d08759802b 100644 --- a/web/src/hooks/useAllPossibleSearchResults.ts +++ b/web/src/hooks/useAllPossibleSearchResults.ts @@ -7,6 +7,7 @@ import { ExtendedPageModel, useLoadFromEndpoint, } from 'shared/api' +import { formatPossibleSearchResults } from 'shared/hooks/useSearch' type UseAllPossibleSearchResultsProps = { city: string @@ -27,19 +28,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], + () => formatPossibleSearchResults(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, } } diff --git a/web/src/routes/SearchPage.tsx b/web/src/routes/SearchPage.tsx index 64f35eb9fb..c472166156 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' @@ -37,7 +38,7 @@ const SearchPage = ({ city, cityCode, languageCode, pathname }: CityRouteProps): const navigate = useNavigate() const { - data: allPossibleResults, + data: allPossibleContentLanguageResults, loading, error, } = useAllPossibleSearchResults({ @@ -45,8 +46,20 @@ const SearchPage = ({ city, cityCode, languageCode, pathname }: CityRouteProps): language: languageCode, cmsApiBaseUrl, }) + const contentLanguageResults = useSearch(allPossibleContentLanguageResults, query) - const results = useSearch(allPossibleResults, query) + const fallbackLanguageCode = config.sourceLanguage + const { data: allPossibleFallbackLanguageResults } = useAllPossibleSearchResults({ + city: cityCode, + language: fallbackLanguageCode, + cmsApiBaseUrl, + }) + const fallbackLanguageResults = useSearch(allPossibleFallbackLanguageResults, query) + + const results = + languageCode === fallbackLanguageCode + ? contentLanguageResults + : contentLanguageResults?.concat(fallbackLanguageResults ?? []) if (!city) { return null