Open walter-ayala opened 8 months ago
Could you share your TypesenseInstantSearchAdapter initialization code?
Could you share your TypesenseInstantSearchAdapter initialization code?
That's my custom hook:
import Config from 'react-native-config';
import TypesenseInstantSearchAdapter from 'typesense-instantsearch-adapter';
export const useSearchVehicle = () => {
const typesenseInstantsearchAdapter = useMemo(() => {
return new TypesenseInstantSearchAdapter({
server: {
nodes: [
{
host: Config.TYPESENSE_HOST, // For Typesense Cloud use xxx.a1.typesense.net
port: 8108, // For Typesense Cloud use 443
protocol: 'http', // For Typesense Cloud use https
},
],
apiKey: Config.TYPESENSE_API_KEY,
connectionTimeoutSeconds: 500,
},
// The following parameters are directly passed to Typesense's search API endpoint.
// So you can pass any parameters supported by the search endpoint below.
// query_by is required.
additionalSearchParameters: {
sort_by: 'typeRecord:asc',
query_by: 'brandName,modelName',
filter_by: 'typeRecord:=MODEL',
},
});
}, [Config])
const searchClient = typesenseInstantsearchAdapter.searchClient;
return {searchClient}
};
And that is my component to search and show the hits:
/* eslint-disable react/no-unstable-nested-components */
import React, {useEffect, useRef, useState} from 'react';
import {
FlatList,
Image,
Keyboard,
TouchableOpacity,
View,
} from 'react-native';
import {Portal} from 'react-native-portalize';
import {Modalize} from 'react-native-modalize';
import Text from "../../../common/components/Text/Text";
import Input from '../../../common/components/Inputs/Input';
import {t} from 'i18next';
import {IModelCarModal} from '../../../vehicles/models/model';
import {IconSearch} from '../../../common/components/Icons/IconSearch';
import {height, resize} from '../../../common/styles/helpers';
import {COLORS} from '../../../common/styles/constants/colors';
import emptyResult from '../../assets/EmptyResult.png';
import style from '../styles';
import {IconRightArrow} from '../../../common/components/Icons/IconRigthArrow';
import useMixpanel from '../../../common/hooks/useMixpanel';
import useMixpanel from '../../../common/hooks/useSearchVehicle';
import {
InstantSearch,
useInfiniteHits,
useSearchBox,
} from 'react-instantsearch-core';
import Config from 'react-native-config';
const SearchVehicleModal: React.FC<IModelCarModal> = props => {
const modalizeRef = useRef<Modalize>(null);
const currentStyle = style();
const [keyboardOffset, setKeyboardOffset] = useState(0);
const [timer, setTimer] = useState<NodeJS.Timeout | null>(null);
const {searchClient}=useSearchVehicle();
const onOpen = () => {
modalizeRef.current?.open();
};
useEffect(() => {
setTimeout(() => {
if (modalizeRef.current) {
onOpen();
}
}, 200);
}, [modalizeRef.current]);
useEffect(() => {
return () => {
if (timer) {
clearTimeout(timer);
}
};
}, [timer]);
const {track} = useMixpanel();
useEffect(() => {
const showSubscription = Keyboard.addListener(
'keyboardDidShow',
onKeyboardShow,
);
const hideSubscription = Keyboard.addListener(
'keyboardDidHide',
onKeyboardHide,
);
return () => {
showSubscription.remove();
hideSubscription.remove();
};
}, []);
const onKeyboardShow = event => {
if (props.carModel.trim() == '') {
setKeyboardOffset(event.endCoordinates.height);
}
};
const onKeyboardHide = () => {
setKeyboardOffset(0);
};
const handleInputChange = (model: string) => {
props.onChange(model);
if (timer) {
clearTimeout(timer);
}
const newTimer = setTimeout(() => {
track('Input Value Entered', {value: model});
}, 2000);
setTimer(newTimer);
};
return (
<Portal>
<InstantSearch
searchClient={searchClient}
future={{ preserveSharedStateOnUnmount: false }}
indexName={Config.TYPESENSE_INDEX_NAME}>
<Modalize
ref={modalizeRef}
// panGestureEnabled={props.carModel.trim() !== ''}
// alwaysOpen={125}
panGestureEnabled={false}
adjustToContentHeight
withOverlay={false}
// alwaysOpen={props.carModel.trim() !== '' ? height / 1.1 : 125}
handlePosition="inside"
closeOnOverlayTap={false}
modalStyle={[
{
borderWidth: resize(1),
borderTopStartRadius: resize(24),
borderTopEndRadius: resize(24),
borderColor: COLORS.GREY,
},
// Platform.OS === 'ios' && {
// bottom: props.carModel.trim() !== '' ? keyboardOffset : undefined,
// },
]}
FooterComponent={() => {
if (props.carModel.trim() === '') {
return null;
}
return (
<TouchableOpacity
onPress={props.onAddManually}
style={currentStyle.footerModalContainer}>
<Text style={currentStyle.footerButtonModal}>
{t('add_car:model_car_modal:add_manually')}
</Text>
</TouchableOpacity>
);
}}
HeaderComponent={<SearchBox handleInput={handleInputChange} />}
>
{props.carModel.trim() !== '' ? (
<InfiniteHits
onPress={props.onPress}
isKeyboardShow={keyboardOffset !== 0}
/>
) : null}
</Modalize>
</InstantSearch>
</Portal>
);
};
function InfiniteHits({...props}) {
const {hits, isLastPage, showMore} = useInfiniteHits();
const currentStyle = style();
return (
<FlatList
data={hits}
style={{height: props.isKeyboardShow ? height / 4 : height / 1.6}}
keyExtractor={item => item.objectID}
onEndReached={() => {
if (!isLastPage) {
showMore();
}
}}
ListHeaderComponent={
<>
{hits.length !== 0 && (
<Text style={currentStyle.subtitleModal}>
{t('add_car:model_car_modal:results')}
</Text>
)}
</>
}
ListEmptyComponent={
<View style={currentStyle.emptyModalContainer}>
<>
<Image source={emptyResult} style={currentStyle.emptyImageModal} />
<Text style={currentStyle.withoutResultsText}>Sin resultados</Text>
<Text style={currentStyle.tryAgainText}>
{t('add_car:model_car_modal:try_search')}
</Text>
</>
</View>
}
renderItem={({item}) => (
<TouchableOpacity
style={currentStyle.itemModalContainer}
key={item.objectID}
activeOpacity={0.6}
onPress={() =>
props.onPress({
brandID: item.brandID,
brandName: item.brandName,
modelName: item.modelName,
objectID: item.objectID,
availableYears: item.availableYears,
})
}>
<Text style={currentStyle.modelCarItem}>
{`${item.brandName} ${item.modelName}`}
</Text>
<IconRightArrow color="#000" size={0} />
</TouchableOpacity>
)}
/>
);
}
function SearchBox(props) {
const {query, refine} = useSearchBox();
const currentStyle = style();
const [inputValue, setInputValue] = useState(query);
const inputRef = useRef(null);
useEffect(() => {
const loadingTimeout = setTimeout(() => {
props.handleInput(inputValue);
refine(inputValue);
}, 500);
return () => {
if (loadingTimeout) {
clearTimeout(loadingTimeout);
}
};
}, [inputValue]);
function setQuery(newQuery: string) {
setInputValue(newQuery);
// props.handleInput(newQuery);
// refine(newQuery);
}
// Track when the InstantSearch query changes to synchronize it with
// the React state.
// We bypass the state update if the input is focused to avoid concurrent
// updates when typing.
// if (query !== inputValue && !inputRef.current?.isFocused()) {
// setInputValue(query);
// }
return (
<View style={[currentStyle.headerModalContainer]}>
<Input
rightIcon={
<View>
<IconSearch />
</View>
}
refs={inputRef}
title={'Busca tu vehículo'}
value={inputValue}
onChangeText={setQuery}
placeholder={'Buscar'}
clearButtonMode="while-editing"
autoCapitalize="none"
autoCorrect={false}
spellCheck={false}
/>
</View>
);
}
export default SearchVehicleModal;
In iOS everything works, the problem is in android
Description
I'm using TypesenseInstantSearchAdapter from 'typesense-instantsearch-adapter' and InstantSearch, useInfiniteHits, useSearchBox from 'react-instantsearch-core' in my react native app, when i run the app for ios that part works, but in android don't working
Information about it
That is my dependencies in my package.json