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 ? (
|
||||
|
|
|
@ -30,7 +30,7 @@ import {FollowingEndOfFeed} from 'view/com/posts/FollowingEndOfFeed'
|
|||
import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
|
||||
import {HomeHeader} from '../com/home/HomeHeader'
|
||||
|
||||
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
|
||||
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home' | 'Start'>
|
||||
export function HomeScreen(props: Props) {
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
const {data: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} =
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React, {useMemo} from 'react'
|
||||
import React, {useCallback, useMemo} from 'react'
|
||||
import {StyleSheet} from 'react-native'
|
||||
import {
|
||||
AppBskyActorDefs,
|
||||
AppBskyGraphGetActorStarterPacks,
|
||||
moderateProfile,
|
||||
ModerationOpts,
|
||||
RichText as RichTextAPI,
|
||||
|
@ -9,7 +10,11 @@ import {
|
|||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {
|
||||
InfiniteData,
|
||||
UseInfiniteQueryResult,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query'
|
||||
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||
|
@ -22,18 +27,23 @@ import {useAgent, useSession} from '#/state/session'
|
|||
import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useComposerControls} from '#/state/shell/composer'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {IS_DEV, IS_TESTFLIGHT} from 'lib/app-info'
|
||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||
import {ComposeIcon2} from 'lib/icons'
|
||||
import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
|
||||
import {useGate} from 'lib/statsig/statsig'
|
||||
import {combinedDisplayName} from 'lib/strings/display-names'
|
||||
import {isInvalidHandle} from 'lib/strings/handles'
|
||||
import {colors, s} from 'lib/styles'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {listenSoftReset} from 'state/events'
|
||||
import {useActorStarterPacksQuery} from 'state/queries/actor-starter-packs'
|
||||
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
|
||||
import {ProfileHeader, ProfileHeaderLoading} from '#/screens/Profile/Header'
|
||||
import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed'
|
||||
import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels'
|
||||
import {ScreenHider} from '#/components/moderation/ScreenHider'
|
||||
import {ProfileStarterPacks} from '#/components/StarterPack/ProfileStarterPacks'
|
||||
import {ExpoScrollForwarderView} from '../../../modules/expo-scroll-forwarder'
|
||||
import {ProfileFeedgens} from '../com/feeds/ProfileFeedgens'
|
||||
import {ProfileLists} from '../com/lists/ProfileLists'
|
||||
|
@ -69,6 +79,7 @@ export function ProfileScreen({route}: Props) {
|
|||
} = useProfileQuery({
|
||||
did: resolvedDid,
|
||||
})
|
||||
const starterPacksQuery = useActorStarterPacksQuery({did: resolvedDid})
|
||||
|
||||
const onPressTryAgain = React.useCallback(() => {
|
||||
if (resolveError) {
|
||||
|
@ -86,7 +97,7 @@ export function ProfileScreen({route}: Props) {
|
|||
}, [queryClient, profile?.viewer?.blockedBy, resolvedDid])
|
||||
|
||||
// Most pushes will happen here, since we will have only placeholder data
|
||||
if (isLoadingDid || isLoadingProfile) {
|
||||
if (isLoadingDid || isLoadingProfile || starterPacksQuery.isLoading) {
|
||||
return (
|
||||
<CenteredView>
|
||||
<ProfileHeaderLoading />
|
||||
|
@ -108,6 +119,7 @@ export function ProfileScreen({route}: Props) {
|
|||
return (
|
||||
<ProfileScreenLoaded
|
||||
profile={profile}
|
||||
starterPacksQuery={starterPacksQuery}
|
||||
moderationOpts={moderationOpts}
|
||||
isPlaceholderProfile={isPlaceholderProfile}
|
||||
hideBackButton={!!route.params.hideBackButton}
|
||||
|
@ -131,11 +143,16 @@ function ProfileScreenLoaded({
|
|||
isPlaceholderProfile,
|
||||
moderationOpts,
|
||||
hideBackButton,
|
||||
starterPacksQuery,
|
||||
}: {
|
||||
profile: AppBskyActorDefs.ProfileViewDetailed
|
||||
moderationOpts: ModerationOpts
|
||||
hideBackButton: boolean
|
||||
isPlaceholderProfile: boolean
|
||||
starterPacksQuery: UseInfiniteQueryResult<
|
||||
InfiniteData<AppBskyGraphGetActorStarterPacks.OutputSchema, unknown>,
|
||||
Error
|
||||
>
|
||||
}) {
|
||||
const profile = useProfileShadow(profileUnshadowed)
|
||||
const {hasSession, currentAccount} = useSession()
|
||||
|
@ -153,6 +170,9 @@ function ProfileScreenLoaded({
|
|||
const [currentPage, setCurrentPage] = React.useState(0)
|
||||
const {_} = useLingui()
|
||||
const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
|
||||
const gate = useGate()
|
||||
const starterPacksEnabled =
|
||||
IS_DEV || IS_TESTFLIGHT || (!isWeb && gate('starter_packs_enabled'))
|
||||
|
||||
const [scrollViewTag, setScrollViewTag] = React.useState<number | null>(null)
|
||||
|
||||
|
@ -162,6 +182,7 @@ function ProfileScreenLoaded({
|
|||
const likesSectionRef = React.useRef<SectionRef>(null)
|
||||
const feedsSectionRef = React.useRef<SectionRef>(null)
|
||||
const listsSectionRef = React.useRef<SectionRef>(null)
|
||||
const starterPacksSectionRef = React.useRef<SectionRef>(null)
|
||||
const labelsSectionRef = React.useRef<SectionRef>(null)
|
||||
|
||||
useSetTitle(combinedDisplayName(profile))
|
||||
|
@ -183,31 +204,23 @@ function ProfileScreenLoaded({
|
|||
const showMediaTab = !hasLabeler
|
||||
const showLikesTab = isMe
|
||||
const showFeedsTab = isMe || (profile.associated?.feedgens || 0) > 0
|
||||
const showStarterPacksTab =
|
||||
starterPacksEnabled &&
|
||||
(isMe || !!starterPacksQuery.data?.pages?.[0].starterPacks.length)
|
||||
const showListsTab =
|
||||
hasSession && (isMe || (profile.associated?.lists || 0) > 0)
|
||||
|
||||
const sectionTitles = useMemo<string[]>(() => {
|
||||
return [
|
||||
showFiltersTab ? _(msg`Labels`) : undefined,
|
||||
showListsTab && hasLabeler ? _(msg`Lists`) : undefined,
|
||||
showPostsTab ? _(msg`Posts`) : undefined,
|
||||
showRepliesTab ? _(msg`Replies`) : undefined,
|
||||
showMediaTab ? _(msg`Media`) : undefined,
|
||||
showLikesTab ? _(msg`Likes`) : undefined,
|
||||
showFeedsTab ? _(msg`Feeds`) : undefined,
|
||||
showListsTab && !hasLabeler ? _(msg`Lists`) : undefined,
|
||||
].filter(Boolean) as string[]
|
||||
}, [
|
||||
showPostsTab,
|
||||
showRepliesTab,
|
||||
showMediaTab,
|
||||
showLikesTab,
|
||||
showFeedsTab,
|
||||
showListsTab,
|
||||
showFiltersTab,
|
||||
hasLabeler,
|
||||
_,
|
||||
])
|
||||
const sectionTitles = [
|
||||
showFiltersTab ? _(msg`Labels`) : undefined,
|
||||
showListsTab && hasLabeler ? _(msg`Lists`) : undefined,
|
||||
showPostsTab ? _(msg`Posts`) : undefined,
|
||||
showRepliesTab ? _(msg`Replies`) : undefined,
|
||||
showMediaTab ? _(msg`Media`) : undefined,
|
||||
showLikesTab ? _(msg`Likes`) : undefined,
|
||||
showFeedsTab ? _(msg`Feeds`) : undefined,
|
||||
showStarterPacksTab ? _(msg`Starter Packs`) : undefined,
|
||||
showListsTab && !hasLabeler ? _(msg`Lists`) : undefined,
|
||||
].filter(Boolean) as string[]
|
||||
|
||||
let nextIndex = 0
|
||||
let filtersIndex: number | null = null
|
||||
|
@ -216,6 +229,7 @@ function ProfileScreenLoaded({
|
|||
let mediaIndex: number | null = null
|
||||
let likesIndex: number | null = null
|
||||
let feedsIndex: number | null = null
|
||||
let starterPacksIndex: number | null = null
|
||||
let listsIndex: number | null = null
|
||||
if (showFiltersTab) {
|
||||
filtersIndex = nextIndex++
|
||||
|
@ -235,11 +249,14 @@ function ProfileScreenLoaded({
|
|||
if (showFeedsTab) {
|
||||
feedsIndex = nextIndex++
|
||||
}
|
||||
if (showStarterPacksTab) {
|
||||
starterPacksIndex = nextIndex++
|
||||
}
|
||||
if (showListsTab) {
|
||||
listsIndex = nextIndex++
|
||||
}
|
||||
|
||||
const scrollSectionToTop = React.useCallback(
|
||||
const scrollSectionToTop = useCallback(
|
||||
(index: number) => {
|
||||
if (index === filtersIndex) {
|
||||
labelsSectionRef.current?.scrollToTop()
|
||||
|
@ -253,6 +270,8 @@ function ProfileScreenLoaded({
|
|||
likesSectionRef.current?.scrollToTop()
|
||||
} else if (index === feedsIndex) {
|
||||
feedsSectionRef.current?.scrollToTop()
|
||||
} else if (index === starterPacksIndex) {
|
||||
starterPacksSectionRef.current?.scrollToTop()
|
||||
} else if (index === listsIndex) {
|
||||
listsSectionRef.current?.scrollToTop()
|
||||
}
|
||||
|
@ -265,6 +284,7 @@ function ProfileScreenLoaded({
|
|||
likesIndex,
|
||||
feedsIndex,
|
||||
listsIndex,
|
||||
starterPacksIndex,
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -290,7 +310,7 @@ function ProfileScreenLoaded({
|
|||
// events
|
||||
// =
|
||||
|
||||
const onPressCompose = React.useCallback(() => {
|
||||
const onPressCompose = () => {
|
||||
track('ProfileScreen:PressCompose')
|
||||
const mention =
|
||||
profile.handle === currentAccount?.handle ||
|
||||
|
@ -298,23 +318,20 @@ function ProfileScreenLoaded({
|
|||
? undefined
|
||||
: profile.handle
|
||||
openComposer({mention})
|
||||
}, [openComposer, currentAccount, track, profile])
|
||||
}
|
||||
|
||||
const onPageSelected = React.useCallback((i: number) => {
|
||||
const onPageSelected = (i: number) => {
|
||||
setCurrentPage(i)
|
||||
}, [])
|
||||
}
|
||||
|
||||
const onCurrentPageSelected = React.useCallback(
|
||||
(index: number) => {
|
||||
scrollSectionToTop(index)
|
||||
},
|
||||
[scrollSectionToTop],
|
||||
)
|
||||
const onCurrentPageSelected = (index: number) => {
|
||||
scrollSectionToTop(index)
|
||||
}
|
||||
|
||||
// rendering
|
||||
// =
|
||||
|
||||
const renderHeader = React.useCallback(() => {
|
||||
const renderHeader = () => {
|
||||
return (
|
||||
<ExpoScrollForwarderView scrollViewTag={scrollViewTag}>
|
||||
<ProfileHeader
|
||||
|
@ -327,16 +344,7 @@ function ProfileScreenLoaded({
|
|||
/>
|
||||
</ExpoScrollForwarderView>
|
||||
)
|
||||
}, [
|
||||
scrollViewTag,
|
||||
profile,
|
||||
labelerInfo,
|
||||
hasDescription,
|
||||
descriptionRT,
|
||||
moderationOpts,
|
||||
hideBackButton,
|
||||
showPlaceholder,
|
||||
])
|
||||
}
|
||||
|
||||
return (
|
||||
<ScreenHider
|
||||
|
@ -442,6 +450,19 @@ function ProfileScreenLoaded({
|
|||
/>
|
||||
)
|
||||
: null}
|
||||
{showStarterPacksTab
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileStarterPacks
|
||||
ref={starterPacksSectionRef}
|
||||
isMe={isMe}
|
||||
starterPacksQuery={starterPacksQuery}
|
||||
scrollElRef={scrollElRef as ListRef}
|
||||
headerOffset={headerHeight}
|
||||
enabled={isFocused}
|
||||
setScrollViewTag={setScrollViewTag}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
{showListsTab && !profile.associated?.labeler
|
||||
? ({headerHeight, isFocused, scrollElRef}) => (
|
||||
<ProfileLists
|
||||
|
|
|
@ -45,6 +45,14 @@ export function Icons() {
|
|||
<Loader size="lg" fill={t.atoms.text.color} />
|
||||
<Loader size="xl" fill={t.atoms.text.color} />
|
||||
</View>
|
||||
|
||||
<View style={[a.flex_row, a.gap_xl]}>
|
||||
<Globe size="xs" gradient="sky" />
|
||||
<Globe size="sm" gradient="sky" />
|
||||
<Globe size="md" gradient="sky" />
|
||||
<Globe size="lg" gradient="sky" />
|
||||
<Globe size="xl" gradient="sky" />
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -100,12 +100,18 @@ function ProfileCard() {
|
|||
)
|
||||
}
|
||||
|
||||
const HIDDEN_BACK_BNT_ROUTES = ['StarterPackWizard', 'StarterPackEdit']
|
||||
|
||||
function BackBtn() {
|
||||
const {isTablet} = useWebMediaQueries()
|
||||
const pal = usePalette('default')
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {_} = useLingui()
|
||||
const shouldShow = useNavigationState(state => !isStateAtTabRoot(state))
|
||||
const shouldShow = useNavigationState(
|
||||
state =>
|
||||
!isStateAtTabRoot(state) &&
|
||||
!HIDDEN_BACK_BNT_ROUTES.includes(getCurrentRoute(state).name),
|
||||
)
|
||||
|
||||
const onPressBack = React.useCallback(() => {
|
||||
if (navigation.canGoBack()) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue