From 6927373b6076ccc3914d84c97f9ae20c3444e05a Mon Sep 17 00:00:00 2001 From: Ryan Skinner Date: Wed, 22 May 2024 12:18:17 -0400 Subject: [PATCH 1/5] add profiles to search history --- src/view/screens/Search/Search.tsx | 192 ++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 2 deletions(-) diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index 9dd1c397f3..cd531ec497 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -1,8 +1,11 @@ import React from 'react' import { ActivityIndicator, + Image, + ImageStyle, Platform, Pressable, + StyleProp, StyleSheet, TextInput, View, @@ -488,6 +491,9 @@ export function SearchScreen( const [showAutocomplete, setShowAutocomplete] = React.useState(false) const [searchHistory, setSearchHistory] = React.useState([]) + const [selectedProfiles, setSelectedProfiles] = React.useState< + AppBskyActorDefs.ProfileViewBasic[] + >([]) useFocusEffect( useNonReactiveCallback(() => { @@ -504,6 +510,10 @@ export function SearchScreen( if (history !== null) { setSearchHistory(JSON.parse(history)) } + const profiles = await AsyncStorage.getItem('selectedProfiles') + if (profiles !== null) { + setSelectedProfiles(JSON.parse(profiles)) + } } catch (e: any) { logger.error('Failed to load search history', {message: e}) } @@ -562,6 +572,30 @@ export function SearchScreen( [searchHistory, setSearchHistory], ) + const updateSelectedProfiles = React.useCallback( + async (profile: AppBskyActorDefs.ProfileViewBasic) => { + let newProfiles = [ + profile, + ...selectedProfiles.filter(p => p.did !== profile.did), + ] + + if (newProfiles.length > 5) { + newProfiles = newProfiles.slice(0, 5) + } + + setSelectedProfiles(newProfiles) + try { + await AsyncStorage.setItem( + 'selectedProfiles', + JSON.stringify(newProfiles), + ) + } catch (e: any) { + logger.error('Failed to save selected profiles', {message: e}) + } + }, + [selectedProfiles, setSelectedProfiles], + ) + const navigateToItem = React.useCallback( (item: string) => { scrollToTopWeb() @@ -578,6 +612,22 @@ export function SearchScreen( [updateSearchHistory, navigation], ) + const navigateToProfile = React.useCallback( + (profile: AppBskyActorDefs.ProfileViewBasic) => { + scrollToTopWeb() + setShowAutocomplete(false) + updateSelectedProfiles(profile) + + if (isWeb) { + navigation.push('Profile', {name: profile.handle}) + } else { + textInput.current?.blur() + navigation.navigate('Profile', {name: profile.handle}) + } + }, + [updateSelectedProfiles, navigation], + ) + const onSubmit = React.useCallback(() => { navigateToItem(searchText) }, [navigateToItem, searchText]) @@ -598,6 +648,13 @@ export function SearchScreen( [navigateToItem], ) + const handleProfileClick = React.useCallback( + (profile: AppBskyActorDefs.ProfileViewBasic) => { + navigateToProfile(profile) + }, + [navigateToProfile], + ) + const onSoftReset = React.useCallback(() => { if (isWeb) { // Empty params resets the URL to be /search rather than /search?q= @@ -629,6 +686,22 @@ export function SearchScreen( [searchHistory], ) + const handleRemoveProfile = React.useCallback( + (profileToRemove: AppBskyActorDefs.ProfileViewBasic) => { + const updatedProfiles = selectedProfiles.filter( + profile => profile.did !== profileToRemove.did, + ) + setSelectedProfiles(updatedProfiles) + AsyncStorage.setItem( + 'selectedProfiles', + JSON.stringify(updatedProfiles), + ).catch(e => { + logger.error('Failed to update selected profiles', {message: e}) + }) + }, + [selectedProfiles], + ) + return ( ) : ( )} @@ -814,12 +891,14 @@ let AutocompleteResults = ({ searchText, onSubmit, onResultPress, + onProfileClick, }: { isAutocompleteFetching: boolean autocompleteData: AppBskyActorDefs.ProfileViewBasic[] | undefined searchText: string onSubmit: () => void onResultPress: () => void + onProfileClick: (profile: AppBskyActorDefs.ProfileViewBasic) => void }): React.ReactNode => { const moderationOpts = useModerationOpts() const {_} = useLingui() @@ -850,7 +929,10 @@ let AutocompleteResults = ({ key={item.did} profile={item} moderation={moderateProfile(item, moderationOpts)} - onPress={onResultPress} + onPress={() => { + onProfileClick(item) + onResultPress() + }} /> ))} @@ -861,17 +943,31 @@ let AutocompleteResults = ({ } AutocompleteResults = React.memo(AutocompleteResults) +function truncateText(text: string, maxLength: number) { + if (text.length > maxLength) { + return text.substring(0, maxLength) + '...' + } + return text +} + function SearchHistory({ searchHistory, + selectedProfiles, onItemClick, + onProfileClick, onRemoveItemClick, + onRemoveProfileClick, }: { searchHistory: string[] + selectedProfiles: AppBskyActorDefs.ProfileViewBasic[] onItemClick: (item: string) => void + onProfileClick: (profile: AppBskyActorDefs.ProfileViewBasic) => void onRemoveItemClick: (item: string) => void + onRemoveProfileClick: (profile: AppBskyActorDefs.ProfileViewBasic) => void }) { const {isTabletOrDesktop} = useWebMediaQueries() const pal = usePalette('default') + return ( + {selectedProfiles.length > 0 && ( + + + Recent Profiles + + + {selectedProfiles.slice(0, 5).map((profile, index) => ( + + onProfileClick(profile)} + hitSlop={HITSLOP_10} + style={styles.profilePressable}> + } + accessibilityIgnoresInvertColors + /> + + {truncateText(profile.displayName || '', 12)} + + + @{truncateText(profile.handle, 12)} + + + onRemoveProfileClick(profile)} + hitSlop={HITSLOP_10} + style={styles.profileRemoveBtn}> + + + + ))} + + + )} {searchHistory.length > 0 && ( Recent Searches - {searchHistory.map((historyItem, index) => ( + {searchHistory.slice(0, 5).map((historyItem, index) => ( Date: Wed, 22 May 2024 12:34:04 -0400 Subject: [PATCH 2/5] increasing horizontal padding slightly --- src/view/screens/Search/Search.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index cd531ec497..0c092c4726 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -1134,7 +1134,7 @@ const styles = StyleSheet.create({ flexDirection: 'row', flexWrap: 'nowrap', paddingVertical: 20, - paddingHorizontal: 5, + paddingHorizontal: 10, }, profileItem: { alignItems: 'center', From b25e1d5ece11f846dc13f8cc7110a0a73108fbd2 Mon Sep 17 00:00:00 2001 From: Ryan Skinner Date: Wed, 22 May 2024 15:34:01 -0400 Subject: [PATCH 3/5] tightening up styling --- src/view/screens/Search/Search.tsx | 56 +++++++++++++++++++----------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index 0c092c4726..29c3241622 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -965,7 +965,7 @@ function SearchHistory({ onRemoveItemClick: (item: string) => void onRemoveProfileClick: (profile: AppBskyActorDefs.ProfileViewBasic) => void }) { - const {isTabletOrDesktop} = useWebMediaQueries() + const {isTabletOrDesktop, isMobile} = useWebMediaQueries() const pal = usePalette('default') return ( @@ -976,20 +976,30 @@ function SearchHistory({ height: isWeb ? '100vh' : undefined, }}> + {(searchHistory.length > 0 || selectedProfiles.length > 0) && ( + + Recent Searches + + )} {selectedProfiles.length > 0 && ( - - - Recent Profiles - + {selectedProfiles.slice(0, 5).map((profile, index) => ( - + onProfileClick(profile)} @@ -1025,9 +1035,6 @@ function SearchHistory({ )} {searchHistory.length > 0 && ( - - Recent Searches - {searchHistory.slice(0, 5).map((historyItem, index) => ( Date: Wed, 22 May 2024 16:38:10 -0400 Subject: [PATCH 4/5] fixing navigation issue --- src/view/screens/Search/Search.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index 29c3241622..f8948e4413 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -603,10 +603,10 @@ export function SearchScreen( updateSearchHistory(item) if (isWeb) { - navigation.push('Search', {q: item}) + navigation.replace('Search', {q: item}) } else { textInput.current?.blur() - navigation.setParams({q: item}) + navigation.replace('Search', {q: item}) } }, [updateSearchHistory, navigation], @@ -619,13 +619,19 @@ export function SearchScreen( updateSelectedProfiles(profile) if (isWeb) { - navigation.push('Profile', {name: profile.handle}) + navigation.reset({ + index: 1, + routes: [ + {name: 'Search', params: {q: searchText}}, + {name: 'Profile', params: {name: profile.handle}}, + ], + }) } else { textInput.current?.blur() navigation.navigate('Profile', {name: profile.handle}) } }, - [updateSelectedProfiles, navigation], + [updateSelectedProfiles, navigation, searchText], ) const onSubmit = React.useCallback(() => { From dadbde2460da745e186911faf30dc40e9eeb8bdb Mon Sep 17 00:00:00 2001 From: Ryan Skinner Date: Thu, 30 May 2024 15:32:11 -0400 Subject: [PATCH 5/5] making corrections --- src/view/screens/Search/Search.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index f8948e4413..8f9cc8e27c 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -603,10 +603,10 @@ export function SearchScreen( updateSearchHistory(item) if (isWeb) { - navigation.replace('Search', {q: item}) + navigation.push('Search', {q: item}) } else { textInput.current?.blur() - navigation.replace('Search', {q: item}) + navigation.setParams({q: item}) } }, [updateSearchHistory, navigation], @@ -1025,6 +1025,8 @@ function SearchHistory({ onRemoveProfileClick(profile)} hitSlop={HITSLOP_10} style={styles.profileRemoveBtn}>