Fixes and improvements to the Profile Preview modal (#992)

* Fix: use more reliable navigation method

* Fix: show lightbox over the active modal

* Fix: close the profile preview on navigation

* Factor out UserPreviewLink and add preview behavior to notifications

* Fix postmeta overflow on web

* Fix lint
zio/stable
Paul Frazee 2023-07-07 12:00:17 -05:00 committed by GitHub
parent d8aded7b15
commit 237e957d16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 101 additions and 59 deletions

View File

@ -279,6 +279,24 @@ export class ShellUiModel {
return false
}
/**
* used to clear out any modals, eg for a navigation
*/
closeAllActiveElements() {
if (this.isLightboxActive) {
this.closeLightbox()
}
while (this.isModalActive) {
this.closeModal()
}
if (this.isComposerActive) {
this.closeComposer()
}
if (this.isDrawerOpen) {
this.closeDrawer()
}
}
openDrawer() {
this.isDrawerOpen = true
}

View File

@ -22,7 +22,8 @@ import {sanitizeDisplayName} from 'lib/strings/display-names'
import {pluralize} from 'lib/strings/helpers'
import {HeartIconSolid} from 'lib/icons'
import {Text} from '../util/text/Text'
import {UserAvatar} from '../util/UserAvatar'
import {UserAvatar, PreviewableUserAvatar} from '../util/UserAvatar'
import {UserPreviewLink} from '../util/UserPreviewLink'
import {ImageHorzList} from '../util/images/ImageHorzList'
import {Post} from '../post/Post'
import {Link, TextLink} from '../util/Link'
@ -42,6 +43,7 @@ const EXPANDED_AUTHOR_EL_HEIGHT = 35
interface Author {
href: string
did: string
handle: string
displayName?: string
avatar?: string
@ -91,6 +93,7 @@ export const FeedItem = observer(function ({
return [
{
href: `/profile/${item.author.handle}`,
did: item.author.did,
handle: item.author.handle,
displayName: item.author.displayName,
avatar: item.author.avatar,
@ -102,6 +105,7 @@ export const FeedItem = observer(function ({
...(item.additional?.map(({author}) => {
return {
href: `/profile/${author.handle}`,
did: author.did,
handle: author.handle,
displayName: author.displayName,
avatar: author.avatar,
@ -282,17 +286,13 @@ function CondensedAuthorsList({
if (authors.length === 1) {
return (
<View style={styles.avis}>
<Link
style={s.mr5}
href={authors[0].href}
title={`@${authors[0].handle}`}
asAnchor>
<UserAvatar
size={35}
avatar={authors[0].avatar}
moderation={authors[0].moderation.avatar}
/>
</Link>
<PreviewableUserAvatar
size={35}
did={authors[0].did}
handle={authors[0].handle}
avatar={authors[0].avatar}
moderation={authors[0].moderation.avatar}
/>
</View>
)
}
@ -356,12 +356,11 @@ function ExpandedAuthorsList({
visible ? s.mb10 : undefined,
]}>
{authors.map(author => (
<Link
key={author.href}
href={author.href}
title={sanitizeDisplayName(author.displayName || author.handle)}
style={styles.expandedAuthor}
asAnchor>
<UserPreviewLink
key={author.did}
did={author.did}
handle={author.handle}
style={styles.expandedAuthor}>
<View style={styles.expandedAuthorAvi}>
<UserAvatar
size={35}
@ -382,7 +381,7 @@ function ExpandedAuthorsList({
</Text>
</Text>
</View>
</Link>
</UserPreviewLink>
))}
</Animated.View>
)

View File

@ -25,7 +25,7 @@ import {ImageHider} from '../util/moderation/ImageHider'
import {Text} from '../util/text/Text'
import {RichText} from '../util/text/RichText'
import * as Toast from '../util/Toast'
import {UserAvatar} from '../util/UserAvatar'
import {PreviewableUserAvatar} from '../util/UserAvatar'
import {useStores} from 'state/index'
import {s, colors} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
@ -127,8 +127,6 @@ const PostLoaded = observer(
const itemUrip = new AtUri(item.post.uri)
const itemHref = `/profile/${item.post.author.handle}/post/${itemUrip.rkey}`
const itemTitle = `Post by ${item.post.author.handle}`
const authorHref = `/profile/${item.post.author.handle}`
const authorTitle = item.post.author.handle
let replyAuthorDid = ''
if (record.reply) {
const urip = new AtUri(record.reply.parent?.uri || record.reply.root.uri)
@ -214,13 +212,13 @@ const PostLoaded = observer(
{showReplyLine && <View style={styles.replyLine} />}
<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

View File

@ -33,6 +33,7 @@ import {isDesktopWeb, isNative} from 'platform/detection'
import {FollowState} from 'state/models/cache/my-follows'
import {shareUrl} from 'lib/sharing'
import {formatCount} from '../util/numeric/format'
import {navigate} from '../../../Navigation'
const BACK_HITSLOP = {left: 30, top: 30, right: 30, bottom: 30}
@ -143,13 +144,15 @@ const ProfileHeaderLoaded = observer(
const onPressFollowers = React.useCallback(() => {
track('ProfileHeader:FollowersButtonClicked')
navigation.push('ProfileFollowers', {name: view.handle})
}, [track, navigation, view])
navigate('ProfileFollowers', {name: view.handle})
store.shell.closeAllActiveElements() // for when used in the profile preview modal
}, [track, view, store.shell])
const onPressFollows = React.useCallback(() => {
track('ProfileHeader:FollowsButtonClicked')
navigation.push('ProfileFollows', {name: view.handle})
}, [track, navigation, view])
navigate('ProfileFollows', {name: view.handle})
store.shell.closeAllActiveElements() // for when used in the profile preview modal
}, [track, view, store.shell])
const onPressShare = React.useCallback(() => {
track('ProfileHeader:ShareButtonClicked')

View File

@ -7,7 +7,7 @@ import {usePalette} from 'lib/hooks/usePalette'
import {UserAvatar} from './UserAvatar'
import {observer} from 'mobx-react-lite'
import {sanitizeDisplayName} from 'lib/strings/display-names'
import {isAndroid, isIOS} from 'platform/detection'
import {isAndroid} from 'platform/detection'
interface PostMetaOpts {
authorAvatar?: string
@ -88,6 +88,6 @@ const styles = StyleSheet.create({
},
maxWidth: {
flex: isAndroid ? 1 : undefined,
maxWidth: isIOS ? '80%' : undefined,
maxWidth: !isAndroid ? '80%' : undefined,
},
})

View File

@ -1,5 +1,5 @@
import React, {useMemo} from 'react'
import {Pressable, StyleSheet, View} from 'react-native'
import {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,12 +12,11 @@ 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'
import {UserPreviewLink} from './UserPreviewLink'
type Type = 'user' | 'algo' | 'list'
@ -257,28 +256,10 @@ 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="">
<UserPreviewLink did={props.did} handle={props.handle}>
<UserAvatar {...props} />
</Pressable>
</UserPreviewLink>
)
}

View File

@ -0,0 +1,43 @@
import React from 'react'
import {Pressable, StyleProp, ViewStyle} from 'react-native'
import {useStores} from 'state/index'
import {Link} from './Link'
import {isDesktopWeb} from 'platform/detection'
interface UserPreviewLinkProps {
did: string
handle: string
style?: StyleProp<ViewStyle>
}
export function UserPreviewLink(
props: React.PropsWithChildren<UserPreviewLinkProps>,
) {
const store = useStores()
if (isDesktopWeb) {
return (
<Link
href={`/profile/${props.handle}`}
title={props.handle}
asAnchor
style={props.style}>
{props.children}
</Link>
)
}
return (
<Pressable
onPress={() =>
store.shell.openModal({
name: 'profile-preview',
did: props.did,
})
}
accessibilityRole="button"
accessibilityLabel={props.handle}
accessibilityHint=""
style={props.style}>
{props.children}
</Pressable>
)
}

View File

@ -61,7 +61,6 @@ const ShellInner = observer(() => {
</Drawer>
</ErrorBoundary>
</View>
<Lightbox />
<Composer
active={store.shell.isComposerActive}
onClose={() => store.shell.closeComposer()}
@ -71,6 +70,7 @@ const ShellInner = observer(() => {
quote={store.shell.composerOpts?.quote}
/>
<ModalsContainer />
<Lightbox />
</>
)
})