import React from 'react' import {ActivityIndicator, StyleSheet, RefreshControl, View} from 'react-native' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome' import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewHeader} from 'view/com/util/ViewHeader' import {FAB} from 'view/com/util/fab/FAB' import {Link} from 'view/com/util/Link' import {NativeStackScreenProps, FeedsTabNavigatorParams} from 'lib/routes/types' import {observer} from 'mobx-react-lite' import {usePalette} from 'lib/hooks/usePalette' import {useStores} from 'state/index' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {ComposeIcon2, CogIcon} from 'lib/icons' import {s} from 'lib/styles' import {SearchInput} from 'view/com/util/forms/SearchInput' import {UserAvatar} from 'view/com/util/UserAvatar' import { LoadingPlaceholder, FeedFeedLoadingPlaceholder, } from 'view/com/util/LoadingPlaceholder' import {ErrorMessage} from 'view/com/util/error/ErrorMessage' import debounce from 'lodash.debounce' import {Text} from 'view/com/util/text/Text' import {MyFeedsItem} from 'state/models/ui/my-feeds' import {FeedSourceModel} from 'state/models/content/feed-source' import {FlatList} from 'view/com/util/Views' import {useFocusEffect} from '@react-navigation/native' import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' import {Trans, msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useSetMinimalShellMode} from '#/state/shell' type Props = NativeStackScreenProps export const FeedsScreen = withAuthRequired( observer(function FeedsScreenImpl({}: Props) { const pal = usePalette('default') const store = useStores() const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() const {isMobile, isTabletOrDesktop} = useWebMediaQueries() const myFeeds = store.me.myFeeds const [query, setQuery] = React.useState('') const debouncedSearchFeeds = React.useMemo( () => debounce(q => myFeeds.discovery.search(q), 500), // debounce for 500ms [myFeeds], ) useFocusEffect( React.useCallback(() => { setMinimalShellMode(false) myFeeds.setup() const softResetSub = store.onScreenSoftReset(() => myFeeds.refresh()) return () => { softResetSub.remove() } }, [store, myFeeds, setMinimalShellMode]), ) React.useEffect(() => { // watch for changes to saved/pinned feeds return myFeeds.registerListeners() }, [myFeeds]) const onPressCompose = React.useCallback(() => { store.shell.openComposer({}) }, [store]) const onChangeQuery = React.useCallback( (text: string) => { setQuery(text) if (text.length > 1) { debouncedSearchFeeds(text) } else { myFeeds.discovery.refresh() } }, [debouncedSearchFeeds, myFeeds.discovery], ) const onPressCancelSearch = React.useCallback(() => { setQuery('') myFeeds.discovery.refresh() }, [myFeeds]) const onSubmitQuery = React.useCallback(() => { debouncedSearchFeeds(query) debouncedSearchFeeds.flush() }, [debouncedSearchFeeds, query]) const renderHeaderBtn = React.useCallback(() => { return ( ) }, [pal, _]) const onRefresh = React.useCallback(() => { myFeeds.refresh() }, [myFeeds]) const renderItem = React.useCallback( ({item}: {item: MyFeedsItem}) => { if (item.type === 'discover-feeds-loading') { return } else if (item.type === 'spinner') { return ( ) } else if (item.type === 'error') { return } else if (item.type === 'saved-feeds-header') { if (!isMobile) { return ( My Feeds ) } return } else if (item.type === 'saved-feeds-loading') { return ( <> {Array.from(Array(item.numItems)).map((_i, i) => ( ))} ) } else if (item.type === 'saved-feed') { return } else if (item.type === 'discover-feeds-header') { return ( <> Discover new feeds {!isMobile && ( )} {isMobile && ( )} ) } else if (item.type === 'discover-feed') { return ( ) } else if (item.type === 'discover-feeds-no-results') { return ( No results found for "{query}" ) } return null }, [ isMobile, pal, query, onChangeQuery, onPressCancelSearch, onSubmitQuery, _, ], ) return ( {isMobile && ( )} item._reactKey} contentContainerStyle={styles.contentContainer} refreshControl={ } renderItem={renderItem} initialNumToRender={10} onEndReached={() => myFeeds.loadMore()} extraData={myFeeds.isLoading} // @ts-ignore our .web version only -prf desktopFixedHeight /> } accessibilityRole="button" accessibilityLabel={_(msg`New post`)} accessibilityHint="" /> ) }), ) function SavedFeed({feed}: {feed: FeedSourceModel}) { const pal = usePalette('default') const {isMobile} = useWebMediaQueries() return ( {feed.error ? ( ) : ( )} {feed.displayName} {feed.error ? ( Feed offline ) : null} {isMobile && ( )} ) } function SavedFeedLoadingPlaceholder() { const pal = usePalette('default') const {isMobile} = useWebMediaQueries() return ( ) } const styles = StyleSheet.create({ container: { flex: 1, }, list: { height: '100%', }, contentContainer: { paddingBottom: 100, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 16, paddingHorizontal: 16, paddingVertical: 12, }, savedFeed: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, paddingVertical: 14, gap: 12, borderBottomWidth: 1, }, savedFeedMobile: { paddingVertical: 10, }, offlineSlug: { borderWidth: 1, borderRadius: 4, paddingHorizontal: 4, paddingVertical: 2, }, })