import React, { createRef, Dispatch, useEffect, useMemo, useState } from 'react' import { Animated, Image, StyleSheet, Text, View } from 'react-native' import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder' import { Feather } from '@expo/vector-icons' import ParseContent from '@components/ParseContent' import { useTheme } from '@utils/styles/ThemeManager' import { StyleConstants } from '@utils/styles/constants' import { useTranslation } from 'react-i18next' import Emojis from '@components/Timelines/Timeline/Shared/Emojis' import { LinearGradient } from 'expo-linear-gradient' import { AccountAction } from '../Account' import { ButtonRow } from '@root/components/Button' import { useMutation, useQuery, useQueryClient } from 'react-query' import { relationshipFetch } from '@root/utils/fetches/relationshipFetch' import client from '@root/api/client' import { useNavigation } from '@react-navigation/native' import getCurrentTab from '@root/utils/getCurrentTab' const fireMutation = async ({ type, id, stateKey, prevState }: { type: 'follow' id: string stateKey: 'following' prevState: boolean }) => { let res switch (type) { case 'follow': res = await client({ method: 'post', instance: 'local', url: `accounts/${id}/${prevState ? 'un' : ''}${type}` }) if (res.body[stateKey] === !prevState) { return Promise.resolve() } else { return Promise.reject() } break } } export interface Props { accountDispatch?: Dispatch account: Mastodon.Account | undefined disableActions?: boolean } const AccountInformation: React.FC = ({ accountDispatch, account, disableActions = false }) => { const navigation = useNavigation() const { t } = useTranslation('sharedAccount') const { theme } = useTheme() const [avatarLoaded, setAvatarLoaded] = useState(false) const relationshipQueryKey = ['Relationship', { id: account?.id }] const { status, data, refetch } = useQuery( relationshipQueryKey, relationshipFetch, { enabled: false } ) useEffect(() => { if (account?.id) { refetch() } }, [account]) const queryClient = useQueryClient() const { mutate, status: mutateStatus } = useMutation(fireMutation, { onMutate: () => { queryClient.cancelQueries(relationshipQueryKey) const oldData = queryClient.getQueryData(relationshipQueryKey) queryClient.setQueryData(relationshipQueryKey, (old: any) => { old && (old.following = !old?.following) return old }) return oldData }, onError: (err, _, oldData) => { queryClient.setQueryData(relationshipQueryKey, oldData) }, onSettled: () => { queryClient.invalidateQueries(['Following']) } }) const ShimmerPlaceholder = createShimmerPlaceholder(LinearGradient) const shimmerAvatarRef = createRef() const shimmerNameRef = createRef() const shimmerAccountRef = createRef() const shimmerCreatedRef = createRef() const shimmerStatTootRef = createRef() const shimmerStatFolloingRef = createRef() const shimmerStatFollowerRef = createRef() useEffect(() => { const informationAnimated = Animated.stagger(400, [ Animated.parallel([ shimmerAvatarRef.current!.getAnimated(), shimmerNameRef.current!.getAnimated(), shimmerAccountRef.current!.getAnimated(), shimmerCreatedRef.current!.getAnimated(), shimmerStatTootRef.current!.getAnimated(), shimmerStatFolloingRef.current!.getAnimated(), shimmerStatFollowerRef.current!.getAnimated() ]) ]) Animated.loop(informationAnimated).start() }, []) const followingButton = useMemo( () => ( mutate({ type: 'follow', id: account!.id, stateKey: 'following', prevState: data!.following }) } disabled={ status !== 'success' || (mutateStatus !== 'success' && mutateStatus !== 'idle') } /> ), [data, status, mutateStatus] ) return ( accountDispatch && accountDispatch({ type: 'informationLayout', payload: { y: nativeEvent.layout.y, height: nativeEvent.layout.height } }) } > {/* Moved or not: {account.moved} */} setAvatarLoaded(true)} /> {!disableActions && ( navigation.navigate(getCurrentTab(navigation), { screen: 'Screen-Shared-Compose', params: { type: 'conversation', incomingStatus: { account } } }) } style={{ marginRight: StyleConstants.Spacing.S }} /> {followingButton} )} {account?.emojis ? ( ) : ( {account?.display_name || account?.username} )} @{account?.acct} {account?.locked && ( )} {account?.bot && ( )} {account?.fields && account.fields.length > 0 && ( {account.fields.map((field, index) => ( {field.verified_at && ( )} ))} )} {account?.note && ( )} {t('content.created_at', { date: new Date(account?.created_at!).toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' }) })} {t('content.summary.statuses_count', { count: account?.statuses_count || 0 })} {t('content.summary.followers_count', { count: account?.followers_count || 0 })} {t('content.summary.following_count', { count: account?.following_count || 0 })} ) } const styles = StyleSheet.create({ base: { marginTop: -StyleConstants.Spacing.Global.PagePadding * 3, padding: StyleConstants.Spacing.Global.PagePadding }, avatarAndActions: { flexDirection: 'row', justifyContent: 'space-between' }, avatar: { width: StyleConstants.Avatar.L, height: StyleConstants.Avatar.L, borderRadius: 8 }, actions: { alignSelf: 'flex-end', flexDirection: 'row' }, display_name: { flexDirection: 'row', marginTop: StyleConstants.Spacing.M, marginBottom: StyleConstants.Spacing.XS }, account: { flexDirection: 'row', alignItems: 'center' }, account_types: { marginLeft: StyleConstants.Spacing.S }, fields: { borderTopWidth: StyleSheet.hairlineWidth, marginBottom: StyleConstants.Spacing.M }, field: { flex: 1, flexDirection: 'row', borderBottomWidth: StyleSheet.hairlineWidth, paddingTop: StyleConstants.Spacing.S, paddingBottom: StyleConstants.Spacing.S }, fieldLeft: { flexBasis: '30%', flexDirection: 'row', alignItems: 'center', justifyContent: 'center', borderRightWidth: 1, paddingLeft: StyleConstants.Spacing.S, paddingRight: StyleConstants.Spacing.S }, fieldCheck: { marginLeft: StyleConstants.Spacing.XS }, fieldRight: { flexBasis: '70%', alignItems: 'center', justifyContent: 'center', paddingLeft: StyleConstants.Spacing.S, paddingRight: StyleConstants.Spacing.S }, note: { marginBottom: StyleConstants.Spacing.L }, created_at: { flexDirection: 'row', alignItems: 'center' }, created_at_icon: { marginRight: StyleConstants.Spacing.XS }, stats: { flex: 1, flexDirection: 'row', justifyContent: 'space-between' }, stat: { fontSize: StyleConstants.Font.Size.S } }) export default AccountInformation