Profile card hover preview (#3508)

* feat: initial user card hover

* feat: flesh it out some more

* fix: initialize middlewares once

* chore: remove floating-ui react-native

* chore: clean up

* Update moderation apis, fix lint

* Refactor profile hover card to alf

* Clean up

* Debounce, fix positioning when loading

* Fix going away

* Close on all link presses

* Tweak styles

* Disable on mobile web

* cleanup some of the changes pt. 1

* cleanup some of the changes pt. 2

* cleanup some of the changes pt. 3

* cleanup some of the changes pt. 4

* Re-revert files

* Fix handle presentation

* Don't follow yourself, silly

* Collapsed notifications group

* ProfileCard

* Tree view replies

* Suggested follows

* Fix hover-back-on-card edge case

* Moar

---------

Co-authored-by: Mary <git@mary.my.id>
Co-authored-by: Hailey <me@haileyok.com>
This commit is contained in:
Eric Bailey 2024-04-12 17:01:32 -05:00 committed by GitHub
parent f91aa37c6b
commit 1f61109cfa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 576 additions and 146 deletions

View file

@ -1,18 +1,19 @@
import React, {memo} from 'react'
import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native'
import {Text} from './text/Text'
import {TextLinkOnWebOnly} from './Link'
import {niceDate} from 'lib/strings/time'
import {AppBskyActorDefs, ModerationDecision, ModerationUI} from '@atproto/api'
import {usePrefetchProfileQuery} from '#/state/queries/profile'
import {usePalette} from 'lib/hooks/usePalette'
import {TypographyVariant} from 'lib/ThemeContext'
import {UserAvatar} from './UserAvatar'
import {makeProfileLink} from 'lib/routes/links'
import {sanitizeDisplayName} from 'lib/strings/display-names'
import {sanitizeHandle} from 'lib/strings/handles'
import {niceDate} from 'lib/strings/time'
import {TypographyVariant} from 'lib/ThemeContext'
import {isAndroid, isWeb} from 'platform/detection'
import {TextLinkOnWebOnly} from './Link'
import {Text} from './text/Text'
import {TimeElapsed} from './TimeElapsed'
import {makeProfileLink} from 'lib/routes/links'
import {AppBskyActorDefs, ModerationDecision, ModerationUI} from '@atproto/api'
import {usePrefetchProfileQuery} from '#/state/queries/profile'
import {PreviewableUserAvatar} from './UserAvatar'
interface PostMetaOpts {
author: AppBskyActorDefs.ProfileViewBasic
@ -38,9 +39,11 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
<View style={[styles.container, opts.style]}>
{opts.showAvatar && (
<View style={styles.avatar}>
<UserAvatar
avatar={opts.author.avatar}
<PreviewableUserAvatar
size={opts.avatarSize || 16}
did={opts.author.did}
handle={opts.author.handle}
avatar={opts.author.avatar}
moderation={opts.avatarModeration}
type={opts.author.associated?.labeler ? 'labeler' : 'user'}
/>

View file

@ -1,30 +1,32 @@
import React, {memo, useMemo} from 'react'
import {Image, StyleSheet, TouchableOpacity, View} from 'react-native'
import Svg, {Circle, Rect, Path} from 'react-native-svg'
import {Image as RNImage} from 'react-native-image-crop-picker'
import {useLingui} from '@lingui/react'
import {msg, Trans} from '@lingui/macro'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import Svg, {Circle, Path, Rect} from 'react-native-svg'
import {ModerationUI} from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {HighPriorityImage} from 'view/com/util/images/Image'
import {openCamera, openCropper, openPicker} from '../../../lib/media/picker'
import {
usePhotoLibraryPermission,
useCameraPermission,
} from 'lib/hooks/usePermissions'
import {colors} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
import {isWeb, isAndroid, isNative} from 'platform/detection'
import {UserPreviewLink} from './UserPreviewLink'
import * as Menu from '#/components/Menu'
import {
Camera_Stroke2_Corner0_Rounded as Camera,
useCameraPermission,
usePhotoLibraryPermission,
} from 'lib/hooks/usePermissions'
import {makeProfileLink} from 'lib/routes/links'
import {colors} from 'lib/styles'
import {isAndroid, isNative, isWeb} from 'platform/detection'
import {HighPriorityImage} from 'view/com/util/images/Image'
import {tokens, useTheme} from '#/alf'
import {
Camera_Filled_Stroke2_Corner0_Rounded as CameraFilled,
Camera_Stroke2_Corner0_Rounded as Camera,
} from '#/components/icons/Camera'
import {StreamingLive_Stroke2_Corner0_Rounded as Library} from '#/components/icons/StreamingLive'
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
import {useTheme, tokens} from '#/alf'
import {Link} from '#/components/Link'
import * as Menu from '#/components/Menu'
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
import {openCamera, openCropper, openPicker} from '../../../lib/media/picker'
export type UserAvatarType = 'user' | 'algo' | 'list' | 'labeler'
@ -372,10 +374,18 @@ export {EditableUserAvatar}
let PreviewableUserAvatar = (
props: PreviewableUserAvatarProps,
): React.ReactNode => {
const {_} = useLingui()
return (
<UserPreviewLink did={props.did} handle={props.handle}>
<UserAvatar {...props} />
</UserPreviewLink>
<ProfileHoverCard did={props.did}>
<Link
label={_(msg`See profile`)}
to={makeProfileLink({
did: props.did,
handle: props.handle,
})}>
<UserAvatar {...props} />
</Link>
</ProfileHoverCard>
)
}
PreviewableUserAvatar = memo(PreviewableUserAvatar)

View file

@ -1,31 +0,0 @@
import React from 'react'
import {StyleProp, ViewStyle} from 'react-native'
import {Link} from './Link'
import {isWeb} from 'platform/detection'
import {makeProfileLink} from 'lib/routes/links'
import {usePrefetchProfileQuery} from '#/state/queries/profile'
interface UserPreviewLinkProps {
did: string
handle: string
style?: StyleProp<ViewStyle>
}
export function UserPreviewLink(
props: React.PropsWithChildren<UserPreviewLinkProps>,
) {
const prefetchProfileQuery = usePrefetchProfileQuery()
return (
<Link
onPointerEnter={() => {
if (isWeb) {
prefetchProfileQuery(props.did)
}
}}
href={makeProfileLink(props)}
title={props.handle}
asAnchor
style={props.style}>
{props.children}
</Link>
)
}