about summary refs log tree commit diff
path: root/src/view/com/profile/ProfileHeader.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/profile/ProfileHeader.tsx')
-rw-r--r--src/view/com/profile/ProfileHeader.tsx598
1 files changed, 0 insertions, 598 deletions
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
deleted file mode 100644
index 17dc5ce1b..000000000
--- a/src/view/com/profile/ProfileHeader.tsx
+++ /dev/null
@@ -1,598 +0,0 @@
-import React, {memo, useMemo} from 'react'
-import {
-  StyleSheet,
-  TouchableOpacity,
-  TouchableWithoutFeedback,
-  View,
-} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {useNavigation} from '@react-navigation/native'
-import {
-  AppBskyActorDefs,
-  ModerationOpts,
-  moderateProfile,
-  RichText as RichTextAPI,
-} from '@atproto/api'
-import {Trans, msg} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {NavigationProp} from 'lib/routes/types'
-import {isNative} from 'platform/detection'
-import {BlurView} from '../util/BlurView'
-import * as Toast from '../util/Toast'
-import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
-import {Text} from '../util/text/Text'
-import {ThemedText} from '../util/text/ThemedText'
-import {RichText} from '#/components/RichText'
-import {UserAvatar} from '../util/UserAvatar'
-import {UserBanner} from '../util/UserBanner'
-import {ProfileHeaderAlerts} from '../util/moderation/ProfileHeaderAlerts'
-import {formatCount} from '../util/numeric/format'
-import {Link} from '../util/Link'
-import {ProfileHeaderSuggestedFollows} from './ProfileHeaderSuggestedFollows'
-import {useModalControls} from '#/state/modals'
-import {useLightboxControls, ProfileImageLightbox} from '#/state/lightbox'
-import {
-  useProfileBlockMutationQueue,
-  useProfileFollowMutationQueue,
-} from '#/state/queries/profile'
-import {usePalette} from 'lib/hooks/usePalette'
-import {useAnalytics} from 'lib/analytics/analytics'
-import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
-import {BACK_HITSLOP} from 'lib/constants'
-import {isInvalidHandle, sanitizeHandle} from 'lib/strings/handles'
-import {makeProfileLink} from 'lib/routes/links'
-import {pluralize} from 'lib/strings/helpers'
-import {sanitizeDisplayName} from 'lib/strings/display-names'
-import {s, colors} from 'lib/styles'
-import {logger} from '#/logger'
-import {useSession} from '#/state/session'
-import {Shadow} from '#/state/cache/types'
-import {useRequireAuth} from '#/state/session'
-import {LabelInfo} from '../util/moderation/LabelInfo'
-import {useProfileShadow} from 'state/cache/profile-shadow'
-import {atoms as a} from '#/alf'
-import {ProfileMenu} from 'view/com/profile/ProfileMenu'
-import * as Prompt from '#/components/Prompt'
-
-let ProfileHeaderLoading = (_props: {}): React.ReactNode => {
-  const pal = usePalette('default')
-  return (
-    <View style={pal.view}>
-      <LoadingPlaceholder width="100%" height={150} style={{borderRadius: 0}} />
-      <View
-        style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
-        <LoadingPlaceholder width={80} height={80} style={styles.br40} />
-      </View>
-      <View style={styles.content}>
-        <View style={[styles.buttonsLine]}>
-          <LoadingPlaceholder width={167} height={31} style={styles.br50} />
-        </View>
-      </View>
-    </View>
-  )
-}
-ProfileHeaderLoading = memo(ProfileHeaderLoading)
-export {ProfileHeaderLoading}
-
-interface Props {
-  profile: AppBskyActorDefs.ProfileViewDetailed
-  descriptionRT: RichTextAPI | null
-  moderationOpts: ModerationOpts
-  hideBackButton?: boolean
-  isPlaceholderProfile?: boolean
-}
-
-let ProfileHeader = ({
-  profile: profileUnshadowed,
-  descriptionRT,
-  moderationOpts,
-  hideBackButton = false,
-  isPlaceholderProfile,
-}: Props): React.ReactNode => {
-  const profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> =
-    useProfileShadow(profileUnshadowed)
-  const pal = usePalette('default')
-  const palInverted = usePalette('inverted')
-  const {currentAccount, hasSession} = useSession()
-  const requireAuth = useRequireAuth()
-  const {_} = useLingui()
-  const {openModal} = useModalControls()
-  const {openLightbox} = useLightboxControls()
-  const navigation = useNavigation<NavigationProp>()
-  const {track} = useAnalytics()
-  const invalidHandle = isInvalidHandle(profile.handle)
-  const {isDesktop} = useWebMediaQueries()
-  const [showSuggestedFollows, setShowSuggestedFollows] = React.useState(false)
-  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
-    profile,
-    'ProfileHeader',
-  )
-  const [__, queueUnblock] = useProfileBlockMutationQueue(profile)
-  const unblockPromptControl = Prompt.usePromptControl()
-  const moderation = useMemo(
-    () => moderateProfile(profile, moderationOpts),
-    [profile, moderationOpts],
-  )
-
-  const onPressBack = React.useCallback(() => {
-    if (navigation.canGoBack()) {
-      navigation.goBack()
-    } else {
-      navigation.navigate('Home')
-    }
-  }, [navigation])
-
-  const onPressAvi = React.useCallback(() => {
-    if (
-      profile.avatar &&
-      !(moderation.avatar.blur && moderation.avatar.noOverride)
-    ) {
-      openLightbox(new ProfileImageLightbox(profile))
-    }
-  }, [openLightbox, profile, moderation])
-
-  const onPressFollow = () => {
-    requireAuth(async () => {
-      try {
-        track('ProfileHeader:FollowButtonClicked')
-        await queueFollow()
-        Toast.show(
-          _(
-            msg`Following ${sanitizeDisplayName(
-              profile.displayName || profile.handle,
-            )}`,
-          ),
-        )
-      } catch (e: any) {
-        if (e?.name !== 'AbortError') {
-          logger.error('Failed to follow', {message: String(e)})
-          Toast.show(_(msg`There was an issue! ${e.toString()}`))
-        }
-      }
-    })
-  }
-
-  const onPressUnfollow = () => {
-    requireAuth(async () => {
-      try {
-        track('ProfileHeader:UnfollowButtonClicked')
-        await queueUnfollow()
-        Toast.show(
-          _(
-            msg`No longer following ${sanitizeDisplayName(
-              profile.displayName || profile.handle,
-            )}`,
-          ),
-        )
-      } catch (e: any) {
-        if (e?.name !== 'AbortError') {
-          logger.error('Failed to unfollow', {message: String(e)})
-          Toast.show(_(msg`There was an issue! ${e.toString()}`))
-        }
-      }
-    })
-  }
-
-  const onPressEditProfile = React.useCallback(() => {
-    track('ProfileHeader:EditProfileButtonClicked')
-    openModal({
-      name: 'edit-profile',
-      profile,
-    })
-  }, [track, openModal, profile])
-
-  const unblockAccount = React.useCallback(async () => {
-    track('ProfileHeader:UnblockAccountButtonClicked')
-    try {
-      await queueUnblock()
-      Toast.show(_(msg`Account unblocked`))
-    } catch (e: any) {
-      if (e?.name !== 'AbortError') {
-        logger.error('Failed to unblock account', {message: e})
-        Toast.show(_(msg`There was an issue! ${e.toString()}`))
-      }
-    }
-  }, [_, queueUnblock, track])
-
-  const isMe = React.useMemo(
-    () => currentAccount?.did === profile.did,
-    [currentAccount, profile],
-  )
-
-  const blockHide =
-    !isMe && (profile.viewer?.blocking || profile.viewer?.blockedBy)
-  const following = formatCount(profile.followsCount || 0)
-  const followers = formatCount(profile.followersCount || 0)
-  const pluralizedFollowers = pluralize(profile.followersCount || 0, 'follower')
-
-  return (
-    <View style={[pal.view]} pointerEvents="box-none">
-      <View pointerEvents="none">
-        {isPlaceholderProfile ? (
-          <LoadingPlaceholder
-            width="100%"
-            height={150}
-            style={{borderRadius: 0}}
-          />
-        ) : (
-          <UserBanner banner={profile.banner} moderation={moderation.avatar} />
-        )}
-      </View>
-      <View style={styles.content} pointerEvents="box-none">
-        <View style={[styles.buttonsLine]} pointerEvents="box-none">
-          {isMe ? (
-            <TouchableOpacity
-              testID="profileHeaderEditProfileButton"
-              onPress={onPressEditProfile}
-              style={[styles.btn, styles.mainBtn, pal.btn]}
-              accessibilityRole="button"
-              accessibilityLabel={_(msg`Edit profile`)}
-              accessibilityHint={_(
-                msg`Opens editor for profile display name, avatar, background image, and description`,
-              )}>
-              <Text type="button" style={pal.text}>
-                <Trans>Edit Profile</Trans>
-              </Text>
-            </TouchableOpacity>
-          ) : profile.viewer?.blocking ? (
-            profile.viewer?.blockingByList ? null : (
-              <TouchableOpacity
-                testID="unblockBtn"
-                onPress={() => unblockPromptControl.open()}
-                style={[styles.btn, styles.mainBtn, pal.btn]}
-                accessibilityRole="button"
-                accessibilityLabel={_(msg`Unblock`)}
-                accessibilityHint="">
-                <Text type="button" style={[pal.text, s.bold]}>
-                  <Trans context="action">Unblock</Trans>
-                </Text>
-              </TouchableOpacity>
-            )
-          ) : !profile.viewer?.blockedBy ? (
-            <>
-              {hasSession && (
-                <TouchableOpacity
-                  testID="suggestedFollowsBtn"
-                  onPress={() => setShowSuggestedFollows(!showSuggestedFollows)}
-                  style={[
-                    styles.btn,
-                    styles.mainBtn,
-                    pal.btn,
-                    {
-                      paddingHorizontal: 10,
-                      backgroundColor: showSuggestedFollows
-                        ? pal.colors.text
-                        : pal.colors.backgroundLight,
-                    },
-                  ]}
-                  accessibilityRole="button"
-                  accessibilityLabel={_(
-                    msg`Show follows similar to ${profile.handle}`,
-                  )}
-                  accessibilityHint={_(
-                    msg`Shows a list of users similar to this user.`,
-                  )}>
-                  <FontAwesomeIcon
-                    icon="user-plus"
-                    style={[
-                      pal.text,
-                      {
-                        color: showSuggestedFollows
-                          ? pal.textInverted.color
-                          : pal.text.color,
-                      },
-                    ]}
-                    size={14}
-                  />
-                </TouchableOpacity>
-              )}
-
-              {profile.viewer?.following ? (
-                <TouchableOpacity
-                  testID="unfollowBtn"
-                  onPress={onPressUnfollow}
-                  style={[styles.btn, styles.mainBtn, pal.btn]}
-                  accessibilityRole="button"
-                  accessibilityLabel={_(msg`Unfollow ${profile.handle}`)}
-                  accessibilityHint={_(
-                    msg`Hides posts from ${profile.handle} in your feed`,
-                  )}>
-                  <FontAwesomeIcon
-                    icon="check"
-                    style={[pal.text, s.mr5]}
-                    size={14}
-                  />
-                  <Text type="button" style={pal.text}>
-                    <Trans>Following</Trans>
-                  </Text>
-                </TouchableOpacity>
-              ) : (
-                <TouchableOpacity
-                  testID="followBtn"
-                  onPress={onPressFollow}
-                  style={[styles.btn, styles.mainBtn, palInverted.view]}
-                  accessibilityRole="button"
-                  accessibilityLabel={_(msg`Follow ${profile.handle}`)}
-                  accessibilityHint={_(
-                    msg`Shows posts from ${profile.handle} in your feed`,
-                  )}>
-                  <FontAwesomeIcon
-                    icon="plus"
-                    style={[palInverted.text, s.mr5]}
-                  />
-                  <Text type="button" style={[palInverted.text, s.bold]}>
-                    <Trans>Follow</Trans>
-                  </Text>
-                </TouchableOpacity>
-              )}
-            </>
-          ) : null}
-          <ProfileMenu profile={profile} />
-        </View>
-        <View pointerEvents="none">
-          <Text
-            testID="profileHeaderDisplayName"
-            type="title-2xl"
-            style={[pal.text, styles.title]}>
-            {sanitizeDisplayName(
-              profile.displayName || sanitizeHandle(profile.handle),
-              moderation.profile,
-            )}
-          </Text>
-        </View>
-        <View style={styles.handleLine} pointerEvents="none">
-          {profile.viewer?.followedBy && !blockHide ? (
-            <View style={[styles.pill, pal.btn, s.mr5]}>
-              <Text type="xs" style={[pal.text]}>
-                <Trans>Follows you</Trans>
-              </Text>
-            </View>
-          ) : undefined}
-          <ThemedText
-            type={invalidHandle ? 'xs' : 'md'}
-            fg={invalidHandle ? 'error' : 'light'}
-            border={invalidHandle ? 'error' : undefined}
-            style={[
-              invalidHandle ? styles.invalidHandle : undefined,
-              styles.handle,
-            ]}>
-            {invalidHandle ? _(msg`⚠Invalid Handle`) : `@${profile.handle}`}
-          </ThemedText>
-        </View>
-        {!isPlaceholderProfile && !blockHide && (
-          <>
-            <View style={styles.metricsLine} pointerEvents="box-none">
-              <Link
-                testID="profileHeaderFollowersButton"
-                style={[s.flexRow, s.mr10]}
-                href={makeProfileLink(profile, 'followers')}
-                onPressOut={() =>
-                  track(`ProfileHeader:FollowersButtonClicked`, {
-                    handle: profile.handle,
-                  })
-                }
-                asAnchor
-                accessibilityLabel={`${followers} ${pluralizedFollowers}`}
-                accessibilityHint={_(msg`Opens followers list`)}>
-                <Text type="md" style={[s.bold, pal.text]}>
-                  {followers}{' '}
-                </Text>
-                <Text type="md" style={[pal.textLight]}>
-                  {pluralizedFollowers}
-                </Text>
-              </Link>
-              <Link
-                testID="profileHeaderFollowsButton"
-                style={[s.flexRow, s.mr10]}
-                href={makeProfileLink(profile, 'follows')}
-                onPressOut={() =>
-                  track(`ProfileHeader:FollowsButtonClicked`, {
-                    handle: profile.handle,
-                  })
-                }
-                asAnchor
-                accessibilityLabel={_(msg`${following} following`)}
-                accessibilityHint={_(msg`Opens following list`)}>
-                <Trans>
-                  <Text type="md" style={[s.bold, pal.text]}>
-                    {following}{' '}
-                  </Text>
-                  <Text type="md" style={[pal.textLight]}>
-                    following
-                  </Text>
-                </Trans>
-              </Link>
-              <Text type="md" style={[s.bold, pal.text]}>
-                {formatCount(profile.postsCount || 0)}{' '}
-                <Text type="md" style={[pal.textLight]}>
-                  {pluralize(profile.postsCount || 0, 'post')}
-                </Text>
-              </Text>
-            </View>
-            {descriptionRT && !moderation.profile.blur ? (
-              <View pointerEvents="auto" style={[styles.description]}>
-                <RichText
-                  testID="profileHeaderDescription"
-                  style={[a.text_md]}
-                  numberOfLines={15}
-                  value={descriptionRT}
-                />
-              </View>
-            ) : undefined}
-          </>
-        )}
-        <ProfileHeaderAlerts moderation={moderation} />
-        {isMe && (
-          <LabelInfo details={{did: profile.did}} labels={profile.labels} />
-        )}
-      </View>
-
-      {showSuggestedFollows && (
-        <ProfileHeaderSuggestedFollows
-          actorDid={profile.did}
-          requestDismiss={() => {
-            if (showSuggestedFollows) {
-              setShowSuggestedFollows(false)
-            } else {
-              track('ProfileHeader:SuggestedFollowsOpened')
-              setShowSuggestedFollows(true)
-            }
-          }}
-        />
-      )}
-
-      {!isDesktop && !hideBackButton && (
-        <TouchableWithoutFeedback
-          testID="profileHeaderBackBtn"
-          onPress={onPressBack}
-          hitSlop={BACK_HITSLOP}
-          accessibilityRole="button"
-          accessibilityLabel={_(msg`Back`)}
-          accessibilityHint="">
-          <View style={styles.backBtnWrapper}>
-            <BlurView style={styles.backBtn} blurType="dark">
-              <FontAwesomeIcon size={18} icon="angle-left" style={s.white} />
-            </BlurView>
-          </View>
-        </TouchableWithoutFeedback>
-      )}
-      <TouchableWithoutFeedback
-        testID="profileHeaderAviButton"
-        onPress={onPressAvi}
-        accessibilityRole="image"
-        accessibilityLabel={_(msg`View ${profile.handle}'s avatar`)}
-        accessibilityHint="">
-        <View
-          style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
-          <UserAvatar
-            size={80}
-            avatar={profile.avatar}
-            moderation={moderation.avatar}
-          />
-        </View>
-      </TouchableWithoutFeedback>
-      <Prompt.Basic
-        control={unblockPromptControl}
-        title={_(msg`Unblock Account?`)}
-        description={_(
-          msg`The account will be able to interact with you after unblocking.`,
-        )}
-        onConfirm={unblockAccount}
-        confirmButtonCta={
-          profile.viewer?.blocking ? _(msg`Unblock`) : _(msg`Block`)
-        }
-        confirmButtonColor="negative"
-      />
-    </View>
-  )
-}
-ProfileHeader = memo(ProfileHeader)
-export {ProfileHeader}
-
-const styles = StyleSheet.create({
-  banner: {
-    width: '100%',
-    height: 120,
-  },
-  backBtnWrapper: {
-    position: 'absolute',
-    top: 10,
-    left: 10,
-    width: 30,
-    height: 30,
-    overflow: 'hidden',
-    borderRadius: 15,
-    // @ts-ignore web only
-    cursor: 'pointer',
-  },
-  backBtn: {
-    width: 30,
-    height: 30,
-    borderRadius: 15,
-    alignItems: 'center',
-    justifyContent: 'center',
-  },
-  avi: {
-    position: 'absolute',
-    top: 110,
-    left: 10,
-    width: 84,
-    height: 84,
-    borderRadius: 42,
-    borderWidth: 2,
-  },
-  content: {
-    paddingTop: 8,
-    paddingHorizontal: 14,
-    paddingBottom: 4,
-  },
-
-  buttonsLine: {
-    flexDirection: 'row',
-    marginLeft: 'auto',
-    marginBottom: 12,
-  },
-  primaryBtn: {
-    backgroundColor: colors.blue3,
-    paddingHorizontal: 24,
-    paddingVertical: 6,
-  },
-  mainBtn: {
-    paddingHorizontal: 24,
-  },
-  secondaryBtn: {
-    paddingHorizontal: 14,
-  },
-  btn: {
-    flexDirection: 'row',
-    alignItems: 'center',
-    justifyContent: 'center',
-    paddingVertical: 7,
-    borderRadius: 50,
-    marginLeft: 6,
-  },
-  title: {lineHeight: 38},
-
-  // Word wrapping appears fine on
-  // mobile but overflows on desktop
-  handle: isNative
-    ? {}
-    : {
-        // @ts-ignore web only -prf
-        wordBreak: 'break-all',
-      },
-  invalidHandle: {
-    borderWidth: 1,
-    borderRadius: 4,
-    paddingHorizontal: 4,
-  },
-
-  handleLine: {
-    flexDirection: 'row',
-    marginBottom: 8,
-  },
-
-  metricsLine: {
-    flexDirection: 'row',
-    marginBottom: 8,
-  },
-
-  description: {
-    marginBottom: 8,
-  },
-
-  detailLine: {
-    flexDirection: 'row',
-    alignItems: 'center',
-    marginBottom: 5,
-  },
-
-  pill: {
-    borderRadius: 4,
-    paddingHorizontal: 6,
-    paddingVertical: 2,
-  },
-
-  br40: {borderRadius: 40},
-  br50: {borderRadius: 50},
-})