diff options
author | Hailey <me@haileyok.com> | 2024-02-12 11:47:22 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-12 11:47:22 -0800 |
commit | fe57335b864f2cdd74af1516f022bda7c6191b29 (patch) | |
tree | ba3515c18b019f5f3f1487855b031c2c66425c06 /src/view/com/post-thread/PostThreadFollowBtn.tsx | |
parent | a97d46998160b2d904b7f093dc087254ca8abd6f (diff) | |
download | voidsky-fe57335b864f2cdd74af1516f022bda7c6191b29.tar.zst |
Add follow button to highlighted post (#2828)
* add user-minus icon * add follow button to highlighted post * web hack for animations * adjustments * remove static string width, use flexbox * Revert "add user-minus icon" This reverts commit f1aafb3e39dce131b729864924d63a22368f9187. * better displaying of display name
Diffstat (limited to 'src/view/com/post-thread/PostThreadFollowBtn.tsx')
-rw-r--r-- | src/view/com/post-thread/PostThreadFollowBtn.tsx | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/src/view/com/post-thread/PostThreadFollowBtn.tsx b/src/view/com/post-thread/PostThreadFollowBtn.tsx new file mode 100644 index 000000000..e5b747cc9 --- /dev/null +++ b/src/view/com/post-thread/PostThreadFollowBtn.tsx @@ -0,0 +1,154 @@ +import React from 'react' +import {StyleSheet, TouchableOpacity, View} from 'react-native' +import {useNavigation} from '@react-navigation/native' +import {AppBskyActorDefs} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' + +import {logger} from '#/logger' +import {Text} from 'view/com/util/text/Text' +import * as Toast from 'view/com/util/Toast' +import {s} from 'lib/styles' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {Shadow, useProfileShadow} from 'state/cache/profile-shadow' +import {track} from 'lib/analytics/analytics' +import { + useProfileFollowMutationQueue, + useProfileQuery, +} from 'state/queries/profile' +import {useRequireAuth} from 'state/session' + +export function PostThreadFollowBtn({did}: {did: string}) { + const {data: profile, isLoading} = useProfileQuery({did}) + + // We will never hit this - the profile will always be cached or loaded above + // but it keeps the typechecker happy + if (isLoading || !profile) return null + + return <PostThreadFollowBtnLoaded profile={profile} /> +} + +function PostThreadFollowBtnLoaded({ + profile: profileUnshadowed, +}: { + profile: AppBskyActorDefs.ProfileViewDetailed +}) { + const navigation = useNavigation() + const {_} = useLingui() + const pal = usePalette('default') + const palInverted = usePalette('inverted') + const {isTabletOrDesktop} = useWebMediaQueries() + const profile: Shadow<AppBskyActorDefs.ProfileViewBasic> = + useProfileShadow(profileUnshadowed) + const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile) + const requireAuth = useRequireAuth() + + const isFollowing = !!profile.viewer?.following + const [wasFollowing, setWasFollowing] = React.useState<boolean>(isFollowing) + + // This prevents the button from disappearing as soon as we follow. + const showFollowBtn = React.useMemo( + () => !isFollowing || !wasFollowing, + [isFollowing, wasFollowing], + ) + + /** + * We want this button to stay visible even after following, so that the user can unfollow if they want. + * However, we need it to disappear after we push to a screen and then come back. We also need it to + * show up if we view the post while following, go to the profile and unfollow, then come back to the + * post. + * + * We want to update wasFollowing both on blur and on focus so that we hit all these cases. On native, + * we could do this only on focus because the transition animation gives us time to not notice the + * sudden rendering of the button. However, on web if we do this, there's an obvious flicker once the + * button renders. So, we update the state in both cases. + */ + React.useEffect(() => { + const updateWasFollowing = () => { + if (wasFollowing !== isFollowing) { + setWasFollowing(isFollowing) + } + } + + const unsubscribeFocus = navigation.addListener('focus', updateWasFollowing) + const unsubscribeBlur = navigation.addListener('blur', updateWasFollowing) + + return () => { + unsubscribeFocus() + unsubscribeBlur() + } + }, [isFollowing, wasFollowing, navigation]) + + const onPress = React.useCallback(() => { + if (!isFollowing) { + requireAuth(async () => { + try { + track('ProfileHeader:FollowButtonClicked') + await queueFollow() + } catch (e: any) { + if (e?.name !== 'AbortError') { + logger.error('Failed to follow', {message: String(e)}) + Toast.show(_(msg`There was an issue! ${e.toString()}`)) + } + } + }) + } else { + requireAuth(async () => { + try { + track('ProfileHeader:UnfollowButtonClicked') + await queueUnfollow() + } catch (e: any) { + if (e?.name !== 'AbortError') { + logger.error('Failed to unfollow', {message: String(e)}) + Toast.show(_(msg`There was an issue! ${e.toString()}`)) + } + } + }) + } + }, [isFollowing, requireAuth, queueFollow, _, queueUnfollow]) + + if (!showFollowBtn) return null + + return ( + <View style={{width: isTabletOrDesktop ? 130 : 120}}> + <View style={styles.btnOuter}> + <TouchableOpacity + testID="followBtn" + onPress={onPress} + style={[styles.btn, !isFollowing ? palInverted.view : pal.viewLight]} + accessibilityRole="button" + accessibilityLabel={_(msg`Follow ${profile.handle}`)} + accessibilityHint={_( + msg`Shows posts from ${profile.handle} in your feed`, + )}> + {isTabletOrDesktop && ( + <FontAwesomeIcon + icon={!isFollowing ? 'plus' : 'check'} + style={[!isFollowing ? palInverted.text : pal.text, s.mr5]} + /> + )} + <Text + type="button" + style={[!isFollowing ? palInverted.text : pal.text, s.bold]} + numberOfLines={1}> + {!isFollowing ? <Trans>Follow</Trans> : <Trans>Following</Trans>} + </Text> + </TouchableOpacity> + </View> + </View> + ) +} + +const styles = StyleSheet.create({ + btnOuter: { + marginLeft: 'auto', + }, + btn: { + flexDirection: 'row', + borderRadius: 50, + paddingVertical: 8, + paddingHorizontal: 14, + }, +}) |