diff options
Diffstat (limited to 'src/view/com')
-rw-r--r-- | src/view/com/notifications/NotificationFeed.tsx | 4 | ||||
-rw-r--r-- | src/view/com/notifications/NotificationFeedItem.tsx | 54 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThreadItem.tsx | 107 | ||||
-rw-r--r-- | src/view/com/posts/PostFeedItem.tsx | 27 |
4 files changed, 158 insertions, 34 deletions
diff --git a/src/view/com/notifications/NotificationFeed.tsx b/src/view/com/notifications/NotificationFeed.tsx index 73cebf868..1f87b3186 100644 --- a/src/view/com/notifications/NotificationFeed.tsx +++ b/src/view/com/notifications/NotificationFeed.tsx @@ -1,7 +1,7 @@ import React from 'react' import { ActivityIndicator, - ListRenderItemInfo, + type ListRenderItemInfo, StyleSheet, View, } from 'react-native' @@ -16,7 +16,7 @@ import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useNotificationFeedQuery} from '#/state/queries/notifications/feed' import {EmptyState} from '#/view/com/util/EmptyState' import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' -import {List, ListRef} from '#/view/com/util/List' +import {List, type ListRef} from '#/view/com/util/List' import {NotificationFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn' import {NotificationFeedItem} from './NotificationFeedItem' diff --git a/src/view/com/notifications/NotificationFeedItem.tsx b/src/view/com/notifications/NotificationFeedItem.tsx index a30aba7d8..1f99a3c34 100644 --- a/src/view/com/notifications/NotificationFeedItem.tsx +++ b/src/view/com/notifications/NotificationFeedItem.tsx @@ -446,6 +446,55 @@ let NotificationFeedItem = ({ </Trans> ) icon = <VerifiedCheck size="xl" fill={t.palette.contrast_500} /> + } else if (item.type === 'like-via-repost') { + a11yLabel = hasMultipleAuthors + ? _( + msg`${firstAuthorName} and ${plural(additionalAuthorsCount, { + one: `${formattedAuthorsCount} other`, + other: `${formattedAuthorsCount} others`, + })} liked your repost`, + ) + : _(msg`${firstAuthorName} liked your repost`) + notificationContent = hasMultipleAuthors ? ( + <Trans> + {firstAuthorLink} and{' '} + <Text style={[a.text_md, a.font_bold, a.leading_snug]}> + <Plural + value={additionalAuthorsCount} + one={`${formattedAuthorsCount} other`} + other={`${formattedAuthorsCount} others`} + /> + </Text>{' '} + liked your repost + </Trans> + ) : ( + <Trans>{firstAuthorLink} liked your repost</Trans> + ) + } else if (item.type === 'repost-via-repost') { + a11yLabel = hasMultipleAuthors + ? _( + msg`${firstAuthorName} and ${plural(additionalAuthorsCount, { + one: `${formattedAuthorsCount} other`, + other: `${formattedAuthorsCount} others`, + })} reposted your repost`, + ) + : _(msg`${firstAuthorName} reposted your repost`) + notificationContent = hasMultipleAuthors ? ( + <Trans> + {firstAuthorLink} and{' '} + <Text style={[a.text_md, a.font_bold, a.leading_snug]}> + <Plural + value={additionalAuthorsCount} + one={`${formattedAuthorsCount} other`} + other={`${formattedAuthorsCount} others`} + /> + </Text>{' '} + reposted your repost + </Trans> + ) : ( + <Trans>{firstAuthorLink} reposted your repost</Trans> + ) + icon = <RepostIcon size="xl" style={{color: t.palette.positive_600}} /> } else { return null } @@ -553,7 +602,10 @@ let NotificationFeedItem = ({ </TimeElapsed> </Text> </ExpandListPressable> - {item.type === 'post-like' || item.type === 'repost' ? ( + {item.type === 'post-like' || + item.type === 'repost' || + item.type === 'like-via-repost' || + item.type === 'repost-via-repost' ? ( <View style={[a.pt_2xs]}> <AdditionalPostText post={item.subject} /> </View> diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 82852aa62..77adebac9 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -1,4 +1,4 @@ -import React, {memo, useMemo} from 'react' +import {memo, useCallback, useMemo, useState} from 'react' import { type GestureResponderEvent, StyleSheet, @@ -6,7 +6,7 @@ import { View, } from 'react-native' import { - type AppBskyFeedDefs, + AppBskyFeedDefs, AppBskyFeedPost, type AppBskyFeedThreadgate, AtUri, @@ -35,10 +35,12 @@ import { usePostShadow, } from '#/state/cache/post-shadow' import {useProfileShadow} from '#/state/cache/profile-shadow' +import {FeedFeedbackProvider, useFeedFeedback} from '#/state/feed-feedback' import {useLanguagePrefs} from '#/state/preferences' import {type ThreadPost} from '#/state/queries/post-thread' import {useSession} from '#/state/session' import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' +import {useUnstablePostSource} from '#/state/unstable-post-source' import {PostThreadFollowBtn} from '#/view/com/post-thread/PostThreadFollowBtn' import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' import {Link, TextLink} from '#/view/com/util/Link' @@ -201,18 +203,21 @@ let PostThreadItemLoaded = ({ hideTopBorder?: boolean threadgateRecord?: AppBskyFeedThreadgate.Record }): React.ReactNode => { + const {currentAccount, hasSession} = useSession() + const source = useUnstablePostSource(post.uri) + const feedFeedback = useFeedFeedback(source?.feed, hasSession) + const t = useTheme() const pal = usePalette('default') const {_, i18n} = useLingui() const langPrefs = useLanguagePrefs() const {openComposer} = useOpenComposer() - const [limitLines, setLimitLines] = React.useState( + const [limitLines, setLimitLines] = useState( () => countLines(richText?.text) >= MAX_POST_LINES, ) - const {currentAccount} = useSession() const shadowedPostAuthor = useProfileShadow(post.author) const rootUri = record.reply?.root?.uri || post.uri - const postHref = React.useMemo(() => { + const postHref = useMemo(() => { const urip = new AtUri(post.uri) return makeProfileLink(post.author, 'post', urip.rkey) }, [post.uri, post.author]) @@ -220,12 +225,12 @@ let PostThreadItemLoaded = ({ const authorHref = makeProfileLink(post.author) const authorTitle = post.author.handle const isThreadAuthor = getThreadAuthor(post, record) === currentAccount?.did - const likesHref = React.useMemo(() => { + const likesHref = useMemo(() => { const urip = new AtUri(post.uri) return makeProfileLink(post.author, 'post', urip.rkey, 'liked-by') }, [post.uri, post.author]) const likesTitle = _(msg`Likes on this post`) - const repostsHref = React.useMemo(() => { + const repostsHref = useMemo(() => { const urip = new AtUri(post.uri) return makeProfileLink(post.author, 'post', urip.rkey, 'reposted-by') }, [post.uri, post.author]) @@ -233,7 +238,7 @@ let PostThreadItemLoaded = ({ const threadgateHiddenReplies = useMergedThreadgateHiddenReplies({ threadgateRecord, }) - const additionalPostAlerts: AppModerationCause[] = React.useMemo(() => { + const additionalPostAlerts: AppModerationCause[] = useMemo(() => { const isPostHiddenByThreadgate = threadgateHiddenReplies.has(post.uri) const isControlledByViewer = new AtUri(rootUri).host === currentAccount?.did return isControlledByViewer && isPostHiddenByThreadgate @@ -246,7 +251,7 @@ let PostThreadItemLoaded = ({ ] : [] }, [post, currentAccount?.did, threadgateHiddenReplies, rootUri]) - const quotesHref = React.useMemo(() => { + const quotesHref = useMemo(() => { const urip = new AtUri(post.uri) return makeProfileLink(post.author, 'post', urip.rkey, 'quotes') }, [post.uri, post.author]) @@ -270,7 +275,15 @@ let PostThreadItemLoaded = ({ [post, langPrefs.primaryLanguage], ) - const onPressReply = React.useCallback(() => { + const onPressReply = () => { + if (source) { + feedFeedback.sendInteraction({ + item: post.uri, + event: 'app.bsky.feed.defs#interactionReply', + feedContext: source.post.feedContext, + reqId: source.post.reqId, + }) + } openComposer({ replyTo: { uri: post.uri, @@ -282,14 +295,46 @@ let PostThreadItemLoaded = ({ }, onPost: onPostReply, }) - }, [openComposer, post, record, onPostReply, moderation]) + } - const onPressShowMore = React.useCallback(() => { + const onOpenAuthor = () => { + if (source) { + feedFeedback.sendInteraction({ + item: post.uri, + event: 'app.bsky.feed.defs#clickthroughAuthor', + feedContext: source.post.feedContext, + reqId: source.post.reqId, + }) + } + } + + const onOpenEmbed = () => { + if (source) { + feedFeedback.sendInteraction({ + item: post.uri, + event: 'app.bsky.feed.defs#clickthroughEmbed', + feedContext: source.post.feedContext, + reqId: source.post.reqId, + }) + } + } + + const onPressShowMore = useCallback(() => { setLimitLines(false) }, [setLimitLines]) const {isActive: live} = useActorStatus(post.author) + const reason = source?.post.reason + const viaRepost = useMemo(() => { + if (AppBskyFeedDefs.isReasonRepost(reason) && reason.uri && reason.cid) { + return { + uri: reason.uri, + cid: reason.cid, + } + } + }, [reason]) + if (!record) { return <ErrorMessage message={_(msg`Invalid or unsupported post record`)} /> } @@ -309,10 +354,8 @@ let PostThreadItemLoaded = ({ <View style={[ styles.replyLine, - { - flexGrow: 1, - backgroundColor: pal.colors.replyLine, - }, + a.flex_grow, + {backgroundColor: pal.colors.replyLine}, ]} /> </View> @@ -334,13 +377,15 @@ let PostThreadItemLoaded = ({ moderation={moderation.ui('avatar')} type={post.author.associated?.labeler ? 'labeler' : 'user'} live={live} + onBeforePress={onOpenAuthor} /> <View style={[a.flex_1]}> <View style={[a.flex_row, a.align_center]}> <Link style={[a.flex_shrink]} href={authorHref} - title={authorTitle}> + title={authorTitle} + onBeforePress={onOpenAuthor}> <Text emoji style={[ @@ -413,6 +458,7 @@ let PostThreadItemLoaded = ({ embed={post.embed} moderation={moderation} viewContext={PostEmbedViewContext.ThreadHighlighted} + onOpen={onOpenEmbed} /> </View> )} @@ -494,16 +540,21 @@ let PostThreadItemLoaded = ({ marginLeft: -5, }, ]}> - <PostControls - big - post={post} - record={record} - richText={richText} - onPressReply={onPressReply} - onPostReply={onPostReply} - logContext="PostThreadItem" - threadgateRecord={threadgateRecord} - /> + <FeedFeedbackProvider value={feedFeedback}> + <PostControls + big + post={post} + record={record} + richText={richText} + onPressReply={onPressReply} + onPostReply={onPostReply} + logContext="PostThreadItem" + threadgateRecord={threadgateRecord} + feedContext={source?.post?.feedContext} + reqId={source?.post?.reqId} + viaRepost={viaRepost} + /> + </FeedFeedbackProvider> </View> </View> </View> @@ -779,7 +830,7 @@ function ExpandedPostDetails({ const isRootPost = !('reply' in post.record) const langPrefs = useLanguagePrefs() - const onTranslatePress = React.useCallback( + const onTranslatePress = useCallback( (e: GestureResponderEvent) => { e.preventDefault() openLink(translatorUrl, true) diff --git a/src/view/com/posts/PostFeedItem.tsx b/src/view/com/posts/PostFeedItem.tsx index 3735bbb5a..b9aa67673 100644 --- a/src/view/com/posts/PostFeedItem.tsx +++ b/src/view/com/posts/PostFeedItem.tsx @@ -33,9 +33,10 @@ import { usePostShadow, } from '#/state/cache/post-shadow' import {useFeedFeedbackContext} from '#/state/feed-feedback' -import {precacheProfile} from '#/state/queries/profile' +import {unstableCacheProfileView} from '#/state/queries/profile' import {useSession} from '#/state/session' import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' +import {useSetUnstablePostSource} from '#/state/unstable-post-source' import {FeedNameText} from '#/view/com/util/FeedInfoText' import {Link, TextLink, TextLinkOnWebOnly} from '#/view/com/util/Link' import {PostEmbeds, PostEmbedViewContext} from '#/view/com/util/post-embeds' @@ -174,7 +175,8 @@ let FeedItemInner = ({ const urip = new AtUri(post.uri) return makeProfileLink(post.author, 'post', urip.rkey) }, [post.uri, post.author]) - const {sendInteraction} = useFeedFeedbackContext() + const {sendInteraction, feedDescriptor} = useFeedFeedbackContext() + const unstableSetPostSource = useSetUnstablePostSource() const onPressReply = () => { sendInteraction({ @@ -229,7 +231,16 @@ let FeedItemInner = ({ feedContext, reqId, }) - precacheProfile(queryClient, post.author) + unstableCacheProfileView(queryClient, post.author) + unstableSetPostSource(post.uri, { + feed: feedDescriptor, + post: { + post, + reason: AppBskyFeedDefs.isReasonRepost(reason) ? reason : undefined, + feedContext, + reqId, + }, + }) } const outerStyles = [ @@ -263,6 +274,15 @@ let FeedItemInner = ({ const {isActive: live} = useActorStatus(post.author) + const viaRepost = useMemo(() => { + if (AppBskyFeedDefs.isReasonRepost(reason) && reason.uri && reason.cid) { + return { + uri: reason.uri, + cid: reason.cid, + } + } + }, [reason]) + return ( <Link testID={`feedItem-by-${post.author.handle}`} @@ -450,6 +470,7 @@ let FeedItemInner = ({ reqId={reqId} threadgateRecord={threadgateRecord} onShowLess={onShowLess} + viaRepost={viaRepost} /> </View> |