mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at rm-patch-drawer 140 lines 4.6 kB view raw
1import React from 'react' 2import {AppBskyActorDefs} from '@atproto/api' 3import {msg, Trans} from '@lingui/macro' 4import {useLingui} from '@lingui/react' 5import {useNavigation} from '@react-navigation/native' 6 7import {logger} from '#/logger' 8import {Shadow, useProfileShadow} from '#/state/cache/profile-shadow' 9import { 10 useProfileFollowMutationQueue, 11 useProfileQuery, 12} from '#/state/queries/profile' 13import {useRequireAuth} from '#/state/session' 14import * as Toast from '#/view/com/util/Toast' 15import {atoms as a, useBreakpoints} from '#/alf' 16import {Button, ButtonIcon, ButtonText} from '#/components/Button' 17import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' 18import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' 19 20export function PostThreadFollowBtn({did}: {did: string}) { 21 const {data: profile, isLoading} = useProfileQuery({did}) 22 23 // We will never hit this - the profile will always be cached or loaded above 24 // but it keeps the typechecker happy 25 if (isLoading || !profile) return null 26 27 return <PostThreadFollowBtnLoaded profile={profile} /> 28} 29 30function PostThreadFollowBtnLoaded({ 31 profile: profileUnshadowed, 32}: { 33 profile: AppBskyActorDefs.ProfileViewDetailed 34}) { 35 const navigation = useNavigation() 36 const {_} = useLingui() 37 const {gtMobile} = useBreakpoints() 38 const profile: Shadow<AppBskyActorDefs.ProfileViewBasic> = 39 useProfileShadow(profileUnshadowed) 40 const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue( 41 profile, 42 'PostThreadItem', 43 ) 44 const requireAuth = useRequireAuth() 45 46 const isFollowing = !!profile.viewer?.following 47 const isFollowedBy = !!profile.viewer?.followedBy 48 const [wasFollowing, setWasFollowing] = React.useState<boolean>(isFollowing) 49 50 // This prevents the button from disappearing as soon as we follow. 51 const showFollowBtn = React.useMemo( 52 () => !isFollowing || !wasFollowing, 53 [isFollowing, wasFollowing], 54 ) 55 56 /** 57 * We want this button to stay visible even after following, so that the user can unfollow if they want. 58 * However, we need it to disappear after we push to a screen and then come back. We also need it to 59 * show up if we view the post while following, go to the profile and unfollow, then come back to the 60 * post. 61 * 62 * We want to update wasFollowing both on blur and on focus so that we hit all these cases. On native, 63 * we could do this only on focus because the transition animation gives us time to not notice the 64 * sudden rendering of the button. However, on web if we do this, there's an obvious flicker once the 65 * button renders. So, we update the state in both cases. 66 */ 67 React.useEffect(() => { 68 const updateWasFollowing = () => { 69 if (wasFollowing !== isFollowing) { 70 setWasFollowing(isFollowing) 71 } 72 } 73 74 const unsubscribeFocus = navigation.addListener('focus', updateWasFollowing) 75 const unsubscribeBlur = navigation.addListener('blur', updateWasFollowing) 76 77 return () => { 78 unsubscribeFocus() 79 unsubscribeBlur() 80 } 81 }, [isFollowing, wasFollowing, navigation]) 82 83 const onPress = React.useCallback(() => { 84 if (!isFollowing) { 85 requireAuth(async () => { 86 try { 87 await queueFollow() 88 } catch (e: any) { 89 if (e?.name !== 'AbortError') { 90 logger.error('Failed to follow', {message: String(e)}) 91 Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark') 92 } 93 } 94 }) 95 } else { 96 requireAuth(async () => { 97 try { 98 await queueUnfollow() 99 } catch (e: any) { 100 if (e?.name !== 'AbortError') { 101 logger.error('Failed to unfollow', {message: String(e)}) 102 Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark') 103 } 104 } 105 }) 106 } 107 }, [isFollowing, requireAuth, queueFollow, _, queueUnfollow]) 108 109 if (!showFollowBtn) return null 110 111 return ( 112 <Button 113 testID="followBtn" 114 label={_(msg`Follow ${profile.handle}`)} 115 onPress={onPress} 116 size="small" 117 variant="solid" 118 color={isFollowing ? 'secondary' : 'secondary_inverted'} 119 style={[a.rounded_full]}> 120 {gtMobile && ( 121 <ButtonIcon 122 icon={isFollowing ? Check : Plus} 123 position="left" 124 size="sm" 125 /> 126 )} 127 <ButtonText> 128 {!isFollowing ? ( 129 isFollowedBy ? ( 130 <Trans>Follow Back</Trans> 131 ) : ( 132 <Trans>Follow</Trans> 133 ) 134 ) : ( 135 <Trans>Following</Trans> 136 )} 137 </ButtonText> 138 </Button> 139 ) 140}