import React, {useCallback, useMemo} from 'react' import { RefreshControl, StyleSheet, View, ActivityIndicator, Pressable, TouchableOpacity, } from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' import {useAnalytics} from 'lib/analytics/analytics' import {usePalette} from 'lib/hooks/usePalette' import {CommonNavigatorParams} from 'lib/routes/types' import {observer} from 'mobx-react-lite' import {useStores} from 'state/index' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewHeader} from 'view/com/util/ViewHeader' import {CenteredView} from 'view/com/util/Views' import {Text} from 'view/com/util/text/Text' import {isWeb} from 'platform/detection' import {s, colors} from 'lib/styles' import DraggableFlatList, { ShadowDecorator, ScaleDecorator, } from 'react-native-draggable-flatlist' import {CustomFeed} from 'view/com/feeds/CustomFeed' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {CustomFeedModel} from 'state/models/feeds/custom-feed' import * as Toast from 'view/com/util/Toast' import {Haptics} from 'lib/haptics' import {Link, TextLink} from 'view/com/util/Link' type Props = NativeStackScreenProps export const SavedFeeds = withAuthRequired( observer(function SavedFeedsImpl({}: Props) { const pal = usePalette('default') const store = useStores() const {isMobile, isTabletOrDesktop} = useWebMediaQueries() const {screen} = useAnalytics() const savedFeeds = useMemo(() => store.me.savedFeeds, [store]) useFocusEffect( useCallback(() => { screen('SavedFeeds') store.shell.setMinimalShellMode(false) savedFeeds.refresh() }, [screen, store, savedFeeds]), ) const renderListEmptyComponent = useCallback(() => { return ( You don't have any saved feeds. ) }, [pal, isMobile]) const renderListFooterComponent = useCallback(() => { return ( <> Discover new feeds Feeds are custom algorithms that users build with a little coding expertise.{' '} {' '} for more information. {savedFeeds.isLoading && } ) }, [pal, savedFeeds.isLoading]) const onRefresh = useCallback(() => savedFeeds.refresh(), [savedFeeds]) const onDragEnd = useCallback( async ({data}: {data: CustomFeedModel[]}) => { try { await savedFeeds.reorderPinnedFeeds(data) } catch (e) { Toast.show('There was an issue contacting the server') store.log.error('Failed to save pinned feed order', {e}) } }, [savedFeeds, store], ) return ( item.data.uri} refreshing={savedFeeds.isRefreshing} refreshControl={ } renderItem={({item, drag}) => } getItemLayout={(data, index) => ({ length: 77, offset: 77 * index, index, })} initialNumToRender={10} ListFooterComponent={renderListFooterComponent} ListEmptyComponent={renderListEmptyComponent} extraData={savedFeeds.isLoading} onDragEnd={onDragEnd} /> ) }), ) const ListItem = observer(function ListItemImpl({ item, drag, }: { item: CustomFeedModel drag: () => void }) { const pal = usePalette('default') const store = useStores() const savedFeeds = useMemo(() => store.me.savedFeeds, [store]) const isPinned = savedFeeds.isPinned(item) const onTogglePinned = useCallback(() => { Haptics.default() savedFeeds.togglePinnedFeed(item).catch(e => { Toast.show('There was an issue contacting the server') store.log.error('Failed to toggle pinned feed', {e}) }) }, [savedFeeds, item, store]) const onPressUp = useCallback( () => savedFeeds.movePinnedFeed(item, 'up').catch(e => { Toast.show('There was an issue contacting the server') store.log.error('Failed to set pinned feed order', {e}) }), [store, savedFeeds, item], ) const onPressDown = useCallback( () => savedFeeds.movePinnedFeed(item, 'down').catch(e => { Toast.show('There was an issue contacting the server') store.log.error('Failed to set pinned feed order', {e}) }), [store, savedFeeds, item], ) return ( {isPinned && isWeb ? ( ) : isPinned ? ( ) : null} ) }) const styles = StyleSheet.create({ desktopContainer: { borderLeftWidth: 1, borderRightWidth: 1, // @ts-ignore only rendered on web minHeight: '100vh', }, empty: { paddingHorizontal: 20, paddingVertical: 20, borderRadius: 16, marginHorizontal: 24, marginTop: 10, }, itemContainer: { flex: 1, flexDirection: 'row', alignItems: 'center', borderBottomWidth: 1, paddingRight: 16, }, webArrowButtonsContainer: { paddingLeft: 16, flexDirection: 'column', justifyContent: 'space-around', }, webArrowUpButton: { marginBottom: 10, }, noBorder: { borderTopWidth: 0, }, footerText: { paddingHorizontal: 26, paddingTop: 22, paddingBottom: 100, }, footerLinks: { borderBottomWidth: 1, borderTopWidth: 0, }, footerLink: { flexDirection: 'row', paddingHorizontal: 26, paddingVertical: 18, gap: 18, }, })