diff options
Diffstat (limited to 'src/view/com/util')
-rw-r--r-- | src/view/com/util/PostCtrls.tsx | 36 | ||||
-rw-r--r-- | src/view/com/util/PostEmbeds/QuoteEmbed.tsx | 58 | ||||
-rw-r--r-- | src/view/com/util/PostEmbeds/index.tsx | 28 | ||||
-rw-r--r-- | src/view/com/util/PostMeta.tsx | 30 |
4 files changed, 146 insertions, 6 deletions
diff --git a/src/view/com/util/PostCtrls.tsx b/src/view/com/util/PostCtrls.tsx index e42c5e63b..cb4dfab26 100644 --- a/src/view/com/util/PostCtrls.tsx +++ b/src/view/com/util/PostCtrls.tsx @@ -26,6 +26,7 @@ import { } from 'lib/icons' import {s, colors} from 'lib/styles' import {useTheme} from 'lib/ThemeContext' +import {useStores} from 'state/index' interface PostCtrlsOpts { itemUri: string @@ -33,6 +34,13 @@ interface PostCtrlsOpts { itemHref: string itemTitle: string isAuthor: boolean + author: { + handle: string + displayName: string + avatar: string + } + text: string + indexedAt: string big?: boolean style?: StyleProp<ViewStyle> replyCount?: number @@ -86,6 +94,7 @@ function ctrlAnimStyle(interp: Animated.Value) { */ export function PostCtrls(opts: PostCtrlsOpts) { + const store = useStores() const theme = useTheme() const defaultCtrlColor = React.useMemo( () => ({ @@ -98,7 +107,8 @@ export function PostCtrls(opts: PostCtrlsOpts) { // DISABLED see #135 // const repostRef = React.useRef<TriggerableAnimatedRef | null>(null) // const likeRef = React.useRef<TriggerableAnimatedRef | null>(null) - const onPressToggleRepostWrapper = () => { + const onRepost = () => { + store.shell.closeModal() if (!opts.isReposted) { ReactNativeHapticFeedback.trigger('impactMedium') setRepostMod(1) @@ -122,6 +132,30 @@ export function PostCtrls(opts: PostCtrlsOpts) { .then(() => setRepostMod(0)) } } + + const onQuote = () => { + store.shell.closeModal() + store.shell.openComposer({ + quote: { + uri: opts.itemUri, + cid: opts.itemCid, + text: opts.text, + author: opts.author, + indexedAt: opts.indexedAt, + }, + }) + ReactNativeHapticFeedback.trigger('impactMedium') + } + + const onPressToggleRepostWrapper = () => { + store.shell.openModal({ + name: 'repost', + onRepost: onRepost, + onQuote: onQuote, + isReposted: opts.isReposted, + }) + } + const onPressToggleUpvoteWrapper = () => { if (!opts.isUpvoted) { ReactNativeHapticFeedback.trigger('impactMedium') diff --git a/src/view/com/util/PostEmbeds/QuoteEmbed.tsx b/src/view/com/util/PostEmbeds/QuoteEmbed.tsx new file mode 100644 index 000000000..76b71a53d --- /dev/null +++ b/src/view/com/util/PostEmbeds/QuoteEmbed.tsx @@ -0,0 +1,58 @@ +import {StyleSheet} from 'react-native' +import React from 'react' +import {AtUri} from '../../../../third-party/uri' +import {PostMeta} from '../PostMeta' +import {Link} from '../Link' +import {Text} from '../text/Text' +import {usePalette} from 'lib/hooks/usePalette' +import {ComposerOptsQuote} from 'state/models/shell-ui' + +const QuoteEmbed = ({quote}: {quote: ComposerOptsQuote}) => { + const pal = usePalette('default') + const itemUrip = new AtUri(quote.uri) + const itemHref = `/profile/${quote.author.handle}/post/${itemUrip.rkey}` + const itemTitle = `Post by ${quote.author.handle}` + const isEmpty = React.useMemo( + () => quote.text.trim().length === 0, + [quote.text], + ) + return ( + <Link + style={[styles.container, pal.border]} + href={itemHref} + title={itemTitle}> + <PostMeta + authorAvatar={quote.author.avatar} + authorHandle={quote.author.handle} + authorDisplayName={quote.author.displayName} + timestamp={quote.indexedAt} + /> + <Text type="post-text" style={pal.text} numberOfLines={6}> + {isEmpty ? ( + <Text style={pal.link} lineHeight={1.5}> + View post + </Text> + ) : ( + quote.text + )} + </Text> + </Link> + ) +} + +export default QuoteEmbed + +const styles = StyleSheet.create({ + container: { + borderRadius: 8, + paddingVertical: 8, + paddingHorizontal: 12, + marginVertical: 8, + borderWidth: 1, + }, + quotePost: { + flex: 1, + paddingLeft: 13, + paddingRight: 8, + }, +}) diff --git a/src/view/com/util/PostEmbeds/index.tsx b/src/view/com/util/PostEmbeds/index.tsx index d2186b600..3d3356712 100644 --- a/src/view/com/util/PostEmbeds/index.tsx +++ b/src/view/com/util/PostEmbeds/index.tsx @@ -6,7 +6,12 @@ import { ViewStyle, Image as RNImage, } from 'react-native' -import {AppBskyEmbedImages, AppBskyEmbedExternal} from '@atproto/api' +import { + AppBskyEmbedImages, + AppBskyEmbedExternal, + AppBskyEmbedRecord, + AppBskyFeedPost, +} from '@atproto/api' import {Link} from '../Link' import {AutoSizedImage} from '../images/AutoSizedImage' import {ImageLayoutGrid} from '../images/ImageLayoutGrid' @@ -17,8 +22,10 @@ import {saveImageModal} from 'lib/media/manip' import YoutubeEmbed from './YoutubeEmbed' import ExternalLinkEmbed from './ExternalLinkEmbed' import {getYoutubeVideoId} from 'lib/strings/url-helpers' +import QuoteEmbed from './QuoteEmbed' type Embed = + | AppBskyEmbedRecord.Presented | AppBskyEmbedImages.Presented | AppBskyEmbedExternal.Presented | {$type: string; [k: string]: unknown} @@ -32,6 +39,25 @@ export function PostEmbeds({ }) { const pal = usePalette('default') const store = useStores() + if (AppBskyEmbedRecord.isPresented(embed)) { + if ( + AppBskyEmbedRecord.isPresentedRecord(embed.record) && + AppBskyFeedPost.isRecord(embed.record.record) && + AppBskyFeedPost.validateRecord(embed.record.record).success + ) { + return ( + <QuoteEmbed + quote={{ + author: embed.record.author, + cid: embed.record.cid, + uri: embed.record.uri, + indexedAt: embed.record.record.createdAt, // TODO + text: embed.record.record.text, + }} + /> + ) + } + } if (AppBskyEmbedImages.isPresented(embed)) { if (embed.images.length > 0) { const uris = embed.images.map(img => img.fullsize) diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx index a07d91899..0c5d41cab 100644 --- a/src/view/com/util/PostMeta.tsx +++ b/src/view/com/util/PostMeta.tsx @@ -4,15 +4,17 @@ import {Text} from './text/Text' import {ago} from 'lib/strings/time' import {usePalette} from 'lib/hooks/usePalette' import {useStores} from 'state/index' +import {UserAvatar} from './UserAvatar' import {observer} from 'mobx-react-lite' import FollowButton from '../profile/FollowButton' interface PostMetaOpts { + authorAvatar: string | undefined authorHandle: string authorDisplayName: string | undefined timestamp: string - did: string - declarationCid: string + did?: string + declarationCid?: string showFollowBtn?: boolean } @@ -27,11 +29,18 @@ export const PostMeta = observer(function (opts: PostMetaOpts) { // don't change this UI immediately, but rather upon future // renders const isFollowing = React.useMemo( - () => store.me.follows.isFollowing(opts.did), + () => + typeof opts.did === 'string' && store.me.follows.isFollowing(opts.did), [opts.did, store.me.follows], ) - if (opts.showFollowBtn && !isMe && !isFollowing) { + if ( + opts.showFollowBtn && + !isMe && + !isFollowing && + opts.did && + opts.declarationCid + ) { // two-liner with follow button return ( <View style={[styles.metaTwoLine]}> @@ -71,6 +80,16 @@ export const PostMeta = observer(function (opts: PostMetaOpts) { // one-liner return ( <View style={styles.meta}> + {typeof opts.authorAvatar !== 'undefined' && ( + <View style={[styles.metaItem, styles.avatar]}> + <UserAvatar + avatar={opts.authorAvatar} + handle={opts.authorHandle} + displayName={opts.authorDisplayName} + size={16} + /> + </View> + )} <View style={[styles.metaItem, styles.maxWidth]}> <Text type="lg-bold" @@ -107,6 +126,9 @@ const styles = StyleSheet.create({ metaItem: { paddingRight: 5, }, + avatar: { + alignSelf: 'center', + }, maxWidth: { maxWidth: '80%', }, |