diff options
Diffstat (limited to 'src/view/com/posts')
-rw-r--r-- | src/view/com/posts/AviFollowButton.tsx | 115 | ||||
-rw-r--r-- | src/view/com/posts/AviFollowButton.web.tsx | 1 | ||||
-rw-r--r-- | src/view/com/posts/FeedItem.tsx | 21 |
3 files changed, 130 insertions, 7 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 + ) +} diff --git a/src/view/com/posts/AviFollowButton.web.tsx b/src/view/com/posts/AviFollowButton.web.tsx new file mode 100644 index 000000000..6ad3c9f1f --- /dev/null +++ b/src/view/com/posts/AviFollowButton.web.tsx @@ -0,0 +1 @@ +export {Fragment as AviFollowButton} from 'react' diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx index 8077c2968..b10ffe19f 100644 --- a/src/view/com/posts/FeedItem.tsx +++ b/src/view/com/posts/FeedItem.tsx @@ -41,6 +41,7 @@ import {PostEmbeds} from '../util/post-embeds' import {PostMeta} from '../util/PostMeta' import {Text} from '../util/text/Text' import {PreviewableUserAvatar} from '../util/UserAvatar' +import {AviFollowButton} from './AviFollowButton' interface FeedItemProps { record: AppBskyFeedPost.Record @@ -284,13 +285,15 @@ let FeedItemInner = ({ <View style={styles.layout}> <View style={styles.layoutAvi}> - <PreviewableUserAvatar - size={52} - profile={post.author} - moderation={moderation.ui('avatar')} - type={post.author.associated?.labeler ? 'labeler' : 'user'} - onBeforePress={onOpenAuthor} - /> + <AviFollowButton author={post.author} moderation={moderation}> + <PreviewableUserAvatar + size={52} + profile={post.author} + moderation={moderation.ui('avatar')} + type={post.author.associated?.labeler ? 'labeler' : 'user'} + onBeforePress={onOpenAuthor} + /> + </AviFollowButton> {isThreadParent && ( <View style={[ @@ -470,9 +473,13 @@ const styles = StyleSheet.create({ }, layoutAvi: { paddingLeft: 8, + position: 'relative', + zIndex: 999, }, layoutContent: { + position: 'relative', flex: 1, + zIndex: 0, }, alert: { marginTop: 6, |