import React, {useMemo, useState} from 'react' import {observer} from 'mobx-react-lite' import {StyleSheet, Text, View} from 'react-native' import Clipboard from '@react-native-clipboard/clipboard' import {AtUri} from '../../../third-party/uri' import * as PostType from '../../../third-party/api/src/client/types/app/bsky/feed/post' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FeedItemModel} from '../../../state/models/feed-view' import {Link} from '../util/Link' import {UserInfoText} from '../util/UserInfoText' import {PostMeta} from '../util/PostMeta' import {PostCtrls} from '../util/PostCtrls' import {PostEmbeds} from '../util/PostEmbeds' import {RichText} from '../util/RichText' import * as Toast from '../util/Toast' import {UserAvatar} from '../util/UserAvatar' import {s, colors} from '../../lib/styles' import {useStores} from '../../../state' const TOP_REPLY_LINE_LENGTH = 12 const REPLYING_TO_LINE_LENGTH = 8 export const FeedItem = observer(function FeedItem({ item, }: { item: FeedItemModel }) { const store = useStores() const [deleted, setDeleted] = useState(false) const record = item.record as unknown as PostType.Record const itemHref = useMemo(() => { const urip = new AtUri(item.uri) return `/profile/${item.author.handle}/post/${urip.rkey}` }, [item.uri, item.author.handle]) const itemTitle = `Post by ${item.author.handle}` const authorHref = `/profile/${item.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 replyHref = useMemo(() => { if (!record.reply) return '' const urip = new AtUri(record.reply.parent?.uri || record.reply.root.uri) return `/profile/${urip.hostname}/post/${urip.rkey}` }, [record.reply]) const onPressReply = () => { store.shell.openComposer({ replyTo: { uri: item.uri, cid: item.cid, text: item.record.text as string, author: { handle: item.author.handle, displayName: item.author.displayName, avatar: item.author.avatar, }, }, }) } const onPressToggleRepost = () => { item .toggleRepost() .catch(e => console.error('Failed to toggle repost', record, e)) } const onPressToggleUpvote = () => { item .toggleUpvote() .catch(e => console.error('Failed to toggle upvote', record, e)) } const onCopyPostText = () => { Clipboard.setString(record.text) Toast.show('Copied to clipboard') } const onDeletePost = () => { item.delete().then( () => { setDeleted(true) Toast.show('Post deleted') }, e => { console.error(e) Toast.show('Failed to delete post, please try again') }, ) } if (deleted) { return } const outerStyles = [ styles.outer, item._isThreadChild ? styles.outerNoTop : undefined, item._isThreadParent ? styles.outerNoBottom : undefined, ] return ( {item._isThreadChild && } {item._isThreadParent && ( )} {item.repostedBy && ( Reposted by {item.repostedBy.displayName || item.repostedBy.handle} )} {item.trendedBy && ( Trending with {item.trendedBy.displayName || item.trendedBy.handle} )} {item.additionalParentPost ? ( {item.additionalParentPost?.thread?.record.text} ) : undefined} {!item._isThreadChild ? ( ) : undefined} {!item._isThreadChild && replyHref !== '' && ( Replying to )} ) }) const styles = StyleSheet.create({ outer: { borderRadius: 6, margin: 2, marginBottom: 0, backgroundColor: colors.white, padding: 10, }, outerNoTop: { marginTop: 1, borderTopLeftRadius: 0, borderTopRightRadius: 0, }, outerNoBottom: { marginBottom: 0, borderBottomLeftRadius: 0, borderBottomRightRadius: 0, }, topReplyLine: { position: 'absolute', left: 34, top: -1 * TOP_REPLY_LINE_LENGTH + 10, height: TOP_REPLY_LINE_LENGTH, borderLeftWidth: 2, borderLeftColor: colors.gray2, }, bottomReplyLine: { position: 'absolute', left: 34, top: 70, bottom: 0, borderLeftWidth: 2, borderLeftColor: colors.gray2, }, bottomReplyLineSmallAvi: { top: 50, }, includeReason: { flexDirection: 'row', paddingLeft: 60, }, includeReasonIcon: { marginRight: 4, color: colors.gray4, }, replyingToLine: { position: 'absolute', left: 24, bottom: -1 * REPLYING_TO_LINE_LENGTH + 6, height: REPLYING_TO_LINE_LENGTH, borderLeftWidth: 2, borderLeftColor: colors.gray2, }, replyingTo: { flexDirection: 'row', backgroundColor: colors.white, paddingBottom: 8, paddingRight: 24, }, replyingToAvatar: { marginLeft: 9, marginRight: 19, marginTop: 1, }, replyingToTextContainer: { flex: 1, flexDirection: 'row', height: 34, alignItems: 'center', }, replyingToText: { flex: 1, color: colors.gray5, }, layout: { flexDirection: 'row', }, layoutAvi: { width: 60, paddingTop: 5, }, layoutContent: { flex: 1, }, postTextContainer: { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', paddingBottom: 8, minHeight: 36, }, postText: { fontFamily: 'System', fontSize: 16, lineHeight: 20.8, // 1.3 of 16px }, })