Remove Profile Preview modal (#2790)
parent
06f81d6948
commit
d9b62955b5
|
@ -144,7 +144,6 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
|
||||||
getComponent={() => ProfileScreen}
|
getComponent={() => ProfileScreen}
|
||||||
options={({route}) => ({
|
options={({route}) => ({
|
||||||
title: bskyTitle(`@${route.params.name}`, unreadCountLabel),
|
title: bskyTitle(`@${route.params.name}`, unreadCountLabel),
|
||||||
animation: 'none',
|
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|
|
@ -26,11 +26,6 @@ export interface EditProfileModal {
|
||||||
onUpdate?: () => void
|
onUpdate?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProfilePreviewModal {
|
|
||||||
name: 'profile-preview'
|
|
||||||
did: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ServerInputModal {
|
export interface ServerInputModal {
|
||||||
name: 'server-input'
|
name: 'server-input'
|
||||||
initialService: string
|
initialService: string
|
||||||
|
@ -202,7 +197,6 @@ export type Modal =
|
||||||
| ChangeHandleModal
|
| ChangeHandleModal
|
||||||
| DeleteAccountModal
|
| DeleteAccountModal
|
||||||
| EditProfileModal
|
| EditProfileModal
|
||||||
| ProfilePreviewModal
|
|
||||||
| BirthDateSettingsModal
|
| BirthDateSettingsModal
|
||||||
| VerifyEmailModal
|
| VerifyEmailModal
|
||||||
| ChangeEmailModal
|
| ChangeEmailModal
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
import React, {useRef, useEffect} from 'react'
|
import React, {useRef, useEffect} from 'react'
|
||||||
import {StyleSheet} from 'react-native'
|
import {StyleSheet} from 'react-native'
|
||||||
import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {SafeAreaView} from 'react-native-safe-area-context'
|
||||||
import BottomSheet from '@gorhom/bottom-sheet'
|
import BottomSheet from '@gorhom/bottom-sheet'
|
||||||
import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
|
import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {timeout} from 'lib/async/timeout'
|
|
||||||
import {navigate} from '../../../Navigation'
|
|
||||||
import once from 'lodash.once'
|
|
||||||
|
|
||||||
import {useModals, useModalControls} from '#/state/modals'
|
import {useModals, useModalControls} from '#/state/modals'
|
||||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
|
||||||
import * as ConfirmModal from './Confirm'
|
import * as ConfirmModal from './Confirm'
|
||||||
import * as EditProfileModal from './EditProfile'
|
import * as EditProfileModal from './EditProfile'
|
||||||
import * as ProfilePreviewModal from './ProfilePreview'
|
|
||||||
import * as ServerInputModal from './ServerInput'
|
import * as ServerInputModal from './ServerInput'
|
||||||
import * as RepostModal from './Repost'
|
import * as RepostModal from './Repost'
|
||||||
import * as SelfLabelModal from './SelfLabel'
|
import * as SelfLabelModal from './SelfLabel'
|
||||||
|
@ -50,34 +45,14 @@ export function ModalsContainer() {
|
||||||
const {closeModal} = useModalControls()
|
const {closeModal} = useModalControls()
|
||||||
const bottomSheetRef = useRef<BottomSheet>(null)
|
const bottomSheetRef = useRef<BottomSheet>(null)
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const safeAreaInsets = useSafeAreaInsets()
|
|
||||||
|
|
||||||
const activeModal = activeModals[activeModals.length - 1]
|
const activeModal = activeModals[activeModals.length - 1]
|
||||||
|
|
||||||
const navigateOnce = once(navigate)
|
|
||||||
|
|
||||||
// It seems like the bottom sheet bugs out when this callback changes.
|
|
||||||
const onBottomSheetAnimate = useNonReactiveCallback(
|
|
||||||
(_fromIndex: number, toIndex: number) => {
|
|
||||||
if (activeModal?.name === 'profile-preview' && toIndex === 1) {
|
|
||||||
// begin loading the profile screen behind the scenes
|
|
||||||
navigateOnce('Profile', {name: activeModal.did})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
const onBottomSheetChange = async (snapPoint: number) => {
|
const onBottomSheetChange = async (snapPoint: number) => {
|
||||||
if (snapPoint === -1) {
|
if (snapPoint === -1) {
|
||||||
closeModal()
|
closeModal()
|
||||||
} else if (activeModal?.name === 'profile-preview' && snapPoint === 1) {
|
|
||||||
await navigateOnce('Profile', {name: activeModal.did})
|
|
||||||
// There is no particular callback for when the view has actually been presented.
|
|
||||||
// This delay gives us a decent chance the navigation has flushed *and* images have loaded.
|
|
||||||
// It's acceptable because the data is already being fetched + it usually takes longer anyway.
|
|
||||||
// TODO: Figure out why avatar/cover don't always show instantly from cache.
|
|
||||||
await timeout(200)
|
|
||||||
closeModal()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
bottomSheetRef.current?.close()
|
bottomSheetRef.current?.close()
|
||||||
closeModal()
|
closeModal()
|
||||||
|
@ -91,7 +66,6 @@ export function ModalsContainer() {
|
||||||
}
|
}
|
||||||
}, [isModalActive, bottomSheetRef, activeModal?.name])
|
}, [isModalActive, bottomSheetRef, activeModal?.name])
|
||||||
|
|
||||||
let needsSafeTopInset = false
|
|
||||||
let snapPoints: (string | number)[] = DEFAULT_SNAPPOINTS
|
let snapPoints: (string | number)[] = DEFAULT_SNAPPOINTS
|
||||||
let element
|
let element
|
||||||
if (activeModal?.name === 'confirm') {
|
if (activeModal?.name === 'confirm') {
|
||||||
|
@ -100,10 +74,6 @@ export function ModalsContainer() {
|
||||||
} else if (activeModal?.name === 'edit-profile') {
|
} else if (activeModal?.name === 'edit-profile') {
|
||||||
snapPoints = EditProfileModal.snapPoints
|
snapPoints = EditProfileModal.snapPoints
|
||||||
element = <EditProfileModal.Component {...activeModal} />
|
element = <EditProfileModal.Component {...activeModal} />
|
||||||
} else if (activeModal?.name === 'profile-preview') {
|
|
||||||
snapPoints = ProfilePreviewModal.snapPoints
|
|
||||||
element = <ProfilePreviewModal.Component {...activeModal} />
|
|
||||||
needsSafeTopInset = true // Need to align with the target profile screen.
|
|
||||||
} else if (activeModal?.name === 'server-input') {
|
} else if (activeModal?.name === 'server-input') {
|
||||||
snapPoints = ServerInputModal.snapPoints
|
snapPoints = ServerInputModal.snapPoints
|
||||||
element = <ServerInputModal.Component {...activeModal} />
|
element = <ServerInputModal.Component {...activeModal} />
|
||||||
|
@ -200,12 +170,10 @@ export function ModalsContainer() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const topInset = needsSafeTopInset ? safeAreaInsets.top - HANDLE_HEIGHT : 0
|
|
||||||
return (
|
return (
|
||||||
<BottomSheet
|
<BottomSheet
|
||||||
ref={bottomSheetRef}
|
ref={bottomSheetRef}
|
||||||
snapPoints={snapPoints}
|
snapPoints={snapPoints}
|
||||||
topInset={topInset}
|
|
||||||
handleHeight={HANDLE_HEIGHT}
|
handleHeight={HANDLE_HEIGHT}
|
||||||
index={isModalActive ? 0 : -1}
|
index={isModalActive ? 0 : -1}
|
||||||
enablePanDownToClose
|
enablePanDownToClose
|
||||||
|
@ -216,7 +184,6 @@ export function ModalsContainer() {
|
||||||
}
|
}
|
||||||
handleIndicatorStyle={{backgroundColor: pal.text.color}}
|
handleIndicatorStyle={{backgroundColor: pal.text.color}}
|
||||||
handleStyle={[styles.handle, pal.view]}
|
handleStyle={[styles.handle, pal.view]}
|
||||||
onAnimate={onBottomSheetAnimate}
|
|
||||||
onChange={onBottomSheetChange}>
|
onChange={onBottomSheetChange}>
|
||||||
{element}
|
{element}
|
||||||
</BottomSheet>
|
</BottomSheet>
|
||||||
|
|
|
@ -9,7 +9,6 @@ import {useModals, useModalControls} from '#/state/modals'
|
||||||
import type {Modal as ModalIface} from '#/state/modals'
|
import type {Modal as ModalIface} from '#/state/modals'
|
||||||
import * as ConfirmModal from './Confirm'
|
import * as ConfirmModal from './Confirm'
|
||||||
import * as EditProfileModal from './EditProfile'
|
import * as EditProfileModal from './EditProfile'
|
||||||
import * as ProfilePreviewModal from './ProfilePreview'
|
|
||||||
import * as ServerInputModal from './ServerInput'
|
import * as ServerInputModal from './ServerInput'
|
||||||
import * as ReportModal from './report/Modal'
|
import * as ReportModal from './report/Modal'
|
||||||
import * as AppealLabelModal from './AppealLabel'
|
import * as AppealLabelModal from './AppealLabel'
|
||||||
|
@ -85,8 +84,6 @@ function Modal({modal}: {modal: ModalIface}) {
|
||||||
element = <ConfirmModal.Component {...modal} />
|
element = <ConfirmModal.Component {...modal} />
|
||||||
} else if (modal.name === 'edit-profile') {
|
} else if (modal.name === 'edit-profile') {
|
||||||
element = <EditProfileModal.Component {...modal} />
|
element = <EditProfileModal.Component {...modal} />
|
||||||
} else if (modal.name === 'profile-preview') {
|
|
||||||
element = <ProfilePreviewModal.Component {...modal} />
|
|
||||||
} else if (modal.name === 'server-input') {
|
} else if (modal.name === 'server-input') {
|
||||||
element = <ServerInputModal.Component {...modal} />
|
element = <ServerInputModal.Component {...modal} />
|
||||||
} else if (modal.name === 'report') {
|
} else if (modal.name === 'report') {
|
||||||
|
|
|
@ -1,134 +0,0 @@
|
||||||
import React, {useState, useEffect} from 'react'
|
|
||||||
import {ActivityIndicator, StyleSheet, View} from 'react-native'
|
|
||||||
import {AppBskyActorDefs, ModerationOpts, moderateProfile} from '@atproto/api'
|
|
||||||
import {ThemedText} from '../util/text/ThemedText'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
|
||||||
import {useAnalytics} from 'lib/analytics/analytics'
|
|
||||||
import {ProfileHeader} from '../profile/ProfileHeader'
|
|
||||||
import {InfoCircleIcon} from 'lib/icons'
|
|
||||||
import {useNavigationState} from '@react-navigation/native'
|
|
||||||
import {s} from 'lib/styles'
|
|
||||||
import {useModerationOpts} from '#/state/queries/preferences'
|
|
||||||
import {useProfileQuery} from '#/state/queries/profile'
|
|
||||||
import {ErrorScreen} from '../util/error/ErrorScreen'
|
|
||||||
import {CenteredView} from '../util/Views'
|
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
|
||||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
|
||||||
import {Trans, msg} from '@lingui/macro'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
export const snapPoints = [520, '100%']
|
|
||||||
|
|
||||||
export function Component({did}: {did: string}) {
|
|
||||||
const pal = usePalette('default')
|
|
||||||
const {_} = useLingui()
|
|
||||||
const moderationOpts = useModerationOpts()
|
|
||||||
const {
|
|
||||||
data: profile,
|
|
||||||
error: profileError,
|
|
||||||
refetch: refetchProfile,
|
|
||||||
isLoading: isLoadingProfile,
|
|
||||||
} = useProfileQuery({
|
|
||||||
did: did,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (isLoadingProfile || !moderationOpts) {
|
|
||||||
return (
|
|
||||||
<CenteredView style={[pal.view, s.flex1]}>
|
|
||||||
<ProfileHeader
|
|
||||||
profile={null}
|
|
||||||
moderation={null}
|
|
||||||
isProfilePreview={true}
|
|
||||||
/>
|
|
||||||
</CenteredView>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (profileError) {
|
|
||||||
return (
|
|
||||||
<ErrorScreen
|
|
||||||
title={_(msg`Not Found`)}
|
|
||||||
message={cleanError(profileError)}
|
|
||||||
onPressTryAgain={refetchProfile}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (profile && moderationOpts) {
|
|
||||||
return <ComponentLoaded profile={profile} moderationOpts={moderationOpts} />
|
|
||||||
}
|
|
||||||
// should never happen
|
|
||||||
return (
|
|
||||||
<ErrorScreen
|
|
||||||
title={_(msg`Oops!`)}
|
|
||||||
message={_(msg`Something went wrong and we're not sure what.`)}
|
|
||||||
onPressTryAgain={refetchProfile}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function ComponentLoaded({
|
|
||||||
profile: profileUnshadowed,
|
|
||||||
moderationOpts,
|
|
||||||
}: {
|
|
||||||
profile: AppBskyActorDefs.ProfileViewDetailed
|
|
||||||
moderationOpts: ModerationOpts
|
|
||||||
}) {
|
|
||||||
const pal = usePalette('default')
|
|
||||||
const profile = useProfileShadow(profileUnshadowed)
|
|
||||||
const {screen} = useAnalytics()
|
|
||||||
const moderation = React.useMemo(
|
|
||||||
() => moderateProfile(profile, moderationOpts),
|
|
||||||
[profile, moderationOpts],
|
|
||||||
)
|
|
||||||
|
|
||||||
// track the navigator state to detect if a page-load occurred
|
|
||||||
const navState = useNavigationState(state => state)
|
|
||||||
const [initNavState] = useState(navState)
|
|
||||||
const isLoading = initNavState !== navState
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
screen('Profile:Preview')
|
|
||||||
}, [screen])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View testID="profilePreview" style={[pal.view, s.flex1]}>
|
|
||||||
<View style={[styles.headerWrapper]}>
|
|
||||||
<ProfileHeader
|
|
||||||
profile={profile}
|
|
||||||
moderation={moderation}
|
|
||||||
hideBackButton
|
|
||||||
isProfilePreview
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={[styles.hintWrapper, pal.view]}>
|
|
||||||
<View style={styles.hint}>
|
|
||||||
{isLoading ? (
|
|
||||||
<ActivityIndicator />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<InfoCircleIcon size={21} style={pal.textLight} />
|
|
||||||
<ThemedText type="xl" fg="light">
|
|
||||||
<Trans>Swipe up to see more</Trans>
|
|
||||||
</ThemedText>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
headerWrapper: {
|
|
||||||
height: 440,
|
|
||||||
},
|
|
||||||
hintWrapper: {
|
|
||||||
height: 80,
|
|
||||||
},
|
|
||||||
hint: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'center',
|
|
||||||
gap: 8,
|
|
||||||
paddingHorizontal: 14,
|
|
||||||
borderRadius: 6,
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -1,9 +1,8 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Pressable, StyleProp, ViewStyle} from 'react-native'
|
import {StyleProp, ViewStyle} from 'react-native'
|
||||||
import {Link} from './Link'
|
import {Link} from './Link'
|
||||||
import {isAndroid, isWeb} from 'platform/detection'
|
import {isWeb} from 'platform/detection'
|
||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {useModalControls} from '#/state/modals'
|
|
||||||
import {usePrefetchProfileQuery} from '#/state/queries/profile'
|
import {usePrefetchProfileQuery} from '#/state/queries/profile'
|
||||||
|
|
||||||
interface UserPreviewLinkProps {
|
interface UserPreviewLinkProps {
|
||||||
|
@ -14,38 +13,19 @@ interface UserPreviewLinkProps {
|
||||||
export function UserPreviewLink(
|
export function UserPreviewLink(
|
||||||
props: React.PropsWithChildren<UserPreviewLinkProps>,
|
props: React.PropsWithChildren<UserPreviewLinkProps>,
|
||||||
) {
|
) {
|
||||||
const {openModal} = useModalControls()
|
|
||||||
const prefetchProfileQuery = usePrefetchProfileQuery()
|
const prefetchProfileQuery = usePrefetchProfileQuery()
|
||||||
|
|
||||||
if (isWeb || isAndroid) {
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
onPointerEnter={() => {
|
|
||||||
if (isWeb) {
|
|
||||||
prefetchProfileQuery(props.did)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
href={makeProfileLink(props)}
|
|
||||||
title={props.handle}
|
|
||||||
asAnchor
|
|
||||||
style={props.style}>
|
|
||||||
{props.children}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Link
|
||||||
onPress={() =>
|
onPointerEnter={() => {
|
||||||
openModal({
|
if (isWeb) {
|
||||||
name: 'profile-preview',
|
prefetchProfileQuery(props.did)
|
||||||
did: props.did,
|
}
|
||||||
})
|
}}
|
||||||
}
|
href={makeProfileLink(props)}
|
||||||
accessibilityRole="button"
|
title={props.handle}
|
||||||
accessibilityLabel={props.handle}
|
asAnchor
|
||||||
accessibilityHint=""
|
|
||||||
style={props.style}>
|
style={props.style}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Pressable>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue