Starter Packs (#4332)
Co-authored-by: Dan Abramov <dan.abramov@gmail.com> Co-authored-by: Paul Frazee <pfrazee@gmail.com> Co-authored-by: Eric Bailey <git@esb.lol> Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
parent
35f64535cb
commit
f089f45781
115 changed files with 6336 additions and 237 deletions
|
@ -7,7 +7,6 @@ import {useNavigation} from '@react-navigation/native'
|
|||
|
||||
import {useAnalytics} from '#/lib/analytics/analytics'
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
import {s} from '#/lib/styles'
|
||||
import {isIOS, isNative} from '#/platform/detection'
|
||||
|
@ -22,13 +21,16 @@ import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
|
|||
import {Text} from '#/view/com/util/text/Text'
|
||||
import {Login} from '#/screens/Login'
|
||||
import {Signup} from '#/screens/Signup'
|
||||
import {LandingScreen} from '#/screens/StarterPack/StarterPackLandingScreen'
|
||||
import {SplashScreen} from './SplashScreen'
|
||||
|
||||
enum ScreenState {
|
||||
S_LoginOrCreateAccount,
|
||||
S_Login,
|
||||
S_CreateAccount,
|
||||
S_StarterPack,
|
||||
}
|
||||
export {ScreenState as LoggedOutScreenState}
|
||||
|
||||
export function LoggedOut({onDismiss}: {onDismiss?: () => void}) {
|
||||
const {hasSession} = useSession()
|
||||
|
@ -37,18 +39,21 @@ export function LoggedOut({onDismiss}: {onDismiss?: () => void}) {
|
|||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {screen} = useAnalytics()
|
||||
const {requestedAccountSwitchTo} = useLoggedOutView()
|
||||
const [screenState, setScreenState] = React.useState<ScreenState>(
|
||||
requestedAccountSwitchTo
|
||||
? requestedAccountSwitchTo === 'new'
|
||||
? ScreenState.S_CreateAccount
|
||||
: ScreenState.S_Login
|
||||
: ScreenState.S_LoginOrCreateAccount,
|
||||
)
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [screenState, setScreenState] = React.useState<ScreenState>(() => {
|
||||
if (requestedAccountSwitchTo === 'new') {
|
||||
return ScreenState.S_CreateAccount
|
||||
} else if (requestedAccountSwitchTo === 'starterpack') {
|
||||
return ScreenState.S_StarterPack
|
||||
} else if (requestedAccountSwitchTo != null) {
|
||||
return ScreenState.S_Login
|
||||
} else {
|
||||
return ScreenState.S_LoginOrCreateAccount
|
||||
}
|
||||
})
|
||||
const {clearRequestedAccount} = useLoggedOutViewControls()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const isFirstScreen = screenState === ScreenState.S_LoginOrCreateAccount
|
||||
|
||||
const isFirstScreen = screenState === ScreenState.S_LoginOrCreateAccount
|
||||
React.useEffect(() => {
|
||||
screen('Login')
|
||||
setMinimalShellMode(true)
|
||||
|
@ -66,18 +71,9 @@ export function LoggedOut({onDismiss}: {onDismiss?: () => void}) {
|
|||
}, [navigation])
|
||||
|
||||
return (
|
||||
<View
|
||||
testID="noSessionView"
|
||||
style={[
|
||||
s.hContentRegion,
|
||||
pal.view,
|
||||
{
|
||||
// only needed if dismiss button is present
|
||||
paddingTop: onDismiss && isMobile ? 40 : 0,
|
||||
},
|
||||
]}>
|
||||
<View testID="noSessionView" style={[s.hContentRegion, pal.view]}>
|
||||
<ErrorBoundary>
|
||||
{onDismiss ? (
|
||||
{onDismiss && screenState === ScreenState.S_LoginOrCreateAccount ? (
|
||||
<Pressable
|
||||
accessibilityHint={_(msg`Go back`)}
|
||||
accessibilityLabel={_(msg`Go back`)}
|
||||
|
@ -132,7 +128,9 @@ export function LoggedOut({onDismiss}: {onDismiss?: () => void}) {
|
|||
</Pressable>
|
||||
) : null}
|
||||
|
||||
{screenState === ScreenState.S_LoginOrCreateAccount ? (
|
||||
{screenState === ScreenState.S_StarterPack ? (
|
||||
<LandingScreen setScreenState={setScreenState} />
|
||||
) : screenState === ScreenState.S_LoginOrCreateAccount ? (
|
||||
<SplashScreen
|
||||
onPressSignin={() => {
|
||||
setScreenState(ScreenState.S_Login)
|
||||
|
|
|
@ -329,6 +329,9 @@ const styles = StyleSheet.create({
|
|||
flex: 1,
|
||||
gap: 14,
|
||||
},
|
||||
border: {
|
||||
borderTopWidth: hairlineWidth,
|
||||
},
|
||||
headerContainer: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
|
|
|
@ -52,7 +52,16 @@ import {TimeElapsed} from '../util/TimeElapsed'
|
|||
import {PreviewableUserAvatar, UserAvatar} from '../util/UserAvatar'
|
||||
|
||||
import hairlineWidth = StyleSheet.hairlineWidth
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {parseTenorGif} from '#/lib/strings/embed-player'
|
||||
import {logger} from '#/logger'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {DM_SERVICE_HEADERS} from 'state/queries/messages/const'
|
||||
import {useAgent} from 'state/session'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import {StarterPack} from '#/components/icons/StarterPack'
|
||||
import {Notification as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
|
||||
|
||||
const MAX_AUTHORS = 5
|
||||
|
||||
|
@ -89,7 +98,10 @@ let FeedItem = ({
|
|||
} else if (item.type === 'reply') {
|
||||
const urip = new AtUri(item.notification.uri)
|
||||
return `/profile/${urip.host}/post/${urip.rkey}`
|
||||
} else if (item.type === 'feedgen-like') {
|
||||
} else if (
|
||||
item.type === 'feedgen-like' ||
|
||||
item.type === 'starterpack-joined'
|
||||
) {
|
||||
if (item.subjectUri) {
|
||||
const urip = new AtUri(item.subjectUri)
|
||||
return `/profile/${urip.host}/feed/${urip.rkey}`
|
||||
|
@ -176,6 +188,13 @@ let FeedItem = ({
|
|||
icon = <PersonPlusIcon size="xl" style={{color: t.palette.primary_500}} />
|
||||
} else if (item.type === 'feedgen-like') {
|
||||
action = _(msg`liked your custom feed`)
|
||||
} else if (item.type === 'starterpack-joined') {
|
||||
icon = (
|
||||
<View style={{height: 30, width: 30}}>
|
||||
<StarterPack width={30} gradient="sky" />
|
||||
</View>
|
||||
)
|
||||
action = _(msg`signed up with your starter pack`)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
@ -289,6 +308,20 @@ let FeedItem = ({
|
|||
showLikes
|
||||
/>
|
||||
) : null}
|
||||
{item.type === 'starterpack-joined' ? (
|
||||
<View>
|
||||
<View
|
||||
style={[
|
||||
a.border,
|
||||
a.p_sm,
|
||||
a.rounded_sm,
|
||||
a.mt_sm,
|
||||
t.atoms.border_contrast_low,
|
||||
]}>
|
||||
<StarterPackCard starterPack={item.subject} />
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
</Link>
|
||||
)
|
||||
|
@ -319,14 +352,63 @@ function ExpandListPressable({
|
|||
}
|
||||
}
|
||||
|
||||
function SayHelloBtn({profile}: {profile: AppBskyActorDefs.ProfileViewBasic}) {
|
||||
const {_} = useLingui()
|
||||
const agent = useAgent()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const [isLoading, setIsLoading] = React.useState(false)
|
||||
|
||||
if (
|
||||
profile.associated?.chat?.allowIncoming === 'none' ||
|
||||
(profile.associated?.chat?.allowIncoming === 'following' &&
|
||||
!profile.viewer?.followedBy)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
label={_(msg`Say hello!`)}
|
||||
variant="ghost"
|
||||
color="primary"
|
||||
size="xsmall"
|
||||
style={[a.self_center, {marginLeft: 'auto'}]}
|
||||
disabled={isLoading}
|
||||
onPress={async () => {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
const res = await agent.api.chat.bsky.convo.getConvoForMembers(
|
||||
{
|
||||
members: [profile.did, agent.session!.did!],
|
||||
},
|
||||
{headers: DM_SERVICE_HEADERS},
|
||||
)
|
||||
navigation.navigate('MessagesConversation', {
|
||||
conversation: res.data.convo.id,
|
||||
})
|
||||
} catch (e) {
|
||||
logger.error('Failed to get conversation', {safeMessage: e})
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}}>
|
||||
<ButtonText>
|
||||
<Trans>Say hello!</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
function CondensedAuthorsList({
|
||||
visible,
|
||||
authors,
|
||||
onToggleAuthorsExpanded,
|
||||
showDmButton = true,
|
||||
}: {
|
||||
visible: boolean
|
||||
authors: Author[]
|
||||
onToggleAuthorsExpanded: () => void
|
||||
showDmButton?: boolean
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
|
@ -355,7 +437,7 @@ function CondensedAuthorsList({
|
|||
}
|
||||
if (authors.length === 1) {
|
||||
return (
|
||||
<View style={styles.avis}>
|
||||
<View style={[styles.avis]}>
|
||||
<PreviewableUserAvatar
|
||||
size={35}
|
||||
profile={authors[0].profile}
|
||||
|
@ -363,6 +445,7 @@ function CondensedAuthorsList({
|
|||
type={authors[0].profile.associated?.labeler ? 'labeler' : 'user'}
|
||||
accessible={false}
|
||||
/>
|
||||
{showDmButton ? <SayHelloBtn profile={authors[0].profile} /> : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import React from 'react'
|
||||
import {StyleProp, TextStyle, View} from 'react-native'
|
||||
import {AppBskyActorDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {Shadow} from '#/state/cache/types'
|
||||
import {useProfileFollowMutationQueue} from '#/state/queries/profile'
|
||||
import {Button, ButtonType} from '../util/forms/Button'
|
||||
import * as Toast from '../util/Toast'
|
||||
import {useProfileFollowMutationQueue} from '#/state/queries/profile'
|
||||
import {Shadow} from '#/state/cache/types'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
export function FollowButton({
|
||||
unfollowedType = 'inverted',
|
||||
|
@ -19,7 +20,7 @@ export function FollowButton({
|
|||
followedType?: ButtonType
|
||||
profile: Shadow<AppBskyActorDefs.ProfileViewBasic>
|
||||
labelStyle?: StyleProp<TextStyle>
|
||||
logContext: 'ProfileCard'
|
||||
logContext: 'ProfileCard' | 'StarterPackProfilesList'
|
||||
}) {
|
||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
|
||||
profile,
|
||||
|
|
|
@ -251,12 +251,14 @@ export function ProfileCardWithFollowBtn({
|
|||
noBorder,
|
||||
followers,
|
||||
onPress,
|
||||
logContext = 'ProfileCard',
|
||||
}: {
|
||||
profile: AppBskyActorDefs.ProfileViewBasic
|
||||
noBg?: boolean
|
||||
noBorder?: boolean
|
||||
followers?: AppBskyActorDefs.ProfileView[] | undefined
|
||||
onPress?: () => void
|
||||
logContext?: 'ProfileCard' | 'StarterPackProfilesList'
|
||||
}) {
|
||||
const {currentAccount} = useSession()
|
||||
const isMe = profile.did === currentAccount?.did
|
||||
|
@ -271,7 +273,7 @@ export function ProfileCardWithFollowBtn({
|
|||
isMe
|
||||
? undefined
|
||||
: profileShadow => (
|
||||
<FollowButton profile={profileShadow} logContext="ProfileCard" />
|
||||
<FollowButton profile={profileShadow} logContext={logContext} />
|
||||
)
|
||||
}
|
||||
onPress={onPress}
|
||||
|
@ -314,6 +316,7 @@ const styles = StyleSheet.create({
|
|||
paddingRight: 10,
|
||||
},
|
||||
details: {
|
||||
justifyContent: 'center',
|
||||
paddingLeft: 54,
|
||||
paddingRight: 10,
|
||||
paddingBottom: 10,
|
||||
|
@ -339,7 +342,6 @@ const styles = StyleSheet.create({
|
|||
|
||||
followedBy: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingLeft: 54,
|
||||
paddingRight: 20,
|
||||
marginBottom: 10,
|
||||
|
|
|
@ -21,7 +21,9 @@ import {Text} from '../util/text/Text'
|
|||
import {UserAvatar, UserAvatarType} from '../util/UserAvatar'
|
||||
import {CenteredView} from '../util/Views'
|
||||
import hairlineWidth = StyleSheet.hairlineWidth
|
||||
|
||||
import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
|
||||
import {StarterPack} from '#/components/icons/StarterPack'
|
||||
|
||||
export function ProfileSubpageHeader({
|
||||
isLoading,
|
||||
|
@ -44,7 +46,7 @@ export function ProfileSubpageHeader({
|
|||
handle: string
|
||||
}
|
||||
| undefined
|
||||
avatarType: UserAvatarType
|
||||
avatarType: UserAvatarType | 'starter-pack'
|
||||
}>) {
|
||||
const setDrawerOpen = useSetDrawerOpen()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
|
@ -127,7 +129,11 @@ export function ProfileSubpageHeader({
|
|||
accessibilityLabel={_(msg`View the avatar`)}
|
||||
accessibilityHint=""
|
||||
style={{width: 58}}>
|
||||
<UserAvatar type={avatarType} size={58} avatar={avatar} />
|
||||
{avatarType === 'starter-pack' ? (
|
||||
<StarterPack width={58} gradient="sky" />
|
||||
) : (
|
||||
<UserAvatar type={avatarType} size={58} avatar={avatar} />
|
||||
)}
|
||||
</Pressable>
|
||||
<View style={{flex: 1}}>
|
||||
{isLoading ? (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue