Improve usability of search on web (#3663)
* dont select the text on web * TODO REVERT THESE CHANGES * use `usethrottledvalue` for autocomplete * use `isFetching` from query * rm setTimeout * getting there * improve functionality of cancel button * rm todo * add comment back * encode `searchText` rather than `queryTerm` * use "back" on web in some cases * don't flash results in autocomplete * remove unnecesary usestate * rename everything to `query` temporarily * revert accidental lint * rm todo * rm comment * use `useFocusEffect` to update the query term on back navigation * `searchText` is always defined here * Fix race * remove back functionality * use `keepPreviousData` for query * rename `q` to `queryParam` * remove hack * remove `q=` on cancel * blur on submit * use `setParams` instead of `replace` * use `replace` on web still * clear the search input when we clear `q` on native * onPress dismiss attempt * Adjustments * Fix search history * Always hide autocomplete * Clear right pane search on select * `blur` on autosuggestion press * Rename to reduce diff * Fixes * Unify codepaths * Fixes * precache the autosuggestion * do the cache in the link card * Revert "precache the autosuggestion" This reverts commit 79c433e984621ba4231a2a4c4b3f4690b0516b4d. * use `throttledValue` and `keepPreviousData` in sidebar search * show spinner when fetching pt 1 * show spinner when fetching pt 2 * show spinner properly for autocomplete * Fix extra border * Position fixed * TS * Revert "TS" This reverts commit df187ea2d7a96d0f1832bc2392215f4d969a87c9. * Revert "Position fixed" This reverts commit 9c721c952b0fa4e5e4a23de38cab916ab13397e6. * Maybe fix iPad * Revert "TODO REVERT THESE CHANGES" This reverts commit 279f717f3091c9df8c73ba35f9a038e12f5a1122. * Rename var --------- Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
This commit is contained in:
parent
d81a373d21
commit
5f9136479b
3 changed files with 155 additions and 171 deletions
|
@ -1,33 +1,35 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
ViewStyle,
|
||||
TextInput,
|
||||
View,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
ActivityIndicator,
|
||||
StyleSheet,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {useNavigation, StackActions} from '@react-navigation/native'
|
||||
import {
|
||||
AppBskyActorDefs,
|
||||
moderateProfile,
|
||||
ModerationDecision,
|
||||
} from '@atproto/api'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {StackActions, useNavigation} from '@react-navigation/native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {s} from '#/lib/styles'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {Link} from '#/view/com/util/Link'
|
||||
import {s} from '#/lib/styles'
|
||||
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
|
||||
import {useModerationOpts} from '#/state/queries/preferences'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {MagnifyingGlassIcon2} from 'lib/icons'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import {precacheProfile} from 'state/queries/profile'
|
||||
import {Link} from '#/view/com/util/Link'
|
||||
import {UserAvatar} from '#/view/com/util/UserAvatar'
|
||||
import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
|
||||
import {useModerationOpts} from '#/state/queries/preferences'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
|
||||
export const MATCH_HANDLE =
|
||||
/@?([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(?:\.[a-zA-Z]{2,}))/
|
||||
|
@ -84,11 +86,19 @@ export function SearchLinkCard({
|
|||
export function SearchProfileCard({
|
||||
profile,
|
||||
moderation,
|
||||
onPress: onPressInner,
|
||||
}: {
|
||||
profile: AppBskyActorDefs.ProfileViewBasic
|
||||
moderation: ModerationDecision
|
||||
onPress: () => void
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const onPress = React.useCallback(() => {
|
||||
precacheProfile(queryClient, profile)
|
||||
onPressInner()
|
||||
}, [queryClient, profile, onPressInner])
|
||||
|
||||
return (
|
||||
<Link
|
||||
|
@ -96,7 +106,8 @@ export function SearchProfileCard({
|
|||
href={makeProfileLink(profile)}
|
||||
title={profile.handle}
|
||||
asAnchor
|
||||
anchorNoUnderline>
|
||||
anchorNoUnderline
|
||||
onBeforePress={onPress}>
|
||||
<View
|
||||
style={[
|
||||
pal.border,
|
||||
|
@ -138,63 +149,35 @@ export function DesktopSearch() {
|
|||
const {_} = useLingui()
|
||||
const pal = usePalette('default')
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const searchDebounceTimeout = React.useRef<NodeJS.Timeout | undefined>(
|
||||
undefined,
|
||||
)
|
||||
const [isActive, setIsActive] = React.useState<boolean>(false)
|
||||
const [isFetching, setIsFetching] = React.useState<boolean>(false)
|
||||
const [query, setQuery] = React.useState<string>('')
|
||||
const [searchResults, setSearchResults] = React.useState<
|
||||
AppBskyActorDefs.ProfileViewBasic[]
|
||||
>([])
|
||||
const {data: autocompleteData, isFetching} = useActorAutocompleteQuery(
|
||||
query,
|
||||
true,
|
||||
)
|
||||
|
||||
const moderationOpts = useModerationOpts()
|
||||
const search = useActorAutocompleteFn()
|
||||
|
||||
const onChangeText = React.useCallback(
|
||||
async (text: string) => {
|
||||
setQuery(text)
|
||||
|
||||
if (text.length > 0) {
|
||||
setIsFetching(true)
|
||||
setIsActive(true)
|
||||
|
||||
if (searchDebounceTimeout.current)
|
||||
clearTimeout(searchDebounceTimeout.current)
|
||||
|
||||
searchDebounceTimeout.current = setTimeout(async () => {
|
||||
const results = await search({query: text})
|
||||
|
||||
if (results) {
|
||||
setSearchResults(results)
|
||||
setIsFetching(false)
|
||||
}
|
||||
}, 300)
|
||||
} else {
|
||||
if (searchDebounceTimeout.current)
|
||||
clearTimeout(searchDebounceTimeout.current)
|
||||
setSearchResults([])
|
||||
setIsFetching(false)
|
||||
setIsActive(false)
|
||||
}
|
||||
},
|
||||
[setQuery, search, setSearchResults],
|
||||
)
|
||||
const onChangeText = React.useCallback((text: string) => {
|
||||
setQuery(text)
|
||||
setIsActive(text.length > 0)
|
||||
}, [])
|
||||
|
||||
const onPressCancelSearch = React.useCallback(() => {
|
||||
setQuery('')
|
||||
setIsActive(false)
|
||||
if (searchDebounceTimeout.current)
|
||||
clearTimeout(searchDebounceTimeout.current)
|
||||
}, [setQuery])
|
||||
|
||||
const onSubmit = React.useCallback(() => {
|
||||
setIsActive(false)
|
||||
if (!query.length) return
|
||||
setSearchResults([])
|
||||
if (searchDebounceTimeout.current)
|
||||
clearTimeout(searchDebounceTimeout.current)
|
||||
navigation.dispatch(StackActions.push('Search', {q: query}))
|
||||
}, [query, navigation, setSearchResults])
|
||||
}, [query, navigation])
|
||||
|
||||
const onSearchProfileCardPress = React.useCallback(() => {
|
||||
setQuery('')
|
||||
setIsActive(false)
|
||||
}, [])
|
||||
|
||||
const queryMaybeHandle = React.useMemo(() => {
|
||||
const match = MATCH_HANDLE.exec(query)
|
||||
|
@ -246,7 +229,7 @@ export function DesktopSearch() {
|
|||
|
||||
{query !== '' && isActive && moderationOpts && (
|
||||
<View style={[pal.view, pal.borderDark, styles.resultsContainer]}>
|
||||
{isFetching ? (
|
||||
{isFetching && !autocompleteData?.length ? (
|
||||
<View style={{padding: 8}}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
|
@ -255,7 +238,11 @@ export function DesktopSearch() {
|
|||
<SearchLinkCard
|
||||
label={_(msg`Search for "${query}"`)}
|
||||
to={`/search?q=${encodeURIComponent(query)}`}
|
||||
style={{borderBottomWidth: 1}}
|
||||
style={
|
||||
queryMaybeHandle || (autocompleteData?.length ?? 0) > 0
|
||||
? {borderBottomWidth: 1}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
|
||||
{queryMaybeHandle ? (
|
||||
|
@ -265,11 +252,12 @@ export function DesktopSearch() {
|
|||
/>
|
||||
) : null}
|
||||
|
||||
{searchResults.map(item => (
|
||||
{autocompleteData?.map(item => (
|
||||
<SearchProfileCard
|
||||
key={item.did}
|
||||
profile={item}
|
||||
moderation={moderateProfile(item, moderationOpts)}
|
||||
onPress={onSearchProfileCardPress}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue