import React, {useMemo, useState} from 'react' import {observer} from 'mobx-react-lite' import {Linking, StyleSheet, View} from 'react-native' import Clipboard from '@react-native-clipboard/clipboard' import Svg, {Circle, Line} from 'react-native-svg' import {AtUri} from '../../../third-party/uri' import { FontAwesomeIcon, FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' import {FeedItemModel} from 'state/models/feed-view' import {Link} from '../util/Link' import {Text} from '../util/text/Text' import {UserInfoText} from '../util/UserInfoText' import {PostMeta} from '../util/PostMeta' import {PostCtrls} from '../util/PostCtrls' import {PostEmbeds} from '../util/PostEmbeds' import {PostMutedWrapper} from '../util/PostMuted' import {RichText} from '../util/text/RichText' import * as Toast from '../util/Toast' import {UserAvatar} from '../util/UserAvatar' import {s} from 'lib/styles' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' import {useAnalytics} from 'lib/analytics' export const FeedItem = observer(function ({ item, showReplyLine, showFollowBtn, ignoreMuteFor, }: { item: FeedItemModel showReplyLine?: boolean showFollowBtn?: boolean ignoreMuteFor?: string }) { const store = useStores() const pal = usePalette('default') const {track} = useAnalytics() const [deleted, setDeleted] = useState(false) const record = item.postRecord const itemUri = item.post.uri const itemCid = item.post.cid const itemHref = useMemo(() => { const urip = new AtUri(item.post.uri) return `/profile/${item.post.author.handle}/post/${urip.rkey}` }, [item.post.uri, item.post.author.handle]) const itemTitle = `Post by ${item.post.author.handle}` const authorHref = `/profile/${item.post.author.handle}` const replyAuthorDid = useMemo(() => { if (!record?.reply) { return '' } const urip = new AtUri(record.reply.parent?.uri || record.reply.root.uri) return urip.hostname }, [record?.reply]) const onPressReply = () => { track('FeedItem:PostReply') store.shell.openComposer({ replyTo: { uri: item.post.uri, cid: item.post.cid, text: record?.text || '', author: { handle: item.post.author.handle, displayName: item.post.author.displayName, avatar: item.post.author.avatar, }, }, }) } const onPressToggleRepost = () => { track('FeedItem:PostRepost') return item .toggleRepost() .catch(e => store.log.error('Failed to toggle repost', e)) } const onPressToggleUpvote = () => { track('FeedItem:PostLike') return item .toggleUpvote() .catch(e => store.log.error('Failed to toggle upvote', e)) } const onCopyPostText = () => { Clipboard.setString(record?.text || '') Toast.show('Copied to clipboard') } const onOpenTranslate = React.useCallback(() => { Linking.openURL( encodeURI(`https://translate.google.com/#auto|en|${record?.text || ''}`), ) }, [record]) const onDeletePost = () => { track('FeedItem:PostDelete') item.delete().then( () => { setDeleted(true) Toast.show('Post deleted') }, e => { store.log.error('Failed to delete post', e) Toast.show('Failed to delete post, please try again') }, ) } if (!record || deleted) { return } const isChild = item._isThreadChild || (!item.reason && !item._hideParent && item.reply) const isSmallTop = isChild && item._isThreadChild const isNoTop = isChild && !item._isThreadChild const isMuted = item.post.author.viewer?.muted && ignoreMuteFor !== item.post.author.did const outerStyles = [ styles.outer, pal.view, {borderColor: pal.colors.border}, isSmallTop ? styles.outerSmallTop : undefined, isNoTop ? styles.outerNoTop : undefined, item._isThreadParent ? styles.outerNoBottom : undefined, ] return ( {isChild && !item._isThreadChild && item.replyParent ? ( ) : undefined} {item._isThreadChild && ( )} {(showReplyLine || item._isThreadParent) && ( )} {item.reasonRepost && ( Reposted by{' '} {item.reasonRepost.by.displayName || item.reasonRepost.by.handle} )} {!isChild && replyAuthorDid !== '' && ( Reply to )} {item.richText?.text ? ( ) : undefined} {item._isThreadChildElided ? ( View full thread ) : undefined} ) }) const styles = StyleSheet.create({ outer: { borderTopWidth: 1, padding: 10, paddingRight: 15, paddingBottom: 8, }, outerNoTop: { borderTopWidth: 0, paddingTop: 0, }, outerSmallTop: { borderTopWidth: 0, }, outerNoBottom: { paddingBottom: 2, }, topReplyLine: { position: 'absolute', left: 42, top: 0, height: 6, borderLeftWidth: 2, }, bottomReplyLine: { position: 'absolute', left: 42, top: 72, bottom: 0, borderLeftWidth: 2, }, bottomReplyLineNoTop: {top: 64}, includeReason: { flexDirection: 'row', paddingLeft: 50, paddingRight: 20, marginTop: 2, marginBottom: 2, }, includeReasonIcon: { marginRight: 4, }, layout: { flexDirection: 'row', marginTop: 1, }, layoutAvi: { width: 70, paddingLeft: 8, }, layoutContent: { flex: 1, }, postTextContainer: { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', paddingBottom: 4, }, embed: { marginBottom: 6, }, ctrls: { marginTop: 4, }, viewFullThread: { paddingTop: 12, paddingBottom: 2, paddingLeft: 80, }, viewFullThreadDots: { position: 'absolute', left: 41, top: 0, }, })