diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-03-31 13:17:26 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-31 13:17:26 -0500 |
commit | a3334a01a221877d3e06e02f960fda441f3460bd (patch) | |
tree | 64cdbb1232d1a3c00750c346b6e3ae529b51d1b0 /src/view/com/post-thread | |
parent | 19f3a2fa92a61ddb785fc4e42d73792c1d0e772c (diff) | |
download | voidsky-a3334a01a221877d3e06e02f960fda441f3460bd.tar.zst |
Lex refactor (#362)
* Remove the hackcheck for upgrades * Rename the PostEmbeds folder to match the codebase style * Updates to latest lex refactor * Update to use new bsky agent * Update to use api package's richtext library * Switch to upsertProfile * Add TextEncoder/TextDecoder polyfill * Add Intl.Segmenter polyfill * Update composer to calculate lengths by grapheme * Fix detox * Fix login in e2e * Create account e2e passing * Implement an e2e mocking framework * Don't use private methods on mobx models as mobx can't track them * Add tooling for e2e-specific builds and add e2e media-picker mock * Add some tests and fix some bugs around profile editing * Add shell tests * Add home screen tests * Add thread screen tests * Add tests for other user profile screens * Add search screen tests * Implement profile imagery change tools and tests * Update to new embed behaviors * Add post tests * Fix to profile-screen test * Fix session resumption * Update web composer to new api * 1.11.0 * Fix pagination cursor parameters * Add quote posts to notifications * Fix embed layouts * Remove youtube inline player and improve tap handling on link cards * Reset minimal shell mode on all screen loads and feed swipes (close #299) * Update podfile.lock * Improve post notfound UI (close #366) * Bump atproto packages
Diffstat (limited to 'src/view/com/post-thread')
-rw-r--r-- | src/view/com/post-thread/PostLikedBy.tsx (renamed from src/view/com/post-thread/PostVotedBy.tsx) | 19 | ||||
-rw-r--r-- | src/view/com/post-thread/PostRepostedBy.tsx | 1 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThread.tsx | 60 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThreadItem.tsx | 271 |
4 files changed, 198 insertions, 153 deletions
diff --git a/src/view/com/post-thread/PostVotedBy.tsx b/src/view/com/post-thread/PostLikedBy.tsx index f86798097..9fb46702e 100644 --- a/src/view/com/post-thread/PostVotedBy.tsx +++ b/src/view/com/post-thread/PostLikedBy.tsx @@ -2,24 +2,18 @@ import React, {useEffect} from 'react' import {observer} from 'mobx-react-lite' import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native' import {CenteredView, FlatList} from '../util/Views' -import {VotesViewModel, VoteItem} from 'state/models/votes-view' +import {LikesViewModel, LikeItem} from 'state/models/likes-view' import {ErrorMessage} from '../util/error/ErrorMessage' import {ProfileCardWithFollowBtn} from '../profile/ProfileCard' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' -export const PostVotedBy = observer(function PostVotedBy({ - uri, - direction, -}: { - uri: string - direction: 'up' | 'down' -}) { +export const PostLikedBy = observer(function PostVotedBy({uri}: {uri: string}) { const pal = usePalette('default') const store = useStores() const view = React.useMemo( - () => new VotesViewModel(store, {uri, direction}), - [store, uri, direction], + () => new LikesViewModel(store, {uri}), + [store, uri], ) useEffect(() => { @@ -55,11 +49,10 @@ export const PostVotedBy = observer(function PostVotedBy({ // loaded // = - const renderItem = ({item}: {item: VoteItem}) => ( + const renderItem = ({item}: {item: LikeItem}) => ( <ProfileCardWithFollowBtn key={item.actor.did} did={item.actor.did} - declarationCid={item.actor.declaration.cid} handle={item.actor.handle} displayName={item.actor.displayName} avatar={item.actor.avatar} @@ -68,7 +61,7 @@ export const PostVotedBy = observer(function PostVotedBy({ ) return ( <FlatList - data={view.votes} + data={view.likes} keyExtractor={item => item.actor.did} refreshControl={ <RefreshControl diff --git a/src/view/com/post-thread/PostRepostedBy.tsx b/src/view/com/post-thread/PostRepostedBy.tsx index fda54469c..147d0271f 100644 --- a/src/view/com/post-thread/PostRepostedBy.tsx +++ b/src/view/com/post-thread/PostRepostedBy.tsx @@ -64,7 +64,6 @@ export const PostRepostedBy = observer(function PostRepostedBy({ <ProfileCardWithFollowBtn key={item.did} did={item.did} - declarationCid={item.declaration.cid} handle={item.handle} displayName={item.displayName} avatar={item.avatar} diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index d0452331b..569c6e392 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -1,17 +1,30 @@ import React, {useRef} from 'react' import {observer} from 'mobx-react-lite' -import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native' +import { + ActivityIndicator, + RefreshControl, + StyleSheet, + TouchableOpacity, + View, +} from 'react-native' import {CenteredView, FlatList} from '../util/Views' import { PostThreadViewModel, PostThreadViewPostModel, } from 'state/models/post-thread-view' +import { + FontAwesomeIcon, + FontAwesomeIconStyle, +} from '@fortawesome/react-native-fontawesome' import {PostThreadItem} from './PostThreadItem' import {ComposePrompt} from '../composer/Prompt' import {ErrorMessage} from '../util/error/ErrorMessage' +import {Text} from '../util/text/Text' import {s} from 'lib/styles' import {isDesktopWeb} from 'platform/detection' import {usePalette} from 'lib/hooks/usePalette' +import {useNavigation} from '@react-navigation/native' +import {NavigationProp} from 'lib/routes/types' const REPLY_PROMPT = {_reactKey: '__reply__', _isHighlightedPost: false} const BOTTOM_BORDER = { @@ -32,6 +45,7 @@ export const PostThread = observer(function PostThread({ const pal = usePalette('default') const ref = useRef<FlatList>(null) const [isRefreshing, setIsRefreshing] = React.useState(false) + const navigation = useNavigation<NavigationProp>() const posts = React.useMemo(() => { if (view.thread) { return Array.from(flattenThread(view.thread)).concat([BOTTOM_BORDER]) @@ -41,6 +55,7 @@ export const PostThread = observer(function PostThread({ // events // = + const onRefresh = React.useCallback(async () => { setIsRefreshing(true) try { @@ -50,6 +65,7 @@ export const PostThread = observer(function PostThread({ } setIsRefreshing(false) }, [view, setIsRefreshing]) + const onLayout = React.useCallback(() => { const index = posts.findIndex(post => post._isHighlightedPost) if (index !== -1) { @@ -60,6 +76,7 @@ export const PostThread = observer(function PostThread({ }) } }, [posts, ref]) + const onScrollToIndexFailed = React.useCallback( (info: { index: number @@ -73,6 +90,15 @@ export const PostThread = observer(function PostThread({ }, [ref], ) + + const onPressBack = React.useCallback(() => { + if (navigation.canGoBack()) { + navigation.goBack() + } else { + navigation.navigate('Home') + } + }, [navigation]) + const renderItem = React.useCallback( ({item}: {item: YieldedItem}) => { if (item === REPLY_PROMPT) { @@ -104,6 +130,30 @@ export const PostThread = observer(function PostThread({ // error // = if (view.hasError) { + if (view.notFound) { + return ( + <CenteredView> + <View style={[pal.view, pal.border, styles.notFoundContainer]}> + <Text type="title-lg" style={[pal.text, s.mb5]}> + Post not found + </Text> + <Text type="md" style={[pal.text, s.mb10]}> + The post may have been deleted. + </Text> + <TouchableOpacity onPress={onPressBack}> + <Text type="2xl" style={pal.link}> + <FontAwesomeIcon + icon="angle-left" + style={[pal.link as FontAwesomeIconStyle, s.mr5]} + size={14} + /> + Back + </Text> + </TouchableOpacity> + </View> + </CenteredView> + ) + } return ( <CenteredView> <ErrorMessage message={view.error} onPressTryAgain={onRefresh} /> @@ -159,12 +209,18 @@ function* flattenThread( yield* flattenThread(reply as PostThreadViewPostModel) } } - } else if (!isAscending && !post.parent && post.post.replyCount > 0) { + } else if (!isAscending && !post.parent && post.post.replyCount) { post._hasMore = true } } const styles = StyleSheet.create({ + notFoundContainer: { + margin: 10, + paddingHorizontal: 18, + paddingVertical: 14, + borderRadius: 6, + }, bottomBorder: { borderBottomWidth: 1, }, diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 17c7943d9..cf2148060 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -19,7 +19,7 @@ import {ago} from 'lib/strings/time' import {pluralize} from 'lib/strings/helpers' import {useStores} from 'state/index' import {PostMeta} from '../util/PostMeta' -import {PostEmbeds} from '../util/PostEmbeds' +import {PostEmbeds} from '../util/post-embeds' import {PostCtrls} from '../util/PostCtrls' import {PostMutedWrapper} from '../util/PostMuted' import {ErrorMessage} from '../util/error/ErrorMessage' @@ -38,7 +38,7 @@ export const PostThreadItem = observer(function PostThreadItem({ const store = useStores() const [deleted, setDeleted] = React.useState(false) const record = item.postRecord - const hasEngagement = item.post.upvoteCount || item.post.repostCount + const hasEngagement = item.post.likeCount || item.post.repostCount const itemUri = item.post.uri const itemCid = item.post.cid @@ -49,11 +49,11 @@ export const PostThreadItem = observer(function PostThreadItem({ const itemTitle = `Post by ${item.post.author.handle}` const authorHref = `/profile/${item.post.author.handle}` const authorTitle = item.post.author.handle - const upvotesHref = React.useMemo(() => { + const likesHref = React.useMemo(() => { const urip = new AtUri(item.post.uri) - return `/profile/${item.post.author.handle}/post/${urip.rkey}/upvoted-by` + return `/profile/${item.post.author.handle}/post/${urip.rkey}/liked-by` }, [item.post.uri, item.post.author.handle]) - const upvotesTitle = 'Likes on this post' + const likesTitle = 'Likes on this post' const repostsHref = React.useMemo(() => { const urip = new AtUri(item.post.uri) return `/profile/${item.post.author.handle}/post/${urip.rkey}/reposted-by` @@ -80,10 +80,10 @@ export const PostThreadItem = observer(function PostThreadItem({ .toggleRepost() .catch(e => store.log.error('Failed to toggle repost', e)) }, [item, store]) - const onPressToggleUpvote = React.useCallback(() => { + const onPressToggleLike = React.useCallback(() => { return item - .toggleUpvote() - .catch(e => store.log.error('Failed to toggle upvote', e)) + .toggleLike() + .catch(e => store.log.error('Failed to toggle like', e)) }, [item, store]) const onCopyPostText = React.useCallback(() => { Clipboard.setString(record?.text || '') @@ -125,153 +125,151 @@ export const PostThreadItem = observer(function PostThreadItem({ if (item._isHighlightedPost) { return ( - <> - <View - style={[ - styles.outer, - styles.outerHighlighted, - {borderTopColor: pal.colors.border}, - pal.view, - ]}> - <View style={styles.layout}> - <View style={styles.layoutAvi}> - <Link href={authorHref} title={authorTitle} asAnchor> - <UserAvatar size={52} avatar={item.post.author.avatar} /> - </Link> - </View> - <View style={styles.layoutContent}> - <View style={[styles.meta, styles.metaExpandedLine1]}> - <View style={[s.flexRow, s.alignBaseline]}> - <Link - style={styles.metaItem} - href={authorHref} - title={authorTitle}> - <Text - type="xl-bold" - style={[pal.text]} - numberOfLines={1} - lineHeight={1.2}> - {item.post.author.displayName || item.post.author.handle} - </Text> - </Link> - <Text type="md" style={[styles.metaItem, pal.textLight]}> - · {ago(item.post.indexedAt)} - </Text> - </View> - <View style={s.flex1} /> - <PostDropdownBtn - style={styles.metaItem} - itemUri={itemUri} - itemCid={itemCid} - itemHref={itemHref} - itemTitle={itemTitle} - isAuthor={item.post.author.did === store.me.did} - onCopyPostText={onCopyPostText} - onOpenTranslate={onOpenTranslate} - onDeletePost={onDeletePost}> - <FontAwesomeIcon - icon="ellipsis-h" - size={14} - style={[s.mt2, s.mr5, pal.textLight]} - /> - </PostDropdownBtn> - </View> - <View style={styles.meta}> + <View + testID={`postThreadItem-by-${item.post.author.handle}`} + style={[ + styles.outer, + styles.outerHighlighted, + {borderTopColor: pal.colors.border}, + pal.view, + ]}> + <View style={styles.layout}> + <View style={styles.layoutAvi}> + <Link href={authorHref} title={authorTitle} asAnchor> + <UserAvatar size={52} avatar={item.post.author.avatar} /> + </Link> + </View> + <View style={styles.layoutContent}> + <View style={[styles.meta, styles.metaExpandedLine1]}> + <View style={[s.flexRow, s.alignBaseline]}> <Link style={styles.metaItem} href={authorHref} title={authorTitle}> - <Text type="md" style={[pal.textLight]} numberOfLines={1}> - @{item.post.author.handle} + <Text + type="xl-bold" + style={[pal.text]} + numberOfLines={1} + lineHeight={1.2}> + {item.post.author.displayName || item.post.author.handle} </Text> </Link> + <Text type="md" style={[styles.metaItem, pal.textLight]}> + · {ago(item.post.indexedAt)} + </Text> </View> - </View> - </View> - <View style={[s.pl10, s.pr10, s.pb10]}> - {item.richText?.text ? ( - <View - style={[ - styles.postTextContainer, - styles.postTextLargeContainer, - ]}> - <RichText - type="post-text-lg" - richText={item.richText} - lineHeight={1.3} - /> - </View> - ) : undefined} - <PostEmbeds embed={item.post.embed} style={s.mb10} /> - {item._isHighlightedPost && hasEngagement ? ( - <View style={[styles.expandedInfo, pal.border]}> - {item.post.repostCount ? ( - <Link - style={styles.expandedInfoItem} - href={repostsHref} - title={repostsTitle}> - <Text type="lg" style={pal.textLight}> - <Text type="xl-bold" style={pal.text}> - {item.post.repostCount} - </Text>{' '} - {pluralize(item.post.repostCount, 'repost')} - </Text> - </Link> - ) : ( - <></> - )} - {item.post.upvoteCount ? ( - <Link - style={styles.expandedInfoItem} - href={upvotesHref} - title={upvotesTitle}> - <Text type="lg" style={pal.textLight}> - <Text type="xl-bold" style={pal.text}> - {item.post.upvoteCount} - </Text>{' '} - {pluralize(item.post.upvoteCount, 'like')} - </Text> - </Link> - ) : ( - <></> - )} - </View> - ) : ( - <></> - )} - <View style={[s.pl10, s.pb5]}> - <PostCtrls - big + <View style={s.flex1} /> + <PostDropdownBtn + testID="postDropdownBtn" + style={styles.metaItem} itemUri={itemUri} itemCid={itemCid} itemHref={itemHref} itemTitle={itemTitle} - author={{ - avatar: item.post.author.avatar!, - handle: item.post.author.handle, - displayName: item.post.author.displayName!, - }} - text={item.richText?.text || record.text} - indexedAt={item.post.indexedAt} isAuthor={item.post.author.did === store.me.did} - isReposted={!!item.post.viewer.repost} - isUpvoted={!!item.post.viewer.upvote} - onPressReply={onPressReply} - onPressToggleRepost={onPressToggleRepost} - onPressToggleUpvote={onPressToggleUpvote} onCopyPostText={onCopyPostText} onOpenTranslate={onOpenTranslate} - onDeletePost={onDeletePost} + onDeletePost={onDeletePost}> + <FontAwesomeIcon + icon="ellipsis-h" + size={14} + style={[s.mt2, s.mr5, pal.textLight]} + /> + </PostDropdownBtn> + </View> + <View style={styles.meta}> + <Link + style={styles.metaItem} + href={authorHref} + title={authorTitle}> + <Text type="md" style={[pal.textLight]} numberOfLines={1}> + @{item.post.author.handle} + </Text> + </Link> + </View> + </View> + </View> + <View style={[s.pl10, s.pr10, s.pb10]}> + {item.richText?.text ? ( + <View + style={[styles.postTextContainer, styles.postTextLargeContainer]}> + <RichText + type="post-text-lg" + richText={item.richText} + lineHeight={1.3} /> </View> + ) : undefined} + <PostEmbeds embed={item.post.embed} style={s.mb10} /> + {item._isHighlightedPost && hasEngagement ? ( + <View style={[styles.expandedInfo, pal.border]}> + {item.post.repostCount ? ( + <Link + style={styles.expandedInfoItem} + href={repostsHref} + title={repostsTitle}> + <Text testID="repostCount" type="lg" style={pal.textLight}> + <Text type="xl-bold" style={pal.text}> + {item.post.repostCount} + </Text>{' '} + {pluralize(item.post.repostCount, 'repost')} + </Text> + </Link> + ) : ( + <></> + )} + {item.post.likeCount ? ( + <Link + style={styles.expandedInfoItem} + href={likesHref} + title={likesTitle}> + <Text testID="likeCount" type="lg" style={pal.textLight}> + <Text type="xl-bold" style={pal.text}> + {item.post.likeCount} + </Text>{' '} + {pluralize(item.post.likeCount, 'like')} + </Text> + </Link> + ) : ( + <></> + )} + </View> + ) : ( + <></> + )} + <View style={[s.pl10, s.pb5]}> + <PostCtrls + big + itemUri={itemUri} + itemCid={itemCid} + itemHref={itemHref} + itemTitle={itemTitle} + author={{ + avatar: item.post.author.avatar!, + handle: item.post.author.handle, + displayName: item.post.author.displayName!, + }} + text={item.richText?.text || record.text} + indexedAt={item.post.indexedAt} + isAuthor={item.post.author.did === store.me.did} + isReposted={!!item.post.viewer?.repost} + isLiked={!!item.post.viewer?.like} + onPressReply={onPressReply} + onPressToggleRepost={onPressToggleRepost} + onPressToggleLike={onPressToggleLike} + onCopyPostText={onCopyPostText} + onOpenTranslate={onOpenTranslate} + onDeletePost={onDeletePost} + /> </View> </View> - </> + </View> ) } else { return ( <PostMutedWrapper isMuted={item.post.author.viewer?.muted === true}> <Link + testID={`postThreadItem-by-${item.post.author.handle}`} style={[styles.outer, {borderTopColor: pal.colors.border}, pal.view]} href={itemHref} title={itemTitle} @@ -305,7 +303,6 @@ export const PostThreadItem = observer(function PostThreadItem({ timestamp={item.post.indexedAt} postHref={itemHref} did={item.post.author.did} - declarationCid={item.post.author.declaration.cid} /> {item.richText?.text ? ( <View style={styles.postTextContainer}> @@ -333,12 +330,12 @@ export const PostThreadItem = observer(function PostThreadItem({ isAuthor={item.post.author.did === store.me.did} replyCount={item.post.replyCount} repostCount={item.post.repostCount} - upvoteCount={item.post.upvoteCount} - isReposted={!!item.post.viewer.repost} - isUpvoted={!!item.post.viewer.upvote} + likeCount={item.post.likeCount} + isReposted={!!item.post.viewer?.repost} + isLiked={!!item.post.viewer?.like} onPressReply={onPressReply} onPressToggleRepost={onPressToggleRepost} - onPressToggleUpvote={onPressToggleUpvote} + onPressToggleLike={onPressToggleLike} onCopyPostText={onCopyPostText} onOpenTranslate={onOpenTranslate} onDeletePost={onDeletePost} |