diff options
author | Ansh Nanda <anshnanda10@gmail.com> | 2023-05-16 18:28:44 -0700 |
---|---|---|
committer | Ansh Nanda <anshnanda10@gmail.com> | 2023-05-16 18:28:44 -0700 |
commit | 53ca0cd626cc71fea38fb0f59f68092ab406d143 (patch) | |
tree | 1976638d19e7ad542a8cf71641fc7d9d77718653 /src | |
parent | 139027ac5f843c65aae5ad824d049aae6dae9f79 (diff) | |
download | voidsky-53ca0cd626cc71fea38fb0f59f68092ab406d143.tar.zst |
drag to rearrange pinned items
Diffstat (limited to 'src')
-rw-r--r-- | src/Navigation.tsx | 2 | ||||
-rw-r--r-- | src/lib/routes/types.ts | 1 | ||||
-rw-r--r-- | src/routes.ts | 1 | ||||
-rw-r--r-- | src/view/com/algos/AlgoItem.tsx | 8 | ||||
-rw-r--r-- | src/view/com/algos/SavedFeedItem.tsx | 50 | ||||
-rw-r--r-- | src/view/screens/PinnedFeeds.tsx | 134 | ||||
-rw-r--r-- | src/view/screens/SavedFeeds.tsx | 99 |
7 files changed, 256 insertions, 39 deletions
diff --git a/src/Navigation.tsx b/src/Navigation.tsx index d4c992eb6..17d80dfdc 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -54,6 +54,7 @@ import {BlockedAccounts} from 'view/screens/BlockedAccounts' import {getRoutingInstrumentation} from 'lib/sentry' import {SavedFeeds} from './view/screens/SavedFeeds' import {CustomFeed} from './view/screens/CustomFeed' +import {PinnedFeeds} from 'view/screens/PinnedFeeds' const navigationRef = createNavigationContainerRef<AllNavigatorParams>() @@ -94,6 +95,7 @@ function commonScreens(Stack: typeof HomeTab) { <Stack.Screen name="CopyrightPolicy" component={CopyrightPolicyScreen} /> <Stack.Screen name="AppPasswords" component={AppPasswords} /> <Stack.Screen name="SavedFeeds" component={SavedFeeds} /> + <Stack.Screen name="PinnedFeeds" component={PinnedFeeds} /> <Stack.Screen name="CustomFeed" component={CustomFeed} /> <Stack.Screen name="MutedAccounts" component={MutedAccounts} /> <Stack.Screen name="BlockedAccounts" component={BlockedAccounts} /> diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 77ed58cc3..12ff27070 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -21,6 +21,7 @@ export type CommonNavigatorParams = { CopyrightPolicy: undefined AppPasswords: undefined SavedFeeds: undefined + PinnedFeeds: undefined CustomFeed: {name?: string; rkey: string} MutedAccounts: undefined BlockedAccounts: undefined diff --git a/src/routes.ts b/src/routes.ts index 2cdaa28bb..6998226c0 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -15,6 +15,7 @@ export const router = new Router({ Log: '/sys/log', AppPasswords: '/settings/app-passwords', SavedFeeds: '/settings/saved-feeds', + PinnedFeeds: '/settings/pinned-feeds', CustomFeed: '/profile/:name/feed/:rkey', MutedAccounts: '/settings/muted-accounts', BlockedAccounts: '/settings/blocked-accounts', diff --git a/src/view/com/algos/AlgoItem.tsx b/src/view/com/algos/AlgoItem.tsx index 6fbbd0df1..b28545c17 100644 --- a/src/view/com/algos/AlgoItem.tsx +++ b/src/view/com/algos/AlgoItem.tsx @@ -24,13 +24,11 @@ const AlgoItem = observer( item, style, showBottom = true, - onLongPress, reloadOnFocus = false, }: { item: AlgoItemModel style?: StyleProp<ViewStyle> showBottom?: boolean - onLongPress?: () => void reloadOnFocus?: boolean }) => { const store = useStores() @@ -54,7 +52,6 @@ const AlgoItem = observer( rkey: item.data.uri, }) }} - onLongPress={onLongPress} key={item.data.uri}> <View style={[styles.headerContainer]}> <View style={[s.mr10]}> @@ -64,8 +61,9 @@ const AlgoItem = observer( <Text style={[pal.text, s.bold]}> {item.data.displayName ?? 'Feed name'} </Text> - <Text style={[pal.textLight, styles.description]}> - {item.data.description ?? 'Feed description'} + <Text style={[pal.textLight, styles.description]} numberOfLines={5}> + {item.data.description ?? + "Explore our Feed for the latest updates and insights! Dive into a world of intriguing articles, trending news, and exciting stories that cover a wide range of topics. From technology breakthroughs to lifestyle tips, there's something here for everyone. Stay informed and get inspired with us. Join the conversation now!"} </Text> </View> </View> diff --git a/src/view/com/algos/SavedFeedItem.tsx b/src/view/com/algos/SavedFeedItem.tsx new file mode 100644 index 000000000..bb4ec10b3 --- /dev/null +++ b/src/view/com/algos/SavedFeedItem.tsx @@ -0,0 +1,50 @@ +import React from 'react' +import {View, TouchableOpacity, StyleSheet} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {colors} from 'lib/styles' +import {observer} from 'mobx-react-lite' +import {AlgoItemModel} from 'state/models/feeds/algo/algo-item' +import {SavedFeedsModel} from 'state/models/feeds/algo/saved' +import AlgoItem from './AlgoItem' + +export const SavedFeedItem = observer( + ({item, savedFeeds}: {item: AlgoItemModel; savedFeeds: SavedFeedsModel}) => { + const isPinned = savedFeeds.isPinned(item) + + return ( + <View style={styles.itemContainer}> + <AlgoItem + key={item.data.uri} + item={item} + showBottom={false} + style={styles.item} + /> + <TouchableOpacity + accessibilityRole="button" + onPress={() => { + savedFeeds.togglePinnedFeed(item) + console.log('pinned', savedFeeds.pinned) + console.log('isPinned', savedFeeds.isPinned(item)) + }}> + <FontAwesomeIcon + icon="thumb-tack" + size={20} + color={isPinned ? colors.blue3 : colors.gray3} + /> + </TouchableOpacity> + </View> + ) + }, +) + +const styles = StyleSheet.create({ + itemContainer: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + marginRight: 18, + }, + item: { + borderTopWidth: 0, + }, +}) diff --git a/src/view/screens/PinnedFeeds.tsx b/src/view/screens/PinnedFeeds.tsx new file mode 100644 index 000000000..2a0e3deff --- /dev/null +++ b/src/view/screens/PinnedFeeds.tsx @@ -0,0 +1,134 @@ +import React, {useCallback, useMemo} from 'react' +import { + RefreshControl, + StyleSheet, + View, + ActivityIndicator, + Pressable, +} from 'react-native' +import {useFocusEffect} from '@react-navigation/native' +import {NativeStackScreenProps} from '@react-navigation/native-stack' +import {useAnalytics} from 'lib/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 {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 {isDesktopWeb} from 'platform/detection' +import {s} from 'lib/styles' +import DraggableFlatList, { + ShadowDecorator, + ScaleDecorator, +} from 'react-native-draggable-flatlist' +import {SavedFeedItem} from 'view/com/algos/SavedFeedItem' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' + +type Props = NativeStackScreenProps<CommonNavigatorParams, 'PinnedFeeds'> + +export const PinnedFeeds = withAuthRequired( + observer(({}: Props) => { + // hooks for global items + const pal = usePalette('default') + const rootStore = useStores() + const {screen} = useAnalytics() + + // hooks for local + const savedFeeds = useMemo(() => rootStore.me.savedFeeds, [rootStore]) + useFocusEffect( + useCallback(() => { + screen('SavedFeeds') + rootStore.shell.setMinimalShellMode(false) + savedFeeds.refresh() + }, [screen, rootStore, savedFeeds]), + ) + const _ListEmptyComponent = () => { + return ( + <View + style={[ + pal.border, + !isDesktopWeb && s.flex1, + pal.viewLight, + styles.empty, + ]}> + <Text type="lg" style={[pal.text]}> + You don't have any pinned feeds. To pin a feed, go back to the Saved + Feeds screen and click the pin icon! + </Text> + </View> + ) + } + const _ListFooterComponent = () => { + return ( + <View style={styles.footer}> + {savedFeeds.isLoading && <ActivityIndicator />} + </View> + ) + } + + return ( + <CenteredView style={[s.flex1]}> + <ViewHeader title="Arrange Pinned Feeds" showOnDesktop /> + <DraggableFlatList + containerStyle={[!isDesktopWeb && s.flex1]} + data={savedFeeds.pinned} + keyExtractor={item => item.data.uri} + refreshing={savedFeeds.isRefreshing} + refreshControl={ + <RefreshControl + refreshing={savedFeeds.isRefreshing} + onRefresh={() => savedFeeds.refresh()} + tintColor={pal.colors.text} + titleColor={pal.colors.text} + /> + } + renderItem={({item, drag}) => ( + <ScaleDecorator> + <ShadowDecorator> + <Pressable + accessibilityRole="button" + onLongPress={drag} + style={styles.itemContainer}> + <FontAwesomeIcon icon="bars" size={20} style={styles.icon} /> + <SavedFeedItem item={item} savedFeeds={savedFeeds} /> + </Pressable> + </ShadowDecorator> + </ScaleDecorator> + )} + initialNumToRender={10} + ListFooterComponent={_ListFooterComponent} + ListEmptyComponent={_ListEmptyComponent} + extraData={savedFeeds.isLoading} + onDragEnd={({data}) => savedFeeds.reorderPinnedFeeds(data)} + // @ts-ignore our .web version only -prf + desktopFixedHeight + /> + </CenteredView> + ) + }), +) + +const styles = StyleSheet.create({ + footer: { + paddingVertical: 20, + }, + empty: { + paddingHorizontal: 20, + paddingVertical: 20, + borderRadius: 16, + marginHorizontal: 24, + marginTop: 10, + }, + itemContainer: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + marginLeft: 18, + }, + item: { + borderTopWidth: 0, + }, + icon: {marginRight: 10}, +}) diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx index 65ffdb233..8403efc6e 100644 --- a/src/view/screens/SavedFeeds.tsx +++ b/src/view/screens/SavedFeeds.tsx @@ -6,6 +6,7 @@ import { ActivityIndicator, FlatList, TouchableOpacity, + ScrollView, } from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' @@ -14,21 +15,21 @@ import {usePalette} from 'lib/hooks/usePalette' import {CommonNavigatorParams} from 'lib/routes/types' import {observer} from 'mobx-react-lite' import {useStores} from 'state/index' -import AlgoItem from 'view/com/algos/AlgoItem' 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 {isDesktopWeb} from 'platform/detection' -import {colors, s} from 'lib/styles' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {AlgoItemModel} from 'state/models/feeds/algo/algo-item' +import {s} from 'lib/styles' import {SavedFeedsModel} from 'state/models/feeds/algo/saved' +import {Link} from 'view/com/util/Link' +import {UserAvatar} from 'view/com/util/UserAvatar' +import {SavedFeedItem} from 'view/com/algos/SavedFeedItem' type Props = NativeStackScreenProps<CommonNavigatorParams, 'SavedFeeds'> export const SavedFeeds = withAuthRequired( - observer(({}: Props) => { + observer(({navigation}: Props) => { // hooks for global items const pal = usePalette('default') const rootStore = useStores() @@ -87,6 +88,12 @@ export const SavedFeeds = withAuthRequired( <SavedFeedItem item={item} savedFeeds={savedFeeds} /> )} initialNumToRender={10} + ListHeaderComponent={() => ( + <ListHeaderComponent + savedFeeds={savedFeeds} + navigation={navigation} + /> + )} ListFooterComponent={_ListFooterComponent} ListEmptyComponent={_ListEmptyComponent} extraData={savedFeeds.isLoading} @@ -98,31 +105,53 @@ export const SavedFeeds = withAuthRequired( }), ) -const SavedFeedItem = observer( - ({item, savedFeeds}: {item: AlgoItemModel; savedFeeds: SavedFeedsModel}) => { - const isPinned = savedFeeds.isPinned(item) - +const ListHeaderComponent = observer( + ({ + savedFeeds, + navigation, + }: { + savedFeeds: SavedFeedsModel + navigation: Props['navigation'] + }) => { return ( - <View style={styles.itemContainer}> - <AlgoItem - key={item.data.uri} - item={item} - showBottom={false} - style={styles.item} - /> - <TouchableOpacity - accessibilityRole="button" - onPress={() => { - savedFeeds.togglePinnedFeed(item) - console.log('pinned', savedFeeds.pinned) - console.log('isPinned', savedFeeds.isPinned(item)) - }}> - <FontAwesomeIcon - icon="thumb-tack" - size={20} - color={isPinned ? colors.blue3 : colors.gray3} - /> - </TouchableOpacity> + <View style={styles.headerContainer}> + {savedFeeds.pinned.length > 0 ? ( + <View style={styles.pinnedContainer}> + <View style={styles.pinnedHeader}> + <Text type="lg-bold">Pinned Feeds</Text> + <Link href="/settings/pinned-feeds"> + <Text style={styles.editPinned}>Edit</Text> + </Link> + </View> + + <ScrollView + horizontal={true} + showsHorizontalScrollIndicator={false}> + {savedFeeds.pinned.map(item => { + return ( + <TouchableOpacity + key={item.data.uri} + accessibilityRole="button" + onPress={() => { + navigation.navigate('CustomFeed', { + rkey: item.data.uri, + name: item.data.displayName, + }) + }} + style={styles.pinnedItem}> + <UserAvatar avatar={item.data.avatar} size={80} /> + <Text type="sm-medium" numberOfLines={1}> + {item.data.displayName ?? + `${item.data.creator.displayName}'s feed`} + </Text> + </TouchableOpacity> + ) + })} + </ScrollView> + </View> + ) : null} + + <Text type="lg-bold">All Saved Feeds</Text> </View> ) }, @@ -139,12 +168,14 @@ const styles = StyleSheet.create({ marginHorizontal: 24, marginTop: 10, }, - itemContainer: { - flexDirection: 'row', + headerContainer: {paddingHorizontal: 18}, + pinnedContainer: {marginBottom: 18, gap: 18}, + pinnedHeader: {flexDirection: 'row', justifyContent: 'space-between'}, + pinnedItem: { + flex: 1, alignItems: 'center', marginRight: 18, + maxWidth: 100, }, - item: { - borderTopWidth: 0, - }, + editPinned: {textDecorationLine: 'underline'}, }) |