Remove old old onboarding (#3674)

zio/stable
Eric Bailey 2024-04-23 19:30:49 -05:00 committed by GitHub
parent 24da3a8f4e
commit 05beb1bbad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 8 additions and 1196 deletions

View File

@ -1,3 +1,2 @@
export const LOGIN_INCLUDE_DEV_SERVERS = true export const LOGIN_INCLUDE_DEV_SERVERS = true
export const PWI_ENABLED = true export const PWI_ENABLED = true
export const NEW_ONBOARDING_ENABLED = true

View File

@ -1,51 +0,0 @@
import React from 'react'
import {SafeAreaView, Platform} from 'react-native'
import {ErrorBoundary} from 'view/com/util/ErrorBoundary'
import {s} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
import {Welcome} from './onboarding/Welcome'
import {RecommendedFeeds} from './onboarding/RecommendedFeeds'
import {RecommendedFollows} from './onboarding/RecommendedFollows'
import {useSetMinimalShellMode} from '#/state/shell/minimal-mode'
import {useOnboardingState, useOnboardingDispatch} from '#/state/shell'
export function Onboarding() {
const pal = usePalette('default')
const setMinimalShellMode = useSetMinimalShellMode()
const onboardingState = useOnboardingState()
const onboardingDispatch = useOnboardingDispatch()
React.useEffect(() => {
setMinimalShellMode(true)
}, [setMinimalShellMode])
const next = () => onboardingDispatch({type: 'next'})
const skip = () => onboardingDispatch({type: 'skip'})
return (
<SafeAreaView
testID="onboardingView"
style={[
s.hContentRegion,
pal.view,
// @ts-ignore web only -esb
Platform.select({
web: {
height: '100vh',
},
}),
]}>
<ErrorBoundary>
{onboardingState.step === 'Welcome' && (
<Welcome skip={skip} next={next} />
)}
{onboardingState.step === 'RecommendedFeeds' && (
<RecommendedFeeds next={next} />
)}
{onboardingState.step === 'RecommendedFollows' && (
<RecommendedFollows next={next} />
)}
</ErrorBoundary>
</SafeAreaView>
)
}

View File

@ -1,211 +0,0 @@
import React from 'react'
import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useSuggestedFeedsQuery} from '#/state/queries/suggested-feeds'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
import {Button} from 'view/com/util/forms/Button'
import {Mobile, TabletOrDesktop} from 'view/com/util/layouts/Breakpoints'
import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout'
import {Text} from 'view/com/util/text/Text'
import {ViewHeader} from 'view/com/util/ViewHeader'
import {RecommendedFeedsItem} from './RecommendedFeedsItem'
type Props = {
next: () => void
}
export function RecommendedFeeds({next}: Props) {
const pal = usePalette('default')
const {_} = useLingui()
const {isTabletOrMobile} = useWebMediaQueries()
const {isLoading, data} = useSuggestedFeedsQuery()
const hasFeeds = data && data.pages[0].feeds.length
const title = (
<>
<Trans>
<Text
style={[
pal.textLight,
tdStyles.title1,
isTabletOrMobile && tdStyles.title1Small,
]}>
Choose your
</Text>
<Text
style={[
pal.link,
tdStyles.title2,
isTabletOrMobile && tdStyles.title2Small,
]}>
Recommended
</Text>
<Text
style={[
pal.link,
tdStyles.title2,
isTabletOrMobile && tdStyles.title2Small,
]}>
Feeds
</Text>
</Trans>
<Text type="2xl-medium" style={[pal.textLight, tdStyles.description]}>
<Trans>
Feeds are created by users to curate content. Choose some feeds that
you find interesting.
</Trans>
</Text>
<View
style={{
flexDirection: 'row',
justifyContent: 'flex-end',
marginTop: 20,
}}>
<Button onPress={next} testID="continueBtn">
<View
style={{
flexDirection: 'row',
alignItems: 'center',
paddingLeft: 2,
gap: 6,
}}>
<Text
type="2xl-medium"
style={{color: '#fff', position: 'relative', top: -1}}>
<Trans>Next</Trans>
</Text>
<FontAwesomeIcon icon="angle-right" color="#fff" size={14} />
</View>
</Button>
</View>
</>
)
return (
<>
<TabletOrDesktop>
<TitleColumnLayout
testID="recommendedFeedsOnboarding"
title={title}
horizontal
titleStyle={isTabletOrMobile ? undefined : {minWidth: 470}}
contentStyle={{paddingHorizontal: 0}}>
{hasFeeds ? (
<FlatList
data={data.pages[0].feeds}
renderItem={({item}) => <RecommendedFeedsItem item={item} />}
keyExtractor={item => item.uri}
style={{flex: 1}}
/>
) : isLoading ? (
<View>
<ActivityIndicator size="large" />
</View>
) : (
<ErrorMessage message={_(msg`Failed to load recommended feeds`)} />
)}
</TitleColumnLayout>
</TabletOrDesktop>
<Mobile>
<View style={[mStyles.container]} testID="recommendedFeedsOnboarding">
<ViewHeader
title={_(msg`Recommended Feeds`)}
showBackButton={false}
showOnDesktop
/>
<Text type="lg-medium" style={[pal.text, mStyles.header]}>
<Trans>
Check out some recommended feeds. Tap + to add them to your list
of pinned feeds.
</Trans>
</Text>
{hasFeeds ? (
<FlatList
data={data.pages[0].feeds}
renderItem={({item}) => <RecommendedFeedsItem item={item} />}
keyExtractor={item => item.uri}
style={{flex: 1}}
showsVerticalScrollIndicator={false}
/>
) : isLoading ? (
<View style={{flex: 1}}>
<ActivityIndicator size="large" />
</View>
) : (
<View style={{flex: 1}}>
<ErrorMessage
message={_(msg`Failed to load recommended feeds`)}
/>
</View>
)}
<Button
onPress={next}
label={_(msg`Continue`)}
testID="continueBtn"
style={mStyles.button}
labelStyle={mStyles.buttonText}
/>
</View>
</Mobile>
</>
)
}
const tdStyles = StyleSheet.create({
container: {
flex: 1,
marginHorizontal: 16,
justifyContent: 'space-between',
},
title1: {
fontSize: 36,
fontWeight: '800',
textAlign: 'right',
},
title1Small: {
fontSize: 24,
},
title2: {
fontSize: 58,
fontWeight: '800',
textAlign: 'right',
},
title2Small: {
fontSize: 36,
},
description: {
maxWidth: 400,
marginTop: 10,
marginLeft: 'auto',
textAlign: 'right',
},
})
const mStyles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'space-between',
},
header: {
marginBottom: 16,
marginHorizontal: 16,
},
button: {
marginBottom: 16,
marginHorizontal: 16,
marginTop: 16,
alignItems: 'center',
},
buttonText: {
textAlign: 'center',
fontSize: 18,
paddingVertical: 4,
},
})

View File

@ -1,172 +0,0 @@
import React from 'react'
import {View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {AppBskyFeedDefs, RichText as BskRichText} from '@atproto/api'
import {Text} from 'view/com/util/text/Text'
import {RichText} from 'view/com/util/text/RichText'
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 {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {sanitizeHandle} from 'lib/strings/handles'
import {
usePreferencesQuery,
usePinFeedMutation,
useRemoveFeedMutation,
} from '#/state/queries/preferences'
import {logger} from '#/logger'
import {useAnalytics} from '#/lib/analytics/analytics'
import {Trans, msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
export function RecommendedFeedsItem({
item,
}: {
item: AppBskyFeedDefs.GeneratorView
}) {
const {isMobile} = useWebMediaQueries()
const pal = usePalette('default')
const {_} = useLingui()
const {data: preferences} = usePreferencesQuery()
const {
mutateAsync: pinFeed,
variables: pinnedFeed,
reset: resetPinFeed,
} = usePinFeedMutation()
const {
mutateAsync: removeFeed,
variables: removedFeed,
reset: resetRemoveFeed,
} = useRemoveFeedMutation()
const {track} = useAnalytics()
if (!item || !preferences) return null
const isPinned =
!removedFeed?.uri &&
(pinnedFeed?.uri || preferences.feeds.saved.includes(item.uri))
const onToggle = async () => {
if (isPinned) {
try {
await removeFeed({uri: item.uri})
resetRemoveFeed()
} catch (e) {
Toast.show(_(msg`There was an issue contacting your server`))
logger.error('Failed to unsave feed', {message: e})
}
} else {
try {
await pinFeed({uri: item.uri})
resetPinFeed()
track('Onboarding:CustomFeedAdded')
} catch (e) {
Toast.show(_(msg`There was an issue contacting your server`))
logger.error('Failed to pin feed', {message: e})
}
}
}
return (
<View testID={`feed-${item.displayName}`}>
<View
style={[
pal.border,
{
flex: isMobile ? 1 : undefined,
flexDirection: 'row',
gap: 18,
maxWidth: isMobile ? undefined : 670,
borderRightWidth: isMobile ? undefined : 1,
paddingHorizontal: 24,
paddingVertical: isMobile ? 12 : 24,
borderTopWidth: 1,
},
]}>
<View style={{marginTop: 2}}>
<UserAvatar type="algo" size={42} avatar={item.avatar} />
</View>
<View style={{flex: isMobile ? 1 : undefined}}>
<Text
type="2xl-bold"
numberOfLines={1}
style={[pal.text, {fontSize: 19}]}>
{item.displayName}
</Text>
<Text style={[pal.textLight, {marginBottom: 8}]} numberOfLines={1}>
<Trans>by {sanitizeHandle(item.creator.handle, '@')}</Trans>
</Text>
{item.description ? (
<RichText
type="xl"
style={[
pal.text,
{
flex: isMobile ? 1 : undefined,
maxWidth: 550,
marginBottom: 18,
},
]}
richText={new BskRichText({text: item.description || ''})}
numberOfLines={6}
/>
) : null}
<View style={{flexDirection: 'row', alignItems: 'center', gap: 12}}>
<Button
type="inverted"
style={{paddingVertical: 6}}
onPress={onToggle}>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
paddingRight: 2,
gap: 6,
}}>
{isPinned ? (
<>
<FontAwesomeIcon
icon="check"
size={16}
color={pal.colors.textInverted}
/>
<Text type="lg-medium" style={pal.textInverted}>
<Trans>Added</Trans>
</Text>
</>
) : (
<>
<FontAwesomeIcon
icon="plus"
size={16}
color={pal.colors.textInverted}
/>
<Text type="lg-medium" style={pal.textInverted}>
<Trans>Add</Trans>
</Text>
</>
)}
</View>
</Button>
<View style={{flexDirection: 'row', gap: 4}}>
<HeartIcon
size={16}
strokeWidth={2.5}
style={[pal.textLight, {position: 'relative', top: 2}]}
/>
<Text type="lg-medium" style={[pal.text, pal.textLight]}>
{item.likeCount || 0}
</Text>
</View>
</View>
</View>
</View>
</View>
)
}

View File

@ -1,272 +0,0 @@
import React from 'react'
import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native'
import {AppBskyActorDefs, moderateProfile} from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {logger} from '#/logger'
import {useModerationOpts} from '#/state/queries/preferences'
import {useSuggestedFollowsQuery} from '#/state/queries/suggested-follows'
import {useGetSuggestedFollowersByActor} from '#/state/queries/suggested-follows'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {Button} from 'view/com/util/forms/Button'
import {Mobile, TabletOrDesktop} from 'view/com/util/layouts/Breakpoints'
import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout'
import {Text} from 'view/com/util/text/Text'
import {ViewHeader} from 'view/com/util/ViewHeader'
import {RecommendedFollowsItem} from './RecommendedFollowsItem'
type Props = {
next: () => void
}
export function RecommendedFollows({next}: Props) {
const pal = usePalette('default')
const {_} = useLingui()
const {isTabletOrMobile} = useWebMediaQueries()
const {data: suggestedFollows} = useSuggestedFollowsQuery()
const getSuggestedFollowsByActor = useGetSuggestedFollowersByActor()
const [additionalSuggestions, setAdditionalSuggestions] = React.useState<{
[did: string]: AppBskyActorDefs.ProfileView[]
}>({})
const existingDids = React.useRef<string[]>([])
const moderationOpts = useModerationOpts()
const title = (
<>
<Trans>
<Text
style={[
pal.textLight,
tdStyles.title1,
isTabletOrMobile && tdStyles.title1Small,
]}>
Follow some
</Text>
<Text
style={[
pal.link,
tdStyles.title2,
isTabletOrMobile && tdStyles.title2Small,
]}>
Recommended
</Text>
<Text
style={[
pal.link,
tdStyles.title2,
isTabletOrMobile && tdStyles.title2Small,
]}>
Users
</Text>
</Trans>
<Text type="2xl-medium" style={[pal.textLight, tdStyles.description]}>
<Trans>
Follow some users to get started. We can recommend you more users
based on who you find interesting.
</Trans>
</Text>
<View
style={{
flexDirection: 'row',
justifyContent: 'flex-end',
marginTop: 20,
}}>
<Button onPress={next} testID="continueBtn">
<View
style={{
flexDirection: 'row',
alignItems: 'center',
paddingLeft: 2,
gap: 6,
}}>
<Text
type="2xl-medium"
style={{color: '#fff', position: 'relative', top: -1}}>
<Trans context="action">Done</Trans>
</Text>
<FontAwesomeIcon icon="angle-right" color="#fff" size={14} />
</View>
</Button>
</View>
</>
)
const suggestions = React.useMemo(() => {
if (!suggestedFollows) return []
const additional = Object.entries(additionalSuggestions)
const items = suggestedFollows.pages.flatMap(page => page.actors)
outer: while (additional.length) {
const additionalAccount = additional.shift()
if (!additionalAccount) break
const [followedUser, relatedAccounts] = additionalAccount
for (let i = 0; i < items.length; i++) {
if (items[i].did === followedUser) {
items.splice(i + 1, 0, ...relatedAccounts)
continue outer
}
}
}
existingDids.current = items.map(i => i.did)
return items
}, [suggestedFollows, additionalSuggestions])
const onFollowStateChange = React.useCallback(
async ({following, did}: {following: boolean; did: string}) => {
if (following) {
try {
const {suggestions: results} = await getSuggestedFollowsByActor(did)
if (results.length) {
const deduped = results.filter(
r => !existingDids.current.find(did => did === r.did),
)
setAdditionalSuggestions(s => ({
...s,
[did]: deduped.slice(0, 3),
}))
}
} catch (e) {
logger.error('RecommendedFollows: failed to get suggestions', {
message: e,
})
}
}
// not handling the unfollow case
},
[existingDids, getSuggestedFollowsByActor, setAdditionalSuggestions],
)
return (
<>
<TabletOrDesktop>
<TitleColumnLayout
testID="recommendedFollowsOnboarding"
title={title}
horizontal
titleStyle={isTabletOrMobile ? undefined : {minWidth: 470}}
contentStyle={{paddingHorizontal: 0}}>
{!suggestedFollows || !moderationOpts ? (
<ActivityIndicator size="large" />
) : (
<FlatList
data={suggestions}
renderItem={({item}) => (
<RecommendedFollowsItem
profile={item}
onFollowStateChange={onFollowStateChange}
moderation={moderateProfile(item, moderationOpts)}
/>
)}
keyExtractor={item => item.did}
style={{flex: 1}}
/>
)}
</TitleColumnLayout>
</TabletOrDesktop>
<Mobile>
<View style={[mStyles.container]} testID="recommendedFollowsOnboarding">
<View>
<ViewHeader
title={_(msg`Recommended Users`)}
showBackButton={false}
showOnDesktop
/>
<Text type="lg-medium" style={[pal.text, mStyles.header]}>
<Trans>
Check out some recommended users. Follow them to see similar
users.
</Trans>
</Text>
</View>
{!suggestedFollows || !moderationOpts ? (
<ActivityIndicator size="large" />
) : (
<FlatList
data={suggestions}
renderItem={({item}) => (
<RecommendedFollowsItem
profile={item}
onFollowStateChange={onFollowStateChange}
moderation={moderateProfile(item, moderationOpts)}
/>
)}
keyExtractor={item => item.did}
style={{flex: 1}}
showsVerticalScrollIndicator={false}
/>
)}
<Button
onPress={next}
label={_(msg`Continue`)}
testID="continueBtn"
style={mStyles.button}
labelStyle={mStyles.buttonText}
/>
</View>
</Mobile>
</>
)
}
const tdStyles = StyleSheet.create({
container: {
flex: 1,
marginHorizontal: 16,
justifyContent: 'space-between',
},
title1: {
fontSize: 36,
fontWeight: '800',
textAlign: 'right',
},
title1Small: {
fontSize: 24,
},
title2: {
fontSize: 58,
fontWeight: '800',
textAlign: 'right',
},
title2Small: {
fontSize: 36,
},
description: {
maxWidth: 400,
marginTop: 10,
marginLeft: 'auto',
textAlign: 'right',
},
})
const mStyles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'space-between',
},
header: {
marginBottom: 16,
marginHorizontal: 16,
},
button: {
marginBottom: 16,
marginHorizontal: 16,
marginTop: 16,
alignItems: 'center',
},
buttonText: {
textAlign: 'center',
fontSize: 18,
paddingVertical: 4,
},
})

View File

@ -1,202 +0,0 @@
import React from 'react'
import {View, StyleSheet, ActivityIndicator} from 'react-native'
import {ModerationDecision, AppBskyActorDefs} from '@atproto/api'
import {Button} from '#/view/com/util/forms/Button'
import {usePalette} from 'lib/hooks/usePalette'
import {sanitizeDisplayName} from 'lib/strings/display-names'
import {sanitizeHandle} from 'lib/strings/handles'
import {s} from 'lib/styles'
import {UserAvatar} from 'view/com/util/UserAvatar'
import {Text} from 'view/com/util/text/Text'
import Animated, {FadeInRight} from 'react-native-reanimated'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {useAnalytics} from 'lib/analytics/analytics'
import {useLingui} from '@lingui/react'
import {Trans, msg} from '@lingui/macro'
import {Shadow, useProfileShadow} from '#/state/cache/profile-shadow'
import {useProfileFollowMutationQueue} from '#/state/queries/profile'
import {logger} from '#/logger'
type Props = {
profile: AppBskyActorDefs.ProfileViewBasic
moderation: ModerationDecision
onFollowStateChange: (props: {
did: string
following: boolean
}) => Promise<void>
}
export function RecommendedFollowsItem({
profile,
moderation,
onFollowStateChange,
}: React.PropsWithChildren<Props>) {
const pal = usePalette('default')
const {isMobile} = useWebMediaQueries()
const shadowedProfile = useProfileShadow(profile)
return (
<Animated.View
entering={FadeInRight}
style={[
styles.cardContainer,
pal.view,
pal.border,
{
maxWidth: isMobile ? undefined : 670,
borderRightWidth: isMobile ? undefined : 1,
},
]}>
<ProfileCard
key={profile.did}
profile={shadowedProfile}
onFollowStateChange={onFollowStateChange}
moderation={moderation}
/>
</Animated.View>
)
}
function ProfileCard({
profile,
onFollowStateChange,
moderation,
}: {
profile: Shadow<AppBskyActorDefs.ProfileViewBasic>
moderation: ModerationDecision
onFollowStateChange: (props: {
did: string
following: boolean
}) => Promise<void>
}) {
const {track} = useAnalytics()
const pal = usePalette('default')
const {_} = useLingui()
const [addingMoreSuggestions, setAddingMoreSuggestions] =
React.useState(false)
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
profile,
'RecommendedFollowsItem',
)
const onToggleFollow = React.useCallback(async () => {
try {
if (profile.viewer?.following) {
await queueUnfollow()
} else {
setAddingMoreSuggestions(true)
await queueFollow()
await onFollowStateChange({did: profile.did, following: true})
setAddingMoreSuggestions(false)
track('Onboarding:SuggestedFollowFollowed')
}
} catch (e: any) {
if (e?.name !== 'AbortError') {
logger.error('RecommendedFollows: failed to toggle following', {
message: e,
})
}
} finally {
setAddingMoreSuggestions(false)
}
}, [
profile,
queueFollow,
queueUnfollow,
setAddingMoreSuggestions,
track,
onFollowStateChange,
])
return (
<View style={styles.card}>
<View style={styles.layout}>
<View style={styles.layoutAvi}>
<UserAvatar
size={40}
avatar={profile.avatar}
moderation={moderation.ui('avatar')}
/>
</View>
<View style={styles.layoutContent}>
<Text
type="2xl-bold"
style={[s.bold, pal.text]}
numberOfLines={1}
lineHeight={1.2}>
{sanitizeDisplayName(
profile.displayName || sanitizeHandle(profile.handle),
moderation.ui('displayName'),
)}
</Text>
<Text type="xl" style={[pal.textLight]} numberOfLines={1}>
{sanitizeHandle(profile.handle, '@')}
</Text>
</View>
<Button
type={profile.viewer?.following ? 'default' : 'inverted'}
labelStyle={styles.followButton}
onPress={onToggleFollow}
label={profile.viewer?.following ? _(msg`Unfollow`) : _(msg`Follow`)}
/>
</View>
{profile.description ? (
<View style={styles.details}>
<Text type="lg" style={pal.text} numberOfLines={4}>
{profile.description as string}
</Text>
</View>
) : undefined}
{addingMoreSuggestions ? (
<View style={styles.addingMoreContainer}>
<ActivityIndicator size="small" color={pal.colors.text} />
<Text style={[pal.text]}>
<Trans>Finding similar accounts...</Trans>
</Text>
</View>
) : null}
</View>
)
}
const styles = StyleSheet.create({
cardContainer: {
borderTopWidth: 1,
},
card: {
paddingHorizontal: 10,
},
layout: {
flexDirection: 'row',
alignItems: 'center',
},
layoutAvi: {
width: 54,
paddingLeft: 4,
paddingTop: 8,
paddingBottom: 10,
},
layoutContent: {
flex: 1,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10,
},
details: {
paddingLeft: 54,
paddingRight: 10,
paddingBottom: 10,
},
addingMoreContainer: {
flexDirection: 'row',
alignItems: 'center',
paddingLeft: 54,
paddingTop: 4,
paddingBottom: 12,
gap: 4,
},
followButton: {
fontSize: 16,
},
})

View File

@ -1,10 +0,0 @@
import 'react'
import {withBreakpoints} from 'view/com/util/layouts/withBreakpoints'
import {WelcomeDesktop} from './WelcomeDesktop'
import {WelcomeMobile} from './WelcomeMobile'
export const Welcome = withBreakpoints(
WelcomeMobile,
WelcomeDesktop,
WelcomeDesktop,
)

View File

@ -1,126 +0,0 @@
import React from 'react'
import {StyleSheet, View} from 'react-native'
import {useMediaQuery} from 'react-responsive'
import {Text} from 'view/com/util/text/Text'
import {s} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {TitleColumnLayout} from 'view/com/util/layouts/TitleColumnLayout'
import {Button} from 'view/com/util/forms/Button'
import {Trans} from '@lingui/macro'
type Props = {
next: () => void
skip: () => void
}
export function WelcomeDesktop({next}: Props) {
const pal = usePalette('default')
const horizontal = useMediaQuery({minWidth: 1300})
const title = (
<Trans>
<Text
style={[
pal.textLight,
{
fontSize: 36,
fontWeight: '800',
textAlign: horizontal ? 'right' : 'left',
},
]}>
Welcome to
</Text>
<Text
style={[
pal.link,
{
fontSize: 72,
fontWeight: '800',
textAlign: horizontal ? 'right' : 'left',
},
]}>
Bluesky
</Text>
</Trans>
)
return (
<TitleColumnLayout
testID="welcomeOnboarding"
title={title}
horizontal={horizontal}
titleStyle={horizontal ? {paddingBottom: 160} : undefined}>
<View style={[styles.row]}>
<FontAwesomeIcon icon={'globe'} size={36} color={pal.colors.link} />
<View style={[styles.rowText]}>
<Text type="xl-bold" style={[pal.text]}>
<Trans>Bluesky is public.</Trans>
</Text>
<Text type="xl" style={[pal.text, s.pt2]}>
<Trans>
Your posts, likes, and blocks are public. Mutes are private.
</Trans>
</Text>
</View>
</View>
<View style={[styles.row]}>
<FontAwesomeIcon icon={'at'} size={36} color={pal.colors.link} />
<View style={[styles.rowText]}>
<Text type="xl-bold" style={[pal.text]}>
<Trans>Bluesky is open.</Trans>
</Text>
<Text type="xl" style={[pal.text, s.pt2]}>
<Trans>Never lose access to your followers and data.</Trans>
</Text>
</View>
</View>
<View style={[styles.row]}>
<FontAwesomeIcon icon={'gear'} size={36} color={pal.colors.link} />
<View style={[styles.rowText]}>
<Text type="xl-bold" style={[pal.text]}>
<Trans>Bluesky is flexible.</Trans>
</Text>
<Text type="xl" style={[pal.text, s.pt2]}>
<Trans>
Choose the algorithms that power your experience with custom
feeds.
</Trans>
</Text>
</View>
</View>
<View style={styles.spacer} />
<View style={{flexDirection: 'row'}}>
<Button onPress={next} testID="continueBtn">
<View
style={{
flexDirection: 'row',
alignItems: 'center',
paddingLeft: 2,
gap: 6,
}}>
<Text
type="2xl-medium"
style={{color: '#fff', position: 'relative', top: -1}}>
<Trans context="action">Next</Trans>
</Text>
<FontAwesomeIcon icon="angle-right" color="#fff" size={14} />
</View>
</Button>
</View>
</TitleColumnLayout>
)
}
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
columnGap: 20,
alignItems: 'center',
marginVertical: 20,
},
rowText: {
flex: 1,
},
spacer: {
height: 20,
},
})

View File

@ -1,136 +0,0 @@
import React from 'react'
import {Pressable, StyleSheet, View} from 'react-native'
import {Text} from 'view/com/util/text/Text'
import {s} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {Button} from 'view/com/util/forms/Button'
import {ViewHeader} from 'view/com/util/ViewHeader'
import {useLingui} from '@lingui/react'
import {Trans, msg} from '@lingui/macro'
type Props = {
next: () => void
skip: () => void
}
export function WelcomeMobile({next, skip}: Props) {
const pal = usePalette('default')
const {_} = useLingui()
return (
<View style={[styles.container]} testID="welcomeOnboarding">
<ViewHeader
showOnDesktop
showBorder={false}
showBackButton={false}
title=""
renderButton={() => {
return (
<Pressable
accessibilityRole="button"
style={[s.flexRow, s.alignCenter]}
onPress={skip}>
<Text style={[pal.link]}>
<Trans>Skip</Trans>
</Text>
<FontAwesomeIcon
icon={'chevron-right'}
size={14}
color={pal.colors.link}
/>
</Pressable>
)
}}
/>
<View>
<Text style={[pal.text, styles.title]}>
<Trans>
Welcome to{' '}
<Text style={[pal.text, pal.link, styles.title]}>Bluesky</Text>
</Trans>
</Text>
<View style={styles.spacer} />
<View style={[styles.row]}>
<FontAwesomeIcon icon={'globe'} size={36} color={pal.colors.link} />
<View style={[styles.rowText]}>
<Text type="lg-bold" style={[pal.text]}>
<Trans>Bluesky is public.</Trans>
</Text>
<Text type="lg-thin" style={[pal.text, s.pt2]}>
<Trans>
Your posts, likes, and blocks are public. Mutes are private.
</Trans>
</Text>
</View>
</View>
<View style={[styles.row]}>
<FontAwesomeIcon icon={'at'} size={36} color={pal.colors.link} />
<View style={[styles.rowText]}>
<Text type="lg-bold" style={[pal.text]}>
<Trans>Bluesky is open.</Trans>
</Text>
<Text type="lg-thin" style={[pal.text, s.pt2]}>
<Trans>Never lose access to your followers and data.</Trans>
</Text>
</View>
</View>
<View style={[styles.row]}>
<FontAwesomeIcon icon={'gear'} size={36} color={pal.colors.link} />
<View style={[styles.rowText]}>
<Text type="lg-bold" style={[pal.text]}>
<Trans>Bluesky is flexible.</Trans>
</Text>
<Text type="lg-thin" style={[pal.text, s.pt2]}>
<Trans>
Choose the algorithms that power your experience with custom
feeds.
</Trans>
</Text>
</View>
</View>
</View>
<Button
onPress={next}
label={_(msg`Continue`)}
testID="continueBtn"
style={[styles.buttonContainer]}
labelStyle={styles.buttonText}
/>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginBottom: 60,
marginHorizontal: 16,
justifyContent: 'space-between',
},
title: {
fontSize: 42,
fontWeight: '800',
},
row: {
flexDirection: 'row',
columnGap: 20,
alignItems: 'center',
marginVertical: 20,
},
rowText: {
flex: 1,
},
spacer: {
height: 20,
},
buttonContainer: {
alignItems: 'center',
},
buttonText: {
textAlign: 'center',
fontSize: 18,
marginVertical: 4,
},
})

View File

@ -1,11 +1,8 @@
import * as React from 'react' import * as React from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {PWI_ENABLED, NEW_ONBOARDING_ENABLED} from '#/lib/build-flags'
// Based on @react-navigation/native-stack/src/createNativeStackNavigator.ts // Based on @react-navigation/native-stack/src/createNativeStackNavigator.ts
// MIT License // MIT License
// Copyright (c) 2017 React Navigation Contributors // Copyright (c) 2017 React Navigation Contributors
import { import {
createNavigatorFactory, createNavigatorFactory,
EventArg, EventArg,
@ -21,24 +18,24 @@ import type {
NativeStackNavigationEventMap, NativeStackNavigationEventMap,
NativeStackNavigationOptions, NativeStackNavigationOptions,
} from '@react-navigation/native-stack' } from '@react-navigation/native-stack'
import type {NativeStackNavigatorProps} from '@react-navigation/native-stack/src/types'
import {NativeStackView} from '@react-navigation/native-stack' import {NativeStackView} from '@react-navigation/native-stack'
import type {NativeStackNavigatorProps} from '@react-navigation/native-stack/src/types'
import {BottomBarWeb} from './bottom-bar/BottomBarWeb' import {PWI_ENABLED} from '#/lib/build-flags'
import {DesktopLeftNav} from './desktop/LeftNav'
import {DesktopRightNav} from './desktop/RightNav'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {useSession} from '#/state/session'
import {useOnboardingState} from '#/state/shell' import {useOnboardingState} from '#/state/shell'
import { import {
useLoggedOutView, useLoggedOutView,
useLoggedOutViewControls, useLoggedOutViewControls,
} from '#/state/shell/logged-out' } from '#/state/shell/logged-out'
import {useSession} from '#/state/session'
import {isWeb} from 'platform/detection' import {isWeb} from 'platform/detection'
import {Deactivated} from '#/screens/Deactivated' import {Deactivated} from '#/screens/Deactivated'
import {Onboarding} from '#/screens/Onboarding'
import {LoggedOut} from '../com/auth/LoggedOut' import {LoggedOut} from '../com/auth/LoggedOut'
import {Onboarding} from '../com/auth/Onboarding' import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
import {Onboarding as NewOnboarding} from '#/screens/Onboarding' import {DesktopLeftNav} from './desktop/LeftNav'
import {DesktopRightNav} from './desktop/RightNav'
type NativeStackNavigationOptionsWithAuth = NativeStackNavigationOptions & { type NativeStackNavigationOptionsWithAuth = NativeStackNavigationOptions & {
requireAuth?: boolean requireAuth?: boolean
@ -112,12 +109,8 @@ function NativeStackNavigator({
return <LoggedOut onDismiss={() => setShowLoggedOut(false)} /> return <LoggedOut onDismiss={() => setShowLoggedOut(false)} />
} }
if (onboardingState.isActive) { if (onboardingState.isActive) {
if (NEW_ONBOARDING_ENABLED) {
return <NewOnboarding />
} else {
return <Onboarding /> return <Onboarding />
} }
}
const newDescriptors: typeof descriptors = {} const newDescriptors: typeof descriptors = {}
for (let key in descriptors) { for (let key in descriptors) {
const descriptor = descriptors[key] const descriptor = descriptors[key]