diff options
Diffstat (limited to 'src/view/com')
-rw-r--r-- | src/view/com/pager/PagerWithHeader.tsx | 22 | ||||
-rw-r--r-- | src/view/com/pager/PagerWithHeader.web.tsx | 34 | ||||
-rw-r--r-- | src/view/com/profile/ProfileHeader.tsx | 123 | ||||
-rw-r--r-- | src/view/com/util/UserAvatar.tsx | 5 |
4 files changed, 71 insertions, 113 deletions
diff --git a/src/view/com/pager/PagerWithHeader.tsx b/src/view/com/pager/PagerWithHeader.tsx index 7e9ed24db..31abc1ab7 100644 --- a/src/view/com/pager/PagerWithHeader.tsx +++ b/src/view/com/pager/PagerWithHeader.tsx @@ -61,25 +61,21 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>( const headerHeight = headerOnlyHeight + tabBarHeight // capture the header bar sizing - const onTabBarLayout = React.useCallback( - (evt: LayoutChangeEvent) => { - const height = evt.nativeEvent.layout.height - if (height > 0) { - // The rounding is necessary to prevent jumps on iOS - setTabBarHeight(Math.round(height)) - } - }, - [setTabBarHeight], - ) - const onHeaderOnlyLayout = React.useCallback( + const onTabBarLayout = useNonReactiveCallback((evt: LayoutChangeEvent) => { + const height = evt.nativeEvent.layout.height + if (height > 0) { + // The rounding is necessary to prevent jumps on iOS + setTabBarHeight(Math.round(height)) + } + }) + const onHeaderOnlyLayout = useNonReactiveCallback( (evt: LayoutChangeEvent) => { const height = evt.nativeEvent.layout.height - if (height > 0) { + if (height > 0 && isHeaderReady) { // The rounding is necessary to prevent jumps on iOS setHeaderOnlyHeight(Math.round(height)) } }, - [setHeaderOnlyHeight], ) const renderTabBar = React.useCallback( diff --git a/src/view/com/pager/PagerWithHeader.web.tsx b/src/view/com/pager/PagerWithHeader.web.tsx index 4f959d548..9c63c149f 100644 --- a/src/view/com/pager/PagerWithHeader.web.tsx +++ b/src/view/com/pager/PagerWithHeader.web.tsx @@ -31,6 +31,7 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>( children, testID, items, + isHeaderReady, renderHeader, initialPage, onPageSelected, @@ -46,6 +47,7 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>( <PagerTabBar items={items} renderHeader={renderHeader} + isHeaderReady={isHeaderReady} currentPage={currentPage} onCurrentPageSelected={onCurrentPageSelected} onSelect={props.onSelect} @@ -54,7 +56,14 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>( /> ) }, - [items, renderHeader, currentPage, onCurrentPageSelected, testID], + [ + items, + isHeaderReady, + renderHeader, + currentPage, + onCurrentPageSelected, + testID, + ], ) const onPageSelectedInner = React.useCallback( @@ -80,8 +89,14 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>( {toArray(children) .filter(Boolean) .map((child, i) => { + const isReady = isHeaderReady return ( - <View key={i} collapsable={false}> + <View + key={i} + collapsable={false} + style={{ + display: isReady ? undefined : 'none', + }}> <PagerItem isFocused={i === currentPage} renderTab={child} /> </View> ) @@ -94,6 +109,7 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>( let PagerTabBar = ({ currentPage, items, + isHeaderReady, testID, renderHeader, onCurrentPageSelected, @@ -104,6 +120,7 @@ let PagerTabBar = ({ items: string[] testID?: string renderHeader?: () => JSX.Element + isHeaderReady: boolean onCurrentPageSelected?: (index: number) => void onSelect?: (index: number) => void tabBarAnchor?: JSX.Element | null | undefined @@ -112,7 +129,12 @@ let PagerTabBar = ({ const {isMobile} = useWebMediaQueries() return ( <> - <View style={[!isMobile && styles.headerContainerDesktop, pal.border]}> + <View + style={[ + !isMobile && styles.headerContainerDesktop, + pal.border, + !isHeaderReady && styles.loadingHeader, + ]}> {renderHeader?.()} </View> {tabBarAnchor} @@ -123,6 +145,9 @@ let PagerTabBar = ({ ? styles.tabBarContainerMobile : styles.tabBarContainerDesktop, pal.border, + { + display: isHeaderReady ? undefined : 'none', + }, ]}> <TabBar testID={testID} @@ -183,6 +208,9 @@ const styles = StyleSheet.create({ paddingLeft: 14, paddingRight: 14, }, + loadingHeader: { + borderColor: 'transparent', + }, }) function toArray<T>(v: T | T[]): T[] { diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index 2e80ca808..8fd50fad6 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -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, - }, }) diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx index 00ff7e1ec..f673db1ee 100644 --- a/src/view/com/util/UserAvatar.tsx +++ b/src/view/com/util/UserAvatar.tsx @@ -123,6 +123,7 @@ let UserAvatar = ({ usePlainRNImage = false, }: UserAvatarProps): React.ReactNode => { const pal = usePalette('default') + const backgroundColor = pal.colors.backgroundLight const aviStyle = useMemo(() => { if (type === 'algo' || type === 'list') { @@ -130,14 +131,16 @@ let UserAvatar = ({ width: size, height: size, borderRadius: size > 32 ? 8 : 3, + backgroundColor, } } return { width: size, height: size, borderRadius: Math.floor(size / 2), + backgroundColor, } - }, [type, size]) + }, [type, size, backgroundColor]) const alert = useMemo(() => { if (!moderation?.alert) { |