WIP dialog

This commit is contained in:
Eric Bailey
2024-08-21 10:25:56 -05:00
parent abbeef7b8d
commit ba099ca2bd
3 changed files with 138 additions and 157 deletions
+3 -1
View File
@@ -220,8 +220,10 @@ export function NameAndHandlePlaceholder() {
export function Description({
profile: profileUnshadowed,
numberOfLines = 3,
}: {
profile: AppBskyActorDefs.ProfileViewDetailed
numberOfLines?: number
}) {
const profile = useProfileShadow(profileUnshadowed)
const {description} = profile
@@ -244,7 +246,7 @@ export function Description({
<RichText
value={rt}
style={[a.leading_snug]}
numberOfLines={3}
numberOfLines={numberOfLines}
disableLinks
/>
</View>
@@ -6,7 +6,6 @@ import {
ModerationOpts,
RichText as RichTextAPI,
} from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@@ -23,11 +22,12 @@ import {useRequireAuth, useSession} from '#/state/session'
import {useAnalytics} from 'lib/analytics/analytics'
import {sanitizeDisplayName} from 'lib/strings/display-names'
import {useProfileShadow} from 'state/cache/profile-shadow'
import {ProfileHeaderSuggestedFollows} from '#/view/com/profile/ProfileHeaderSuggestedFollows'
import {SimilarAccountsDialog} from '#/view/com/profile/ProfileHeaderSuggestedFollows'
import {ProfileMenu} from '#/view/com/profile/ProfileMenu'
import * as Toast from '#/view/com/util/Toast'
import {atoms as a, useTheme} from '#/alf'
import {atoms as a} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import {MessageProfileButton} from '#/components/dms/MessageProfileButton'
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
@@ -59,7 +59,6 @@ let ProfileHeaderStandard = ({
}: Props): React.ReactNode => {
const profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> =
useProfileShadow(profileUnshadowed)
const t = useTheme()
const {currentAccount, hasSession} = useSession()
const {_} = useLingui()
const {openModal} = useModalControls()
@@ -68,13 +67,13 @@ let ProfileHeaderStandard = ({
() => moderateProfile(profile, moderationOpts),
[profile, moderationOpts],
)
const [showSuggestedFollows, setShowSuggestedFollows] = React.useState(false)
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
profile,
'ProfileHeader',
)
const [_queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile)
const unblockPromptControl = Prompt.usePromptControl()
const similarAccountsControl = useDialogControl()
const requireAuth = useRequireAuth()
const isBlockedUser =
profile.viewer?.blocking ||
@@ -104,7 +103,7 @@ let ProfileHeaderStandard = ({
),
)
if (gate('auto_show_prof_sugg_follows')) {
setShowSuggestedFollows(true)
similarAccountsControl.open()
}
} catch (e: any) {
if (e?.name !== 'AbortError') {
@@ -205,32 +204,7 @@ let ProfileHeaderStandard = ({
)
) : !profile.viewer?.blockedBy ? (
<>
{hasSession && (
<>
<MessageProfileButton profile={profile} />
<Button
testID="suggestedFollowsBtn"
size="small"
color={showSuggestedFollows ? 'primary' : 'secondary'}
variant="solid"
shape="round"
onPress={() =>
setShowSuggestedFollows(!showSuggestedFollows)
}
label={_(msg`Show follows similar to ${profile.handle}`)}
style={{width: 36, height: 36}}>
<FontAwesomeIcon
icon="user-plus"
style={
showSuggestedFollows
? {color: t.palette.white}
: t.atoms.text
}
size={14}
/>
</Button>
</>
)}
{hasSession && <MessageProfileButton profile={profile} />}
<Button
testID={profile.viewer?.following ? 'unfollowBtn' : 'followBtn'}
@@ -295,19 +269,6 @@ let ProfileHeaderStandard = ({
</>
)}
</View>
{showSuggestedFollows && (
<ProfileHeaderSuggestedFollows
actorDid={profile.did}
requestDismiss={() => {
if (showSuggestedFollows) {
setShowSuggestedFollows(false)
} else {
track('ProfileHeader:SuggestedFollowsOpened')
setShowSuggestedFollows(true)
}
}}
/>
)}
<Prompt.Basic
control={unblockPromptControl}
title={_(msg`Unblock Account?`)}
@@ -320,6 +281,10 @@ let ProfileHeaderStandard = ({
}
confirmButtonColor="negative"
/>
<SimilarAccountsDialog
control={similarAccountsControl}
profile={profile}
/>
</ProfileHeaderShell>
)
}
@@ -1,23 +1,19 @@
import React from 'react'
import {ScrollView, View} from 'react-native'
import {View} from 'react-native'
import {AppBskyActorDefs} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {logEvent} from '#/lib/statsig/statsig'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useSuggestedFollowsByActorQuery} from '#/state/queries/suggested-follows'
import {isWeb} from 'platform/detection'
import {atoms as a, useTheme, ViewStyleProp} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {KnownFollowers} from '#/components/KnownFollowers'
import * as ProfileCard from '#/components/ProfileCard'
import {Text} from '#/components/Typography'
const OUTER_PADDING = a.p_md.padding
const INNER_PADDING = a.p_lg.padding
const TOTAL_HEIGHT = 232
const MOBILE_CARD_WIDTH = 300
function CardOuter({
children,
style,
@@ -30,10 +26,9 @@ function CardOuter({
a.p_lg,
a.rounded_md,
a.border,
t.atoms.bg,
t.atoms.border_contrast_low,
t.atoms.bg_contrast_25,
{
width: MOBILE_CARD_WIDTH,
borderColor: t.atoms.bg_contrast_25.backgroundColor,
},
style,
]}>
@@ -56,106 +51,116 @@ export function SuggestedFollowPlaceholder() {
)
}
export function ProfileHeaderSuggestedFollows({
actorDid,
requestDismiss,
}: {
actorDid: string
requestDismiss: () => void
}) {
export type ProfileSuggestedFollowsProps = {
control: Dialog.DialogOuterProps['control']
profile: AppBskyActorDefs.ProfileViewDetailed
}
export function SimilarAccountsDialog({
control,
profile,
}: ProfileSuggestedFollowsProps) {
const t = useTheme()
const {_} = useLingui()
const [show, setShow] = React.useState(false)
return (
<Dialog.Outer control={control}>
<Dialog.Handle />
<Dialog.ScrollableInner
label={_(msg`Accounts similar to @${profile.handle}`)}>
{show ? (
<SimilarAccounts profile={profile} />
) : (
<>
<Text style={[t.atoms.text, a.text_2xl, a.font_bold, a.mb_sm]}>
<Trans>Similar accounts</Trans>
</Text>
<Text
style={[
t.atoms.text_contrast_medium,
a.text_md,
a.mb_xl,
a.leading_snug,
]}>
<Trans>These are accounts similar to @{profile.handle}</Trans>
</Text>
<Button
label={_(msg`View accounts similar to @${profile.handle}`)}
size="medium"
variant="solid"
color="primary"
onPress={() => {
setShow(true)
}}>
<ButtonText>
<Trans>See similar accounts?</Trans>
</ButtonText>
</Button>
</>
)}
<Dialog.Close />
</Dialog.ScrollableInner>
</Dialog.Outer>
)
}
export function SimilarAccounts({
profile,
}: {
profile: AppBskyActorDefs.ProfileViewDetailed
}) {
const t = useTheme()
const {isLoading: isSuggestionsLoading, data} =
useSuggestedFollowsByActorQuery({
did: actorDid,
did: profile.did,
})
const moderationOpts = useModerationOpts()
const isLoading = isSuggestionsLoading || !moderationOpts
return (
<View
style={{paddingVertical: OUTER_PADDING, height: TOTAL_HEIGHT}}
pointerEvents="box-none">
<View
pointerEvents="box-none"
<View>
<Text style={[t.atoms.text, a.text_2xl, a.font_bold, a.mb_sm]}>
<Trans>Similar accounts</Trans>
</Text>
<Text
style={[
t.atoms.bg_contrast_25,
{
height: '100%',
paddingTop: INNER_PADDING / 2,
},
t.atoms.text_contrast_medium,
a.text_md,
a.mb_xl,
a.leading_snug,
]}>
<View
pointerEvents="box-none"
style={[
a.flex_row,
a.justify_between,
a.align_center,
a.pt_xs,
{
paddingBottom: INNER_PADDING / 2,
paddingLeft: INNER_PADDING,
paddingRight: INNER_PADDING / 2,
},
]}>
<Text style={[a.text_md, a.font_bold, t.atoms.text_contrast_medium]}>
<Trans>Similar accounts</Trans>
</Text>
<Trans>These are accounts similar to @{profile.handle}</Trans>
</Text>
<Button
onPress={requestDismiss}
hitSlop={10}
label={_(msg`Dismiss`)}
size="xsmall"
variant="ghost"
color="secondary"
shape="round">
<ButtonIcon icon={X} size="sm" />
</Button>
</View>
<ScrollView
horizontal={true}
showsHorizontalScrollIndicator={isWeb}
persistentScrollbar={true}
scrollIndicatorInsets={{bottom: 0}}
snapToInterval={MOBILE_CARD_WIDTH + a.gap_sm.gap}
decelerationRate="fast">
<View
style={[
a.flex_row,
a.gap_sm,
{
paddingHorizontal: INNER_PADDING,
paddingBottom: INNER_PADDING,
},
]}>
{isLoading ? (
<>
<SuggestedFollowPlaceholder />
<SuggestedFollowPlaceholder />
<SuggestedFollowPlaceholder />
<SuggestedFollowPlaceholder />
<SuggestedFollowPlaceholder />
</>
) : data ? (
data.suggestions
.filter(s => (s.associated?.labeler ? false : true))
.map(profile => (
<ProfileCard.Link
key={profile.did}
profile={profile}
onPress={() => {
logEvent('profile:header:suggestedFollowsCard:press', {})
}}
style={[a.flex_1]}>
{({hovered, pressed}) => (
<CardOuter
style={[
a.flex_1,
(hovered || pressed) && t.atoms.border_contrast_high,
]}>
<ProfileCard.Outer>
<View style={[a.gap_md]}>
{isLoading ? (
<>
<SuggestedFollowPlaceholder />
<SuggestedFollowPlaceholder />
<SuggestedFollowPlaceholder />
<SuggestedFollowPlaceholder />
<SuggestedFollowPlaceholder />
</>
) : data ? (
data.suggestions
.filter(s => (s.associated?.labeler ? false : true))
.map(profile => (
<ProfileCard.Link
key={profile.did}
profile={profile}
onPress={() => {
logEvent('profile:header:suggestedFollowsCard:press', {})
}}
style={[]}>
{({hovered, pressed}) => (
<CardOuter
style={[
(hovered || pressed) && t.atoms.border_contrast_medium,
]}>
<ProfileCard.Outer>
<View style={[a.gap_md]}>
<View style={[a.gap_xs]}>
<ProfileCard.Header>
<ProfileCard.Avatar
profile={profile}
@@ -173,17 +178,26 @@ export function ProfileHeaderSuggestedFollows({
shape="round"
/>
</ProfileCard.Header>
<ProfileCard.Description profile={profile} />
</ProfileCard.Outer>
</CardOuter>
)}
</ProfileCard.Link>
))
) : (
<View />
)}
</View>
</ScrollView>
<ProfileCard.Description
profile={profile}
numberOfLines={2}
/>
</View>
<KnownFollowers
minimal
profile={profile}
moderationOpts={moderationOpts}
/>
</View>
</ProfileCard.Outer>
</CardOuter>
)}
</ProfileCard.Link>
))
) : (
<View />
)}
</View>
</View>
)