Remove redundant feed-settings page

zio/stable
Paul Frazee 2023-05-17 22:04:01 -05:00
parent 177df36330
commit 998879d6d6
8 changed files with 97 additions and 293 deletions

View File

@ -55,9 +55,8 @@ import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy'
import {AppPasswords} from 'view/screens/AppPasswords' import {AppPasswords} from 'view/screens/AppPasswords'
import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts' import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts'
import {ModerationBlockedAccounts} from 'view/screens/ModerationBlockedAccounts' import {ModerationBlockedAccounts} from 'view/screens/ModerationBlockedAccounts'
import {SavedFeeds} from 'view/screens/SavedFeeds'
import {getRoutingInstrumentation} from 'lib/sentry' import {getRoutingInstrumentation} from 'lib/sentry'
import {SavedFeeds} from './view/screens/SavedFeeds'
import {PinnedFeeds} from 'view/screens/PinnedFeeds'
import {bskyTitle} from 'lib/strings/headings' import {bskyTitle} from 'lib/strings/headings'
const navigationRef = createNavigationContainerRef<AllNavigatorParams>() const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
@ -189,7 +188,6 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
options={{title: title('App Passwords')}} options={{title: title('App Passwords')}}
/> />
<Stack.Screen name="SavedFeeds" component={SavedFeeds} /> <Stack.Screen name="SavedFeeds" component={SavedFeeds} />
<Stack.Screen name="PinnedFeeds" component={PinnedFeeds} />
</> </>
) )
} }

View File

@ -27,7 +27,6 @@ export type CommonNavigatorParams = {
CopyrightPolicy: undefined CopyrightPolicy: undefined
AppPasswords: undefined AppPasswords: undefined
SavedFeeds: undefined SavedFeeds: undefined
PinnedFeeds: undefined
} }
export type BottomTabNavigatorParams = CommonNavigatorParams & { export type BottomTabNavigatorParams = CommonNavigatorParams & {

View File

@ -21,7 +21,6 @@ export const router = new Router({
Log: '/sys/log', Log: '/sys/log',
AppPasswords: '/settings/app-passwords', AppPasswords: '/settings/app-passwords',
SavedFeeds: '/settings/saved-feeds', SavedFeeds: '/settings/saved-feeds',
PinnedFeeds: '/settings/pinned-feeds',
Support: '/support', Support: '/support',
PrivacyPolicy: '/support/privacy', PrivacyPolicy: '/support/privacy',
TermsOfService: '/support/tos', TermsOfService: '/support/tos',

View File

@ -50,7 +50,7 @@ export const SavedFeeds = observer(
return ( return (
<Link <Link
style={[styles.footerLink, pal.border]} style={[styles.footerLink, pal.border]}
href="/settings/pinned-feeds"> href="/settings/saved-feeds">
<FontAwesomeIcon icon="cog" size={18} color={pal.colors.icon} /> <FontAwesomeIcon icon="cog" size={18} color={pal.colors.icon} />
<Text type="lg-medium" style={pal.textLight}> <Text type="lg-medium" style={pal.textLight}>
Settings Settings

View File

@ -60,14 +60,17 @@ import {faPenNib} from '@fortawesome/free-solid-svg-icons/faPenNib'
import {faPenToSquare} from '@fortawesome/free-solid-svg-icons/faPenToSquare' import {faPenToSquare} from '@fortawesome/free-solid-svg-icons/faPenToSquare'
import {faPlus} from '@fortawesome/free-solid-svg-icons/faPlus' import {faPlus} from '@fortawesome/free-solid-svg-icons/faPlus'
import {faQuoteLeft} from '@fortawesome/free-solid-svg-icons/faQuoteLeft' 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 {faShare} from '@fortawesome/free-solid-svg-icons/faShare'
import {faShareFromSquare} from '@fortawesome/free-solid-svg-icons/faShareFromSquare' import {faShareFromSquare} from '@fortawesome/free-solid-svg-icons/faShareFromSquare'
import {faShield} from '@fortawesome/free-solid-svg-icons/faShield' import {faShield} from '@fortawesome/free-solid-svg-icons/faShield'
import {faSquarePlus} from '@fortawesome/free-regular-svg-icons/faSquarePlus' import {faSquarePlus} from '@fortawesome/free-regular-svg-icons/faSquarePlus'
import {faSignal} from '@fortawesome/free-solid-svg-icons/faSignal' import {faSignal} from '@fortawesome/free-solid-svg-icons/faSignal'
import {faReply} from '@fortawesome/free-solid-svg-icons/faReply' import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket'
import {faRetweet} from '@fortawesome/free-solid-svg-icons/faRetweet' import {faTrashCan} from '@fortawesome/free-regular-svg-icons/faTrashCan'
import {faRss} from '@fortawesome/free-solid-svg-icons/faRss'
import {faUser} from '@fortawesome/free-regular-svg-icons/faUser' import {faUser} from '@fortawesome/free-regular-svg-icons/faUser'
import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers' import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers'
import {faUserCheck} from '@fortawesome/free-solid-svg-icons/faUserCheck' 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 {faUserPlus} from '@fortawesome/free-solid-svg-icons/faUserPlus'
import {faUserXmark} from '@fortawesome/free-solid-svg-icons/faUserXmark' import {faUserXmark} from '@fortawesome/free-solid-svg-icons/faUserXmark'
import {faUsersSlash} from '@fortawesome/free-solid-svg-icons/faUsersSlash' 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 {faX} from '@fortawesome/free-solid-svg-icons/faX'
import {faXmark} from '@fortawesome/free-solid-svg-icons/faXmark' import {faXmark} from '@fortawesome/free-solid-svg-icons/faXmark'
import {faPlay} from '@fortawesome/free-solid-svg-icons/faPlay' import {faPlay} from '@fortawesome/free-solid-svg-icons/faPlay'
@ -148,6 +149,7 @@ export function setup() {
faReply, faReply,
faRetweet, faRetweet,
faRss, faRss,
faSatelliteDish,
faShare, faShare,
faShareFromSquare, faShareFromSquare,
faShield, faShield,

View File

@ -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},
})

View File

@ -4,9 +4,8 @@ import {
StyleSheet, StyleSheet,
View, View,
ActivityIndicator, ActivityIndicator,
FlatList, Pressable,
TouchableOpacity, TouchableOpacity,
ScrollView,
} from 'react-native' } from 'react-native'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import {NativeStackScreenProps} from '@react-navigation/native-stack' 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 {Text} from 'view/com/util/text/Text'
import {isDesktopWeb, isWeb} from 'platform/detection' import {isDesktopWeb, isWeb} from 'platform/detection'
import {s} from 'lib/styles' import {s} from 'lib/styles'
import {SavedFeedsModel} from 'state/models/ui/saved-feeds' import DraggableFlatList, {
import {Link} from 'view/com/util/Link' ShadowDecorator,
import {UserAvatar} from 'view/com/util/UserAvatar' ScaleDecorator,
} from 'react-native-draggable-flatlist'
import {SavedFeedItem} from 'view/com/feeds/SavedFeedItem' 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'> type Props = NativeStackScreenProps<CommonNavigatorParams, 'SavedFeeds'>
export const SavedFeeds = withAuthRequired( export const SavedFeeds = withAuthRequired(
observer(({navigation}: Props) => { observer(({}: Props) => {
// hooks for global items // hooks for global items
const pal = usePalette('default') const pal = usePalette('default')
const rootStore = useStores() const rootStore = useStores()
@ -55,8 +56,8 @@ export const SavedFeeds = withAuthRequired(
styles.empty, styles.empty,
]}> ]}>
<Text type="lg" style={[pal.text]}> <Text type="lg" style={[pal.text]}>
You don't have any saved feeds. To save a feed, click the save You don't have any pinned feeds. To pin a feed, go back to the Saved
button when a custom feed or algorithm shows up. Feeds screen and click the pin icon!
</Text> </Text>
</View> </View>
) )
@ -71,10 +72,10 @@ export const SavedFeeds = withAuthRequired(
return ( return (
<CenteredView style={[s.flex1]}> <CenteredView style={[s.flex1]}>
<ViewHeader title="Saved Feeds" showOnDesktop /> <ViewHeader title="Edit My Feeds" showOnDesktop />
<FlatList <DraggableFlatList
style={[!isDesktopWeb && s.flex1]} containerStyle={[!isDesktopWeb && s.flex1]}
data={savedFeeds.feeds} data={[...savedFeeds.pinned, ...savedFeeds.unpinned]} // make a copy so this FlatList re-renders when pinned changes
keyExtractor={item => item.data.uri} keyExtractor={item => item.data.uri}
refreshing={savedFeeds.isRefreshing} refreshing={savedFeeds.isRefreshing}
refreshControl={ refreshControl={
@ -85,19 +86,12 @@ export const SavedFeeds = withAuthRequired(
titleColor={pal.colors.text} titleColor={pal.colors.text}
/> />
} }
renderItem={({item}) => ( renderItem={({item, drag}) => <ListItem item={item} drag={drag} />}
<SavedFeedItem item={item} savedFeeds={savedFeeds} />
)}
initialNumToRender={10} initialNumToRender={10}
ListHeaderComponent={() => (
<ListHeaderComponent
savedFeeds={savedFeeds}
navigation={navigation}
/>
)}
ListFooterComponent={_ListFooterComponent} ListFooterComponent={_ListFooterComponent}
ListEmptyComponent={_ListEmptyComponent} ListEmptyComponent={_ListEmptyComponent}
extraData={savedFeeds.isLoading} extraData={savedFeeds.isLoading}
onDragEnd={({data}) => savedFeeds.reorderPinnedFeeds(data)}
// @ts-ignore our .web version only -prf // @ts-ignore our .web version only -prf
desktopFixedHeight desktopFixedHeight
/> />
@ -106,64 +100,56 @@ export const SavedFeeds = withAuthRequired(
}), }),
) )
const ListHeaderComponent = observer( const ListItem = observer(
({ ({item, drag}: {item: CustomFeedModel; drag: () => void}) => {
savedFeeds,
navigation,
}: {
savedFeeds: SavedFeedsModel
navigation: Props['navigation']
}) => {
const pal = usePalette('default') const pal = usePalette('default')
const rootStore = useStores()
const savedFeeds = useMemo(() => rootStore.me.savedFeeds, [rootStore])
const isPinned = savedFeeds.isPinned(item)
return ( return (
<View style={styles.headerContainer}> <ScaleDecorator>
{savedFeeds.pinned.length > 0 ? ( <ShadowDecorator>
<View style={styles.pinnedContainer}> <Pressable
<View style={styles.pinnedHeader}> accessibilityRole="button"
<Text type="lg-bold" style={[pal.text]}> onLongPress={isPinned ? drag : undefined}
Pinned Feeds style={[styles.itemContainer, pal.border]}>
</Text> {isPinned && isWeb ? (
<Link href="/settings/pinned-feeds"> <View style={styles.webArrowButtonsContainer}>
<Text style={[styles.editPinned, pal.text]}>Edit</Text> <TouchableOpacity
</Link> accessibilityRole="button"
</View> onPress={() => {
savedFeeds.movePinnedItem(item, 'up')
<ScrollView }}>
horizontal={true} <FontAwesomeIcon
showsHorizontalScrollIndicator={isWeb}> icon="arrow-up"
{savedFeeds.pinned.map(item => { size={20}
return ( style={[s.mr10, pal.text, styles.webArrowUpButton]}
<TouchableOpacity />
key={item.data.uri} </TouchableOpacity>
accessibilityRole="button" <TouchableOpacity
onPress={() => { accessibilityRole="button"
navigation.navigate('ProfileCustomFeed', { onPress={() => {
name: item.data.creator.did, savedFeeds.movePinnedItem(item, 'down')
rkey: new AtUri(item.data.uri).rkey, }}>
}) <FontAwesomeIcon
}} icon="arrow-down"
style={styles.pinnedItem}> size={20}
<UserAvatar style={[s.mr10, pal.text]}
type="algo" />
avatar={item.data.avatar} </TouchableOpacity>
size={80} </View>
/> ) : isPinned ? (
<Text <FontAwesomeIcon
type="sm-medium" icon="bars"
numberOfLines={1} size={20}
style={[pal.text, styles.pinnedItemName]}> color={pal.colors.text}
{item.data.displayName ?? style={s.ml20}
`${item.data.creator.displayName}'s feed`} />
</Text> ) : null}
</TouchableOpacity> <SavedFeedItem item={item} savedFeeds={savedFeeds} showSaveBtn />
) </Pressable>
})} </ShadowDecorator>
</ScrollView> </ScaleDecorator>
</View>
) : null}
<Text type="lg-bold">All Saved Feeds</Text>
</View>
) )
}, },
) )
@ -179,15 +165,15 @@ const styles = StyleSheet.create({
marginHorizontal: 24, marginHorizontal: 24,
marginTop: 10, marginTop: 10,
}, },
headerContainer: {paddingHorizontal: 18, paddingTop: 18}, itemContainer: {
pinnedContainer: {marginBottom: 18, gap: 18},
pinnedHeader: {flexDirection: 'row', justifyContent: 'space-between'},
pinnedItem: {
flex: 1, flex: 1,
flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
marginRight: 18, borderTopWidth: 1,
maxWidth: 100,
}, },
pinnedItemName: {marginTop: 8, textAlign: 'center'}, webArrowButtonsContainer: {
editPinned: {textDecorationLine: 'underline'}, flexDirection: 'column',
justifyContent: 'space-around',
},
webArrowUpButton: {marginBottom: 10},
}) })

View File

@ -284,23 +284,6 @@ export const SettingsScreen = withAuthRequired(
<View style={styles.spacer20} /> <View style={styles.spacer20} />
<Link
testID="bookmarkedAlgosBtn"
style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]}
accessibilityHint="Custom Algorithms"
accessibilityLabel="Opens screen with all bookmarked custom algorithms"
href="/settings/saved-feeds">
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="rss"
style={pal.text as FontAwesomeIconStyle}
/>
</View>
<Text type="lg" style={pal.text}>
Custom Algorithms
</Text>
</Link>
<Text type="xl-bold" style={[pal.text, styles.heading]}> <Text type="xl-bold" style={[pal.text, styles.heading]}>
Advanced Advanced
</Text> </Text>
@ -318,6 +301,22 @@ export const SettingsScreen = withAuthRequired(
App passwords App passwords
</Text> </Text>
</Link> </Link>
<Link
testID="savedFeedsBtn"
style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]}
accessibilityHint="Saved Feeds"
accessibilityLabel="Opens screen with all saved feeds"
href="/settings/saved-feeds">
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="satellite-dish"
style={pal.text as FontAwesomeIconStyle}
/>
</View>
<Text type="lg" style={pal.text}>
Saved Feeds
</Text>
</Link>
<TouchableOpacity <TouchableOpacity
testID="contentLanguagesBtn" testID="contentLanguagesBtn"
style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]}