about summary refs log tree commit diff
path: root/src/view/com/posts/AviFollowButton.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/posts/AviFollowButton.tsx')
-rw-r--r--src/view/com/posts/AviFollowButton.tsx115
1 files changed, 115 insertions, 0 deletions
diff --git a/src/view/com/posts/AviFollowButton.tsx b/src/view/com/posts/AviFollowButton.tsx
new file mode 100644
index 000000000..9358967e1
--- /dev/null
+++ b/src/view/com/posts/AviFollowButton.tsx
@@ -0,0 +1,115 @@
+import React, {useState} from 'react'
+import {View} from 'react-native'
+import {AppBskyActorDefs, ModerationDecision} from '@atproto/api'
+import {msg} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {useNavigation} from '@react-navigation/native'
+
+import {createHitslop} from '#/lib/constants'
+import {NavigationProp} from '#/lib/routes/types'
+import {useGate} from '#/lib/statsig/statsig'
+import {sanitizeDisplayName} from '#/lib/strings/display-names'
+import {useProfileShadow} from '#/state/cache/profile-shadow'
+import {useSession} from '#/state/session'
+import {
+  DropdownItem,
+  NativeDropdown,
+} from '#/view/com/util/forms/NativeDropdown'
+import * as Toast from '#/view/com/util/Toast'
+import {atoms as a, useTheme} from '#/alf'
+import {Button} from '#/components/Button'
+import {useFollowMethods} from '#/components/hooks/useFollowMethods'
+import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
+
+export function AviFollowButton({
+  author,
+  moderation,
+  children,
+}: {
+  author: AppBskyActorDefs.ProfileViewBasic
+  moderation: ModerationDecision
+  children: React.ReactNode
+}) {
+  const {_} = useLingui()
+  const t = useTheme()
+  const profile = useProfileShadow(author)
+  const {follow} = useFollowMethods({
+    profile: profile,
+    logContext: 'AvatarButton',
+  })
+  const gate = useGate()
+  const {currentAccount, hasSession} = useSession()
+  const [followed, setFollowed] = useState<string | null>(null)
+  const navigation = useNavigation<NavigationProp>()
+
+  const name = sanitizeDisplayName(
+    profile.displayName || profile.handle,
+    moderation.ui('displayName'),
+  )
+  const isFollowing =
+    profile.viewer?.following ||
+    profile.did === followed ||
+    profile.did === currentAccount?.did
+
+  function onPress() {
+    follow()
+    setFollowed(profile.did)
+    Toast.show(_(msg`Following ${name}`))
+  }
+
+  const items: DropdownItem[] = [
+    {
+      label: _(msg`View profile`),
+      onPress: () => {
+        navigation.navigate('Profile', {name: profile.did})
+      },
+      icon: {
+        ios: {
+          name: 'arrow.up.right.square',
+        },
+        android: '',
+        web: ['far', 'arrow-up-right-from-square'],
+      },
+    },
+    {
+      label: _(msg`Follow ${name}`),
+      onPress: onPress,
+      icon: {
+        ios: {
+          name: 'person.badge.plus',
+        },
+        android: '',
+        web: ['far', 'user-plus'],
+      },
+    },
+  ]
+
+  return hasSession && gate('show_avi_follow_button') ? (
+    <View style={a.relative}>
+      {children}
+
+      {!isFollowing && (
+        <Button
+          label={_(msg`Open ${name} profile shortcut menu`)}
+          hitSlop={createHitslop(3)}
+          style={[
+            a.rounded_full,
+            t.atoms.bg_contrast_975,
+            a.absolute,
+            {
+              bottom: -2,
+              right: -2,
+              borderWidth: 1,
+              borderColor: t.atoms.bg.backgroundColor,
+            },
+          ]}>
+          <NativeDropdown items={items}>
+            <Plus size="sm" fill={t.atoms.bg.backgroundColor} />
+          </NativeDropdown>
+        </Button>
+      )}
+    </View>
+  ) : (
+    children
+  )
+}