This commit is contained in:
Samuel Newman
2025-05-10 00:06:06 +03:00
committed by GitHub
parent 2e80fa3dac
commit a0bd804262
45 changed files with 2021 additions and 451 deletions
@@ -9,6 +9,7 @@ import {
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useActorStatus} from '#/lib/actor-status'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger'
@@ -138,6 +139,8 @@ let ProfileHeaderStandard = ({
[currentAccount, profile],
)
const {isActive: live} = useActorStatus(profile)
return (
<ProfileHeaderShell
profile={profile}
@@ -228,7 +231,8 @@ let ProfileHeaderStandard = ({
) : null}
<ProfileMenu profile={profile} />
</View>
<View style={[a.flex_col, a.gap_2xs, a.pt_2xs, a.pb_sm]}>
<View
style={[a.flex_col, a.gap_2xs, a.pb_sm, live ? a.pt_sm : a.pt_2xs]}>
<View style={[a.flex_row, a.align_center, a.gap_xs, a.flex_1]}>
<Text
emoji
+78 -22
View File
@@ -1,24 +1,35 @@
import React, {memo} from 'react'
import React, {memo, useEffect} from 'react'
import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native'
import {MeasuredDimensions, runOnJS, runOnUI} from 'react-native-reanimated'
import {
type MeasuredDimensions,
runOnJS,
runOnUI,
} from 'react-native-reanimated'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {AppBskyActorDefs, ModerationDecision} from '@atproto/api'
import {type AppBskyActorDefs, type ModerationDecision} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
import {useActorStatus} from '#/lib/actor-status'
import {BACK_HITSLOP} from '#/lib/constants'
import {useHaptics} from '#/lib/haptics'
import {measureHandle, useHandleRef} from '#/lib/hooks/useHandleRef'
import {NavigationProp} from '#/lib/routes/types'
import {type NavigationProp} from '#/lib/routes/types'
import {logger} from '#/logger'
import {isIOS} from '#/platform/detection'
import {Shadow} from '#/state/cache/types'
import {type Shadow} from '#/state/cache/types'
import {useLightboxControls} from '#/state/lightbox'
import {useSession} from '#/state/session'
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {UserBanner} from '#/view/com/util/UserBanner'
import {atoms as a, platform, useTheme} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeftIcon} from '#/components/icons/Arrow'
import {EditLiveDialog} from '#/components/live/EditLiveDialog'
import {LiveIndicator} from '#/components/live/LiveIndicator'
import {LiveStatusDialog} from '#/components/live/LiveStatusDialog'
import {LabelsOnMe} from '#/components/moderation/LabelsOnMe'
import {ProfileHeaderAlerts} from '#/components/moderation/ProfileHeaderAlerts'
import {GrowableAvatar} from './GrowableAvatar'
@@ -45,6 +56,8 @@ let ProfileHeaderShell = ({
const {openLightbox} = useLightboxControls()
const navigation = useNavigation<NavigationProp>()
const {top: topInset} = useSafeAreaInsets()
const playHaptic = useHaptics()
const liveStatusControl = useDialogControl()
const aviRef = useHandleRef()
@@ -79,24 +92,46 @@ let ProfileHeaderShell = ({
[openLightbox],
)
const onPressAvi = React.useCallback(() => {
const modui = moderation.ui('avatar')
const avatar = profile.avatar
if (avatar && !(modui.blur && modui.noOverride)) {
const aviHandle = aviRef.current
runOnUI(() => {
'worklet'
const rect = measureHandle(aviHandle)
runOnJS(_openLightbox)(avatar, rect)
})()
}
}, [profile, moderation, _openLightbox, aviRef])
const isMe = React.useMemo(
() => currentAccount?.did === profile.did,
[currentAccount, profile],
)
const live = useActorStatus(profile)
useEffect(() => {
if (live.isActive) {
logger.metric('live:view:profile', {subject: profile.did})
}
}, [live.isActive, profile.did])
const onPressAvi = React.useCallback(() => {
if (live.isActive) {
playHaptic('Light')
logger.metric('live:card:open', {subject: profile.did, from: 'profile'})
liveStatusControl.open()
} else {
const modui = moderation.ui('avatar')
const avatar = profile.avatar
if (avatar && !(modui.blur && modui.noOverride)) {
const aviHandle = aviRef.current
runOnUI(() => {
'worklet'
const rect = measureHandle(aviHandle)
runOnJS(_openLightbox)(avatar, rect)
})()
}
}
}, [
profile,
moderation,
_openLightbox,
aviRef,
liveStatusControl,
live,
playHaptic,
])
return (
<View style={t.atoms.bg} pointerEvents={isIOS ? 'auto' : 'box-none'}>
<View
@@ -170,21 +205,44 @@ let ProfileHeaderShell = ({
<View
style={[
t.atoms.bg,
{borderColor: t.atoms.bg.backgroundColor},
a.rounded_full,
{
borderWidth: live.isActive ? 3 : 2,
borderColor: live.isActive
? t.palette.negative_500
: t.atoms.bg.backgroundColor,
},
styles.avi,
profile.associated?.labeler && styles.aviLabeler,
]}>
<View ref={aviRef} collapsable={false}>
<UserAvatar
type={profile.associated?.labeler ? 'labeler' : 'user'}
size={90}
size={live.isActive ? 88 : 90}
avatar={profile.avatar}
moderation={moderation.ui('avatar')}
/>
{live.isActive && <LiveIndicator size="large" />}
</View>
</View>
</TouchableWithoutFeedback>
</GrowableAvatar>
{live.isActive &&
(isMe ? (
<EditLiveDialog
control={liveStatusControl}
status={live}
embed={live.embed}
/>
) : (
<LiveStatusDialog
control={liveStatusControl}
status={live}
embed={live.embed}
profile={profile}
/>
))}
</View>
)
}
@@ -219,8 +277,6 @@ const styles = StyleSheet.create({
avi: {
width: 94,
height: 94,
borderRadius: 47,
borderWidth: 2,
},
aviLabeler: {
borderRadius: 10,