From bf37913701836091b3e902694d72d190eeca5ec9 Mon Sep 17 00:00:00 2001 From: Ansh Nanda Date: Tue, 29 Aug 2023 12:16:26 -0700 Subject: fix onboarding on web --- src/lib/hooks/useOnboarding.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/lib/hooks/useOnboarding.ts (limited to 'src/lib/hooks') diff --git a/src/lib/hooks/useOnboarding.ts b/src/lib/hooks/useOnboarding.ts new file mode 100644 index 000000000..cdf24bc14 --- /dev/null +++ b/src/lib/hooks/useOnboarding.ts @@ -0,0 +1,23 @@ +import React from 'react' +import {useStores} from 'state/index' +import {useNavigation} from '@react-navigation/native' +import {NavigationProp} from 'lib/routes/types' +import {isNative, isWeb} from 'platform/detection' + +export function useOnboarding() { + const store = useStores() + const navigation = useNavigation() + + React.useEffect(() => { + if (store.onboarding.isActive) { + if (isWeb) { + store.shell.openModal({name: 'onboarding'}) + return + } + if (isNative) { + navigation.navigate('Welcome') + return + } + } + }, [store.onboarding.isActive, navigation, store.shell]) +} -- cgit 1.4.1 From 5d9534ca7258e6165e537b89d999a8c494501dc0 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Tue, 29 Aug 2023 20:20:51 -0700 Subject: Move onboarding to the withAuthRequired HOC --- src/Navigation.tsx | 18 --------------- src/lib/hooks/useOnboarding.ts | 23 ------------------- src/lib/routes/types.ts | 5 ----- src/state/models/ui/shell.ts | 7 ------ src/view/com/auth/Onboarding.tsx | 40 +++++++++++++++++++++++++++++++++ src/view/com/auth/withAuthRequired.tsx | 4 ++++ src/view/com/modals/Modal.web.tsx | 9 +------- src/view/com/modals/OnboardingModal.tsx | 40 --------------------------------- src/view/screens/Home.tsx | 2 -- 9 files changed, 45 insertions(+), 103 deletions(-) delete mode 100644 src/lib/hooks/useOnboarding.ts create mode 100644 src/view/com/auth/Onboarding.tsx delete mode 100644 src/view/com/modals/OnboardingModal.tsx (limited to 'src/lib/hooks') diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 334370ab0..d45376ef1 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -67,8 +67,6 @@ import {getRoutingInstrumentation} from 'lib/sentry' import {bskyTitle} from 'lib/strings/headings' import {JSX} from 'react/jsx-runtime' import {timeout} from 'lib/async/timeout' -import {RecommendedFeedsScreen} from 'view/screens/onboarding/RecommendedFeeds' -import {WelcomeScreen} from 'view/screens/onboarding/Welcome' const navigationRef = createNavigationContainerRef() @@ -221,22 +219,6 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { component={SavedFeeds} options={{title: title('Edit My Feeds')}} /> - - ) } diff --git a/src/lib/hooks/useOnboarding.ts b/src/lib/hooks/useOnboarding.ts deleted file mode 100644 index cdf24bc14..000000000 --- a/src/lib/hooks/useOnboarding.ts +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react' -import {useStores} from 'state/index' -import {useNavigation} from '@react-navigation/native' -import {NavigationProp} from 'lib/routes/types' -import {isNative, isWeb} from 'platform/detection' - -export function useOnboarding() { - const store = useStores() - const navigation = useNavigation() - - React.useEffect(() => { - if (store.onboarding.isActive) { - if (isWeb) { - store.shell.openModal({name: 'onboarding'}) - return - } - if (isNative) { - navigation.navigate('Welcome') - return - } - } - }, [store.onboarding.isActive, navigation, store.shell]) -} diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 633fa57a5..4eb5e29d2 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -1,6 +1,5 @@ import {NavigationState, PartialState} from '@react-navigation/native' import type {NativeStackNavigationProp} from '@react-navigation/native-stack' -import {OnboardingScreenSteps} from 'state/models/discovery/onboarding' export type {NativeStackScreenProps} from '@react-navigation/native-stack' @@ -30,10 +29,6 @@ export type CommonNavigatorParams = { CopyrightPolicy: undefined AppPasswords: undefined SavedFeeds: undefined -} & OnboardingScreenParams - -export type OnboardingScreenParams = { - [K in keyof typeof OnboardingScreenSteps]: undefined } export type BottomTabNavigatorParams = CommonNavigatorParams & { diff --git a/src/state/models/ui/shell.ts b/src/state/models/ui/shell.ts index 4c36dfdc6..a64047f9f 100644 --- a/src/state/models/ui/shell.ts +++ b/src/state/models/ui/shell.ts @@ -140,10 +140,6 @@ export interface PreferencesHomeFeed { name: 'preferences-home-feed' } -export interface OnboardingModal { - name: 'onboarding' -} - export type Modal = // Account | AddAppPasswordModal @@ -179,9 +175,6 @@ export type Modal = // Generic | ConfirmModal - // Onboarding (only used on web) - | OnboardingModal - interface LightboxModel {} export class ProfileImageLightbox implements LightboxModel { diff --git a/src/view/com/auth/Onboarding.tsx b/src/view/com/auth/Onboarding.tsx new file mode 100644 index 000000000..f43d5d93a --- /dev/null +++ b/src/view/com/auth/Onboarding.tsx @@ -0,0 +1,40 @@ +import React from 'react' +import {SafeAreaView} from 'react-native' +import {observer} from 'mobx-react-lite' +import {ErrorBoundary} from 'view/com/util/ErrorBoundary' +import {s} from 'lib/styles' +import {usePalette} from 'lib/hooks/usePalette' +import {useStores} from 'state/index' +import {useAnalytics} from 'lib/analytics/analytics' +import {CenteredView} from '../util/Views' +import {Welcome} from './onboarding/Welcome' +import {RecommendedFeeds} from './onboarding/RecommendedFeeds' + +export const Onboarding = observer(() => { + const pal = usePalette('default') + const store = useStores() + const {screen} = useAnalytics() + + React.useEffect(() => { + screen('Onboarding') + store.shell.setMinimalShellMode(true) + }, [store, screen]) + + const next = () => store.onboarding.next() + const skip = () => store.onboarding.skip() + + return ( + + + + {store.onboarding.step === 'Welcome' && ( + + )} + {store.onboarding.step === 'RecommendedFeeds' && ( + + )} + + + + ) +}) diff --git a/src/view/com/auth/withAuthRequired.tsx b/src/view/com/auth/withAuthRequired.tsx index 8e57669be..c81c2d5df 100644 --- a/src/view/com/auth/withAuthRequired.tsx +++ b/src/view/com/auth/withAuthRequired.tsx @@ -9,6 +9,7 @@ import {observer} from 'mobx-react-lite' import {useStores} from 'state/index' import {CenteredView} from '../util/Views' import {LoggedOut} from './LoggedOut' +import {Onboarding} from './Onboarding' import {Text} from '../util/text/Text' import {usePalette} from 'lib/hooks/usePalette' import {STATUS_PAGE_URL} from 'lib/constants' @@ -24,6 +25,9 @@ export const withAuthRequired =

( if (!store.session.hasSession) { return } + if (store.onboarding.isActive) { + return + } return }) diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx index 20da99e81..3aeddeb6b 100644 --- a/src/view/com/modals/Modal.web.tsx +++ b/src/view/com/modals/Modal.web.tsx @@ -27,7 +27,6 @@ import * as ContentFilteringSettingsModal from './ContentFilteringSettings' import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings' import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings' import * as ModerationDetailsModal from './ModerationDetails' -import * as OnboardingModal from './OnboardingModal' import * as PreferencesHomeFeed from './PreferencesHomeFeed' export const ModalsContainer = observer(function ModalsContainer() { @@ -55,11 +54,7 @@ function Modal({modal}: {modal: ModalIface}) { } const onPressMask = () => { - if ( - modal.name === 'crop-image' || - modal.name === 'edit-image' || - modal.name === 'onboarding' - ) { + if (modal.name === 'crop-image' || modal.name === 'edit-image') { return // dont close on mask presses during crop } store.shell.closeModal() @@ -114,8 +109,6 @@ function Modal({modal}: {modal: ModalIface}) { element = } else if (modal.name === 'moderation-details') { element = - } else if (modal.name === 'onboarding') { - element = } else { return null } diff --git a/src/view/com/modals/OnboardingModal.tsx b/src/view/com/modals/OnboardingModal.tsx deleted file mode 100644 index 3862736cf..000000000 --- a/src/view/com/modals/OnboardingModal.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react' -import {StyleSheet, View} from 'react-native' -import {useStores} from 'state/index' - -import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb} from 'platform/detection' -import {Welcome} from '../auth/onboarding/Welcome' -import {observer} from 'mobx-react-lite' -import {RecommendedFeeds} from '../auth/onboarding/RecommendedFeeds' - -export const snapPoints = ['90%'] - -export const Component = observer(({}: {}) => { - const pal = usePalette('default') - const store = useStores() - - const next = () => { - const nextScreenName = store.onboarding.next() - if (nextScreenName === 'Home') { - store.shell.closeModal() - } - } - - return ( - - {store.onboarding.step === 'Welcome' ? : null} - {store.onboarding.step === 'RecommendedFeeds' ? ( - - ) : null} - - ) -}) - -const styles = StyleSheet.create({ - container: { - flex: 1, - paddingBottom: isDesktopWeb ? 0 : 50, - maxHeight: '750px', - }, -}) diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 4397200e4..7262756d3 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -21,7 +21,6 @@ import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' import {useAnalytics} from 'lib/analytics/analytics' import {ComposeIcon2} from 'lib/icons' import {isDesktopWeb, isMobileWebMediaQuery, isWeb} from 'platform/detection' -import {useOnboarding} from 'lib/hooks/useOnboarding' const HEADER_OFFSET_MOBILE = 78 const HEADER_OFFSET_DESKTOP = 50 @@ -40,7 +39,6 @@ export const HomeScreen = withAuthRequired( const [requestedCustomFeeds, setRequestedCustomFeeds] = React.useState< string[] >([]) - useOnboarding() React.useEffect(() => { const {pinned} = store.me.savedFeeds -- cgit 1.4.1 From 8ed6e72ea4a5b6546ecbbee6aa4233ee6b3aed21 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Wed, 30 Aug 2023 15:15:10 -0700 Subject: More fixes to mobile onboard --- src/lib/hooks/useWebMediaQueries.tsx | 10 +- src/view/com/auth/onboarding/RecommendedFeeds.tsx | 3 +- .../auth/onboarding/RecommendedFeedsDesktop.tsx | 126 +----------------- .../com/auth/onboarding/RecommendedFeedsItem.tsx | 142 +++++++++++++++++++++ .../com/auth/onboarding/RecommendedFeedsMobile.tsx | 39 +----- .../com/auth/onboarding/RecommendedFeedsTablet.tsx | 91 +++++++++++++ src/view/com/util/layouts/withBreakpoints.tsx | 5 +- src/view/shell/index.web.tsx | 3 +- 8 files changed, 254 insertions(+), 165 deletions(-) create mode 100644 src/view/com/auth/onboarding/RecommendedFeedsItem.tsx create mode 100644 src/view/com/auth/onboarding/RecommendedFeedsTablet.tsx (limited to 'src/lib/hooks') diff --git a/src/lib/hooks/useWebMediaQueries.tsx b/src/lib/hooks/useWebMediaQueries.tsx index 441585442..fd7e383f0 100644 --- a/src/lib/hooks/useWebMediaQueries.tsx +++ b/src/lib/hooks/useWebMediaQueries.tsx @@ -1,8 +1,14 @@ import {useMediaQuery} from 'react-responsive' +import {isNative} from 'platform/detection' export function useWebMediaQueries() { const isDesktop = useMediaQuery({ - query: '(min-width: 1230px)', + query: '(min-width: 1224px)', }) - return {isDesktop} + const isTabletOrMobile = useMediaQuery({query: '(max-width: 1224px)'}) + const isMobile = useMediaQuery({query: '(max-width: 800px)'}) + if (isNative) { + return {isMobile: true, isTabletOrMobile: true, isDesktop: false} + } + return {isMobile, isTabletOrMobile, isDesktop} } diff --git a/src/view/com/auth/onboarding/RecommendedFeeds.tsx b/src/view/com/auth/onboarding/RecommendedFeeds.tsx index 76204ce31..12c984e71 100644 --- a/src/view/com/auth/onboarding/RecommendedFeeds.tsx +++ b/src/view/com/auth/onboarding/RecommendedFeeds.tsx @@ -1,10 +1,11 @@ import 'react' import {withBreakpoints} from 'view/com/util/layouts/withBreakpoints' import {RecommendedFeedsDesktop} from './RecommendedFeedsDesktop' +import {RecommendedFeedsTablet} from './RecommendedFeedsTablet' import {RecommendedFeedsMobile} from './RecommendedFeedsMobile' export const RecommendedFeeds = withBreakpoints( RecommendedFeedsMobile, - RecommendedFeedsMobile, + RecommendedFeedsTablet, RecommendedFeedsDesktop, ) diff --git a/src/view/com/auth/onboarding/RecommendedFeedsDesktop.tsx b/src/view/com/auth/onboarding/RecommendedFeedsDesktop.tsx index d305f4a82..937313bc6 100644 --- a/src/view/com/auth/onboarding/RecommendedFeedsDesktop.tsx +++ b/src/view/com/auth/onboarding/RecommendedFeedsDesktop.tsx @@ -4,14 +4,9 @@ import {observer} from 'mobx-react-lite' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Text} from 'view/com/util/text/Text' import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout' -import {UserAvatar} from 'view/com/util/UserAvatar' import {Button} from 'view/com/util/forms/Button' -import * as Toast from 'view/com/util/Toast' +import {RecommendedFeedsItem} from './RecommendedFeedsItem' import {usePalette} from 'lib/hooks/usePalette' -import {useCustomFeed} from 'lib/hooks/useCustomFeed' -import {makeRecordUri} from 'lib/strings/url-helpers' -import {sanitizeHandle} from 'lib/strings/handles' -import {HeartIcon} from 'lib/icons' import {RECOMMENDED_FEEDS} from 'lib/constants' type Props = { @@ -64,7 +59,7 @@ export const RecommendedFeedsDesktop = observer(({next}: Props) => { contentStyle={{paddingHorizontal: 0}}> } + renderItem={({item}) => } keyExtractor={item => item.did + item.rkey} style={{flex: 1}} /> @@ -72,123 +67,6 @@ export const RecommendedFeedsDesktop = observer(({next}: Props) => { ) }) -const Item = observer(({did, rkey}: {did: string; rkey: string}) => { - const pal = usePalette('default') - const uri = makeRecordUri(did, 'app.bsky.feed.generator', rkey) - const item = useCustomFeed(uri) - if (!item) return null - const onToggle = async () => { - if (item.isSaved) { - try { - await item.unsave() - } catch (e) { - Toast.show('There was an issue contacting your server') - console.error('Failed to unsave feed', {e}) - } - } else { - try { - await item.save() - await item.pin() - } catch (e) { - Toast.show('There was an issue contacting your server') - console.error('Failed to pin feed', {e}) - } - } - } - return ( - - - - - - - - {item.displayName} - - - - by {sanitizeHandle(item.data.creator.handle, '@')} - - - {item.data.description ? ( - - {item.data.description} - - ) : null} - - - - - - - - {item.data.likeCount || 0} - - - - - - - ) -}) - const styles = StyleSheet.create({ container: { flex: 1, diff --git a/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx b/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx new file mode 100644 index 000000000..d16b3213e --- /dev/null +++ b/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx @@ -0,0 +1,142 @@ +import React from 'react' +import {View} from 'react-native' +import {observer} from 'mobx-react-lite' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {Text} from 'view/com/util/text/Text' +import {Button} from 'view/com/util/forms/Button' +import {UserAvatar} from 'view/com/util/UserAvatar' +import * as Toast from 'view/com/util/Toast' +import {HeartIcon} from 'lib/icons' +import {usePalette} from 'lib/hooks/usePalette' +import {useCustomFeed} from 'lib/hooks/useCustomFeed' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {makeRecordUri} from 'lib/strings/url-helpers' +import {sanitizeHandle} from 'lib/strings/handles' + +export const RecommendedFeedsItem = observer( + ({did, rkey}: {did: string; rkey: string}) => { + const {isMobile} = useWebMediaQueries() + const pal = usePalette('default') + const uri = makeRecordUri(did, 'app.bsky.feed.generator', rkey) + const item = useCustomFeed(uri) + if (!item) return null + const onToggle = async () => { + if (item.isSaved) { + try { + await item.unsave() + } catch (e) { + Toast.show('There was an issue contacting your server') + console.error('Failed to unsave feed', {e}) + } + } else { + try { + await item.save() + await item.pin() + } catch (e) { + Toast.show('There was an issue contacting your server') + console.error('Failed to pin feed', {e}) + } + } + } + return ( + + + + + + + + {item.displayName} + + + + by {sanitizeHandle(item.data.creator.handle, '@')} + + + {item.data.description ? ( + + {item.data.description} + + ) : null} + + + + + + + + {item.data.likeCount || 0} + + + + + + + ) + }, +) diff --git a/src/view/com/auth/onboarding/RecommendedFeedsMobile.tsx b/src/view/com/auth/onboarding/RecommendedFeedsMobile.tsx index b84b75df7..a3e379883 100644 --- a/src/view/com/auth/onboarding/RecommendedFeedsMobile.tsx +++ b/src/view/com/auth/onboarding/RecommendedFeedsMobile.tsx @@ -1,14 +1,11 @@ import React from 'react' import {FlatList, StyleSheet, View} from 'react-native' +import {observer} from 'mobx-react-lite' import {Text} from 'view/com/util/text/Text' -import {usePalette} from 'lib/hooks/usePalette' import {Button} from 'view/com/util/forms/Button' -import {observer} from 'mobx-react-lite' -import {CustomFeed} from 'view/com/feeds/CustomFeed' -import {useCustomFeed} from 'lib/hooks/useCustomFeed' -import {makeRecordUri} from 'lib/strings/url-helpers' import {ViewHeader} from 'view/com/util/ViewHeader' -import {isDesktopWeb} from 'platform/detection' +import {RecommendedFeedsItem} from './RecommendedFeedsItem' +import {usePalette} from 'lib/hooks/usePalette' import {RECOMMENDED_FEEDS} from 'lib/constants' type Props = { @@ -31,7 +28,7 @@ export const RecommendedFeedsMobile = observer(({next}: Props) => { } + renderItem={({item}) => } keyExtractor={item => item.did + item.rkey} style={{flex: 1}} /> @@ -47,32 +44,6 @@ export const RecommendedFeedsMobile = observer(({next}: Props) => { ) }) -type ItemProps = { - did: string - rkey: string -} - -const Item = ({item}: {item: ItemProps}) => { - const uri = makeRecordUri(item.did, 'app.bsky.feed.generator', item.rkey) - const data = useCustomFeed(uri) - if (!data) return null - return ( - - ) -} - const styles = StyleSheet.create({ container: { flex: 1, @@ -83,7 +54,7 @@ const styles = StyleSheet.create({ marginHorizontal: 16, }, button: { - marginBottom: 24, + marginBottom: 16, marginHorizontal: 16, marginTop: 16, }, diff --git a/src/view/com/auth/onboarding/RecommendedFeedsTablet.tsx b/src/view/com/auth/onboarding/RecommendedFeedsTablet.tsx new file mode 100644 index 000000000..bc2e57ade --- /dev/null +++ b/src/view/com/auth/onboarding/RecommendedFeedsTablet.tsx @@ -0,0 +1,91 @@ +import React from 'react' +import {FlatList, StyleSheet, View} from 'react-native' +import {observer} from 'mobx-react-lite' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {Text} from 'view/com/util/text/Text' +import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout' +import {Button} from 'view/com/util/forms/Button' +import {RecommendedFeedsItem} from './RecommendedFeedsItem' +import {usePalette} from 'lib/hooks/usePalette' +import {RECOMMENDED_FEEDS} from 'lib/constants' + +type Props = { + next: () => void +} +export const RecommendedFeedsTablet = observer(({next}: Props) => { + const pal = usePalette('default') + + const title = ( + <> + Choose your + Recomended + Feeds + + Feeds are created by users to curate content. Choose some feeds that you + find interesting. + + + + + + ) + + return ( + + } + keyExtractor={item => item.did + item.rkey} + style={{flex: 1}} + /> + + ) +}) + +const styles = StyleSheet.create({ + container: { + flex: 1, + marginHorizontal: 16, + justifyContent: 'space-between', + }, + title1: { + fontSize: 24, + fontWeight: '800', + textAlign: 'right', + }, + title2: { + fontSize: 36, + fontWeight: '800', + textAlign: 'right', + }, + description: { + maxWidth: 400, + marginTop: 10, + marginLeft: 'auto', + textAlign: 'right', + }, +}) diff --git a/src/view/com/util/layouts/withBreakpoints.tsx b/src/view/com/util/layouts/withBreakpoints.tsx index 4214c1040..dc3f50dc9 100644 --- a/src/view/com/util/layouts/withBreakpoints.tsx +++ b/src/view/com/util/layouts/withBreakpoints.tsx @@ -1,6 +1,6 @@ import React from 'react' -import {useMediaQuery} from 'react-responsive' import {isNative} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' export const withBreakpoints =

( @@ -9,8 +9,7 @@ export const withBreakpoints = Desktop: React.ComponentType

, ): React.FC

=> (props: P) => { - const isTabletOrMobile = useMediaQuery({query: '(max-width: 1224px)'}) - const isMobile = useMediaQuery({query: '(max-width: 800px)'}) + const {isMobile, isTabletOrMobile} = useWebMediaQueries() if (isMobile || isNative) { return diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx index 9ad8007f6..e36439c82 100644 --- a/src/view/shell/index.web.tsx +++ b/src/view/shell/index.web.tsx @@ -28,6 +28,7 @@ const ShellInner = observer(() => { }) }, [navigator, store.shell]) + const showBottomBar = !isDesktop && !store.onboarding.isActive const showSideNavs = isDesktop && store.session.hasSession && !store.onboarding.isActive return ( @@ -52,7 +53,7 @@ const ShellInner = observer(() => { onPost={store.shell.composerOpts?.onPost} mention={store.shell.composerOpts?.mention} /> - {!isDesktop && } + {showBottomBar && } {!isDesktop && store.shell.isDrawerOpen && ( -- cgit 1.4.1