Compare commits

...

7 Commits

Author SHA1 Message Date
Paul Frazee deb0ed528e Make the search history profiles a little smaller 2024-05-31 09:32:52 -07:00
Paul Frazee d0e09091fc Merge branch 'feature/search-history-profiles' of https://github.com/skiniks/social-app into skiniks-feature/search-history-profiles 2024-05-31 08:44:37 -07:00
Ryan Skinner dadbde2460 making corrections 2024-05-30 15:32:11 -04:00
Ryan Skinner ac692c77f5 fixing navigation issue 2024-05-22 16:38:10 -04:00
Ryan Skinner b25e1d5ece tightening up styling 2024-05-22 15:34:01 -04:00
Ryan Skinner 0ee601a022 increasing horizontal padding slightly 2024-05-22 12:34:04 -04:00
Ryan Skinner 6927373b60 add profiles to search history 2024-05-22 12:18:17 -04:00
+208 -7
View File
@@ -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<string[]>([])
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,28 @@ export function SearchScreen(
[updateSearchHistory, navigation],
)
const navigateToProfile = React.useCallback(
(profile: AppBskyActorDefs.ProfileViewBasic) => {
scrollToTopWeb()
setShowAutocomplete(false)
updateSelectedProfiles(profile)
if (isWeb) {
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, searchText],
)
const onSubmit = React.useCallback(() => {
navigateToItem(searchText)
}, [navigateToItem, searchText])
@@ -598,6 +654,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 +692,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 (
<View style={isWeb ? null : {flex: 1}}>
<CenteredView
@@ -689,12 +768,16 @@ export function SearchScreen(
searchText={searchText}
onSubmit={onSubmit}
onResultPress={onAutocompleteResultPress}
onProfileClick={handleProfileClick}
/>
) : (
<SearchHistory
searchHistory={searchHistory}
selectedProfiles={selectedProfiles}
onItemClick={handleHistoryItemClick}
onProfileClick={handleProfileClick}
onRemoveItemClick={handleRemoveHistoryItem}
onRemoveProfileClick={handleRemoveProfile}
/>
)}
</View>
@@ -814,12 +897,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 +935,10 @@ let AutocompleteResults = ({
key={item.did}
profile={item}
moderation={moderateProfile(item, moderationOpts)}
onPress={onResultPress}
onPress={() => {
onProfileClick(item)
onResultPress()
}}
/>
))}
<View style={{height: 200}} />
@@ -861,17 +949,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 {isTabletOrDesktop, isMobile} = useWebMediaQueries()
const pal = usePalette('default')
return (
<CenteredView
sideBorders={isTabletOrDesktop}
@@ -880,12 +982,65 @@ function SearchHistory({
height: isWeb ? '100vh' : undefined,
}}>
<View style={styles.searchHistoryContainer}>
{(searchHistory.length > 0 || selectedProfiles.length > 0) && (
<Text style={[pal.text, styles.searchHistoryTitle]}>
<Trans>Recent Searches</Trans>
</Text>
)}
{selectedProfiles.length > 0 && (
<View
style={[
styles.selectedProfilesContainer,
isMobile && styles.selectedProfilesContainerMobile,
]}>
<ScrollView
horizontal={true}
style={styles.profilesRow}
contentContainerStyle={{
borderWidth: 0,
}}>
{selectedProfiles.slice(0, 5).map((profile, index) => (
<View
key={index}
style={[
styles.profileItem,
isMobile && styles.profileItemMobile,
]}>
<Pressable
accessibilityRole="button"
onPress={() => onProfileClick(profile)}
hitSlop={HITSLOP_10}
style={styles.profilePressable}>
<Image
source={{uri: profile.avatar}}
style={styles.profileAvatar as StyleProp<ImageStyle>}
accessibilityIgnoresInvertColors
/>
<Text style={[pal.text, styles.profileName]}>
{truncateText(profile.displayName || '', 12)}
</Text>
</Pressable>
<Pressable
accessibilityRole="button"
accessibilityLabel="Remove profile"
accessibilityHint="Remove profile from search history"
onPress={() => onRemoveProfileClick(profile)}
hitSlop={HITSLOP_10}
style={styles.profileRemoveBtn}>
<FontAwesomeIcon
icon="xmark"
size={14}
style={pal.textLight as FontAwesomeIconStyle}
/>
</Pressable>
</View>
))}
</ScrollView>
</View>
)}
{searchHistory.length > 0 && (
<View style={styles.searchHistoryContent}>
<Text style={[pal.text, styles.searchHistoryTitle]}>
<Trans>Recent Searches</Trans>
</Text>
{searchHistory.map((historyItem, index) => (
{searchHistory.slice(0, 5).map((historyItem, index) => (
<View
key={index}
style={[
@@ -982,11 +1137,57 @@ const styles = StyleSheet.create({
width: '100%',
paddingHorizontal: 12,
},
selectedProfilesContainer: {
marginTop: 10,
paddingHorizontal: 12,
height: 80,
},
selectedProfilesContainerMobile: {
height: 100,
},
profilesRow: {
flexDirection: 'row',
flexWrap: 'nowrap',
},
profileItem: {
alignItems: 'center',
marginRight: 15,
width: 78,
},
profileItemMobile: {
width: 70,
},
profilePressable: {
alignItems: 'center',
},
profileAvatar: {
width: 60,
height: 60,
borderRadius: 45,
},
profileName: {
fontSize: 12,
textAlign: 'center',
marginTop: 5,
},
profileRemoveBtn: {
position: 'absolute',
top: 0,
right: 5,
backgroundColor: 'white',
borderRadius: 10,
width: 18,
height: 18,
alignItems: 'center',
justifyContent: 'center',
},
searchHistoryContent: {
padding: 10,
paddingHorizontal: 10,
borderRadius: 8,
},
searchHistoryTitle: {
fontWeight: 'bold',
paddingVertical: 12,
paddingHorizontal: 10,
},
})