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 lintzio/stable
parent
d8aded7b15
commit
237e957d16
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
|
@ -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 />
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue