diff options
author | Paul Frazee <pfrazee@gmail.com> | 2022-09-02 11:52:33 -0500 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2022-09-02 11:52:33 -0500 |
commit | 2f0939a1c298a9b7db17005677fc2819b5cb4095 (patch) | |
tree | 6458cba2905a0299bd1c6ba6201e49ed2490d26b /src | |
parent | 6835caa7609259a64ba0aa2c9ddb34b7d5eb7793 (diff) | |
download | voidsky-2f0939a1c298a9b7db17005677fc2819b5cb4095.tar.zst |
Implement consistent Link component
Diffstat (limited to 'src')
-rw-r--r-- | src/view/com/feed/FeedItem.tsx | 51 | ||||
-rw-r--r-- | src/view/com/notifications/FeedItem.tsx | 47 | ||||
-rw-r--r-- | src/view/com/post-thread/PostLikedBy.tsx | 9 | ||||
-rw-r--r-- | src/view/com/post-thread/PostRepostedBy.tsx | 9 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThreadItem.tsx | 120 | ||||
-rw-r--r-- | src/view/com/post/Post.tsx | 37 | ||||
-rw-r--r-- | src/view/com/profile/ProfileFollowers.tsx | 10 | ||||
-rw-r--r-- | src/view/com/profile/ProfileFollows.tsx | 9 | ||||
-rw-r--r-- | src/view/com/util/Link.tsx | 28 |
9 files changed, 177 insertions, 143 deletions
diff --git a/src/view/com/feed/FeedItem.tsx b/src/view/com/feed/FeedItem.tsx index 8c1e8dd21..0361925cb 100644 --- a/src/view/com/feed/FeedItem.tsx +++ b/src/view/com/feed/FeedItem.tsx @@ -1,9 +1,10 @@ -import React from 'react' +import React, {useMemo} from 'react' import {observer} from 'mobx-react-lite' import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native' import {bsky, AdxUri} from '@adxp/mock-api' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FeedViewItemModel} from '../../../state/models/feed-view' +import {Link} from '../util/Link' import {s, colors} from '../../lib/styles' import {ago} from '../../lib/strings' import {AVIS} from '../../lib/assets' @@ -18,14 +19,12 @@ export const FeedItem = observer(function FeedItem({ }) { const store = useStores() const record = item.record as unknown as bsky.Post.Record - - const onPressOuter = () => { + const postHref = useMemo(() => { const urip = new AdxUri(item.uri) - store.nav.navigate(`/profile/${item.author.name}/post/${urip.recordKey}`) - } - const onPressAuthor = () => { - store.nav.navigate(`/profile/${item.author.name}`) - } + return `/profile/${item.author.name}/post/${urip.recordKey}` + }, [item.uri, item.author.name]) + const authorHref = `/profile/${item.author.name}` + const onPressReply = () => { store.nav.navigate('/composer') } @@ -41,7 +40,10 @@ export const FeedItem = observer(function FeedItem({ } return ( - <TouchableOpacity style={styles.outer} onPress={onPressOuter}> + <Link + style={styles.outer} + href={postHref} + title={`Post by ${item.author.name}`}> {item.repostedBy && ( <View style={styles.repostedBy}> <FontAwesomeIcon icon="retweet" style={styles.repostedByIcon} /> @@ -51,24 +53,29 @@ export const FeedItem = observer(function FeedItem({ </View> )} <View style={styles.layout}> - <TouchableOpacity style={styles.layoutAvi} onPress={onPressAuthor}> + <Link + style={styles.layoutAvi} + href={authorHref} + title={item.author.name}> <Image style={styles.avi} source={AVIS[item.author.name] || AVIS['alice.com']} /> - </TouchableOpacity> + </Link> <View style={styles.layoutContent}> <View style={styles.meta}> - <Text - style={[styles.metaItem, s.f15, s.bold]} - onPress={onPressAuthor}> - {item.author.displayName} - </Text> - <Text - style={[styles.metaItem, s.f14, s.gray5]} - onPress={onPressAuthor}> - @{item.author.name} - </Text> + <Link + style={styles.metaItem} + href={authorHref} + title={item.author.name}> + <Text style={[s.f15, s.bold]}>{item.author.displayName}</Text> + </Link> + <Link + style={styles.metaItem} + href={authorHref} + title={item.author.name}> + <Text style={[s.f14, s.gray5]}>@{item.author.name}</Text> + </Link> <Text style={[styles.metaItem, s.f14, s.gray5]}> · {ago(item.indexedAt)} </Text> @@ -127,7 +134,7 @@ export const FeedItem = observer(function FeedItem({ </View> </View> </View> - </TouchableOpacity> + </Link> ) }) diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx index 6a418872d..931337926 100644 --- a/src/view/com/notifications/FeedItem.tsx +++ b/src/view/com/notifications/FeedItem.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, {useMemo} from 'react' import {observer} from 'mobx-react-lite' import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native' import {AdxUri} from '@adxp/mock-api' @@ -9,29 +9,36 @@ import {ago} from '../../lib/strings' import {AVIS} from '../../lib/assets' import {PostText} from '../post/PostText' import {Post} from '../post/Post' -import {useStores} from '../../../state' +import {Link} from '../util/Link' export const FeedItem = observer(function FeedItem({ item, }: { item: NotificationsViewItemModel }) { - const store = useStores() - - const onPressOuter = () => { + const itemHref = useMemo(() => { if (item.isLike || item.isRepost) { const urip = new AdxUri(item.subjectUri) - store.nav.navigate(`/profile/${urip.host}/post/${urip.recordKey}`) + return `/profile/${urip.host}/post/${urip.recordKey}` } else if (item.isFollow) { - store.nav.navigate(`/profile/${item.author.name}`) + return `/profile/${item.author.name}` } else if (item.isReply) { const urip = new AdxUri(item.uri) - store.nav.navigate(`/profile/${urip.host}/post/${urip.recordKey}`) + return `/profile/${urip.host}/post/${urip.recordKey}` } - } - const onPressAuthor = () => { - store.nav.navigate(`/profile/${item.author.name}`) - } + return '' + }, [item]) + const itemTitle = useMemo(() => { + if (item.isLike || item.isRepost) { + return 'Post' + } else if (item.isFollow) { + return item.author.name + } else if (item.isReply) { + return 'Post' + } + }, [item]) + const authorHref = `/profile/${item.author.name}` + const authorTitle = item.author.name let action = '' let icon: Props['icon'] @@ -52,22 +59,20 @@ export const FeedItem = observer(function FeedItem({ } return ( - <TouchableOpacity style={styles.outer} onPress={onPressOuter}> + <Link style={styles.outer} href={itemHref} title={itemTitle}> <View style={styles.layout}> - <TouchableOpacity style={styles.layoutAvi} onPress={onPressAuthor}> + <Link style={styles.layoutAvi} href={authorHref} title={authorTitle}> <Image style={styles.avi} source={AVIS[item.author.name] || AVIS['alice.com']} /> - </TouchableOpacity> + </Link> <View style={styles.layoutContent}> <View style={styles.meta}> <FontAwesomeIcon icon={icon} size={14} style={[s.mt2, s.mr5]} /> - <Text - style={[styles.metaItem, s.f14, s.bold]} - onPress={onPressAuthor}> - {item.author.displayName} - </Text> + <Link style={styles.metaItem} href={authorHref} title={authorTitle}> + <Text style={[s.f14, s.bold]}>{item.author.displayName}</Text> + </Link> <Text style={[styles.metaItem, s.f14]}>{action}</Text> <Text style={[styles.metaItem, s.f14, s.gray5]}> {ago(item.indexedAt)} @@ -87,7 +92,7 @@ export const FeedItem = observer(function FeedItem({ ) : ( <></> )} - </TouchableOpacity> + </Link> ) }) diff --git a/src/view/com/post-thread/PostLikedBy.tsx b/src/view/com/post-thread/PostLikedBy.tsx index 77224640c..e928f3591 100644 --- a/src/view/com/post-thread/PostLikedBy.tsx +++ b/src/view/com/post-thread/PostLikedBy.tsx @@ -13,6 +13,7 @@ import { LikedByViewModel, LikedByViewItemModel, } from '../../../state/models/liked-by-view' +import {Link} from '../util/Link' import {useStores} from '../../../state' import {s, colors} from '../../lib/styles' import {AVIS} from '../../lib/assets' @@ -73,12 +74,8 @@ export const PostLikedBy = observer(function PostLikedBy({uri}: {uri: string}) { }) const LikedByItem = ({item}: {item: LikedByViewItemModel}) => { - const store = useStores() - const onPressOuter = () => { - store.nav.navigate(`/profile/${item.name}`) - } return ( - <TouchableOpacity style={styles.outer} onPress={onPressOuter}> + <Link style={styles.outer} href={`/profile/${item.name}`} title={item.name}> <View style={styles.layout}> <View style={styles.layoutAvi}> <Image @@ -91,7 +88,7 @@ const LikedByItem = ({item}: {item: LikedByViewItemModel}) => { <Text style={[s.f14, s.gray5]}>@{item.name}</Text> </View> </View> - </TouchableOpacity> + </Link> ) } diff --git a/src/view/com/post-thread/PostRepostedBy.tsx b/src/view/com/post-thread/PostRepostedBy.tsx index b298a2484..1c4e42a8e 100644 --- a/src/view/com/post-thread/PostRepostedBy.tsx +++ b/src/view/com/post-thread/PostRepostedBy.tsx @@ -13,6 +13,7 @@ import { RepostedByViewModel, RepostedByViewItemModel, } from '../../../state/models/reposted-by-view' +import {Link} from '../util/Link' import {useStores} from '../../../state' import {s, colors} from '../../lib/styles' import {AVIS} from '../../lib/assets' @@ -79,12 +80,8 @@ export const PostRepostedBy = observer(function PostRepostedBy({ }) const RepostedByItem = ({item}: {item: RepostedByViewItemModel}) => { - const store = useStores() - const onPressOuter = () => { - store.nav.navigate(`/profile/${item.name}`) - } return ( - <TouchableOpacity style={styles.outer} onPress={onPressOuter}> + <Link style={styles.outer} href={`/profile/${item.name}`} title={item.name}> <View style={styles.layout}> <View style={styles.layoutAvi}> <Image @@ -97,7 +94,7 @@ const RepostedByItem = ({item}: {item: RepostedByViewItemModel}) => { <Text style={[s.f14, s.gray5]}>@{item.name}</Text> </View> </View> - </TouchableOpacity> + </Link> ) } diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 376ec6805..5909cac8a 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -1,9 +1,10 @@ -import React from 'react' +import React, {useMemo} from 'react' import {observer} from 'mobx-react-lite' import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native' import {bsky, AdxUri} from '@adxp/mock-api' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {PostThreadViewPostModel} from '../../../state/models/post-thread-view' +import {Link} from '../util/Link' import {s, colors} from '../../lib/styles' import {ago, pluralize} from '../../lib/strings' import {AVIS} from '../../lib/assets' @@ -20,25 +21,24 @@ export const PostThreadItem = observer(function PostThreadItem({ const record = item.record as unknown as bsky.Post.Record const hasEngagement = item.likeCount || item.repostCount - const onPressOuter = () => { + const itemHref = useMemo(() => { const urip = new AdxUri(item.uri) - store.nav.navigate(`/profile/${item.author.name}/post/${urip.recordKey}`) - } - const onPressAuthor = () => { - store.nav.navigate(`/profile/${item.author.name}`) - } - const onPressLikes = () => { + return `/profile/${item.author.name}/post/${urip.recordKey}` + }, [item.uri, item.author.name]) + const itemTitle = `Post by ${item.author.name}` + const authorHref = `/profile/${item.author.name}` + const authorTitle = item.author.name + const likesHref = useMemo(() => { const urip = new AdxUri(item.uri) - store.nav.navigate( - `/profile/${item.author.name}/post/${urip.recordKey}/liked-by`, - ) - } - const onPressReposts = () => { + return `/profile/${item.author.name}/post/${urip.recordKey}/liked-by` + }, [item.uri, item.author.name]) + const likesTitle = 'Likes on this post' + const repostsHref = useMemo(() => { const urip = new AdxUri(item.uri) - store.nav.navigate( - `/profile/${item.author.name}/post/${urip.recordKey}/reposted-by`, - ) - } + return `/profile/${item.author.name}/post/${urip.recordKey}/reposted-by` + }, [item.uri, item.author.name]) + const repostsTitle = 'Reposts of this post' + const onPressReply = () => { store.nav.navigate(`/composer?replyTo=${item.uri}`) } @@ -102,29 +102,31 @@ export const PostThreadItem = observer(function PostThreadItem({ return ( <View style={styles.outer}> <View style={styles.layout}> - <TouchableOpacity style={styles.layoutAvi} onPress={onPressAuthor}> + <Link style={styles.layoutAvi} href={authorHref} title={authorTitle}> <Image style={styles.avi} source={AVIS[item.author.name] || AVIS['alice.com']} /> - </TouchableOpacity> + </Link> <View style={styles.layoutContent}> <View style={[styles.meta, s.mt5]}> - <Text - style={[styles.metaItem, s.f15, s.bold]} - onPress={onPressAuthor}> - {item.author.displayName} - </Text> + <Link + style={styles.metaItem} + href={authorHref} + title={authorTitle}> + <Text style={[s.f15, s.bold]}>{item.author.displayName}</Text> + </Link> <Text style={[styles.metaItem, s.f14, s.gray5]}> · {ago(item.indexedAt)} </Text> </View> <View style={styles.meta}> - <Text - style={[styles.metaItem, s.f14, s.gray5]} - onPress={onPressAuthor}> - @{item.author.name} - </Text> + <Link + style={styles.metaItem} + href={authorHref} + title={authorTitle}> + <Text style={[s.f14, s.gray5]}>@{item.author.name}</Text> + </Link> </View> </View> </View> @@ -135,22 +137,28 @@ export const PostThreadItem = observer(function PostThreadItem({ {item._isHighlightedPost && hasEngagement ? ( <View style={styles.expandedInfo}> {item.repostCount ? ( - <Text - style={[styles.expandedInfoItem, s.gray5, s.semiBold]} - onPress={onPressReposts}> - <Text style={[s.bold, s.black]}>{item.repostCount}</Text>{' '} - {pluralize(item.repostCount, 'repost')} - </Text> + <Link + style={styles.expandedInfoItem} + href={repostsHref} + title={repostsTitle}> + <Text style={[s.gray5, s.semiBold]}> + <Text style={[s.bold, s.black]}>{item.repostCount}</Text>{' '} + {pluralize(item.repostCount, 'repost')} + </Text> + </Link> ) : ( <></> )} {item.likeCount ? ( - <Text - style={[styles.expandedInfoItem, s.gray5, s.semiBold]} - onPress={onPressLikes}> - <Text style={[s.bold, s.black]}>{item.likeCount}</Text>{' '} - {pluralize(item.likeCount, 'like')} - </Text> + <Link + style={styles.expandedInfoItem} + href={likesHref} + title={likesTitle}> + <Text style={[s.gray5, s.semiBold]}> + <Text style={[s.bold, s.black]}>{item.likeCount}</Text>{' '} + {pluralize(item.likeCount, 'like')} + </Text> + </Link> ) : ( <></> )} @@ -166,26 +174,28 @@ export const PostThreadItem = observer(function PostThreadItem({ ) } else { return ( - <TouchableOpacity style={styles.outer} onPress={onPressOuter}> + <Link style={styles.outer} href={itemHref} title={itemTitle}> <View style={styles.layout}> - <TouchableOpacity style={styles.layoutAvi} onPress={onPressAuthor}> + <Link style={styles.layoutAvi} href={authorHref} title={authorTitle}> <Image style={styles.avi} source={AVIS[item.author.name] || AVIS['alice.com']} /> - </TouchableOpacity> + </Link> <View style={styles.layoutContent}> <View style={styles.meta}> - <Text - style={[styles.metaItem, s.f15, s.bold]} - onPress={onPressAuthor}> - {item.author.displayName} - </Text> - <Text - style={[styles.metaItem, s.f14, s.gray5]} - onPress={onPressAuthor}> - @{item.author.name} - </Text> + <Link + style={styles.metaItem} + href={authorHref} + title={authorTitle}> + <Text style={[s.f15, s.bold]}>{item.author.displayName}</Text> + </Link> + <Link + style={styles.metaItem} + href={authorHref} + title={authorTitle}> + <Text style={[s.f14, s.gray5]}>@{item.author.name}</Text> + </Link> <Text style={[styles.metaItem, s.f14, s.gray5]}> · {ago(item.indexedAt)} </Text> @@ -196,7 +206,7 @@ export const PostThreadItem = observer(function PostThreadItem({ <Ctrls /> </View> </View> - </TouchableOpacity> + </Link> ) } }) diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx index 0b8ba457e..2de7432bd 100644 --- a/src/view/com/post/Post.tsx +++ b/src/view/com/post/Post.tsx @@ -11,6 +11,7 @@ import { } from 'react-native' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {PostThreadViewModel} from '../../../state/models/post-thread-view' +import {Link} from '../util/Link' import {useStores} from '../../../state' import {s, colors} from '../../lib/styles' import {ago} from '../../lib/strings' @@ -54,13 +55,13 @@ export const Post = observer(function Post({uri}: {uri: string}) { const item = view.thread const record = view.thread?.record as unknown as bsky.Post.Record - const onPressOuter = () => { + const itemHref = useMemo(() => { const urip = new AdxUri(item.uri) - store.nav.navigate(`/profile/${item.author.name}/post/${urip.recordKey}`) - } - const onPressAuthor = () => { - store.nav.navigate(`/profile/${item.author.name}`) - } + return `/profile/${item.author.name}/post/${urip.recordKey}` + }, [item.uri, item.author.name]) + const itemTitle = `Post by ${item.author.name}` + const authorHref = `/profile/${item.author.name}` + const authorTitle = item.author.name const onPressReply = () => { store.nav.navigate(`/composer?replyTo=${item.uri}`) } @@ -76,26 +77,22 @@ export const Post = observer(function Post({uri}: {uri: string}) { } return ( - <TouchableOpacity style={styles.outer} onPress={onPressOuter}> + <Link style={styles.outer} href={itemHref} title={itemTitle}> <View style={styles.layout}> - <TouchableOpacity style={styles.layoutAvi} onPress={onPressAuthor}> + <Link style={styles.layoutAvi} href={authorHref} title={authorTitle}> <Image style={styles.avi} source={AVIS[item.author.name] || AVIS['alice.com']} /> - </TouchableOpacity> + </Link> <View style={styles.layoutContent}> <View style={styles.meta}> - <Text - style={[styles.metaItem, s.f15, s.bold]} - onPress={onPressAuthor}> - {item.author.displayName} - </Text> - <Text - style={[styles.metaItem, s.f14, s.gray5]} - onPress={onPressAuthor}> - @{item.author.name} - </Text> + <Link style={styles.metaItem} href={authorHref} title={authorTitle}> + <Text style={[s.f15, s.bold]}>{item.author.displayName}</Text> + </Link> + <Link style={styles.metaItem} href={authorHref} title={authorTitle}> + <Text style={[s.f14, s.gray5]}>@{item.author.name}</Text> + </Link> <Text style={[styles.metaItem, s.f14, s.gray5]}> · {ago(item.indexedAt)} </Text> @@ -149,7 +146,7 @@ export const Post = observer(function Post({uri}: {uri: string}) { </View> </View> </View> - </TouchableOpacity> + </Link> ) }) diff --git a/src/view/com/profile/ProfileFollowers.tsx b/src/view/com/profile/ProfileFollowers.tsx index 9b0d57e7d..20a2ab913 100644 --- a/src/view/com/profile/ProfileFollowers.tsx +++ b/src/view/com/profile/ProfileFollowers.tsx @@ -6,13 +6,13 @@ import { Image, StyleSheet, Text, - TouchableOpacity, View, } from 'react-native' import { UserFollowersViewModel, FollowerItem, } from '../../../state/models/user-followers-view' +import {Link} from '../util/Link' import {useStores} from '../../../state' import {s, colors} from '../../lib/styles' import {AVIS} from '../../lib/assets' @@ -77,12 +77,8 @@ export const ProfileFollowers = observer(function ProfileFollowers({ }) const User = ({item}: {item: FollowerItem}) => { - const store = useStores() - const onPressOuter = () => { - store.nav.navigate(`/profile/${item.name}`) - } return ( - <TouchableOpacity style={styles.outer} onPress={onPressOuter}> + <Link style={styles.outer} href={`/profile/${item.name}`} title={item.name}> <View style={styles.layout}> <View style={styles.layoutAvi}> <Image @@ -95,7 +91,7 @@ const User = ({item}: {item: FollowerItem}) => { <Text style={[s.f14, s.gray5]}>@{item.name}</Text> </View> </View> - </TouchableOpacity> + </Link> ) } diff --git a/src/view/com/profile/ProfileFollows.tsx b/src/view/com/profile/ProfileFollows.tsx index ed015430d..6517c4596 100644 --- a/src/view/com/profile/ProfileFollows.tsx +++ b/src/view/com/profile/ProfileFollows.tsx @@ -14,6 +14,7 @@ import { FollowItem, } from '../../../state/models/user-follows-view' import {useStores} from '../../../state' +import {Link} from '../util/Link' import {s, colors} from '../../lib/styles' import {AVIS} from '../../lib/assets' @@ -77,12 +78,8 @@ export const ProfileFollows = observer(function ProfileFollows({ }) const User = ({item}: {item: FollowItem}) => { - const store = useStores() - const onPressOuter = () => { - store.nav.navigate(`/profile/${item.name}`) - } return ( - <TouchableOpacity style={styles.outer} onPress={onPressOuter}> + <Link style={styles.outer} href={`/profile/${item.name}`} title={item.name}> <View style={styles.layout}> <View style={styles.layoutAvi}> <Image @@ -95,7 +92,7 @@ const User = ({item}: {item: FollowItem}) => { <Text style={[s.f14, s.gray5]}>@{item.name}</Text> </View> </View> - </TouchableOpacity> + </Link> ) } diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx new file mode 100644 index 000000000..dcb8710f6 --- /dev/null +++ b/src/view/com/util/Link.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import {observer} from 'mobx-react-lite' +import {StyleProp, Text, TouchableOpacity, ViewStyle} from 'react-native' +import {useStores} from '../../../state' +import {LinkActionsModel} from '../../../state/models/shell' + +export const Link = observer(function Link({ + style, + href, + title, + children, +}: { + style?: StyleProp<ViewStyle> + href: string + title?: string + children?: React.ReactNode +}) { + const store = useStores() + const onPress = () => store.nav.navigate(href) + const onLongPress = () => { + store.shell.openModal(new LinkActionsModel(href, title || href)) + } + return ( + <TouchableOpacity style={style} onPress={onPress} onLongPress={onLongPress}> + {children ? children : <Text>{title || 'link'}</Text>} + </TouchableOpacity> + ) +}) |