diff options
-rw-r--r-- | src/Navigation.tsx | 4 | ||||
-rw-r--r-- | src/lib/routes/types.ts | 1 | ||||
-rw-r--r-- | src/routes.ts | 1 | ||||
-rw-r--r-- | src/view/com/feeds/SavedFeeds.tsx | 2 | ||||
-rw-r--r-- | src/view/index.ts | 12 | ||||
-rw-r--r-- | src/view/screens/PinnedFeeds.tsx | 179 | ||||
-rw-r--r-- | src/view/screens/SavedFeeds.tsx | 158 | ||||
-rw-r--r-- | src/view/screens/Settings.tsx | 27 |
8 files changed, 94 insertions, 290 deletions
diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 025020afa..ea36b0f20 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -55,9 +55,8 @@ import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy' import {AppPasswords} from 'view/screens/AppPasswords' import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts' import {ModerationBlockedAccounts} from 'view/screens/ModerationBlockedAccounts' +import {SavedFeeds} from 'view/screens/SavedFeeds' import {getRoutingInstrumentation} from 'lib/sentry' -import {SavedFeeds} from './view/screens/SavedFeeds' -import {PinnedFeeds} from 'view/screens/PinnedFeeds' import {bskyTitle} from 'lib/strings/headings' const navigationRef = createNavigationContainerRef<AllNavigatorParams>() @@ -189,7 +188,6 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { options={{title: title('App Passwords')}} /> <Stack.Screen name="SavedFeeds" component={SavedFeeds} /> - <Stack.Screen name="PinnedFeeds" component={PinnedFeeds} /> </> ) } diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 52d0e9af2..5c5185602 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -27,7 +27,6 @@ export type CommonNavigatorParams = { CopyrightPolicy: undefined AppPasswords: undefined SavedFeeds: undefined - PinnedFeeds: undefined } export type BottomTabNavigatorParams = CommonNavigatorParams & { diff --git a/src/routes.ts b/src/routes.ts index 7501e7abf..c5dc4fb5b 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -21,7 +21,6 @@ export const router = new Router({ Log: '/sys/log', AppPasswords: '/settings/app-passwords', SavedFeeds: '/settings/saved-feeds', - PinnedFeeds: '/settings/pinned-feeds', Support: '/support', PrivacyPolicy: '/support/privacy', TermsOfService: '/support/tos', diff --git a/src/view/com/feeds/SavedFeeds.tsx b/src/view/com/feeds/SavedFeeds.tsx index 06c47d114..2d0057cfb 100644 --- a/src/view/com/feeds/SavedFeeds.tsx +++ b/src/view/com/feeds/SavedFeeds.tsx @@ -50,7 +50,7 @@ export const SavedFeeds = observer( return ( <Link style={[styles.footerLink, pal.border]} - href="/settings/pinned-feeds"> + href="/settings/saved-feeds"> <FontAwesomeIcon icon="cog" size={18} color={pal.colors.icon} /> <Text type="lg-medium" style={pal.textLight}> Settings diff --git a/src/view/index.ts b/src/view/index.ts index 84fc3f315..f06bdaccc 100644 --- a/src/view/index.ts +++ b/src/view/index.ts @@ -60,14 +60,17 @@ import {faPenNib} from '@fortawesome/free-solid-svg-icons/faPenNib' import {faPenToSquare} from '@fortawesome/free-solid-svg-icons/faPenToSquare' import {faPlus} from '@fortawesome/free-solid-svg-icons/faPlus' import {faQuoteLeft} from '@fortawesome/free-solid-svg-icons/faQuoteLeft' +import {faReply} from '@fortawesome/free-solid-svg-icons/faReply' +import {faRetweet} from '@fortawesome/free-solid-svg-icons/faRetweet' +import {faRss} from '@fortawesome/free-solid-svg-icons/faRss' +import {faSatelliteDish} from '@fortawesome/free-solid-svg-icons/faSatelliteDish' import {faShare} from '@fortawesome/free-solid-svg-icons/faShare' import {faShareFromSquare} from '@fortawesome/free-solid-svg-icons/faShareFromSquare' import {faShield} from '@fortawesome/free-solid-svg-icons/faShield' import {faSquarePlus} from '@fortawesome/free-regular-svg-icons/faSquarePlus' import {faSignal} from '@fortawesome/free-solid-svg-icons/faSignal' -import {faReply} from '@fortawesome/free-solid-svg-icons/faReply' -import {faRetweet} from '@fortawesome/free-solid-svg-icons/faRetweet' -import {faRss} from '@fortawesome/free-solid-svg-icons/faRss' +import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket' +import {faTrashCan} from '@fortawesome/free-regular-svg-icons/faTrashCan' import {faUser} from '@fortawesome/free-regular-svg-icons/faUser' import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers' import {faUserCheck} from '@fortawesome/free-solid-svg-icons/faUserCheck' @@ -75,8 +78,6 @@ import {faUserSlash} from '@fortawesome/free-solid-svg-icons/faUserSlash' import {faUserPlus} from '@fortawesome/free-solid-svg-icons/faUserPlus' import {faUserXmark} from '@fortawesome/free-solid-svg-icons/faUserXmark' import {faUsersSlash} from '@fortawesome/free-solid-svg-icons/faUsersSlash' -import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket' -import {faTrashCan} from '@fortawesome/free-regular-svg-icons/faTrashCan' import {faX} from '@fortawesome/free-solid-svg-icons/faX' import {faXmark} from '@fortawesome/free-solid-svg-icons/faXmark' import {faPlay} from '@fortawesome/free-solid-svg-icons/faPlay' @@ -148,6 +149,7 @@ export function setup() { faReply, faRetweet, faRss, + faSatelliteDish, faShare, faShareFromSquare, faShield, diff --git a/src/view/screens/PinnedFeeds.tsx b/src/view/screens/PinnedFeeds.tsx deleted file mode 100644 index a90012093..000000000 --- a/src/view/screens/PinnedFeeds.tsx +++ /dev/null @@ -1,179 +0,0 @@ -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' -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, isWeb} from 'platform/detection' -import {s} from 'lib/styles' -import DraggableFlatList, { - ShadowDecorator, - ScaleDecorator, -} from 'react-native-draggable-flatlist' -import {SavedFeedItem} from 'view/com/feeds/SavedFeedItem' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {CustomFeedModel} from 'state/models/feeds/custom-feed' - -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="Edit My Feeds" showOnDesktop /> - <DraggableFlatList - containerStyle={[!isDesktopWeb && s.flex1]} - data={[...savedFeeds.pinned, ...savedFeeds.unpinned]} // make a copy so this FlatList re-renders when pinned changes - 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}) => <ListItem item={item} drag={drag} />} - initialNumToRender={10} - ListFooterComponent={_ListFooterComponent} - ListEmptyComponent={_ListEmptyComponent} - extraData={savedFeeds.isLoading} - onDragEnd={({data}) => savedFeeds.reorderPinnedFeeds(data)} - // @ts-ignore our .web version only -prf - desktopFixedHeight - /> - </CenteredView> - ) - }), -) - -const ListItem = observer( - ({item, drag}: {item: CustomFeedModel; drag: () => void}) => { - const pal = usePalette('default') - const rootStore = useStores() - const savedFeeds = useMemo(() => rootStore.me.savedFeeds, [rootStore]) - const isPinned = savedFeeds.isPinned(item) - return ( - <ScaleDecorator> - <ShadowDecorator> - <Pressable - accessibilityRole="button" - onLongPress={isPinned ? drag : undefined} - style={[styles.itemContainer, pal.border]}> - {isPinned && isWeb ? ( - <View style={styles.webArrowButtonsContainer}> - <TouchableOpacity - accessibilityRole="button" - onPress={() => { - savedFeeds.movePinnedItem(item, 'up') - }}> - <FontAwesomeIcon - icon="arrow-up" - size={20} - style={[s.mr10, pal.text, styles.webArrowUpButton]} - /> - </TouchableOpacity> - <TouchableOpacity - accessibilityRole="button" - onPress={() => { - savedFeeds.movePinnedItem(item, 'down') - }}> - <FontAwesomeIcon - icon="arrow-down" - size={20} - style={[s.mr10, pal.text]} - /> - </TouchableOpacity> - </View> - ) : isPinned ? ( - <FontAwesomeIcon - icon="bars" - size={20} - color={pal.colors.text} - style={s.ml20} - /> - ) : null} - <SavedFeedItem item={item} savedFeeds={savedFeeds} showSaveBtn /> - </Pressable> - </ShadowDecorator> - </ScaleDecorator> - ) - }, -) - -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', - borderTopWidth: 1, - }, - webArrowButtonsContainer: { - flexDirection: 'column', - justifyContent: 'space-around', - }, - webArrowUpButton: {marginBottom: 10}, -}) diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx index c32639889..b1ea27af8 100644 --- a/src/view/screens/SavedFeeds.tsx +++ b/src/view/screens/SavedFeeds.tsx @@ -4,9 +4,8 @@ import { StyleSheet, View, ActivityIndicator, - FlatList, + Pressable, TouchableOpacity, - ScrollView, } from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' @@ -21,16 +20,18 @@ import {CenteredView} from 'view/com/util/Views' import {Text} from 'view/com/util/text/Text' import {isDesktopWeb, isWeb} from 'platform/detection' import {s} from 'lib/styles' -import {SavedFeedsModel} from 'state/models/ui/saved-feeds' -import {Link} from 'view/com/util/Link' -import {UserAvatar} from 'view/com/util/UserAvatar' +import DraggableFlatList, { + ShadowDecorator, + ScaleDecorator, +} from 'react-native-draggable-flatlist' import {SavedFeedItem} from 'view/com/feeds/SavedFeedItem' -import {AtUri} from '@atproto/api' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {CustomFeedModel} from 'state/models/feeds/custom-feed' type Props = NativeStackScreenProps<CommonNavigatorParams, 'SavedFeeds'> export const SavedFeeds = withAuthRequired( - observer(({navigation}: Props) => { + observer(({}: Props) => { // hooks for global items const pal = usePalette('default') const rootStore = useStores() @@ -55,8 +56,8 @@ export const SavedFeeds = withAuthRequired( styles.empty, ]}> <Text type="lg" style={[pal.text]}> - You don't have any saved feeds. To save a feed, click the save - button when a custom feed or algorithm shows up. + 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> ) @@ -71,10 +72,10 @@ export const SavedFeeds = withAuthRequired( return ( <CenteredView style={[s.flex1]}> - <ViewHeader title="Saved Feeds" showOnDesktop /> - <FlatList - style={[!isDesktopWeb && s.flex1]} - data={savedFeeds.feeds} + <ViewHeader title="Edit My Feeds" showOnDesktop /> + <DraggableFlatList + containerStyle={[!isDesktopWeb && s.flex1]} + data={[...savedFeeds.pinned, ...savedFeeds.unpinned]} // make a copy so this FlatList re-renders when pinned changes keyExtractor={item => item.data.uri} refreshing={savedFeeds.isRefreshing} refreshControl={ @@ -85,19 +86,12 @@ export const SavedFeeds = withAuthRequired( titleColor={pal.colors.text} /> } - renderItem={({item}) => ( - <SavedFeedItem item={item} savedFeeds={savedFeeds} /> - )} + renderItem={({item, drag}) => <ListItem item={item} drag={drag} />} initialNumToRender={10} - ListHeaderComponent={() => ( - <ListHeaderComponent - savedFeeds={savedFeeds} - navigation={navigation} - /> - )} ListFooterComponent={_ListFooterComponent} ListEmptyComponent={_ListEmptyComponent} extraData={savedFeeds.isLoading} + onDragEnd={({data}) => savedFeeds.reorderPinnedFeeds(data)} // @ts-ignore our .web version only -prf desktopFixedHeight /> @@ -106,64 +100,56 @@ export const SavedFeeds = withAuthRequired( }), ) -const ListHeaderComponent = observer( - ({ - savedFeeds, - navigation, - }: { - savedFeeds: SavedFeedsModel - navigation: Props['navigation'] - }) => { +const ListItem = observer( + ({item, drag}: {item: CustomFeedModel; drag: () => void}) => { const pal = usePalette('default') + const rootStore = useStores() + const savedFeeds = useMemo(() => rootStore.me.savedFeeds, [rootStore]) + const isPinned = savedFeeds.isPinned(item) return ( - <View style={styles.headerContainer}> - {savedFeeds.pinned.length > 0 ? ( - <View style={styles.pinnedContainer}> - <View style={styles.pinnedHeader}> - <Text type="lg-bold" style={[pal.text]}> - Pinned Feeds - </Text> - <Link href="/settings/pinned-feeds"> - <Text style={[styles.editPinned, pal.text]}>Edit</Text> - </Link> - </View> - - <ScrollView - horizontal={true} - showsHorizontalScrollIndicator={isWeb}> - {savedFeeds.pinned.map(item => { - return ( - <TouchableOpacity - key={item.data.uri} - accessibilityRole="button" - onPress={() => { - navigation.navigate('ProfileCustomFeed', { - name: item.data.creator.did, - rkey: new AtUri(item.data.uri).rkey, - }) - }} - style={styles.pinnedItem}> - <UserAvatar - type="algo" - avatar={item.data.avatar} - size={80} - /> - <Text - type="sm-medium" - numberOfLines={1} - style={[pal.text, styles.pinnedItemName]}> - {item.data.displayName ?? - `${item.data.creator.displayName}'s feed`} - </Text> - </TouchableOpacity> - ) - })} - </ScrollView> - </View> - ) : null} - - <Text type="lg-bold">All Saved Feeds</Text> - </View> + <ScaleDecorator> + <ShadowDecorator> + <Pressable + accessibilityRole="button" + onLongPress={isPinned ? drag : undefined} + style={[styles.itemContainer, pal.border]}> + {isPinned && isWeb ? ( + <View style={styles.webArrowButtonsContainer}> + <TouchableOpacity + accessibilityRole="button" + onPress={() => { + savedFeeds.movePinnedItem(item, 'up') + }}> + <FontAwesomeIcon + icon="arrow-up" + size={20} + style={[s.mr10, pal.text, styles.webArrowUpButton]} + /> + </TouchableOpacity> + <TouchableOpacity + accessibilityRole="button" + onPress={() => { + savedFeeds.movePinnedItem(item, 'down') + }}> + <FontAwesomeIcon + icon="arrow-down" + size={20} + style={[s.mr10, pal.text]} + /> + </TouchableOpacity> + </View> + ) : isPinned ? ( + <FontAwesomeIcon + icon="bars" + size={20} + color={pal.colors.text} + style={s.ml20} + /> + ) : null} + <SavedFeedItem item={item} savedFeeds={savedFeeds} showSaveBtn /> + </Pressable> + </ShadowDecorator> + </ScaleDecorator> ) }, ) @@ -179,15 +165,15 @@ const styles = StyleSheet.create({ marginHorizontal: 24, marginTop: 10, }, - headerContainer: {paddingHorizontal: 18, paddingTop: 18}, - pinnedContainer: {marginBottom: 18, gap: 18}, - pinnedHeader: {flexDirection: 'row', justifyContent: 'space-between'}, - pinnedItem: { + itemContainer: { flex: 1, + flexDirection: 'row', alignItems: 'center', - marginRight: 18, - maxWidth: 100, + borderTopWidth: 1, + }, + webArrowButtonsContainer: { + flexDirection: 'column', + justifyContent: 'space-around', }, - pinnedItemName: {marginTop: 8, textAlign: 'center'}, - editPinned: {textDecorationLine: 'underline'}, + webArrowUpButton: {marginBottom: 10}, }) diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx index a919f11b0..3ce41f8c0 100644 --- a/src/view/screens/Settings.tsx +++ b/src/view/screens/Settings.tsx @@ -284,38 +284,37 @@ export const SettingsScreen = withAuthRequired( <View style={styles.spacer20} /> + <Text type="xl-bold" style={[pal.text, styles.heading]}> + Advanced + </Text> <Link - testID="bookmarkedAlgosBtn" + testID="appPasswordBtn" style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} - accessibilityHint="Custom Algorithms" - accessibilityLabel="Opens screen with all bookmarked custom algorithms" - href="/settings/saved-feeds"> + href="/settings/app-passwords"> <View style={[styles.iconContainer, pal.btn]}> <FontAwesomeIcon - icon="rss" + icon="lock" style={pal.text as FontAwesomeIconStyle} /> </View> <Text type="lg" style={pal.text}> - Custom Algorithms + App passwords </Text> </Link> - - <Text type="xl-bold" style={[pal.text, styles.heading]}> - Advanced - </Text> <Link - testID="appPasswordBtn" + testID="savedFeedsBtn" style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} - href="/settings/app-passwords"> + accessibilityHint="Saved Feeds" + accessibilityLabel="Opens screen with all saved feeds" + href="/settings/saved-feeds"> <View style={[styles.iconContainer, pal.btn]}> <FontAwesomeIcon - icon="lock" + icon="satellite-dish" style={pal.text as FontAwesomeIconStyle} /> </View> <Text type="lg" style={pal.text}> - App passwords + Saved Feeds </Text> </Link> <TouchableOpacity |