Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Search custom feeds (#1031)

* paginate custom feeds

* basic search

* update `@atproto/api`

* use search from the API

* debounce search for 200ms

authored by

Ansh and committed by
GitHub
38d78e16 8e9b8b6b

+81 -18
+1
.eslintrc.js
··· 15 15 'coverage', 16 16 '*.lock', 17 17 '.husky', 18 + 'patches', 18 19 ], 19 20 overrides: [ 20 21 {
+1 -1
package.json
··· 24 24 "e2e:run": "detox test --configuration ios.sim.debug --take-screenshots all" 25 25 }, 26 26 "dependencies": { 27 - "@atproto/api": "^0.4.2", 27 + "@atproto/api": "^0.4.3", 28 28 "@bam.tech/react-native-image-resizer": "^3.0.4", 29 29 "@braintree/sanitize-url": "^6.0.2", 30 30 "@expo/html-elements": "^0.4.2",
+17 -2
src/state/models/discovery/feeds.ts
··· 82 82 this._xIdle() 83 83 }) 84 84 85 + search = async (query: string) => { 86 + this._xLoading(false) 87 + try { 88 + const results = 89 + await this.rootStore.agent.app.bsky.unspecced.getPopularFeedGenerators({ 90 + limit: DEFAULT_LIMIT, 91 + query: query, 92 + }) 93 + this._replaceAll(results) 94 + } catch (e: any) { 95 + this._xIdle(e) 96 + } 97 + this._xIdle() 98 + } 99 + 85 100 clear() { 86 101 this.isLoading = false 87 102 this.isRefreshing = false ··· 93 108 // state transitions 94 109 // = 95 110 96 - _xLoading() { 111 + _xLoading(isRefreshing = true) { 97 112 this.isLoading = true 98 - this.isRefreshing = true 113 + this.isRefreshing = isRefreshing 99 114 this.error = '' 100 115 } 101 116
+1 -1
src/view/com/auth/create/Step1.tsx
··· 37 37 }, [setIsDefaultSelected, model]) 38 38 39 39 const fetchServiceDescription = React.useMemo( 40 - () => debounce(() => model.fetchServiceDescription(), 1e3), 40 + () => debounce(() => model.fetchServiceDescription(), 1e3), // debouce for 1 second (1e3 = 1000ms) 41 41 [model], 42 42 ) 43 43
+14 -10
src/view/com/search/HeaderWithInput.tsx
··· 21 21 onPressClearQuery: () => void 22 22 onPressCancelSearch: () => void 23 23 onSubmitQuery: () => void 24 + showMenu?: boolean 24 25 } 25 26 export function HeaderWithInput({ 26 27 isInputFocused, ··· 30 31 onPressClearQuery, 31 32 onPressCancelSearch, 32 33 onSubmitQuery, 34 + showMenu = true, 33 35 }: Props) { 34 36 const store = useStores() 35 37 const theme = useTheme() ··· 49 51 50 52 return ( 51 53 <View style={[pal.view, pal.border, styles.header]}> 52 - <TouchableOpacity 53 - testID="viewHeaderBackOrMenuBtn" 54 - onPress={onPressMenu} 55 - hitSlop={MENU_HITSLOP} 56 - style={styles.headerMenuBtn} 57 - accessibilityRole="button" 58 - accessibilityLabel="Menu" 59 - accessibilityHint="Access navigation links and settings"> 60 - <FontAwesomeIcon icon="bars" size={18} color={pal.colors.textLight} /> 61 - </TouchableOpacity> 54 + {showMenu ? ( 55 + <TouchableOpacity 56 + testID="viewHeaderBackOrMenuBtn" 57 + onPress={onPressMenu} 58 + hitSlop={MENU_HITSLOP} 59 + style={styles.headerMenuBtn} 60 + accessibilityRole="button" 61 + accessibilityLabel="Menu" 62 + accessibilityHint="Access navigation links and settings"> 63 + <FontAwesomeIcon icon="bars" size={18} color={pal.colors.textLight} /> 64 + </TouchableOpacity> 65 + ) : null} 62 66 <View 63 67 style={[ 64 68 {backgroundColor: pal.colors.backgroundLight},
+43
src/view/screens/DiscoverFeeds.tsx
··· 14 14 import {usePalette} from 'lib/hooks/usePalette' 15 15 import {s} from 'lib/styles' 16 16 import {CustomFeedModel} from 'state/models/feeds/custom-feed' 17 + import {HeaderWithInput} from 'view/com/search/HeaderWithInput' 18 + import debounce from 'lodash.debounce' 17 19 18 20 type Props = NativeStackScreenProps<CommonNavigatorParams, 'DiscoverFeeds'> 19 21 export const DiscoverFeedsScreen = withAuthRequired( ··· 22 24 const pal = usePalette('default') 23 25 const feeds = React.useMemo(() => new FeedsDiscoveryModel(store), [store]) 24 26 27 + // search stuff 28 + const [isInputFocused, setIsInputFocused] = React.useState<boolean>(false) 29 + const [query, setQuery] = React.useState<string>('') 30 + const debouncedSearchFeeds = React.useMemo( 31 + () => debounce(() => feeds.search(query), 200), // debouce for 200 ms 32 + [feeds, query], 33 + ) 34 + const onChangeQuery = React.useCallback( 35 + (text: string) => { 36 + setQuery(text) 37 + if (text.length > 1) { 38 + debouncedSearchFeeds() 39 + } else { 40 + feeds.refresh() 41 + } 42 + }, 43 + [debouncedSearchFeeds, feeds], 44 + ) 45 + const onPressClearQuery = React.useCallback(() => { 46 + setQuery('') 47 + feeds.refresh() 48 + }, [feeds]) 49 + const onPressCancelSearch = React.useCallback(() => { 50 + setIsInputFocused(false) 51 + setQuery('') 52 + feeds.refresh() 53 + }, [feeds]) 54 + const onSubmitQuery = React.useCallback(() => { 55 + feeds.search(query) 56 + }, [feeds, query]) 57 + 25 58 useFocusEffect( 26 59 React.useCallback(() => { 27 60 store.shell.setMinimalShellMode(false) ··· 68 101 <CenteredView style={[styles.container, pal.view]}> 69 102 <View style={[isDesktopWeb && styles.containerDesktop, pal.border]}> 70 103 <ViewHeader title="Discover Feeds" showOnDesktop /> 104 + <HeaderWithInput 105 + isInputFocused={isInputFocused} 106 + query={query} 107 + setIsInputFocused={setIsInputFocused} 108 + onChangeQuery={onChangeQuery} 109 + onPressClearQuery={onPressClearQuery} 110 + onPressCancelSearch={onPressCancelSearch} 111 + onSubmitQuery={onSubmitQuery} 112 + showMenu={false} 113 + /> 71 114 </View> 72 115 <FlatList 73 116 style={[!isDesktopWeb && s.flex1]}
+4 -4
yarn.lock
··· 40 40 tlds "^1.234.0" 41 41 typed-emitter "^2.1.0" 42 42 43 - "@atproto/api@^0.4.2": 44 - version "0.4.2" 45 - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.4.2.tgz#7790eb049f72437e7454c8ecc29a5ef4201c1ade" 46 - integrity sha512-bwaT+kIJp6wpzlHc1Rus3yi29GKlwvYp4wOWAFmcyYT4qH2ZlE6NfElgi6QwdQD285EhiIKYTv7CAMRp/QO7DQ== 43 + "@atproto/api@^0.4.3": 44 + version "0.4.3" 45 + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.4.3.tgz#d7e478bf7009df2adaf1ac6051eb3e3fea185c90" 46 + integrity sha512-8LdREwmoA58YQDrLS0rohd7cHokhoiXfyYEeNtNlkdO0w/2QpkUCQ1PgPBP2kIRM9PhOEkKp7W3Sn8Te9Qq8jg== 47 47 dependencies: 48 48 "@atproto/common-web" "*" 49 49 "@atproto/uri" "*"