diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-07-06 21:12:54 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-06 21:12:54 -0500 |
commit | 6f69157269b27c4bae9730334f93f295ef0d4b94 (patch) | |
tree | f4a6a96cbfd959399a9b71cd116e9cbcfb26393e | |
parent | df7552135a50d715a50ab592874eb84fc7c8bbcf (diff) | |
download | voidsky-6f69157269b27c4bae9730334f93f295ef0d4b94.tar.zst |
Post UI updates (Profile Preview on mobile) (#990)
* Update postmeta to put the timestamp on the right side on mobile * Drop the two-line PostMeta mode * Add ProfilePreview modal * Tune PostMeta to give the best behavior possible for a given platform * Remove old showFollowBtn attributes * Fix style issue * Switch the follow button in the profile header to use the inverted color for consistency with the rest of the app * Fix lint * Fix darkmode * Tune the profile preview footer * Better analytics choice
-rw-r--r-- | src/lib/analytics/types.ts | 1 | ||||
-rw-r--r-- | src/state/models/ui/shell.ts | 6 | ||||
-rw-r--r-- | src/view/com/modals/Modal.tsx | 4 | ||||
-rw-r--r-- | src/view/com/modals/Modal.web.tsx | 3 | ||||
-rw-r--r-- | src/view/com/modals/ProfilePreview.tsx | 89 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThreadItem.tsx | 38 | ||||
-rw-r--r-- | src/view/com/post/Post.tsx | 1 | ||||
-rw-r--r-- | src/view/com/posts/Feed.tsx | 12 | ||||
-rw-r--r-- | src/view/com/posts/FeedItem.tsx | 23 | ||||
-rw-r--r-- | src/view/com/posts/FeedSlice.tsx | 6 | ||||
-rw-r--r-- | src/view/com/posts/MultiFeed.tsx | 8 | ||||
-rw-r--r-- | src/view/com/profile/ProfileHeader.tsx | 13 | ||||
-rw-r--r-- | src/view/com/util/Link.tsx | 26 | ||||
-rw-r--r-- | src/view/com/util/PostMeta.tsx | 119 | ||||
-rw-r--r-- | src/view/com/util/UserAvatar.tsx | 54 | ||||
-rw-r--r-- | src/view/screens/Feeds.tsx | 1 | ||||
-rw-r--r-- | src/view/screens/Home.tsx | 1 |
17 files changed, 215 insertions, 190 deletions
diff --git a/src/lib/analytics/types.ts b/src/lib/analytics/types.ts index 7caa9b357..585884632 100644 --- a/src/lib/analytics/types.ts +++ b/src/lib/analytics/types.ts @@ -129,6 +129,7 @@ interface ScreenPropertiesMap { Feed: {} Notifications: {} Profile: {} + 'Profile:Preview': {} Settings: {} AppPasswords: {} Moderation: {} diff --git a/src/state/models/ui/shell.ts b/src/state/models/ui/shell.ts index ba03fe1b5..c6e7289df 100644 --- a/src/state/models/ui/shell.ts +++ b/src/state/models/ui/shell.ts @@ -31,6 +31,11 @@ export interface EditProfileModal { onUpdate?: () => void } +export interface ProfilePreviewModal { + name: 'profile-preview' + did: string +} + export interface ServerInputModal { name: 'server-input' initialService: string @@ -128,6 +133,7 @@ export type Modal = | ChangeHandleModal | DeleteAccountModal | EditProfileModal + | ProfilePreviewModal // Curation | ContentFilteringSettingsModal diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx index b276dabc0..ad8794e89 100644 --- a/src/view/com/modals/Modal.tsx +++ b/src/view/com/modals/Modal.tsx @@ -9,6 +9,7 @@ import {usePalette} from 'lib/hooks/usePalette' import * as ConfirmModal from './Confirm' import * as EditProfileModal from './EditProfile' +import * as ProfilePreviewModal from './ProfilePreview' import * as ServerInputModal from './ServerInput' import * as ReportPostModal from './report/ReportPost' import * as RepostModal from './Repost' @@ -62,6 +63,9 @@ export const ModalsContainer = observer(function ModalsContainer() { } else if (activeModal?.name === 'edit-profile') { snapPoints = EditProfileModal.snapPoints element = <EditProfileModal.Component {...activeModal} /> + } else if (activeModal?.name === 'profile-preview') { + snapPoints = ProfilePreviewModal.snapPoints + element = <ProfilePreviewModal.Component {...activeModal} /> } else if (activeModal?.name === 'server-input') { snapPoints = ServerInputModal.snapPoints element = <ServerInputModal.Component {...activeModal} /> diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx index 77842d3e1..20312fe6b 100644 --- a/src/view/com/modals/Modal.web.tsx +++ b/src/view/com/modals/Modal.web.tsx @@ -8,6 +8,7 @@ import {isMobileWeb} from 'platform/detection' import * as ConfirmModal from './Confirm' import * as EditProfileModal from './EditProfile' +import * as ProfilePreviewModal from './ProfilePreview' import * as ServerInputModal from './ServerInput' import * as ReportPostModal from './report/ReportPost' import * as ReportAccountModal from './report/ReportAccount' @@ -68,6 +69,8 @@ function Modal({modal}: {modal: ModalIface}) { element = <ConfirmModal.Component {...modal} /> } else if (modal.name === 'edit-profile') { element = <EditProfileModal.Component {...modal} /> + } else if (modal.name === 'profile-preview') { + element = <ProfilePreviewModal.Component {...modal} /> } else if (modal.name === 'server-input') { element = <ServerInputModal.Component {...modal} /> } else if (modal.name === 'report-post') { diff --git a/src/view/com/modals/ProfilePreview.tsx b/src/view/com/modals/ProfilePreview.tsx new file mode 100644 index 000000000..d3267644b --- /dev/null +++ b/src/view/com/modals/ProfilePreview.tsx @@ -0,0 +1,89 @@ +import React, {useState, useEffect, useCallback} from 'react' +import {StyleSheet, View} from 'react-native' +import {observer} from 'mobx-react-lite' +import {useNavigation, StackActions} from '@react-navigation/native' +import {Text} from '../util/text/Text' +import {useStores} from 'state/index' +import {ProfileModel} from 'state/models/content/profile' +import {usePalette} from 'lib/hooks/usePalette' +import {useAnalytics} from 'lib/analytics/analytics' +import {ProfileHeader} from '../profile/ProfileHeader' +import {Button} from '../util/forms/Button' +import {NavigationProp} from 'lib/routes/types' + +export const snapPoints = [560] + +export const Component = observer(({did}: {did: string}) => { + const store = useStores() + const pal = usePalette('default') + const palInverted = usePalette('inverted') + const navigation = useNavigation<NavigationProp>() + const [model] = useState(new ProfileModel(store, {actor: did})) + const {screen} = useAnalytics() + + useEffect(() => { + screen('Profile:Preview') + model.setup() + }, [model, screen]) + + const onPressViewProfile = useCallback(() => { + navigation.dispatch(StackActions.push('Profile', {name: model.handle})) + store.shell.closeModal() + }, [navigation, store, model]) + + return ( + <View style={pal.view}> + <View style={styles.headerWrapper}> + <ProfileHeader view={model} hideBackButton onRefreshAll={() => {}} /> + </View> + <View style={[styles.buttonsContainer, pal.view]}> + <View style={styles.buttons}> + <Button + type="inverted" + style={[styles.button, styles.buttonWide]} + onPress={onPressViewProfile} + accessibilityLabel="View profile" + accessibilityHint=""> + <Text type="button-lg" style={palInverted.text}> + View Profile + </Text> + </Button> + <Button + type="default" + style={styles.button} + onPress={() => store.shell.closeModal()} + accessibilityLabel="Close this preview" + accessibilityHint=""> + <Text type="button-lg" style={pal.text}> + Close + </Text> + </Button> + </View> + </View> + </View> + ) +}) + +const styles = StyleSheet.create({ + headerWrapper: { + height: 440, + }, + buttonsContainer: { + height: 120, + }, + buttons: { + flexDirection: 'row', + gap: 8, + paddingHorizontal: 14, + paddingTop: 16, + }, + button: { + flex: 2, + flexDirection: 'row', + justifyContent: 'center', + paddingVertical: 12, + }, + buttonWide: { + flex: 3, + }, +}) diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index e1c73c0d5..133d38421 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -13,7 +13,7 @@ import {RichText} from '../util/text/RichText' import {Text} from '../util/text/Text' import {PostDropdownBtn} from '../util/forms/DropdownButton' import * as Toast from '../util/Toast' -import {UserAvatar} from '../util/UserAvatar' +import {PreviewableUserAvatar} from '../util/UserAvatar' import {s} from 'lib/styles' import {ago, niceDate} from 'lib/strings/time' import {sanitizeDisplayName} from 'lib/strings/display-names' @@ -163,22 +163,17 @@ export const PostThreadItem = observer(function PostThreadItem({ <PostSandboxWarning /> <View style={styles.layout}> <View style={styles.layoutAvi}> - <Link - href={authorHref} - title={authorTitle} - asAnchor - accessibilityLabel={`${item.post.author.handle}'s avatar`} - accessibilityHint=""> - <UserAvatar - size={52} - avatar={item.post.author.avatar} - moderation={item.moderation.avatar} - /> - </Link> + <PreviewableUserAvatar + size={52} + did={item.post.author.did} + handle={item.post.author.handle} + avatar={item.post.author.avatar} + moderation={item.moderation.avatar} + /> </View> <View style={styles.layoutContent}> <View style={[styles.meta, styles.metaExpandedLine1]}> - <View style={[s.flexRow, s.alignBaseline]}> + <View style={[s.flexRow]}> <Link style={styles.metaItem} href={authorHref} @@ -353,13 +348,13 @@ export const PostThreadItem = observer(function PostThreadItem({ <PostSandboxWarning /> <View style={styles.layout}> <View style={styles.layoutAvi}> - <Link href={authorHref} title={authorTitle} asAnchor> - <UserAvatar - size={52} - avatar={item.post.author.avatar} - moderation={item.moderation.avatar} - /> - </Link> + <PreviewableUserAvatar + size={52} + did={item.post.author.did} + handle={item.post.author.handle} + avatar={item.post.author.avatar} + moderation={item.moderation.avatar} + /> </View> <View style={styles.layoutContent}> <PostMeta @@ -368,7 +363,6 @@ export const PostThreadItem = observer(function PostThreadItem({ authorHasWarning={!!item.post.author.labels?.length} timestamp={item.post.indexedAt} postHref={itemHref} - did={item.post.author.did} /> <ContentHider moderation={item.moderation.thread} diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx index c380c9743..34154e7ed 100644 --- a/src/view/com/post/Post.tsx +++ b/src/view/com/post/Post.tsx @@ -229,7 +229,6 @@ const PostLoaded = observer( authorHasWarning={!!item.post.author.labels?.length} timestamp={item.post.indexedAt} postHref={itemHref} - did={item.post.author.did} /> {replyAuthorDid !== '' && ( <View style={[s.flexRow, s.mb2, s.alignCenter]}> diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index 921f23190..5035d345d 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -28,7 +28,6 @@ const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} export const Feed = observer(function Feed({ feed, style, - showPostFollowBtn, scrollElRef, onPressTryAgain, onScroll, @@ -41,7 +40,6 @@ export const Feed = observer(function Feed({ }: { feed: PostsFeedModel style?: StyleProp<ViewStyle> - showPostFollowBtn?: boolean scrollElRef?: MutableRefObject<FlatList<any> | null> onPressTryAgain?: () => void onScroll?: OnScrollCb @@ -138,15 +136,9 @@ export const Feed = observer(function Feed({ } else if (item === LOADING_ITEM) { return <PostFeedLoadingPlaceholder /> } - return <FeedSlice slice={item} showFollowBtn={showPostFollowBtn} /> + return <FeedSlice slice={item} /> }, - [ - feed, - onPressTryAgain, - onPressRetryLoadMore, - showPostFollowBtn, - renderEmptyState, - ], + [feed, onPressTryAgain, onPressRetryLoadMore, renderEmptyState], ) const FeedFooter = React.useCallback( diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx index 6ec2c80f4..e1b160dcb 100644 --- a/src/view/com/posts/FeedItem.tsx +++ b/src/view/com/posts/FeedItem.tsx @@ -21,7 +21,7 @@ import {ImageHider} from '../util/moderation/ImageHider' import {RichText} from '../util/text/RichText' import {PostSandboxWarning} from '../util/PostSandboxWarning' import * as Toast from '../util/Toast' -import {UserAvatar} from '../util/UserAvatar' +import {PreviewableUserAvatar} from '../util/UserAvatar' import {s} from 'lib/styles' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' @@ -33,14 +33,12 @@ export const FeedItem = observer(function ({ item, isThreadChild, isThreadParent, - showFollowBtn, ignoreMuteFor, }: { item: PostsFeedItemModel isThreadChild?: boolean isThreadParent?: boolean showReplyLine?: boolean - showFollowBtn?: boolean ignoreMuteFor?: string }) { const store = useStores() @@ -55,7 +53,6 @@ export const FeedItem = observer(function ({ return `/profile/${item.post.author.handle}/post/${urip.rkey}` }, [item.post.uri, item.post.author.handle]) const itemTitle = `Post by ${item.post.author.handle}` - const authorHref = `/profile/${item.post.author.handle}` const replyAuthorDid = useMemo(() => { if (!record?.reply) { return '' @@ -214,13 +211,13 @@ export const FeedItem = observer(function ({ <PostSandboxWarning /> <View style={styles.layout}> <View style={styles.layoutAvi}> - <Link href={authorHref} title={item.post.author.handle} asAnchor> - <UserAvatar - size={52} - avatar={item.post.author.avatar} - moderation={item.moderation.avatar} - /> - </Link> + <PreviewableUserAvatar + size={52} + did={item.post.author.did} + handle={item.post.author.handle} + avatar={item.post.author.avatar} + moderation={item.moderation.avatar} + /> </View> <View style={styles.layoutContent}> <PostMeta @@ -229,8 +226,6 @@ export const FeedItem = observer(function ({ authorHasWarning={!!item.post.author.labels?.length} timestamp={item.post.indexedAt} postHref={itemHref} - did={item.post.author.did} - showFollowBtn={showFollowBtn} /> {!isThreadChild && replyAuthorDid !== '' && ( <View style={[s.flexRow, s.mb2, s.alignCenter]}> @@ -357,9 +352,9 @@ const styles = StyleSheet.create({ layout: { flexDirection: 'row', marginTop: 1, + gap: 10, }, layoutAvi: { - width: 70, paddingLeft: 8, }, layoutContent: { diff --git a/src/view/com/posts/FeedSlice.tsx b/src/view/com/posts/FeedSlice.tsx index d75ff1385..8ac813b92 100644 --- a/src/view/com/posts/FeedSlice.tsx +++ b/src/view/com/posts/FeedSlice.tsx @@ -11,11 +11,9 @@ import {ModerationBehaviorCode} from 'lib/labeling/types' export function FeedSlice({ slice, - showFollowBtn, ignoreMuteFor, }: { slice: PostsFeedSliceModel - showFollowBtn?: boolean ignoreMuteFor?: string }) { if (slice.moderation.list.behavior === ModerationBehaviorCode.Hide) { @@ -32,7 +30,6 @@ export function FeedSlice({ item={slice.items[0]} isThreadParent={slice.isThreadParentAt(0)} isThreadChild={slice.isThreadChildAt(0)} - showFollowBtn={showFollowBtn} ignoreMuteFor={ignoreMuteFor} /> <FeedItem @@ -40,7 +37,6 @@ export function FeedSlice({ item={slice.items[1]} isThreadParent={slice.isThreadParentAt(1)} isThreadChild={slice.isThreadChildAt(1)} - showFollowBtn={showFollowBtn} ignoreMuteFor={ignoreMuteFor} /> <ViewFullThread slice={slice} /> @@ -49,7 +45,6 @@ export function FeedSlice({ item={slice.items[last]} isThreadParent={slice.isThreadParentAt(last)} isThreadChild={slice.isThreadChildAt(last)} - showFollowBtn={showFollowBtn} ignoreMuteFor={ignoreMuteFor} /> </> @@ -64,7 +59,6 @@ export function FeedSlice({ item={item} isThreadParent={slice.isThreadParentAt(i)} isThreadChild={slice.isThreadChildAt(i)} - showFollowBtn={showFollowBtn} ignoreMuteFor={ignoreMuteFor} /> ))} diff --git a/src/view/com/posts/MultiFeed.tsx b/src/view/com/posts/MultiFeed.tsx index 466a7a47d..97899e554 100644 --- a/src/view/com/posts/MultiFeed.tsx +++ b/src/view/com/posts/MultiFeed.tsx @@ -28,7 +28,6 @@ import {CogIcon} from 'lib/icons' export const MultiFeed = observer(function Feed({ multifeed, style, - showPostFollowBtn, scrollElRef, onScroll, scrollEventThrottle, @@ -38,7 +37,6 @@ export const MultiFeed = observer(function Feed({ }: { multifeed: PostsMultiFeedModel style?: StyleProp<ViewStyle> - showPostFollowBtn?: boolean scrollElRef?: MutableRefObject<FlatList<any> | null> onPressTryAgain?: () => void onScroll?: OnScrollCb @@ -105,9 +103,7 @@ export const MultiFeed = observer(function Feed({ </View> ) } else if (item.type === 'feed-slice') { - return ( - <FeedSlice slice={item.slice} showFollowBtn={showPostFollowBtn} /> - ) + return <FeedSlice slice={item.slice} /> } else if (item.type === 'feed-loading') { return <PostFeedLoadingPlaceholder /> } else if (item.type === 'feed-error') { @@ -139,7 +135,7 @@ export const MultiFeed = observer(function Feed({ } return null }, - [showPostFollowBtn, pal], + [pal], ) const ListFooter = React.useCallback( diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index b142e7616..46ff3d979 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -6,10 +6,7 @@ import { TouchableWithoutFeedback, View, } from 'react-native' -import { - FontAwesomeIcon, - FontAwesomeIconStyle, -} from '@fortawesome/react-native-fontawesome' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {useNavigation} from '@react-navigation/native' import {BlurView} from '../util/BlurView' import {ProfileModel} from 'state/models/content/profile' @@ -102,6 +99,7 @@ export const ProfileHeader = observer( const ProfileHeaderLoaded = observer( ({view, onRefreshAll, hideBackButton = false}: Props) => { const pal = usePalette('default') + const palInverted = usePalette('inverted') const store = useStores() const navigation = useNavigation<NavigationProp>() const {track} = useAnalytics() @@ -351,15 +349,15 @@ const ProfileHeaderLoaded = observer( <TouchableOpacity testID="followBtn" onPress={onPressToggleFollow} - style={[styles.btn, styles.primaryBtn]} + style={[styles.btn, styles.mainBtn, palInverted.view]} accessibilityRole="button" accessibilityLabel={`Follow ${view.handle}`} accessibilityHint={`Shows direct posts from ${view.handle} in your feed`}> <FontAwesomeIcon icon="plus" - style={[s.white as FontAwesomeIconStyle, s.mr5]} + style={[palInverted.text, s.mr5]} /> - <Text type="button" style={[s.white, s.bold]}> + <Text type="button" style={[palInverted.text, s.bold]}> Follow </Text> </TouchableOpacity> @@ -609,7 +607,6 @@ const styles = StyleSheet.create({ }, description: { - flex: 1, marginBottom: 8, }, diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx index 1dec97e78..454fd7c21 100644 --- a/src/view/com/util/Link.tsx +++ b/src/view/com/util/Link.tsx @@ -6,6 +6,7 @@ import { Platform, StyleProp, TextStyle, + TextProps, View, ViewStyle, TouchableOpacity, @@ -144,7 +145,7 @@ export const TextLink = observer(function TextLink({ numberOfLines?: number lineHeight?: number dataSet?: any -}) { +} & TextProps) { const {...props} = useLinkProps({to: sanitizeUrl(href)}) const store = useStores() const navigation = useNavigation<NavigationProp>() @@ -186,16 +187,7 @@ export const TextLink = observer(function TextLink({ /** * Only acts as a link on desktop web */ -export const DesktopWebTextLink = observer(function DesktopWebTextLink({ - testID, - type = 'md', - style, - href, - text, - numberOfLines, - lineHeight, - ...props -}: { +interface DesktopWebTextLinkProps extends TextProps { testID?: string type?: TypographyVariant style?: StyleProp<TextStyle> @@ -206,7 +198,17 @@ export const DesktopWebTextLink = observer(function DesktopWebTextLink({ accessible?: boolean accessibilityLabel?: string accessibilityHint?: string -}) { +} +export const DesktopWebTextLink = observer(function DesktopWebTextLink({ + testID, + type = 'md', + style, + href, + text, + numberOfLines, + lineHeight, + ...props +}: DesktopWebTextLinkProps) { if (isDesktopWeb) { return ( <TextLink diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx index 628c88722..396b0278d 100644 --- a/src/view/com/util/PostMeta.tsx +++ b/src/view/com/util/PostMeta.tsx @@ -4,12 +4,10 @@ import {Text} from './text/Text' import {DesktopWebTextLink} from './Link' import {ago, niceDate} 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' -import {FollowState} from 'state/models/cache/my-follows' import {sanitizeDisplayName} from 'lib/strings/display-names' +import {isAndroid, isIOS} from 'platform/detection' interface PostMetaOpts { authorAvatar?: string @@ -18,88 +16,17 @@ interface PostMetaOpts { authorHasWarning: boolean postHref: string timestamp: string - did?: string - showFollowBtn?: boolean } export const PostMeta = observer(function (opts: PostMetaOpts) { const pal = usePalette('default') const displayName = opts.authorDisplayName || opts.authorHandle const handle = opts.authorHandle - const store = useStores() - const isMe = opts.did === store.me.did - const followState = - typeof opts.did === 'string' - ? store.me.follows.getFollowState(opts.did) - : FollowState.Unknown - const [didFollow, setDidFollow] = React.useState(false) - const onToggleFollow = React.useCallback(() => { - setDidFollow(true) - }, [setDidFollow]) - - if ( - opts.showFollowBtn && - !isMe && - (followState === FollowState.NotFollowing || didFollow) && - opts.did - ) { - // two-liner with follow button - return ( - <View style={styles.metaTwoLine}> - <View style={styles.metaTwoLineLeft}> - <View style={styles.metaTwoLineTop}> - <DesktopWebTextLink - type="lg-bold" - style={pal.text} - numberOfLines={1} - lineHeight={1.2} - text={sanitizeDisplayName(displayName)} - href={`/profile/${opts.authorHandle}`} - /> - <Text - type="md" - style={pal.textLight} - lineHeight={1.2} - accessible={false}> - · - </Text> - <DesktopWebTextLink - type="md" - style={[styles.metaItem, pal.textLight]} - lineHeight={1.2} - text={ago(opts.timestamp)} - accessibilityLabel={niceDate(opts.timestamp)} - accessibilityHint="" - href={opts.postHref} - /> - </View> - <DesktopWebTextLink - type="md" - style={[styles.metaItem, pal.textLight]} - lineHeight={1.2} - numberOfLines={1} - text={`@${handle}`} - href={`/profile/${opts.authorHandle}`} - /> - </View> - - <View> - <FollowButton - unfollowedType="default" - did={opts.did} - onToggleFollow={onToggleFollow} - /> - </View> - </View> - ) - } - - // one-liner return ( - <View style={styles.meta}> + <View style={styles.metaOneLine}> {typeof opts.authorAvatar !== 'undefined' && ( - <View style={[styles.metaItem, styles.avatar]}> + <View style={styles.avatar}> <UserAvatar avatar={opts.authorAvatar} size={16} @@ -107,7 +34,7 @@ export const PostMeta = observer(function (opts: PostMetaOpts) { /> </View> )} - <View style={[styles.metaItem, styles.maxWidth]}> + <View style={styles.maxWidth}> <DesktopWebTextLink type="lg-bold" style={pal.text} @@ -128,12 +55,18 @@ export const PostMeta = observer(function (opts: PostMetaOpts) { href={`/profile/${opts.authorHandle}`} /> </View> - <Text type="md" style={pal.textLight} lineHeight={1.2} accessible={false}> - · - </Text> + {!isAndroid && ( + <Text + type="md" + style={pal.textLight} + lineHeight={1.2} + accessible={false}> + · + </Text> + )} <DesktopWebTextLink type="md" - style={[styles.metaItem, pal.textLight]} + style={pal.textLight} lineHeight={1.2} text={ago(opts.timestamp)} accessibilityLabel={niceDate(opts.timestamp)} @@ -145,32 +78,16 @@ export const PostMeta = observer(function (opts: PostMetaOpts) { }) const styles = StyleSheet.create({ - meta: { + metaOneLine: { flexDirection: 'row', paddingBottom: 2, - }, - metaTwoLine: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - width: '100%', - paddingBottom: 4, - }, - metaTwoLineLeft: { - flex: 1, - paddingRight: 40, - }, - metaTwoLineTop: { - flexDirection: 'row', - alignItems: 'baseline', - }, - metaItem: { - paddingRight: 5, + gap: 4, }, avatar: { alignSelf: 'center', }, maxWidth: { - maxWidth: '80%', + flex: isAndroid ? 1 : undefined, + maxWidth: isIOS ? '80%' : undefined, }, }) diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx index b94cf54e9..135615a3b 100644 --- a/src/view/com/util/UserAvatar.tsx +++ b/src/view/com/util/UserAvatar.tsx @@ -1,5 +1,5 @@ import React, {useMemo} from 'react' -import {StyleSheet, View} from 'react-native' +import {Pressable, StyleSheet, View} from 'react-native' import Svg, {Circle, Rect, Path} from 'react-native-svg' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {IconProp} from '@fortawesome/fontawesome-svg-core' @@ -12,13 +12,31 @@ import { import {useStores} from 'state/index' import {colors} from 'lib/styles' import {DropdownButton} from './forms/DropdownButton' +import {Link} from './Link' import {usePalette} from 'lib/hooks/usePalette' import {isWeb, isAndroid} from 'platform/detection' import {Image as RNImage} from 'react-native-image-crop-picker' import {AvatarModeration} from 'lib/labeling/types' +import {isDesktopWeb} from 'platform/detection' type Type = 'user' | 'algo' | 'list' +interface BaseUserAvatarProps { + type?: Type + size: number + avatar?: string | null + moderation?: AvatarModeration +} + +interface UserAvatarProps extends BaseUserAvatarProps { + onSelectNewAvatar?: (img: RNImage | null) => void +} + +interface PreviewableUserAvatarProps extends BaseUserAvatarProps { + did: string + handle: string +} + const BLUR_AMOUNT = isWeb ? 5 : 100 function DefaultAvatar({type, size}: {type: Type; size: number}) { @@ -91,13 +109,7 @@ export function UserAvatar({ avatar, moderation, onSelectNewAvatar, -}: { - type?: Type - size: number - avatar?: string | null - moderation?: AvatarModeration - onSelectNewAvatar?: (img: RNImage | null) => void -}) { +}: UserAvatarProps) { const store = useStores() const pal = usePalette('default') const {requestCameraAccessIfNeeded} = useCameraPermission() @@ -244,6 +256,32 @@ export function UserAvatar({ ) } +export function PreviewableUserAvatar(props: PreviewableUserAvatarProps) { + const store = useStores() + + if (isDesktopWeb) { + return ( + <Link href={`/profile/${props.handle}`} title={props.handle} asAnchor> + <UserAvatar {...props} /> + </Link> + ) + } + return ( + <Pressable + onPress={() => + store.shell.openModal({ + name: 'profile-preview', + did: props.did, + }) + } + accessibilityRole="button" + accessibilityLabel={props.handle} + accessibilityHint=""> + <UserAvatar {...props} /> + </Pressable> + ) +} + const styles = StyleSheet.create({ editButtonContainer: { position: 'absolute', diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx index 7d4452384..1ab59f736 100644 --- a/src/view/screens/Feeds.tsx +++ b/src/view/screens/Feeds.tsx @@ -106,7 +106,6 @@ export const FeedsScreen = withAuthRequired( onScroll={onMainScroll} scrollEventThrottle={100} headerOffset={HEADER_OFFSET} - showPostFollowBtn /> <ViewHeader title="My Feeds" diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index ff2b2a0bd..41459cfa5 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -266,7 +266,6 @@ const FeedPage = observer( key="default" feed={feed} scrollElRef={scrollElRef} - showPostFollowBtn onPressTryAgain={onPressTryAgain} onScroll={onMainScroll} scrollEventThrottle={100} |