import React, {memo, useMemo} from 'react' import {View} from 'react-native' import { AppBskyActorDefs, AppBskyLabelerDefs, moderateProfile, ModerationOpts, RichText as RichTextAPI, } from '@atproto/api' import {msg, Plural, plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' // eslint-disable-next-line @typescript-eslint/no-unused-vars import {MAX_LABELERS} from '#/lib/constants' import {useHaptics} from '#/lib/haptics' import {isAppLabeler} from '#/lib/moderation' import {logger} from '#/logger' import {isIOS, isWeb} from '#/platform/detection' import {useProfileShadow} from '#/state/cache/profile-shadow' import {Shadow} from '#/state/cache/types' import {useModalControls} from '#/state/modals' import {useLabelerSubscriptionMutation} from '#/state/queries/labeler' import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like' import {usePreferencesQuery} from '#/state/queries/preferences' import {useRequireAuth, useSession} from '#/state/session' import {ProfileMenu} from '#/view/com/profile/ProfileMenu' import * as Toast from '#/view/com/util/Toast' import {atoms as a, tokens, useTheme} from '#/alf' import {Button, ButtonText} from '#/components/Button' import {DialogOuterProps, useDialogControl} from '#/components/Dialog' import { Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilled, Heart2_Stroke2_Corner0_Rounded as Heart, } from '#/components/icons/Heart2' import {Link} from '#/components/Link' import * as Prompt from '#/components/Prompt' import {RichText} from '#/components/RichText' import {Text} from '#/components/Typography' import {ProfileHeaderDisplayName} from './DisplayName' import {EditProfileDialog} from './EditProfileDialog' import {ProfileHeaderHandle} from './Handle' import {ProfileHeaderMetrics} from './Metrics' import {ProfileHeaderShell} from './Shell' interface Props { profile: AppBskyActorDefs.ProfileViewDetailed labeler: AppBskyLabelerDefs.LabelerViewDetailed descriptionRT: RichTextAPI | null moderationOpts: ModerationOpts hideBackButton?: boolean isPlaceholderProfile?: boolean } let ProfileHeaderLabeler = ({ profile: profileUnshadowed, labeler, descriptionRT, moderationOpts, hideBackButton = false, isPlaceholderProfile, }: Props): React.ReactNode => { const profile: Shadow = useProfileShadow(profileUnshadowed) const t = useTheme() const {_} = useLingui() const {currentAccount, hasSession} = useSession() const requireAuth = useRequireAuth() const playHaptic = useHaptics() const cantSubscribePrompt = Prompt.usePromptControl() const isSelf = currentAccount?.did === profile.did const moderation = useMemo( () => moderateProfile(profile, moderationOpts), [profile, moderationOpts], ) const {data: preferences} = usePreferencesQuery() const { mutateAsync: toggleSubscription, variables, reset, } = useLabelerSubscriptionMutation() const isSubscribed = variables?.subscribe ?? preferences?.moderationPrefs.labelers.find(l => l.did === profile.did) const {mutateAsync: likeMod, isPending: isLikePending} = useLikeMutation() const {mutateAsync: unlikeMod, isPending: isUnlikePending} = useUnlikeMutation() const [likeUri, setLikeUri] = React.useState( labeler.viewer?.like || '', ) const [likeCount, setLikeCount] = React.useState(labeler.likeCount || 0) const onToggleLiked = React.useCallback(async () => { if (!labeler) { return } try { playHaptic() if (likeUri) { await unlikeMod({uri: likeUri}) setLikeCount(c => c - 1) setLikeUri('') } else { const res = await likeMod({uri: labeler.uri, cid: labeler.cid}) setLikeCount(c => c + 1) setLikeUri(res.uri) } } catch (e: any) { Toast.show( _( msg`There was an issue contacting the server, please check your internet connection and try again.`, ), 'xmark', ) logger.error(`Failed to toggle labeler like`, {message: e.message}) } }, [labeler, playHaptic, likeUri, unlikeMod, likeMod, _]) const {openModal} = useModalControls() const editProfileControl = useDialogControl() const onPressEditProfile = React.useCallback(() => { if (isWeb) { // temp, while we figure out the nested dialog bug openModal({ name: 'edit-profile', profile, }) } else { editProfileControl.open() } }, [editProfileControl, openModal, profile]) const onPressSubscribe = React.useCallback( () => requireAuth(async (): Promise => { const subscribe = !isSubscribed try { await toggleSubscription({ did: profile.did, subscribe, }) logger.metric( subscribe ? 'moderation:subscribedToLabeler' : 'moderation:unsubscribedFromLabeler', {}, {statsig: true}, ) } catch (e: any) { reset() if (e.message === 'MAX_LABELERS') { cantSubscribePrompt.open() return } logger.error(`Failed to subscribe to labeler`, {message: e.message}) } }), [ requireAuth, toggleSubscription, isSubscribed, profile, cantSubscribePrompt, reset, ], ) const isMe = React.useMemo( () => currentAccount?.did === profile.did, [currentAccount, profile], ) return ( {isMe ? ( <> ) : !isAppLabeler(profile.did) ? ( <> ) : null} {!isPlaceholderProfile && ( <> {isSelf && } {descriptionRT && !moderation.ui('profileView').blur ? ( ) : undefined} {!isAppLabeler(profile.did) && ( {typeof likeCount === 'number' && ( {({hovered, focused, pressed}) => ( Liked by{' '} )} )} )} )} ) } ProfileHeaderLabeler = memo(ProfileHeaderLabeler) export {ProfileHeaderLabeler} /** * Keep this in sync with the value of {@link MAX_LABELERS} */ function CantSubscribePrompt({ control, }: { control: DialogOuterProps['control'] }) { const {_} = useLingui() return ( Unable to subscribe We're sorry! You can only subscribe to twenty labelers, and you've reached your limit of twenty. control.close()} cta={_(msg`OK`)} /> ) }