diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/state/models/ui/saved-feeds.ts | 10 | ||||
-rw-r--r-- | src/view/screens/SavedFeeds.tsx | 285 |
2 files changed, 132 insertions, 163 deletions
diff --git a/src/state/models/ui/saved-feeds.ts b/src/state/models/ui/saved-feeds.ts index 881684ee6..667bc03a3 100644 --- a/src/state/models/ui/saved-feeds.ts +++ b/src/state/models/ui/saved-feeds.ts @@ -95,19 +95,15 @@ export class SavedFeedsModel { return } if (direction === 'up' && index !== 0) { - const temp = pinned[index] - pinned[index] = pinned[index - 1] - pinned[index - 1] = temp + ;[pinned[index], pinned[index - 1]] = [pinned[index - 1], pinned[index]] } else if (direction === 'down' && index < pinned.length - 1) { - const temp = pinned[index] - pinned[index] = pinned[index + 1] - pinned[index + 1] = temp + ;[pinned[index], pinned[index + 1]] = [pinned[index + 1], pinned[index]] } + this._updatePinSortOrder(pinned.concat(this.unpinned.map(f => f.uri))) await this.rootStore.preferences.setSavedFeeds( this.rootStore.preferences.savedFeeds, pinned, ) - this._updatePinSortOrder() track('CustomFeed:Reorder', { name: item.displayName, uri: item.uri, diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx index 8f8cdc6c9..0f6278288 100644 --- a/src/view/screens/SavedFeeds.tsx +++ b/src/view/screens/SavedFeeds.tsx @@ -1,6 +1,5 @@ import React, {useCallback, useMemo} from 'react' import { - RefreshControl, StyleSheet, View, ActivityIndicator, @@ -18,23 +17,30 @@ import {SavedFeedsModel} from 'state/models/ui/saved-feeds' 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 {ScrollView, 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 {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' import {FeedSourceModel} from 'state/models/content/feed-source' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import * as Toast from 'view/com/util/Toast' import {Haptics} from 'lib/haptics' -import {Link, TextLink} from 'view/com/util/Link' +import {TextLink} from 'view/com/util/Link' -type Props = NativeStackScreenProps<CommonNavigatorParams, 'SavedFeeds'> +const HITSLOP_TOP = { + top: 20, + left: 20, + bottom: 5, + right: 20, +} +const HITSLOP_BOTTOM = { + top: 5, + left: 20, + bottom: 20, + right: 20, +} +type Props = NativeStackScreenProps<CommonNavigatorParams, 'SavedFeeds'> export const SavedFeeds = withAuthRequired( observer(function SavedFeedsImpl({}: Props) { const pal = usePalette('default') @@ -55,37 +61,76 @@ export const SavedFeeds = withAuthRequired( }, [screen, store, savedFeeds]), ) - const renderListEmptyComponent = useCallback(() => { - return ( - <View - style={[ - pal.border, - isMobile && s.flex1, - pal.viewLight, - styles.empty, - ]}> - <Text type="lg" style={[pal.text]}> - You don't have any saved feeds. - </Text> - </View> - ) - }, [pal, isMobile]) - - const renderListFooterComponent = useCallback(() => { - return ( - <> - <View style={[styles.footerLinks, pal.border]}> - <Link style={styles.footerLink} href="/feeds"> - <FontAwesomeIcon - icon="search" - size={18} - color={pal.colors.icon} - /> - <Text type="lg-medium" style={pal.textLight}> - Discover new feeds - </Text> - </Link> + return ( + <CenteredView + style={[ + s.hContentRegion, + pal.border, + isTabletOrDesktop && styles.desktopContainer, + ]}> + <ViewHeader title="Edit My Feeds" showOnDesktop showBorder /> + <ScrollView style={s.flex1}> + <View style={[pal.text, pal.border, styles.title]}> + <Text type="title" style={pal.text}> + Pinned Feeds + </Text> </View> + {savedFeeds.hasLoaded ? ( + !savedFeeds.pinned.length ? ( + <View + style={[ + pal.border, + isMobile && s.flex1, + pal.viewLight, + styles.empty, + ]}> + <Text type="lg" style={[pal.text]}> + You don't have any pinned feeds. + </Text> + </View> + ) : ( + savedFeeds.pinned.map(feed => ( + <ListItem + key={feed._reactKey} + savedFeeds={savedFeeds} + item={feed} + /> + )) + ) + ) : ( + <ActivityIndicator style={{marginTop: 20}} /> + )} + <View style={[pal.text, pal.border, styles.title]}> + <Text type="title" style={pal.text}> + Saved Feeds + </Text> + </View> + {savedFeeds.hasLoaded ? ( + !savedFeeds.unpinned.length ? ( + <View + style={[ + pal.border, + isMobile && s.flex1, + pal.viewLight, + styles.empty, + ]}> + <Text type="lg" style={[pal.text]}> + You don't have any saved feeds. + </Text> + </View> + ) : ( + savedFeeds.unpinned.map(feed => ( + <ListItem + key={feed._reactKey} + savedFeeds={savedFeeds} + item={feed} + /> + )) + ) + ) : ( + <ActivityIndicator style={{marginTop: 20}} /> + )} + <View style={styles.footerText}> <Text type="sm" style={pal.textLight}> Feeds are custom algorithms that users build with a little coding @@ -99,60 +144,8 @@ export const SavedFeeds = withAuthRequired( for more information. </Text> </View> - {savedFeeds.isLoading && <ActivityIndicator />} - </> - ) - }, [pal, savedFeeds.isLoading]) - - const onRefresh = useCallback(() => savedFeeds.refresh(), [savedFeeds]) - - const onDragEnd = useCallback( - async ({data}: {data: FeedSourceModel[]}) => { - 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 ( - <CenteredView - style={[ - s.hContentRegion, - pal.border, - isTabletOrDesktop && styles.desktopContainer, - ]}> - <ViewHeader title="Edit My Feeds" showOnDesktop showBorder /> - <DraggableFlatList - containerStyle={[isTabletOrDesktop ? s.hContentRegion : s.flex1]} - data={savedFeeds.pinned.concat(savedFeeds.unpinned)} - keyExtractor={item => item.uri} - refreshing={savedFeeds.isRefreshing} - refreshControl={ - <RefreshControl - refreshing={savedFeeds.isRefreshing} - onRefresh={onRefresh} - tintColor={pal.colors.text} - titleColor={pal.colors.text} - /> - } - renderItem={({item, drag}) => ( - <ListItem savedFeeds={savedFeeds} item={item} drag={drag} /> - )} - getItemLayout={(data, index) => ({ - length: 77, - offset: 77 * index, - index, - })} - initialNumToRender={10} - ListFooterComponent={renderListFooterComponent} - ListEmptyComponent={renderListEmptyComponent} - extraData={savedFeeds.isLoading} - onDragEnd={onDragEnd} - /> + <View style={{height: 100}} /> + </ScrollView> </CenteredView> ) }), @@ -161,11 +154,9 @@ export const SavedFeeds = withAuthRequired( const ListItem = observer(function ListItemImpl({ savedFeeds, item, - drag, }: { savedFeeds: SavedFeedsModel item: FeedSourceModel - drag: () => void }) { const pal = usePalette('default') const store = useStores() @@ -196,59 +187,46 @@ const ListItem = observer(function ListItemImpl({ ) return ( - <ScaleDecorator> - <ShadowDecorator> - <Pressable - accessibilityRole="button" - onLongPress={isPinned ? drag : undefined} - delayLongPress={200} - style={[styles.itemContainer, pal.border]}> - {isPinned && isWeb ? ( - <View style={styles.webArrowButtonsContainer}> - <TouchableOpacity accessibilityRole="button" onPress={onPressUp}> - <FontAwesomeIcon - icon="arrow-up" - size={12} - style={[pal.text, styles.webArrowUpButton]} - /> - </TouchableOpacity> - <TouchableOpacity - accessibilityRole="button" - onPress={onPressDown}> - <FontAwesomeIcon - icon="arrow-down" - size={12} - style={[pal.text]} - /> - </TouchableOpacity> - </View> - ) : isPinned ? ( - <FontAwesomeIcon - icon="bars" - size={20} - color={pal.colors.text} - style={s.ml20} - /> - ) : null} - <FeedSourceCard - key={item.uri} - item={item} - showSaveBtn - style={styles.noBorder} - /> + <Pressable + accessibilityRole="button" + style={[styles.itemContainer, pal.border]}> + {isPinned ? ( + <View style={styles.webArrowButtonsContainer}> <TouchableOpacity accessibilityRole="button" - hitSlop={10} - onPress={onTogglePinned}> + onPress={onPressUp} + hitSlop={HITSLOP_TOP}> <FontAwesomeIcon - icon="thumb-tack" - size={20} - color={isPinned ? colors.blue3 : pal.colors.icon} + icon="arrow-up" + size={12} + style={[pal.text, styles.webArrowUpButton]} /> </TouchableOpacity> - </Pressable> - </ShadowDecorator> - </ScaleDecorator> + <TouchableOpacity + accessibilityRole="button" + onPress={onPressDown} + hitSlop={HITSLOP_BOTTOM}> + <FontAwesomeIcon icon="arrow-down" size={12} style={[pal.text]} /> + </TouchableOpacity> + </View> + ) : null} + <FeedSourceCard + key={item.uri} + item={item} + showSaveBtn + style={styles.noBorder} + /> + <TouchableOpacity + accessibilityRole="button" + hitSlop={10} + onPress={onTogglePinned}> + <FontAwesomeIcon + icon="thumb-tack" + size={20} + color={isPinned ? colors.blue3 : pal.colors.icon} + /> + </TouchableOpacity> + </Pressable> ) }) @@ -262,12 +240,17 @@ const styles = StyleSheet.create({ empty: { paddingHorizontal: 20, paddingVertical: 20, - borderRadius: 16, - marginHorizontal: 24, + borderRadius: 8, + marginHorizontal: 10, marginTop: 10, }, + title: { + paddingHorizontal: 14, + paddingTop: 20, + paddingBottom: 10, + borderBottomWidth: 1, + }, itemContainer: { - flex: 1, flexDirection: 'row', alignItems: 'center', borderBottomWidth: 1, @@ -289,14 +272,4 @@ const styles = StyleSheet.create({ paddingTop: 22, paddingBottom: 100, }, - footerLinks: { - borderBottomWidth: 1, - borderTopWidth: 0, - }, - footerLink: { - flexDirection: 'row', - paddingHorizontal: 26, - paddingVertical: 18, - gap: 18, - }, }) |