diff --git a/src/components/forms/SearchInput.tsx b/src/components/forms/SearchInput.tsx new file mode 100644 index 0000000000..30791d0052 --- /dev/null +++ b/src/components/forms/SearchInput.tsx @@ -0,0 +1,75 @@ +import React from 'react' +import {TextInput, View} from 'react-native' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {HITSLOP_10} from '#/lib/constants' +import {isNative} from '#/platform/detection' +import {atoms as a, useTheme} from '#/alf' +import {Button, ButtonIcon} from '#/components/Button' +import * as TextField from '#/components/forms/TextField' +import {MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlassIcon} from '#/components/icons/MagnifyingGlass2' +import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' + +type SearchInputProps = Omit & { + label?: TextField.InputProps['label'] + /** + * Called when the user presses the (X) button + */ + onClearText?: () => void +} + +export const SearchInput = React.forwardRef( + function SearchInput({value, label, onClearText, ...rest}, ref) { + const t = useTheme() + const {_} = useLingui() + + return ( + + + + + + + {value && value.length > 0 && ( + + + + )} + + ) + }, +) diff --git a/src/components/forms/TextField.tsx b/src/components/forms/TextField.tsx index 21928d3df3..96d3481cd4 100644 --- a/src/components/forms/TextField.tsx +++ b/src/components/forms/TextField.tsx @@ -126,7 +126,7 @@ export type InputProps = Omit & { value?: string onChangeText?: (value: string) => void isInvalid?: boolean - inputRef?: React.RefObject + inputRef?: React.RefObject | React.ForwardedRef } export function createInput(Component: typeof TextInput) { diff --git a/src/screens/StarterPack/Wizard/StepFeeds.tsx b/src/screens/StarterPack/Wizard/StepFeeds.tsx index f047b612ae..0cf6ab2312 100644 --- a/src/screens/StarterPack/Wizard/StepFeeds.tsx +++ b/src/screens/StarterPack/Wizard/StepFeeds.tsx @@ -4,17 +4,17 @@ import {KeyboardAwareScrollView} from 'react-native-keyboard-controller' import {AppBskyFeedDefs, ModerationOpts} from '@atproto/api' import {Trans} from '@lingui/macro' +import {DISCOVER_FEED_URI} from '#/lib/constants' import {useA11y} from '#/state/a11y' -import {DISCOVER_FEED_URI} from 'lib/constants' import { useGetPopularFeedsQuery, usePopularFeedsSearch, useSavedFeeds, -} from 'state/queries/feed' -import {SearchInput} from 'view/com/util/forms/SearchInput' -import {List} from 'view/com/util/List' +} from '#/state/queries/feed' +import {List} from '#/view/com/util/List' import {useWizardState} from '#/screens/StarterPack/Wizard/State' import {atoms as a, useTheme} from '#/alf' +import {SearchInput} from '#/components/forms/SearchInput' import {useThrottledValue} from '#/components/hooks/useThrottledValue' import {Loader} from '#/components/Loader' import {ScreenTransition} from '#/components/StarterPack/Wizard/ScreenTransition' @@ -81,12 +81,11 @@ export function StepFeeds({moderationOpts}: {moderationOpts: ModerationOpts}) { return ( - + setQuery(t)} - onPressCancelSearch={() => setQuery('')} - onSubmitQuery={() => {}} + value={query} + onChangeText={t => setQuery(t)} + onClearText={() => setQuery('')} /> @@ -94,7 +93,6 @@ export function StepFeeds({moderationOpts}: {moderationOpts: ModerationOpts}) { data={query ? searchedFeeds : suggestedFeeds} renderItem={renderItem} keyExtractor={keyExtractor} - contentContainerStyle={{paddingTop: 6}} onEndReached={ !query && !screenReaderEnabled ? () => fetchNextPage() : undefined } diff --git a/src/screens/StarterPack/Wizard/StepProfiles.tsx b/src/screens/StarterPack/Wizard/StepProfiles.tsx index c14de847f6..054a6a63e2 100644 --- a/src/screens/StarterPack/Wizard/StepProfiles.tsx +++ b/src/screens/StarterPack/Wizard/StepProfiles.tsx @@ -4,14 +4,14 @@ import {KeyboardAwareScrollView} from 'react-native-keyboard-controller' import {AppBskyActorDefs, ModerationOpts} from '@atproto/api' import {Trans} from '@lingui/macro' +import {isNative} from '#/platform/detection' import {useA11y} from '#/state/a11y' -import {isNative} from 'platform/detection' -import {useActorAutocompleteQuery} from 'state/queries/actor-autocomplete' -import {useActorSearchPaginated} from 'state/queries/actor-search' -import {SearchInput} from 'view/com/util/forms/SearchInput' -import {List} from 'view/com/util/List' +import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' +import {useActorSearchPaginated} from '#/state/queries/actor-search' +import {List} from '#/view/com/util/List' import {useWizardState} from '#/screens/StarterPack/Wizard/State' import {atoms as a, useTheme} from '#/alf' +import {SearchInput} from '#/components/forms/SearchInput' import {Loader} from '#/components/Loader' import {ScreenTransition} from '#/components/StarterPack/Wizard/ScreenTransition' import {WizardProfileCard} from '#/components/StarterPack/Wizard/WizardListCard' @@ -65,12 +65,11 @@ export function StepProfiles({ return ( - + setQuery('')} - onSubmitQuery={() => {}} + value={query} + onChangeText={setQuery} + onClearText={() => setQuery('')} /> diff --git a/src/view/com/util/forms/SearchInput.tsx b/src/view/com/util/forms/SearchInput.tsx deleted file mode 100644 index 5a21d8fdd0..0000000000 --- a/src/view/com/util/forms/SearchInput.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import React from 'react' -import { - StyleProp, - StyleSheet, - TextInput, - TouchableOpacity, - View, - ViewStyle, -} from 'react-native' -import { - FontAwesomeIcon, - FontAwesomeIconStyle, -} from '@fortawesome/react-native-fontawesome' -import {HITSLOP_10} from 'lib/constants' -import {MagnifyingGlassIcon} from 'lib/icons' -import {useTheme} from 'lib/ThemeContext' -import {usePalette} from 'lib/hooks/usePalette' -import {useLingui} from '@lingui/react' -import {msg} from '@lingui/macro' - -interface Props { - query: string - setIsInputFocused?: (v: boolean) => void - onChangeQuery: (v: string) => void - onPressCancelSearch: () => void - onSubmitQuery: () => void - style?: StyleProp -} - -export interface SearchInputRef { - focus?: () => void -} - -export const SearchInput = React.forwardRef( - function SearchInput( - { - query, - setIsInputFocused, - onChangeQuery, - onPressCancelSearch, - onSubmitQuery, - style, - }, - ref, - ) { - const theme = useTheme() - const pal = usePalette('default') - const {_} = useLingui() - const textInput = React.useRef(null) - - const onPressCancelSearchInner = React.useCallback(() => { - onPressCancelSearch() - textInput.current?.blur() - }, [onPressCancelSearch, textInput]) - - React.useImperativeHandle(ref, () => ({ - focus: () => textInput.current?.focus(), - blur: () => textInput.current?.blur(), - })) - - return ( - - - setIsInputFocused?.(true)} - onBlur={() => setIsInputFocused?.(false)} - onChangeText={onChangeQuery} - onSubmitEditing={onSubmitQuery} - accessibilityRole="search" - accessibilityLabel={_(msg`Search`)} - accessibilityHint="" - autoCorrect={false} - autoCapitalize="none" - /> - {query ? ( - - - - ) : undefined} - - ) - }, -) - -const styles = StyleSheet.create({ - container: { - flex: 1, - flexDirection: 'row', - alignItems: 'center', - borderRadius: 30, - paddingHorizontal: 12, - paddingVertical: 8, - }, - icon: { - marginRight: 6, - alignSelf: 'center', - }, - input: { - flex: 1, - fontSize: 17, - minWidth: 0, // overflow mitigation for firefox - }, - cancelBtn: { - paddingLeft: 10, - }, -}) diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx index 310c3dc602..87af59f7f6 100644 --- a/src/view/screens/Feeds.tsx +++ b/src/view/screens/Feeds.tsx @@ -6,6 +6,12 @@ import {useLingui} from '@lingui/react' import {useFocusEffect} from '@react-navigation/native' import debounce from 'lodash.debounce' +import {usePalette} from '#/lib/hooks/usePalette' +import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' +import {ComposeIcon2} from '#/lib/icons' +import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' +import {cleanError} from '#/lib/strings/errors' +import {s} from '#/lib/styles' import {isNative, isWeb} from '#/platform/detection' import { SavedFeedItem, @@ -16,25 +22,19 @@ import { import {useSession} from '#/state/session' import {useSetMinimalShellMode} from '#/state/shell' import {useComposerControls} from '#/state/shell/composer' -import {usePalette} from 'lib/hooks/usePalette' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {ComposeIcon2} from 'lib/icons' -import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' -import {cleanError} from 'lib/strings/errors' -import {s} from 'lib/styles' -import {ErrorMessage} from 'view/com/util/error/ErrorMessage' -import {FAB} from 'view/com/util/fab/FAB' -import {SearchInput} from 'view/com/util/forms/SearchInput' -import {TextLink} from 'view/com/util/Link' -import {List} from 'view/com/util/List' -import {FeedFeedLoadingPlaceholder} from 'view/com/util/LoadingPlaceholder' -import {Text} from 'view/com/util/text/Text' -import {ViewHeader} from 'view/com/util/ViewHeader' +import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' +import {FAB} from '#/view/com/util/fab/FAB' +import {TextLink} from '#/view/com/util/Link' +import {List} from '#/view/com/util/List' +import {FeedFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' +import {Text} from '#/view/com/util/text/Text' +import {ViewHeader} from '#/view/com/util/ViewHeader' import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed' import {NoSavedFeedsOfAnyType} from '#/screens/Feeds/NoSavedFeedsOfAnyType' import {atoms as a, useTheme} from '#/alf' import {Divider} from '#/components/Divider' import * as FeedCard from '#/components/FeedCard' +import {SearchInput} from '#/components/forms/SearchInput' import {IconCircle} from '#/components/IconCircle' import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/components/icons/FilterTimeline' @@ -481,11 +481,12 @@ export function FeedsScreen(_props: Props) { onChangeSearchFocus(true)} + onBlur={() => onChangeSearchFocus(false)} /> diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index 77ef448ede..4a6c87f104 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -62,12 +62,10 @@ import {makeSearchQuery, parseSearchQuery} from '#/screens/Search/utils' import {atoms as a, useBreakpoints, useTheme as useThemeNew, web} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as FeedCard from '#/components/FeedCard' -import * as TextField from '#/components/forms/TextField' +import {SearchInput} from '#/components/forms/SearchInput' import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron' -import {MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass} from '#/components/icons/MagnifyingGlass2' import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu' import {SettingsGear2_Stroke2_Corner0_Rounded as Gear} from '#/components/icons/SettingsGear2' -import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' function Loader() { const pal = usePalette('default') @@ -864,15 +862,16 @@ export function SearchScreen( )} - + + + {showFiltersButton && ( - - )} - - ) -} -SearchInputBox = React.memo(SearchInputBox) - let AutocompleteResults = ({ isAutocompleteFetching, autocompleteData, diff --git a/src/view/shell/desktop/Search.tsx b/src/view/shell/desktop/Search.tsx index b43dbcce32..2780944f11 100644 --- a/src/view/shell/desktop/Search.tsx +++ b/src/view/shell/desktop/Search.tsx @@ -25,11 +25,11 @@ import {s} from '#/lib/styles' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' import {precacheProfile} from '#/state/queries/profile' -import {SearchInput} from '#/view/com/util/forms/SearchInput' import {Link} from '#/view/com/util/Link' import {Text} from '#/view/com/util/text/Text' import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a} from '#/alf' +import {SearchInput} from '#/components/forms/SearchInput' let SearchLinkCard = ({ label, @@ -184,10 +184,10 @@ export function DesktopSearch() { return ( {query !== '' && isActive && moderationOpts && (