Fix flashes and jumps when opening profile (#2815)
* Don't reset the tree when profile loads fully * Give avatars a background color like placeholders * Prevent jumps due to rich text resolving * Rm log * Rm unused
This commit is contained in:
parent
0d00c7d851
commit
d36b91fe67
6 changed files with 141 additions and 135 deletions
|
@ -51,76 +51,47 @@ import {sanitizeDisplayName} from 'lib/strings/display-names'
|
|||
import {shareUrl} from 'lib/sharing'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {logger} from '#/logger'
|
||||
import {useSession, getAgent} from '#/state/session'
|
||||
import {useSession} from '#/state/session'
|
||||
import {Shadow} from '#/state/cache/types'
|
||||
import {useRequireAuth} from '#/state/session'
|
||||
import {LabelInfo} from '../util/moderation/LabelInfo'
|
||||
import {useProfileShadow} from 'state/cache/profile-shadow'
|
||||
|
||||
interface Props {
|
||||
profile: AppBskyActorDefs.ProfileView | null
|
||||
placeholderData?: AppBskyActorDefs.ProfileView | null
|
||||
moderationOpts: ModerationOpts | null
|
||||
hideBackButton?: boolean
|
||||
isProfilePreview?: boolean
|
||||
}
|
||||
|
||||
export function ProfileHeader({
|
||||
profile,
|
||||
moderationOpts,
|
||||
hideBackButton = false,
|
||||
isProfilePreview,
|
||||
}: Props) {
|
||||
let ProfileHeaderLoading = (_props: {}): React.ReactNode => {
|
||||
const pal = usePalette('default')
|
||||
|
||||
// loading
|
||||
// =
|
||||
if (!profile || !moderationOpts) {
|
||||
return (
|
||||
<View style={pal.view}>
|
||||
<LoadingPlaceholder
|
||||
width="100%"
|
||||
height={150}
|
||||
style={{borderRadius: 0}}
|
||||
/>
|
||||
<View
|
||||
style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
|
||||
<LoadingPlaceholder width={80} height={80} style={styles.br40} />
|
||||
</View>
|
||||
<View style={styles.content}>
|
||||
<View style={[styles.buttonsLine]}>
|
||||
<LoadingPlaceholder width={167} height={31} style={styles.br50} />
|
||||
</View>
|
||||
return (
|
||||
<View style={pal.view}>
|
||||
<LoadingPlaceholder width="100%" height={150} style={{borderRadius: 0}} />
|
||||
<View
|
||||
style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
|
||||
<LoadingPlaceholder width={80} height={80} style={styles.br40} />
|
||||
</View>
|
||||
<View style={styles.content}>
|
||||
<View style={[styles.buttonsLine]}>
|
||||
<LoadingPlaceholder width={167} height={31} style={styles.br50} />
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
// loaded
|
||||
// =
|
||||
return (
|
||||
<ProfileHeaderLoaded
|
||||
profile={profile}
|
||||
moderationOpts={moderationOpts}
|
||||
hideBackButton={hideBackButton}
|
||||
isProfilePreview={isProfilePreview}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
ProfileHeaderLoading = memo(ProfileHeaderLoading)
|
||||
export {ProfileHeaderLoading}
|
||||
|
||||
interface LoadedProps {
|
||||
interface Props {
|
||||
profile: AppBskyActorDefs.ProfileViewDetailed
|
||||
descriptionRT: RichTextAPI | null
|
||||
moderationOpts: ModerationOpts
|
||||
hideBackButton?: boolean
|
||||
isProfilePreview?: boolean
|
||||
isPlaceholderProfile?: boolean
|
||||
}
|
||||
|
||||
let ProfileHeaderLoaded = ({
|
||||
let ProfileHeader = ({
|
||||
profile: profileUnshadowed,
|
||||
descriptionRT,
|
||||
moderationOpts,
|
||||
hideBackButton = false,
|
||||
isProfilePreview,
|
||||
}: LoadedProps): React.ReactNode => {
|
||||
isPlaceholderProfile,
|
||||
}: Props): React.ReactNode => {
|
||||
const profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> =
|
||||
useProfileShadow(profileUnshadowed)
|
||||
const pal = usePalette('default')
|
||||
|
@ -144,37 +115,6 @@ let ProfileHeaderLoaded = ({
|
|||
[profile, moderationOpts],
|
||||
)
|
||||
|
||||
/*
|
||||
* BEGIN handle bio facet resolution
|
||||
*/
|
||||
// should be undefined on first render to trigger a resolution
|
||||
const prevProfileDescription = React.useRef<string | undefined>()
|
||||
const [descriptionRT, setDescriptionRT] = React.useState<
|
||||
RichTextAPI | undefined
|
||||
>(
|
||||
profile.description
|
||||
? new RichTextAPI({text: profile.description})
|
||||
: undefined,
|
||||
)
|
||||
React.useEffect(() => {
|
||||
async function resolveRTFacets() {
|
||||
// new each time
|
||||
const rt = new RichTextAPI({text: profile.description || ''})
|
||||
await rt.detectFacets(getAgent())
|
||||
// replace existing RT instance
|
||||
setDescriptionRT(rt)
|
||||
}
|
||||
|
||||
if (profile.description !== prevProfileDescription.current) {
|
||||
// update prev immediately
|
||||
prevProfileDescription.current = profile.description
|
||||
resolveRTFacets()
|
||||
}
|
||||
}, [profile.description, setDescriptionRT])
|
||||
/*
|
||||
* END handle bio facet resolution
|
||||
*/
|
||||
|
||||
const invalidateProfileQuery = React.useCallback(() => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: profileQueryKey(profile.did),
|
||||
|
@ -454,14 +394,9 @@ let ProfileHeaderLoaded = ({
|
|||
const pluralizedFollowers = pluralize(profile.followersCount || 0, 'follower')
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
pal.view,
|
||||
isProfilePreview && isDesktop && styles.loadingBorderStyle,
|
||||
]}
|
||||
pointerEvents="box-none">
|
||||
<View style={[pal.view]} pointerEvents="box-none">
|
||||
<View pointerEvents="none">
|
||||
{isProfilePreview ? (
|
||||
{isPlaceholderProfile ? (
|
||||
<LoadingPlaceholder
|
||||
width="100%"
|
||||
height={150}
|
||||
|
@ -622,7 +557,7 @@ let ProfileHeaderLoaded = ({
|
|||
{invalidHandle ? _(msg`⚠Invalid Handle`) : `@${profile.handle}`}
|
||||
</ThemedText>
|
||||
</View>
|
||||
{!isProfilePreview && !blockHide && (
|
||||
{!isPlaceholderProfile && !blockHide && (
|
||||
<>
|
||||
<View style={styles.metricsLine} pointerEvents="box-none">
|
||||
<Link
|
||||
|
@ -737,7 +672,8 @@ let ProfileHeaderLoaded = ({
|
|||
</View>
|
||||
)
|
||||
}
|
||||
ProfileHeaderLoaded = memo(ProfileHeaderLoaded)
|
||||
ProfileHeader = memo(ProfileHeader)
|
||||
export {ProfileHeader}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
banner: {
|
||||
|
@ -845,9 +781,4 @@ const styles = StyleSheet.create({
|
|||
|
||||
br40: {borderRadius: 40},
|
||||
br50: {borderRadius: 50},
|
||||
|
||||
loadingBorderStyle: {
|
||||
borderLeftWidth: 1,
|
||||
borderRightWidth: 1,
|
||||
},
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue