Search custom feeds (#1031)
* paginate custom feeds * basic search * update `@atproto/api` * use search from the API * debounce search for 200mszio/stable
parent
8e9b8b6b36
commit
38d78e16bf
|
@ -15,6 +15,7 @@ module.exports = {
|
|||
'coverage',
|
||||
'*.lock',
|
||||
'.husky',
|
||||
'patches',
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"e2e:run": "detox test --configuration ios.sim.debug --take-screenshots all"
|
||||
},
|
||||
"dependencies": {
|
||||
"@atproto/api": "^0.4.2",
|
||||
"@atproto/api": "^0.4.3",
|
||||
"@bam.tech/react-native-image-resizer": "^3.0.4",
|
||||
"@braintree/sanitize-url": "^6.0.2",
|
||||
"@expo/html-elements": "^0.4.2",
|
||||
|
|
|
@ -82,6 +82,21 @@ export class FeedsDiscoveryModel {
|
|||
this._xIdle()
|
||||
})
|
||||
|
||||
search = async (query: string) => {
|
||||
this._xLoading(false)
|
||||
try {
|
||||
const results =
|
||||
await this.rootStore.agent.app.bsky.unspecced.getPopularFeedGenerators({
|
||||
limit: DEFAULT_LIMIT,
|
||||
query: query,
|
||||
})
|
||||
this._replaceAll(results)
|
||||
} catch (e: any) {
|
||||
this._xIdle(e)
|
||||
}
|
||||
this._xIdle()
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.isLoading = false
|
||||
this.isRefreshing = false
|
||||
|
@ -93,9 +108,9 @@ export class FeedsDiscoveryModel {
|
|||
// state transitions
|
||||
// =
|
||||
|
||||
_xLoading() {
|
||||
_xLoading(isRefreshing = true) {
|
||||
this.isLoading = true
|
||||
this.isRefreshing = true
|
||||
this.isRefreshing = isRefreshing
|
||||
this.error = ''
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ export const Step1 = observer(({model}: {model: CreateAccountModel}) => {
|
|||
}, [setIsDefaultSelected, model])
|
||||
|
||||
const fetchServiceDescription = React.useMemo(
|
||||
() => debounce(() => model.fetchServiceDescription(), 1e3),
|
||||
() => debounce(() => model.fetchServiceDescription(), 1e3), // debouce for 1 second (1e3 = 1000ms)
|
||||
[model],
|
||||
)
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ interface Props {
|
|||
onPressClearQuery: () => void
|
||||
onPressCancelSearch: () => void
|
||||
onSubmitQuery: () => void
|
||||
showMenu?: boolean
|
||||
}
|
||||
export function HeaderWithInput({
|
||||
isInputFocused,
|
||||
|
@ -30,6 +31,7 @@ export function HeaderWithInput({
|
|||
onPressClearQuery,
|
||||
onPressCancelSearch,
|
||||
onSubmitQuery,
|
||||
showMenu = true,
|
||||
}: Props) {
|
||||
const store = useStores()
|
||||
const theme = useTheme()
|
||||
|
@ -49,16 +51,18 @@ export function HeaderWithInput({
|
|||
|
||||
return (
|
||||
<View style={[pal.view, pal.border, styles.header]}>
|
||||
<TouchableOpacity
|
||||
testID="viewHeaderBackOrMenuBtn"
|
||||
onPress={onPressMenu}
|
||||
hitSlop={MENU_HITSLOP}
|
||||
style={styles.headerMenuBtn}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Menu"
|
||||
accessibilityHint="Access navigation links and settings">
|
||||
<FontAwesomeIcon icon="bars" size={18} color={pal.colors.textLight} />
|
||||
</TouchableOpacity>
|
||||
{showMenu ? (
|
||||
<TouchableOpacity
|
||||
testID="viewHeaderBackOrMenuBtn"
|
||||
onPress={onPressMenu}
|
||||
hitSlop={MENU_HITSLOP}
|
||||
style={styles.headerMenuBtn}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Menu"
|
||||
accessibilityHint="Access navigation links and settings">
|
||||
<FontAwesomeIcon icon="bars" size={18} color={pal.colors.textLight} />
|
||||
</TouchableOpacity>
|
||||
) : null}
|
||||
<View
|
||||
style={[
|
||||
{backgroundColor: pal.colors.backgroundLight},
|
||||
|
|
|
@ -14,6 +14,8 @@ import {isDesktopWeb} from 'platform/detection'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {s} from 'lib/styles'
|
||||
import {CustomFeedModel} from 'state/models/feeds/custom-feed'
|
||||
import {HeaderWithInput} from 'view/com/search/HeaderWithInput'
|
||||
import debounce from 'lodash.debounce'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'DiscoverFeeds'>
|
||||
export const DiscoverFeedsScreen = withAuthRequired(
|
||||
|
@ -22,6 +24,37 @@ export const DiscoverFeedsScreen = withAuthRequired(
|
|||
const pal = usePalette('default')
|
||||
const feeds = React.useMemo(() => new FeedsDiscoveryModel(store), [store])
|
||||
|
||||
// search stuff
|
||||
const [isInputFocused, setIsInputFocused] = React.useState<boolean>(false)
|
||||
const [query, setQuery] = React.useState<string>('')
|
||||
const debouncedSearchFeeds = React.useMemo(
|
||||
() => debounce(() => feeds.search(query), 200), // debouce for 200 ms
|
||||
[feeds, query],
|
||||
)
|
||||
const onChangeQuery = React.useCallback(
|
||||
(text: string) => {
|
||||
setQuery(text)
|
||||
if (text.length > 1) {
|
||||
debouncedSearchFeeds()
|
||||
} else {
|
||||
feeds.refresh()
|
||||
}
|
||||
},
|
||||
[debouncedSearchFeeds, feeds],
|
||||
)
|
||||
const onPressClearQuery = React.useCallback(() => {
|
||||
setQuery('')
|
||||
feeds.refresh()
|
||||
}, [feeds])
|
||||
const onPressCancelSearch = React.useCallback(() => {
|
||||
setIsInputFocused(false)
|
||||
setQuery('')
|
||||
feeds.refresh()
|
||||
}, [feeds])
|
||||
const onSubmitQuery = React.useCallback(() => {
|
||||
feeds.search(query)
|
||||
}, [feeds, query])
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
store.shell.setMinimalShellMode(false)
|
||||
|
@ -68,6 +101,16 @@ export const DiscoverFeedsScreen = withAuthRequired(
|
|||
<CenteredView style={[styles.container, pal.view]}>
|
||||
<View style={[isDesktopWeb && styles.containerDesktop, pal.border]}>
|
||||
<ViewHeader title="Discover Feeds" showOnDesktop />
|
||||
<HeaderWithInput
|
||||
isInputFocused={isInputFocused}
|
||||
query={query}
|
||||
setIsInputFocused={setIsInputFocused}
|
||||
onChangeQuery={onChangeQuery}
|
||||
onPressClearQuery={onPressClearQuery}
|
||||
onPressCancelSearch={onPressCancelSearch}
|
||||
onSubmitQuery={onSubmitQuery}
|
||||
showMenu={false}
|
||||
/>
|
||||
</View>
|
||||
<FlatList
|
||||
style={[!isDesktopWeb && s.flex1]}
|
||||
|
|
|
@ -40,10 +40,10 @@
|
|||
tlds "^1.234.0"
|
||||
typed-emitter "^2.1.0"
|
||||
|
||||
"@atproto/api@^0.4.2":
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.4.2.tgz#7790eb049f72437e7454c8ecc29a5ef4201c1ade"
|
||||
integrity sha512-bwaT+kIJp6wpzlHc1Rus3yi29GKlwvYp4wOWAFmcyYT4qH2ZlE6NfElgi6QwdQD285EhiIKYTv7CAMRp/QO7DQ==
|
||||
"@atproto/api@^0.4.3":
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.4.3.tgz#d7e478bf7009df2adaf1ac6051eb3e3fea185c90"
|
||||
integrity sha512-8LdREwmoA58YQDrLS0rohd7cHokhoiXfyYEeNtNlkdO0w/2QpkUCQ1PgPBP2kIRM9PhOEkKp7W3Sn8Te9Qq8jg==
|
||||
dependencies:
|
||||
"@atproto/common-web" "*"
|
||||
"@atproto/uri" "*"
|
||||
|
|
Loading…
Reference in New Issue