Bluesky app fork with some witchin' additions 💫

Move `PreferencesHomeFeed` to a screen instead of a modal (#1335)

* move `PreferencesHomeFeed` to a screen instead of a modal

* add web route for home feed preferences

* upgrade `@miblanchard/react-native-slider` to fix lint

* fix web route naming

* fix desktop web styling

* add `react-native-slider` mock

authored by Ansh and committed by GitHub 9446c678 a29f10ae

Changed files
+49 -38
__mocks__
@miblanchard
bskyweb
cmd
bskyweb
src
+1
__mocks__/@miblanchard/react-native-slider.js
···
··· 1 + export const Slider = {}
+1
bskyweb/cmd/bskyweb/server.go
··· 168 e.GET("/moderation/blocked-accounts", server.WebGeneric) 169 e.GET("/settings", server.WebGeneric) 170 e.GET("/settings/app-passwords", server.WebGeneric) 171 e.GET("/settings/saved-feeds", server.WebGeneric) 172 e.GET("/sys/debug", server.WebGeneric) 173 e.GET("/sys/log", server.WebGeneric)
··· 168 e.GET("/moderation/blocked-accounts", server.WebGeneric) 169 e.GET("/settings", server.WebGeneric) 170 e.GET("/settings/app-passwords", server.WebGeneric) 171 + e.GET("/settings/home-feed", server.WebGeneric) 172 e.GET("/settings/saved-feeds", server.WebGeneric) 173 e.GET("/sys/debug", server.WebGeneric) 174 e.GET("/sys/log", server.WebGeneric)
+1 -1
package.json
··· 36 "@fortawesome/react-native-fontawesome": "^0.3.0", 37 "@gorhom/bottom-sheet": "^4.4.7", 38 "@mattermost/react-native-paste-input": "^0.6.4", 39 - "@miblanchard/react-native-slider": "^2.2.0", 40 "@react-native-async-storage/async-storage": "^1.17.6", 41 "@react-native-camera-roll/camera-roll": "^5.2.2", 42 "@react-native-clipboard/clipboard": "^1.10.0",
··· 36 "@fortawesome/react-native-fontawesome": "^0.3.0", 37 "@gorhom/bottom-sheet": "^4.4.7", 38 "@mattermost/react-native-paste-input": "^0.6.4", 39 + "@miblanchard/react-native-slider": "^2.3.1", 40 "@react-native-async-storage/async-storage": "^1.17.6", 41 "@react-native-camera-roll/camera-roll": "^5.2.2", 42 "@react-native-clipboard/clipboard": "^1.10.0",
+6
src/Navigation.tsx
··· 67 import {bskyTitle} from 'lib/strings/headings' 68 import {JSX} from 'react/jsx-runtime' 69 import {timeout} from 'lib/async/timeout' 70 71 const navigationRef = createNavigationContainerRef<AllNavigatorParams>() 72 ··· 218 name="SavedFeeds" 219 component={SavedFeeds} 220 options={{title: title('Edit My Feeds')}} 221 /> 222 </> 223 )
··· 67 import {bskyTitle} from 'lib/strings/headings' 68 import {JSX} from 'react/jsx-runtime' 69 import {timeout} from 'lib/async/timeout' 70 + import {PreferencesHomeFeed} from 'view/screens/PreferencesHomeFeed' 71 72 const navigationRef = createNavigationContainerRef<AllNavigatorParams>() 73 ··· 219 name="SavedFeeds" 220 component={SavedFeeds} 221 options={{title: title('Edit My Feeds')}} 222 + /> 223 + <Stack.Screen 224 + name="PreferencesHomeFeed" 225 + component={PreferencesHomeFeed} 226 + options={{title: title('Home Feed Preferences')}} 227 /> 228 </> 229 )
+1
src/lib/routes/types.ts
··· 29 CopyrightPolicy: undefined 30 AppPasswords: undefined 31 SavedFeeds: undefined 32 } 33 34 export type BottomTabNavigatorParams = CommonNavigatorParams & {
··· 29 CopyrightPolicy: undefined 30 AppPasswords: undefined 31 SavedFeeds: undefined 32 + PreferencesHomeFeed: undefined 33 } 34 35 export type BottomTabNavigatorParams = CommonNavigatorParams & {
+1
src/routes.ts
··· 23 Debug: '/sys/debug', 24 Log: '/sys/log', 25 AppPasswords: '/settings/app-passwords', 26 SavedFeeds: '/settings/saved-feeds', 27 Support: '/support', 28 PrivacyPolicy: '/support/privacy',
··· 23 Debug: '/sys/debug', 24 Log: '/sys/log', 25 AppPasswords: '/settings/app-passwords', 26 + PreferencesHomeFeed: '/settings/home-feed', 27 SavedFeeds: '/settings/saved-feeds', 28 Support: '/support', 29 PrivacyPolicy: '/support/privacy',
-5
src/state/models/ui/shell.ts
··· 136 name: 'post-languages-settings' 137 } 138 139 - export interface PreferencesHomeFeed { 140 - name: 'preferences-home-feed' 141 - } 142 - 143 export interface OnboardingModal { 144 name: 'onboarding' 145 } ··· 156 | ContentFilteringSettingsModal 157 | ContentLanguagesSettingsModal 158 | PostLanguagesSettingsModal 159 - | PreferencesHomeFeed 160 161 // Moderation 162 | ModerationDetailsModal
··· 136 name: 'post-languages-settings' 137 } 138 139 export interface OnboardingModal { 140 name: 'onboarding' 141 } ··· 152 | ContentFilteringSettingsModal 153 | ContentLanguagesSettingsModal 154 | PostLanguagesSettingsModal 155 156 // Moderation 157 | ModerationDetailsModal
-4
src/view/com/modals/Modal.tsx
··· 28 import * as ContentFilteringSettingsModal from './ContentFilteringSettings' 29 import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings' 30 import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings' 31 - import * as PreferencesHomeFeed from './PreferencesHomeFeed' 32 import * as OnboardingModal from './OnboardingModal' 33 import * as ModerationDetailsModal from './ModerationDetails' 34 ··· 131 } else if (activeModal?.name === 'post-languages-settings') { 132 snapPoints = PostLanguagesSettingsModal.snapPoints 133 element = <PostLanguagesSettingsModal.Component /> 134 - } else if (activeModal?.name === 'preferences-home-feed') { 135 - snapPoints = PreferencesHomeFeed.snapPoints 136 - element = <PreferencesHomeFeed.Component /> 137 } else if (activeModal?.name === 'onboarding') { 138 snapPoints = OnboardingModal.snapPoints 139 element = <OnboardingModal.Component />
··· 28 import * as ContentFilteringSettingsModal from './ContentFilteringSettings' 29 import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings' 30 import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings' 31 import * as OnboardingModal from './OnboardingModal' 32 import * as ModerationDetailsModal from './ModerationDetails' 33 ··· 130 } else if (activeModal?.name === 'post-languages-settings') { 131 snapPoints = PostLanguagesSettingsModal.snapPoints 132 element = <PostLanguagesSettingsModal.Component /> 133 } else if (activeModal?.name === 'onboarding') { 134 snapPoints = OnboardingModal.snapPoints 135 element = <OnboardingModal.Component />
-4
src/view/com/modals/Modal.web.tsx
··· 29 import * as OnboardingModal from './OnboardingModal' 30 import * as ModerationDetailsModal from './ModerationDetails' 31 32 - import * as PreferencesHomeFeed from './PreferencesHomeFeed' 33 - 34 export const ModalsContainer = observer(function ModalsContainer() { 35 const store = useStores() 36 ··· 107 element = <AltTextImageModal.Component {...modal} /> 108 } else if (modal.name === 'edit-image') { 109 element = <EditImageModal.Component {...modal} /> 110 - } else if (modal.name === 'preferences-home-feed') { 111 - element = <PreferencesHomeFeed.Component /> 112 } else if (modal.name === 'onboarding') { 113 element = <OnboardingModal.Component /> 114 } else if (modal.name === 'moderation-details') {
··· 29 import * as OnboardingModal from './OnboardingModal' 30 import * as ModerationDetailsModal from './ModerationDetails' 31 32 export const ModalsContainer = observer(function ModalsContainer() { 33 const store = useStores() 34 ··· 105 element = <AltTextImageModal.Component {...modal} /> 106 } else if (modal.name === 'edit-image') { 107 element = <EditImageModal.Component {...modal} /> 108 } else if (modal.name === 'onboarding') { 109 element = <OnboardingModal.Component /> 110 } else if (modal.name === 'moderation-details') {
+34 -18
src/view/com/modals/PreferencesHomeFeed.tsx src/view/screens/PreferencesHomeFeed.tsx
··· 1 import React, {useState} from 'react' 2 - import {StyleSheet, TouchableOpacity, View} from 'react-native' 3 import {observer} from 'mobx-react-lite' 4 import {Slider} from '@miblanchard/react-native-slider' 5 - import {Text} from '../util/text/Text' 6 import {useStores} from 'state/index' 7 import {s, colors} from 'lib/styles' 8 import {usePalette} from 'lib/hooks/usePalette' 9 import {isWeb, isDesktopWeb} from 'platform/detection' 10 import {ToggleButton} from 'view/com/util/forms/ToggleButton' 11 - import {ScrollView} from 'view/com/modals/util' 12 - 13 - export const snapPoints = ['90%'] 14 15 function RepliesThresholdInput({enabled}: {enabled: boolean}) { 16 const store = useStores() ··· 43 ) 44 } 45 46 - export const Component = observer(function Component() { 47 const pal = usePalette('default') 48 const store = useStores() 49 50 return ( 51 - <View 52 - testID="preferencesHomeFeedModal" 53 - style={[pal.view, styles.container]}> 54 <View style={styles.titleSection}> 55 - <Text type="title-lg" style={[pal.text, styles.title]}> 56 - Home Feed Preferences 57 - </Text> 58 <Text type="xl" style={[pal.textLight, styles.description]}> 59 Fine-tune the content you see on your home screen. 60 </Text> ··· 119 <TouchableOpacity 120 testID="confirmBtn" 121 onPress={() => { 122 - store.shell.closeModal() 123 }} 124 - style={[styles.btn]} 125 accessibilityRole="button" 126 accessibilityLabel="Confirm" 127 accessibilityHint=""> 128 <Text style={[s.white, s.bold, s.f18]}>Done</Text> 129 </TouchableOpacity> 130 </View> 131 - </View> 132 ) 133 }) 134 135 const styles = StyleSheet.create({ 136 container: { 137 flex: 1, 138 - paddingBottom: isDesktopWeb ? 0 : 60, 139 }, 140 titleSection: { 141 - padding: 20, 142 paddingBottom: 30, 143 }, 144 title: { 145 textAlign: 'center', ··· 165 padding: 14, 166 backgroundColor: colors.blue3, 167 }, 168 btnContainer: { 169 paddingTop: 20, 170 - paddingHorizontal: 20, 171 borderTopWidth: isDesktopWeb ? 0 : 1, 172 }, 173 dimmed: {
··· 1 import React, {useState} from 'react' 2 + import {ScrollView, StyleSheet, TouchableOpacity, View} from 'react-native' 3 import {observer} from 'mobx-react-lite' 4 import {Slider} from '@miblanchard/react-native-slider' 5 + import {Text} from '../com/util/text/Text' 6 import {useStores} from 'state/index' 7 import {s, colors} from 'lib/styles' 8 import {usePalette} from 'lib/hooks/usePalette' 9 import {isWeb, isDesktopWeb} from 'platform/detection' 10 import {ToggleButton} from 'view/com/util/forms/ToggleButton' 11 + import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' 12 + import {ViewHeader} from 'view/com/util/ViewHeader' 13 + import {CenteredView} from 'view/com/util/Views' 14 15 function RepliesThresholdInput({enabled}: {enabled: boolean}) { 16 const store = useStores() ··· 43 ) 44 } 45 46 + type Props = NativeStackScreenProps< 47 + CommonNavigatorParams, 48 + 'PreferencesHomeFeed' 49 + > 50 + export const PreferencesHomeFeed = observer(({navigation}: Props) => { 51 const pal = usePalette('default') 52 const store = useStores() 53 54 return ( 55 + <CenteredView 56 + testID="preferencesHomeFeedScreen" 57 + style={[ 58 + pal.view, 59 + pal.border, 60 + styles.container, 61 + isDesktopWeb && styles.desktopContainer, 62 + ]}> 63 + <ViewHeader title="Home Feed Preferences" showOnDesktop /> 64 <View style={styles.titleSection}> 65 <Text type="xl" style={[pal.textLight, styles.description]}> 66 Fine-tune the content you see on your home screen. 67 </Text> ··· 126 <TouchableOpacity 127 testID="confirmBtn" 128 onPress={() => { 129 + navigation.canGoBack() 130 + ? navigation.goBack() 131 + : navigation.navigate('Settings') 132 }} 133 + style={[styles.btn, isDesktopWeb && styles.btnDesktop]} 134 accessibilityRole="button" 135 accessibilityLabel="Confirm" 136 accessibilityHint=""> 137 <Text style={[s.white, s.bold, s.f18]}>Done</Text> 138 </TouchableOpacity> 139 </View> 140 + </CenteredView> 141 ) 142 }) 143 144 const styles = StyleSheet.create({ 145 container: { 146 flex: 1, 147 + paddingBottom: isDesktopWeb ? 40 : 90, 148 + }, 149 + desktopContainer: { 150 + borderLeftWidth: 1, 151 + borderRightWidth: 1, 152 }, 153 titleSection: { 154 paddingBottom: 30, 155 + paddingTop: isDesktopWeb ? 20 : 0, 156 }, 157 title: { 158 textAlign: 'center', ··· 178 padding: 14, 179 backgroundColor: colors.blue3, 180 }, 181 + btnDesktop: { 182 + marginHorizontal: 'auto', 183 + paddingHorizontal: 80, 184 + }, 185 btnContainer: { 186 paddingTop: 20, 187 borderTopWidth: isDesktopWeb ? 0 : 1, 188 }, 189 dimmed: {
+3 -5
src/view/screens/Settings.tsx
··· 170 }, []) 171 172 const openPreferencesModal = React.useCallback(() => { 173 - store.shell.openModal({ 174 - name: 'preferences-home-feed', 175 - }) 176 - }, [store]) 177 178 const onPressAppPasswords = React.useCallback(() => { 179 navigation.navigate('AppPasswords') ··· 386 Advanced 387 </Text> 388 <TouchableOpacity 389 - testID="preferencesHomeFeedModalButton" 390 style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} 391 onPress={openPreferencesModal} 392 accessibilityRole="button"
··· 170 }, []) 171 172 const openPreferencesModal = React.useCallback(() => { 173 + navigation.navigate('PreferencesHomeFeed') 174 + }, [navigation]) 175 176 const onPressAppPasswords = React.useCallback(() => { 177 navigation.navigate('AppPasswords') ··· 384 Advanced 385 </Text> 386 <TouchableOpacity 387 + testID="preferencesHomeFeedButton" 388 style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} 389 onPress={openPreferencesModal} 390 accessibilityRole="button"
+1 -1
yarn.lock
··· 3497 dependencies: 3498 semver "7.5.4" 3499 3500 - "@miblanchard/react-native-slider@^2.2.0": 3501 version "2.3.1" 3502 resolved "https://registry.yarnpkg.com/@miblanchard/react-native-slider/-/react-native-slider-2.3.1.tgz#79e0f1f9b1ce43ef25ee51ee9256c012e5dfa412" 3503 integrity sha512-J/hZDBWmXq8fJeOnTVHqIUVDHshqMSpJVxJ4WqwuCBKl5Rke9OBYXIdkSlgi75OgtScAr8FKK5KNkDKHUf6JIg==
··· 3497 dependencies: 3498 semver "7.5.4" 3499 3500 + "@miblanchard/react-native-slider@^2.3.1": 3501 version "2.3.1" 3502 resolved "https://registry.yarnpkg.com/@miblanchard/react-native-slider/-/react-native-slider-2.3.1.tgz#79e0f1f9b1ce43ef25ee51ee9256c012e5dfa412" 3503 integrity sha512-J/hZDBWmXq8fJeOnTVHqIUVDHshqMSpJVxJ4WqwuCBKl5Rke9OBYXIdkSlgi75OgtScAr8FKK5KNkDKHUf6JIg==