diff options
Diffstat (limited to 'src/view/screens')
-rw-r--r-- | src/view/screens/CustomFeed.tsx | 33 | ||||
-rw-r--r-- | src/view/screens/Feeds.tsx | 41 | ||||
-rw-r--r-- | src/view/screens/PostThread.tsx | 1 | ||||
-rw-r--r-- | src/view/screens/PreferencesHomeFeed.tsx | 13 | ||||
-rw-r--r-- | src/view/screens/PreferencesThreads.tsx | 173 | ||||
-rw-r--r-- | src/view/screens/Profile.tsx | 4 | ||||
-rw-r--r-- | src/view/screens/Settings.tsx | 22 |
7 files changed, 272 insertions, 15 deletions
diff --git a/src/view/screens/CustomFeed.tsx b/src/view/screens/CustomFeed.tsx index eaa21f292..f8ceda940 100644 --- a/src/view/screens/CustomFeed.tsx +++ b/src/view/screens/CustomFeed.tsx @@ -185,6 +185,17 @@ export const CustomFeedScreenInner = observer( }) }, [store, currentFeed]) + const onPressAbout = React.useCallback(() => { + store.shell.openModal({ + name: 'confirm', + title: currentFeed?.displayName || '', + message: + currentFeed?.data.description || 'This feed has no description.', + confirmBtnText: 'Close', + onPressConfirm() {}, + }) + }, [store, currentFeed]) + const onPressViewAuthor = React.useCallback(() => { navigation.navigate('Profile', {name: handleOrDid}) }, [handleOrDid, navigation]) @@ -233,7 +244,21 @@ export const CustomFeedScreenInner = observer( }, [store, onSoftReset, isScreenFocused]) const dropdownItems: DropdownItem[] = React.useMemo(() => { - let items: DropdownItem[] = [ + return [ + currentFeed + ? { + testID: 'feedHeaderDropdownAboutBtn', + label: 'About this feed', + onPress: onPressAbout, + icon: { + ios: { + name: 'info.circle', + }, + android: '', + web: 'info', + }, + } + : undefined, { testID: 'feedHeaderDropdownViewAuthorBtn', label: 'View author', @@ -292,10 +317,10 @@ export const CustomFeedScreenInner = observer( web: 'share', }, }, - ] - return items + ].filter(Boolean) as DropdownItem[] }, [ - currentFeed?.isSaved, + currentFeed, + onPressAbout, onToggleSaved, onPressReport, onPressShare, diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx index d2c4a6d2d..6ca24bae9 100644 --- a/src/view/screens/Feeds.tsx +++ b/src/view/screens/Feeds.tsx @@ -16,7 +16,10 @@ import {ComposeIcon2, CogIcon} from 'lib/icons' import {s} from 'lib/styles' import {SearchInput} from 'view/com/util/forms/SearchInput' import {UserAvatar} from 'view/com/util/UserAvatar' -import {FeedFeedLoadingPlaceholder} from 'view/com/util/LoadingPlaceholder' +import { + LoadingPlaceholder, + FeedFeedLoadingPlaceholder, +} from 'view/com/util/LoadingPlaceholder' import {ErrorMessage} from 'view/com/util/error/ErrorMessage' import debounce from 'lodash.debounce' import {Text} from 'view/com/util/text/Text' @@ -42,7 +45,12 @@ export const FeedsScreen = withAuthRequired( React.useCallback(() => { store.shell.setMinimalShellMode(false) myFeeds.setup() - }, [store.shell, myFeeds]), + + const softResetSub = store.onScreenSoftReset(() => myFeeds.refresh()) + return () => { + softResetSub.remove() + } + }, [store, myFeeds]), ) const onPressCompose = React.useCallback(() => { @@ -119,6 +127,14 @@ export const FeedsScreen = withAuthRequired( ) } return <View /> + } else if (item.type === 'saved-feeds-loading') { + return ( + <> + {Array.from(Array(item.numItems)).map((_, i) => ( + <SavedFeedLoadingPlaceholder key={`placeholder-${i}`} /> + ))} + </> + ) } else if (item.type === 'saved-feed') { return ( <SavedFeed @@ -262,10 +278,7 @@ function SavedFeed({ asAnchor anchorNoUnderline> <UserAvatar type="algo" size={28} avatar={avatar} /> - <Text - type={isMobile ? 'lg' : 'lg-medium'} - style={[pal.text, s.flex1]} - numberOfLines={1}> + <Text type="lg-medium" style={[pal.text, s.flex1]} numberOfLines={1}> {displayName} </Text> {isMobile && ( @@ -279,6 +292,22 @@ function SavedFeed({ ) } +function SavedFeedLoadingPlaceholder() { + const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() + return ( + <View + style={[ + pal.border, + styles.savedFeed, + isMobile && styles.savedFeedMobile, + ]}> + <LoadingPlaceholder width={28} height={28} style={{borderRadius: 4}} /> + <LoadingPlaceholder width={140} height={12} /> + </View> + ) +} + const styles = StyleSheet.create({ container: { flex: 1, diff --git a/src/view/screens/PostThread.tsx b/src/view/screens/PostThread.tsx index a6aafa530..90b98d052 100644 --- a/src/view/screens/PostThread.tsx +++ b/src/view/screens/PostThread.tsx @@ -74,6 +74,7 @@ export const PostThreadScreen = withAuthRequired(({route}: Props) => { uri={uri} view={view} onPressReply={onPressReply} + treeView={store.preferences.threadTreeViewEnabled} /> </View> {isMobile && ( diff --git a/src/view/screens/PreferencesHomeFeed.tsx b/src/view/screens/PreferencesHomeFeed.tsx index 81bdfc95e..404d006f8 100644 --- a/src/view/screens/PreferencesHomeFeed.tsx +++ b/src/view/screens/PreferencesHomeFeed.tsx @@ -1,6 +1,7 @@ import React, {useState} from 'react' import {ScrollView, StyleSheet, TouchableOpacity, View} from 'react-native' import {observer} from 'mobx-react-lite' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Slider} from '@miblanchard/react-native-slider' import {Text} from '../com/util/text/Text' import {useStores} from 'state/index' @@ -66,7 +67,10 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({ ]}> <ViewHeader title="Home Feed Preferences" showOnDesktop /> <View - style={[styles.titleSection, isTabletOrDesktop && {paddingTop: 20}]}> + style={[ + styles.titleSection, + isTabletOrDesktop && {paddingTop: 20, paddingBottom: 20}, + ]}> <Text type="xl" style={[pal.textLight, styles.description]}> Fine-tune the content you see on your home screen. </Text> @@ -155,11 +159,12 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({ <View style={[pal.viewLight, styles.card]}> <Text type="title-sm" style={[pal.text, s.pb5]}> - Show Posts from My Feeds (Experimental) + <FontAwesomeIcon icon="flask" color={pal.colors.text} /> Show + Posts from My Feeds </Text> <Text style={[pal.text, s.pb10]}> Set this setting to "Yes" to show samples of your saved feeds in - your following feed. + your following feed. This is an experimental feature. </Text> <ToggleButton type="default-light" @@ -175,7 +180,7 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({ style={[ styles.btnContainer, !isTabletOrDesktop && {borderTopWidth: 1, paddingHorizontal: 20}, - pal.borderDark, + pal.border, ]}> <TouchableOpacity testID="confirmBtn" diff --git a/src/view/screens/PreferencesThreads.tsx b/src/view/screens/PreferencesThreads.tsx new file mode 100644 index 000000000..74b28267d --- /dev/null +++ b/src/view/screens/PreferencesThreads.tsx @@ -0,0 +1,173 @@ +import React from 'react' +import {ScrollView, StyleSheet, TouchableOpacity, View} from 'react-native' +import {observer} from 'mobx-react-lite' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {Text} from '../com/util/text/Text' +import {useStores} from 'state/index' +import {s, colors} from 'lib/styles' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {ToggleButton} from 'view/com/util/forms/ToggleButton' +import {RadioGroup} from 'view/com/util/forms/RadioGroup' +import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {ViewHeader} from 'view/com/util/ViewHeader' +import {CenteredView} from 'view/com/util/Views' + +type Props = NativeStackScreenProps<CommonNavigatorParams, 'PreferencesThreads'> +export const PreferencesThreads = observer(function PreferencesThreadsImpl({ + navigation, +}: Props) { + const pal = usePalette('default') + const store = useStores() + const {isTabletOrDesktop} = useWebMediaQueries() + + return ( + <CenteredView + testID="preferencesThreadsScreen" + style={[ + pal.view, + pal.border, + styles.container, + isTabletOrDesktop && styles.desktopContainer, + ]}> + <ViewHeader title="Thread Preferences" showOnDesktop /> + <View + style={[ + styles.titleSection, + isTabletOrDesktop && {paddingTop: 20, paddingBottom: 20}, + ]}> + <Text type="xl" style={[pal.textLight, styles.description]}> + Fine-tune the discussion threads. + </Text> + </View> + + <ScrollView> + <View style={styles.cardsContainer}> + <View style={[pal.viewLight, styles.card]}> + <Text type="title-sm" style={[pal.text, s.pb5]}> + Sort Replies + </Text> + <Text style={[pal.text, s.pb10]}> + Sort replies to the same post by: + </Text> + <View style={[pal.view, {borderRadius: 8, paddingVertical: 6}]}> + <RadioGroup + type="default-light" + items={[ + {key: 'oldest', label: 'Oldest replies first'}, + {key: 'newest', label: 'Newest replies first'}, + {key: 'most-likes', label: 'Most-liked replies first'}, + {key: 'random', label: 'Random (aka "Poster\'s Roulette")'}, + ]} + onSelect={store.preferences.setThreadDefaultSort} + initialSelection={store.preferences.threadDefaultSort} + /> + </View> + </View> + + <View style={[pal.viewLight, styles.card]}> + <Text type="title-sm" style={[pal.text, s.pb5]}> + Prioritize Your Follows + </Text> + <Text style={[pal.text, s.pb10]}> + Show replies by people you follow before all other replies. + </Text> + <ToggleButton + type="default-light" + label={store.preferences.threadFollowedUsersFirst ? 'Yes' : 'No'} + isSelected={store.preferences.threadFollowedUsersFirst} + onPress={store.preferences.toggleThreadFollowedUsersFirst} + /> + </View> + + <View style={[pal.viewLight, styles.card]}> + <Text type="title-sm" style={[pal.text, s.pb5]}> + <FontAwesomeIcon icon="flask" color={pal.colors.text} /> Threaded + Mode + </Text> + <Text style={[pal.text, s.pb10]}> + Set this setting to "Yes" to show replies in a threaded view. This + is an experimental feature. + </Text> + <ToggleButton + type="default-light" + label={store.preferences.threadTreeViewEnabled ? 'Yes' : 'No'} + isSelected={store.preferences.threadTreeViewEnabled} + onPress={store.preferences.toggleThreadTreeViewEnabled} + /> + </View> + </View> + </ScrollView> + + <View + style={[ + styles.btnContainer, + !isTabletOrDesktop && {borderTopWidth: 1, paddingHorizontal: 20}, + pal.border, + ]}> + <TouchableOpacity + testID="confirmBtn" + onPress={() => { + navigation.canGoBack() + ? navigation.goBack() + : navigation.navigate('Settings') + }} + style={[styles.btn, isTabletOrDesktop && styles.btnDesktop]} + accessibilityRole="button" + accessibilityLabel="Confirm" + accessibilityHint=""> + <Text style={[s.white, s.bold, s.f18]}>Done</Text> + </TouchableOpacity> + </View> + </CenteredView> + ) +}) + +const styles = StyleSheet.create({ + container: { + flex: 1, + paddingBottom: 90, + }, + desktopContainer: { + borderLeftWidth: 1, + borderRightWidth: 1, + paddingBottom: 40, + }, + titleSection: { + paddingBottom: 30, + }, + title: { + textAlign: 'center', + marginBottom: 5, + }, + description: { + textAlign: 'center', + paddingHorizontal: 32, + }, + cardsContainer: { + paddingHorizontal: 20, + }, + card: { + padding: 16, + borderRadius: 10, + marginBottom: 20, + }, + btn: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + borderRadius: 32, + padding: 14, + backgroundColor: colors.blue3, + }, + btnDesktop: { + marginHorizontal: 'auto', + paddingHorizontal: 80, + }, + btnContainer: { + paddingTop: 20, + }, + dimmed: { + opacity: 0.3, + }, +}) diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 241bae1ed..efcb588f6 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -187,7 +187,9 @@ export const ProfileScreen = withAuthRequired( /> ) } else if (item instanceof CustomFeedModel) { - return <CustomFeed item={item} showSaveBtn showLikes /> + return ( + <CustomFeed item={item} showSaveBtn showLikes showDescription /> + ) } // if section is posts or posts & replies } else { diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx index 761f50d0a..1ff5f58ff 100644 --- a/src/view/screens/Settings.tsx +++ b/src/view/screens/Settings.tsx @@ -180,6 +180,10 @@ export const SettingsScreen = withAuthRequired( navigation.navigate('PreferencesHomeFeed') }, [navigation]) + const openThreadsPreferences = React.useCallback(() => { + navigation.navigate('PreferencesThreads') + }, [navigation]) + const onPressAppPasswords = React.useCallback(() => { navigation.navigate('AppPasswords') }, [navigation]) @@ -421,6 +425,24 @@ export const SettingsScreen = withAuthRequired( </Text> </TouchableOpacity> <TouchableOpacity + testID="preferencesThreadsButton" + style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} + onPress={openThreadsPreferences} + accessibilityRole="button" + accessibilityHint="" + accessibilityLabel="Opens the threads preferences"> + <View style={[styles.iconContainer, pal.btn]}> + <FontAwesomeIcon + icon={['far', 'comments']} + style={pal.text as FontAwesomeIconStyle} + size={18} + /> + </View> + <Text type="lg" style={pal.text}> + Thread Preferences + </Text> + </TouchableOpacity> + <TouchableOpacity testID="savedFeedsBtn" style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} accessibilityHint="My Saved Feeds" |