diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-04-18 18:29:54 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-18 18:29:54 -0500 |
commit | 75fd653be3a391d0edc11a9b35ed636c7cbe3b11 (patch) | |
tree | 38fc788652cac037df991be73d87eba263dc683f /src/view/screens | |
parent | 1ab8f315179cc1c93f1319b0e6fb1f75aa1c5954 (diff) | |
download | voidsky-75fd653be3a391d0edc11a9b35ed636c7cbe3b11.tar.zst |
Rework search suggestions for performance (#492)
Diffstat (limited to 'src/view/screens')
-rw-r--r-- | src/view/screens/Search.tsx | 197 | ||||
-rw-r--r-- | src/view/screens/Search.web.tsx | 59 | ||||
-rw-r--r-- | src/view/screens/SearchMobile.tsx | 87 |
3 files changed, 40 insertions, 303 deletions
diff --git a/src/view/screens/Search.tsx b/src/view/screens/Search.tsx index ed9effd0b..bf9857df4 100644 --- a/src/view/screens/Search.tsx +++ b/src/view/screens/Search.tsx @@ -1,196 +1 @@ -import React from 'react' -import { - Keyboard, - RefreshControl, - StyleSheet, - TouchableWithoutFeedback, - View, -} from 'react-native' -import {useFocusEffect} from '@react-navigation/native' -import {withAuthRequired} from 'view/com/auth/withAuthRequired' -import {ScrollView} from 'view/com/util/Views' -import { - NativeStackScreenProps, - SearchTabNavigatorParams, -} from 'lib/routes/types' -import {observer} from 'mobx-react-lite' -import {Text} from 'view/com/util/text/Text' -import {useStores} from 'state/index' -import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete' -import {SearchUIModel} from 'state/models/ui/search' -import {FoafsModel} from 'state/models/discovery/foafs' -import {SuggestedActorsModel} from 'state/models/discovery/suggested-actors' -import {HeaderWithInput} from 'view/com/search/HeaderWithInput' -import {Suggestions} from 'view/com/search/Suggestions' -import {SearchResults} from 'view/com/search/SearchResults' -import {s} from 'lib/styles' -import {ProfileCard} from 'view/com/profile/ProfileCard' -import {usePalette} from 'lib/hooks/usePalette' -import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' - -type Props = NativeStackScreenProps<SearchTabNavigatorParams, 'Search'> -export const SearchScreen = withAuthRequired( - observer<Props>(({}: Props) => { - const pal = usePalette('default') - const store = useStores() - const scrollElRef = React.useRef<ScrollView>(null) - const onMainScroll = useOnMainScroll(store) - const [isInputFocused, setIsInputFocused] = React.useState<boolean>(false) - const [query, setQuery] = React.useState<string>('') - const autocompleteView = React.useMemo<UserAutocompleteModel>( - () => new UserAutocompleteModel(store), - [store], - ) - const foafs = React.useMemo<FoafsModel>( - () => new FoafsModel(store), - [store], - ) - const suggestedActors = React.useMemo<SuggestedActorsModel>( - () => new SuggestedActorsModel(store), - [store], - ) - const [searchUIModel, setSearchUIModel] = React.useState< - SearchUIModel | undefined - >() - const [refreshing, setRefreshing] = React.useState(false) - - const onSoftReset = () => { - scrollElRef.current?.scrollTo({x: 0, y: 0}) - } - - useFocusEffect( - React.useCallback(() => { - const softResetSub = store.onScreenSoftReset(onSoftReset) - const cleanup = () => { - softResetSub.remove() - } - - store.shell.setMinimalShellMode(false) - autocompleteView.setup() - if (!foafs.hasData) { - foafs.fetch() - } - if (!suggestedActors.hasLoaded) { - suggestedActors.loadMore(true) - } - - return cleanup - }, [store, autocompleteView, foafs, suggestedActors]), - ) - - const onChangeQuery = React.useCallback( - (text: string) => { - setQuery(text) - if (text.length > 0) { - autocompleteView.setActive(true) - autocompleteView.setPrefix(text) - } else { - autocompleteView.setActive(false) - } - }, - [setQuery, autocompleteView], - ) - - const onPressClearQuery = React.useCallback(() => { - setQuery('') - }, [setQuery]) - - const onPressCancelSearch = React.useCallback(() => { - setQuery('') - autocompleteView.setActive(false) - setSearchUIModel(undefined) - store.shell.setIsDrawerSwipeDisabled(false) - }, [setQuery, autocompleteView, store]) - - const onSubmitQuery = React.useCallback(() => { - const model = new SearchUIModel(store) - model.fetch(query) - setSearchUIModel(model) - store.shell.setIsDrawerSwipeDisabled(true) - }, [query, setSearchUIModel, store]) - - const onRefresh = React.useCallback(async () => { - setRefreshing(true) - try { - await foafs.fetch() - } finally { - setRefreshing(false) - } - }, [foafs, setRefreshing]) - - return ( - <TouchableWithoutFeedback onPress={Keyboard.dismiss}> - <View style={[pal.view, styles.container]}> - <HeaderWithInput - isInputFocused={isInputFocused} - query={query} - setIsInputFocused={setIsInputFocused} - onChangeQuery={onChangeQuery} - onPressClearQuery={onPressClearQuery} - onPressCancelSearch={onPressCancelSearch} - onSubmitQuery={onSubmitQuery} - /> - {searchUIModel ? ( - <SearchResults model={searchUIModel} /> - ) : ( - <ScrollView - ref={scrollElRef} - testID="searchScrollView" - style={pal.view} - onScroll={onMainScroll} - scrollEventThrottle={100} - refreshControl={ - <RefreshControl - refreshing={refreshing} - onRefresh={onRefresh} - tintColor={pal.colors.text} - titleColor={pal.colors.text} - /> - }> - {query && autocompleteView.searchRes.length ? ( - <> - {autocompleteView.searchRes.map(item => ( - <ProfileCard - key={item.did} - testID={`searchAutoCompleteResult-${item.handle}`} - handle={item.handle} - displayName={item.displayName} - labels={item.labels} - avatar={item.avatar} - /> - ))} - </> - ) : query && !autocompleteView.searchRes.length ? ( - <View> - <Text style={[pal.textLight, styles.searchPrompt]}> - No results found for {autocompleteView.prefix} - </Text> - </View> - ) : isInputFocused ? ( - <View> - <Text style={[pal.textLight, styles.searchPrompt]}> - Search for users on the network - </Text> - </View> - ) : ( - <Suggestions foafs={foafs} suggestedActors={suggestedActors} /> - )} - <View style={s.footerSpacer} /> - </ScrollView> - )} - </View> - </TouchableWithoutFeedback> - ) - }), -) - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - - searchPrompt: { - textAlign: 'center', - paddingTop: 10, - }, -}) +export * from './SearchMobile' diff --git a/src/view/screens/Search.web.tsx b/src/view/screens/Search.web.tsx index 92df1d920..62f3fb900 100644 --- a/src/view/screens/Search.web.tsx +++ b/src/view/screens/Search.web.tsx @@ -1,10 +1,8 @@ import React from 'react' -import {StyleSheet, View} from 'react-native' import {SearchUIModel} from 'state/models/ui/search' import {FoafsModel} from 'state/models/discovery/foafs' import {SuggestedActorsModel} from 'state/models/discovery/suggested-actors' import {withAuthRequired} from 'view/com/auth/withAuthRequired' -import {ScrollView} from 'view/com/util/Views' import {Suggestions} from 'view/com/search/Suggestions' import {SearchResults} from 'view/com/search/SearchResults' import {observer} from 'mobx-react-lite' @@ -13,15 +11,12 @@ import { SearchTabNavigatorParams, } from 'lib/routes/types' import {useStores} from 'state/index' -import {s} from 'lib/styles' -import {usePalette} from 'lib/hooks/usePalette' import * as Mobile from './SearchMobile' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' type Props = NativeStackScreenProps<SearchTabNavigatorParams, 'Search'> export const SearchScreen = withAuthRequired( observer(({navigation, route}: Props) => { - const pal = usePalette('default') const store = useStores() const params = route.params || {} const foafs = React.useMemo<FoafsModel>( @@ -59,58 +54,6 @@ export const SearchScreen = withAuthRequired( return <Mobile.SearchScreen navigation={navigation} route={route} /> } - return ( - <ScrollView - testID="searchScrollView" - style={[pal.view, styles.container]} - scrollEventThrottle={100}> - <Suggestions foafs={foafs} suggestedActors={suggestedActors} /> - <View style={s.footerSpacer} /> - </ScrollView> - ) + return <Suggestions foafs={foafs} suggestedActors={suggestedActors} /> }), ) - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - - header: { - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: 12, - paddingTop: 4, - marginBottom: 14, - }, - headerMenuBtn: { - width: 40, - height: 30, - marginLeft: 6, - }, - headerSearchContainer: { - flex: 1, - flexDirection: 'row', - alignItems: 'center', - borderRadius: 30, - paddingHorizontal: 12, - paddingVertical: 8, - }, - headerSearchIcon: { - marginRight: 6, - alignSelf: 'center', - }, - headerSearchInput: { - flex: 1, - fontSize: 17, - }, - headerCancelBtn: { - width: 60, - paddingLeft: 10, - }, - - searchPrompt: { - textAlign: 'center', - paddingTop: 10, - }, -}) diff --git a/src/view/screens/SearchMobile.tsx b/src/view/screens/SearchMobile.tsx index e1fb3ec0a..679fb07c2 100644 --- a/src/view/screens/SearchMobile.tsx +++ b/src/view/screens/SearchMobile.tsx @@ -1,14 +1,13 @@ import React from 'react' import { Keyboard, - RefreshControl, StyleSheet, TouchableWithoutFeedback, View, } from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {withAuthRequired} from 'view/com/auth/withAuthRequired' -import {ScrollView} from 'view/com/util/Views' +import {FlatList, ScrollView} from 'view/com/util/Views' import { NativeStackScreenProps, SearchTabNavigatorParams, @@ -33,7 +32,8 @@ export const SearchScreen = withAuthRequired( observer<Props>(({}: Props) => { const pal = usePalette('default') const store = useStores() - const scrollElRef = React.useRef<ScrollView>(null) + const scrollViewRef = React.useRef<ScrollView>(null) + const flatListRef = React.useRef<FlatList>(null) const onMainScroll = useOnMainScroll(store) const [isInputFocused, setIsInputFocused] = React.useState<boolean>(false) const [query, setQuery] = React.useState<string>('') @@ -52,31 +52,6 @@ export const SearchScreen = withAuthRequired( const [searchUIModel, setSearchUIModel] = React.useState< SearchUIModel | undefined >() - const [refreshing, setRefreshing] = React.useState(false) - - const onSoftReset = () => { - scrollElRef.current?.scrollTo({x: 0, y: 0}) - } - - useFocusEffect( - React.useCallback(() => { - const softResetSub = store.onScreenSoftReset(onSoftReset) - const cleanup = () => { - softResetSub.remove() - } - - store.shell.setMinimalShellMode(false) - autocompleteView.setup() - if (!foafs.hasData) { - foafs.fetch() - } - if (!suggestedActors.hasLoaded) { - suggestedActors.loadMore(true) - } - - return cleanup - }, [store, autocompleteView, foafs, suggestedActors]), - ) const onChangeQuery = React.useCallback( (text: string) => { @@ -109,14 +84,31 @@ export const SearchScreen = withAuthRequired( store.shell.setIsDrawerSwipeDisabled(true) }, [query, setSearchUIModel, store]) - const onRefresh = React.useCallback(async () => { - setRefreshing(true) - try { - await foafs.fetch() - } finally { - setRefreshing(false) - } - }, [foafs, setRefreshing]) + const onSoftReset = React.useCallback(() => { + scrollViewRef.current?.scrollTo({x: 0, y: 0}) + flatListRef.current?.scrollToOffset({offset: 0}) + onPressCancelSearch() + }, [scrollViewRef, flatListRef, onPressCancelSearch]) + + useFocusEffect( + React.useCallback(() => { + const softResetSub = store.onScreenSoftReset(onSoftReset) + const cleanup = () => { + softResetSub.remove() + } + + store.shell.setMinimalShellMode(false) + autocompleteView.setup() + if (!foafs.hasData) { + foafs.fetch() + } + if (!suggestedActors.hasLoaded) { + suggestedActors.loadMore(true) + } + + return cleanup + }, [store, autocompleteView, foafs, suggestedActors, onSoftReset]), + ) return ( <TouchableWithoutFeedback onPress={Keyboard.dismiss}> @@ -132,21 +124,19 @@ export const SearchScreen = withAuthRequired( /> {searchUIModel ? ( <SearchResults model={searchUIModel} /> + ) : !isInputFocused && !query ? ( + <Suggestions + ref={flatListRef} + foafs={foafs} + suggestedActors={suggestedActors} + /> ) : ( <ScrollView - ref={scrollElRef} + ref={scrollViewRef} testID="searchScrollView" style={pal.view} onScroll={onMainScroll} - scrollEventThrottle={100} - refreshControl={ - <RefreshControl - refreshing={refreshing} - onRefresh={onRefresh} - tintColor={pal.colors.text} - titleColor={pal.colors.text} - /> - }> + scrollEventThrottle={100}> {query && autocompleteView.searchRes.length ? ( <> {autocompleteView.searchRes.map(item => ( @@ -155,6 +145,7 @@ export const SearchScreen = withAuthRequired( testID={`searchAutoCompleteResult-${item.handle}`} handle={item.handle} displayName={item.displayName} + labels={item.labels} avatar={item.avatar} /> ))} @@ -171,9 +162,7 @@ export const SearchScreen = withAuthRequired( Search for users on the network </Text> </View> - ) : ( - <Suggestions foafs={foafs} suggestedActors={suggestedActors} /> - )} + ) : null} <View style={s.footerSpacer} /> </ScrollView> )} |