Optimize Drawer re-renders (#2108)

zio/stable
dan 2023-12-06 17:50:06 +00:00 committed by GitHub
parent 8e541d753a
commit 7d158f82fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 315 additions and 185 deletions

View File

@ -55,13 +55,13 @@ import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed'
import {NavSignupCard} from '#/view/shell/NavSignupCard' import {NavSignupCard} from '#/view/shell/NavSignupCard'
import {truncateAndInvalidate} from '#/state/queries/util' import {truncateAndInvalidate} from '#/state/queries/util'
export function DrawerProfileCard({ let DrawerProfileCard = ({
account, account,
onPressProfile, onPressProfile,
}: { }: {
account: SessionAccount account: SessionAccount
onPressProfile: () => void onPressProfile: () => void
}) { }): React.ReactNode => {
const {_} = useLingui() const {_} = useLingui()
const pal = usePalette('default') const pal = usePalette('default')
const {data: profile} = useProfileQuery({did: account.did}) const {data: profile} = useProfileQuery({did: account.did})
@ -103,11 +103,12 @@ export function DrawerProfileCard({
</TouchableOpacity> </TouchableOpacity>
) )
} }
DrawerProfileCard = React.memo(DrawerProfileCard)
export {DrawerProfileCard}
export function DrawerContent() { let DrawerContent = ({}: {}): React.ReactNode => {
const theme = useTheme() const theme = useTheme()
const pal = usePalette('default') const pal = usePalette('default')
const {_} = useLingui()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const setDrawerOpen = useSetDrawerOpen() const setDrawerOpen = useSetDrawerOpen()
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
@ -115,7 +116,6 @@ export function DrawerContent() {
const {isAtHome, isAtSearch, isAtFeeds, isAtNotifications, isAtMyProfile} = const {isAtHome, isAtSearch, isAtFeeds, isAtNotifications, isAtMyProfile} =
useNavigationTabState() useNavigationTabState()
const {hasSession, currentAccount} = useSession() const {hasSession, currentAccount} = useSession()
const numUnreadNotifications = useUnreadNotifications()
// events // events
// = // =
@ -229,158 +229,26 @@ export function DrawerContent() {
<NavSignupCard /> <NavSignupCard />
)} )}
{hasSession && <InviteCodes style={{paddingLeft: 0}} />} {hasSession && <InviteCodes />}
{hasSession && <View style={{height: 10}} />} {hasSession && <View style={{height: 10}} />}
<SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} />
<MenuItem <HomeMenuItem isActive={isAtHome} onPress={onPressHome} />
icon={
isAtSearch ? (
<MagnifyingGlassIcon2Solid
style={pal.text as StyleProp<ViewStyle>}
size={24}
strokeWidth={1.7}
/>
) : (
<MagnifyingGlassIcon2
style={pal.text as StyleProp<ViewStyle>}
size={24}
strokeWidth={1.7}
/>
)
}
label={_(msg`Search`)}
accessibilityLabel={_(msg`Search`)}
accessibilityHint=""
bold={isAtSearch}
onPress={onPressSearch}
/>
<MenuItem
icon={
isAtHome ? (
<HomeIconSolid
style={pal.text as StyleProp<ViewStyle>}
size="24"
strokeWidth={3.25}
/>
) : (
<HomeIcon
style={pal.text as StyleProp<ViewStyle>}
size="24"
strokeWidth={3.25}
/>
)
}
label={_(msg`Home`)}
accessibilityLabel={_(msg`Home`)}
accessibilityHint=""
bold={isAtHome}
onPress={onPressHome}
/>
{hasSession && ( {hasSession && (
<MenuItem <NotificationsMenuItem
icon={ isActive={isAtNotifications}
isAtNotifications ? (
<BellIconSolid
style={pal.text as StyleProp<ViewStyle>}
size="24"
strokeWidth={1.7}
/>
) : (
<BellIcon
style={pal.text as StyleProp<ViewStyle>}
size="24"
strokeWidth={1.7}
/>
)
}
label={_(msg`Notifications`)}
accessibilityLabel={_(msg`Notifications`)}
accessibilityHint={
numUnreadNotifications === ''
? ''
: `${numUnreadNotifications} unread`
}
count={numUnreadNotifications}
bold={isAtNotifications}
onPress={onPressNotifications} onPress={onPressNotifications}
/> />
)} )}
<FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
<MenuItem
icon={
isAtFeeds ? (
<HashtagIcon
strokeWidth={3}
style={pal.text as FontAwesomeIconStyle}
size={24}
/>
) : (
<HashtagIcon
strokeWidth={2}
style={pal.text as FontAwesomeIconStyle}
size={24}
/>
)
}
label={_(msg`Feeds`)}
accessibilityLabel={_(msg`Feeds`)}
accessibilityHint=""
bold={isAtFeeds}
onPress={onPressMyFeeds}
/>
{hasSession && ( {hasSession && (
<> <>
<MenuItem <ListsMenuItem onPress={onPressLists} />
icon={<ListIcon strokeWidth={2} style={pal.text} size={26} />} <ModerationMenuItem onPress={onPressModeration} />
label={_(msg`Lists`)} <ProfileMenuItem
accessibilityLabel={_(msg`Lists`)} isActive={isAtMyProfile}
accessibilityHint=""
onPress={onPressLists}
/>
<MenuItem
icon={<HandIcon strokeWidth={5} style={pal.text} size={24} />}
label={_(msg`Moderation`)}
accessibilityLabel={_(msg`Moderation`)}
accessibilityHint=""
onPress={onPressModeration}
/>
<MenuItem
icon={
isAtMyProfile ? (
<UserIconSolid
style={pal.text as StyleProp<ViewStyle>}
size="26"
strokeWidth={1.5}
/>
) : (
<UserIcon
style={pal.text as StyleProp<ViewStyle>}
size="26"
strokeWidth={1.5}
/>
)
}
label={_(msg`Profile`)}
accessibilityLabel={_(msg`Profile`)}
accessibilityHint=""
onPress={onPressProfile} onPress={onPressProfile}
/> />
<MenuItem <SettingsMenuItem onPress={onPressSettings} />
icon={
<CogIcon
style={pal.text as StyleProp<ViewStyle>}
size="26"
strokeWidth={1.75}
/>
}
label={_(msg`Settings`)}
accessibilityLabel={_(msg`Settings`)}
accessibilityHint=""
onPress={onPressSettings}
/>
</> </>
)} )}
@ -388,43 +256,64 @@ export function DrawerContent() {
<View style={styles.smallSpacer} /> <View style={styles.smallSpacer} />
</ScrollView> </ScrollView>
<View style={styles.footer}> <DrawerFooter
<TouchableOpacity onPressFeedback={onPressFeedback}
accessibilityRole="link" onPressHelp={onPressHelp}
accessibilityLabel={_(msg`Send feedback`)} />
accessibilityHint=""
onPress={onPressFeedback}
style={[
styles.footerBtn,
styles.footerBtnFeedback,
theme.colorScheme === 'light'
? styles.footerBtnFeedbackLight
: styles.footerBtnFeedbackDark,
]}>
<FontAwesomeIcon
style={pal.link as FontAwesomeIconStyle}
size={18}
icon={['far', 'message']}
/>
<Text type="lg-medium" style={[pal.link, s.pl10]}>
<Trans>Feedback</Trans>
</Text>
</TouchableOpacity>
<TouchableOpacity
accessibilityRole="link"
accessibilityLabel={_(msg`Send feedback`)}
accessibilityHint=""
onPress={onPressHelp}
style={[styles.footerBtn]}>
<Text type="lg-medium" style={[pal.link, s.pl10]}>
<Trans>Help</Trans>
</Text>
</TouchableOpacity>
</View>
</SafeAreaView> </SafeAreaView>
</View> </View>
) )
} }
DrawerContent = React.memo(DrawerContent)
export {DrawerContent}
let DrawerFooter = ({
onPressFeedback,
onPressHelp,
}: {
onPressFeedback: () => void
onPressHelp: () => void
}): React.ReactNode => {
const theme = useTheme()
const pal = usePalette('default')
const {_} = useLingui()
return (
<View style={styles.footer}>
<TouchableOpacity
accessibilityRole="link"
accessibilityLabel={_(msg`Send feedback`)}
accessibilityHint=""
onPress={onPressFeedback}
style={[
styles.footerBtn,
styles.footerBtnFeedback,
theme.colorScheme === 'light'
? styles.footerBtnFeedbackLight
: styles.footerBtnFeedbackDark,
]}>
<FontAwesomeIcon
style={pal.link as FontAwesomeIconStyle}
size={18}
icon={['far', 'message']}
/>
<Text type="lg-medium" style={[pal.link, s.pl10]}>
<Trans>Feedback</Trans>
</Text>
</TouchableOpacity>
<TouchableOpacity
accessibilityRole="link"
accessibilityLabel={_(msg`Send feedback`)}
accessibilityHint=""
onPress={onPressHelp}
style={[styles.footerBtn]}>
<Text type="lg-medium" style={[pal.link, s.pl10]}>
<Trans>Help</Trans>
</Text>
</TouchableOpacity>
</View>
)
}
DrawerFooter = React.memo(DrawerFooter)
interface MenuItemProps extends ComponentProps<typeof TouchableOpacity> { interface MenuItemProps extends ComponentProps<typeof TouchableOpacity> {
icon: JSX.Element icon: JSX.Element
@ -433,6 +322,244 @@ interface MenuItemProps extends ComponentProps<typeof TouchableOpacity> {
bold?: boolean bold?: boolean
} }
let SearchMenuItem = ({
isActive,
onPress,
}: {
isActive: boolean
onPress: () => void
}): React.ReactNode => {
const {_} = useLingui()
const pal = usePalette('default')
return (
<MenuItem
icon={
isActive ? (
<MagnifyingGlassIcon2Solid
style={pal.text as StyleProp<ViewStyle>}
size={24}
strokeWidth={1.7}
/>
) : (
<MagnifyingGlassIcon2
style={pal.text as StyleProp<ViewStyle>}
size={24}
strokeWidth={1.7}
/>
)
}
label={_(msg`Search`)}
accessibilityLabel={_(msg`Search`)}
accessibilityHint=""
bold={isActive}
onPress={onPress}
/>
)
}
SearchMenuItem = React.memo(SearchMenuItem)
let HomeMenuItem = ({
isActive,
onPress,
}: {
isActive: boolean
onPress: () => void
}): React.ReactNode => {
const {_} = useLingui()
const pal = usePalette('default')
return (
<MenuItem
icon={
isActive ? (
<HomeIconSolid
style={pal.text as StyleProp<ViewStyle>}
size="24"
strokeWidth={3.25}
/>
) : (
<HomeIcon
style={pal.text as StyleProp<ViewStyle>}
size="24"
strokeWidth={3.25}
/>
)
}
label={_(msg`Home`)}
accessibilityLabel={_(msg`Home`)}
accessibilityHint=""
bold={isActive}
onPress={onPress}
/>
)
}
HomeMenuItem = React.memo(HomeMenuItem)
let NotificationsMenuItem = ({
isActive,
onPress,
}: {
isActive: boolean
onPress: () => void
}): React.ReactNode => {
const {_} = useLingui()
const pal = usePalette('default')
const numUnreadNotifications = useUnreadNotifications()
return (
<MenuItem
icon={
isActive ? (
<BellIconSolid
style={pal.text as StyleProp<ViewStyle>}
size="24"
strokeWidth={1.7}
/>
) : (
<BellIcon
style={pal.text as StyleProp<ViewStyle>}
size="24"
strokeWidth={1.7}
/>
)
}
label={_(msg`Notifications`)}
accessibilityLabel={_(msg`Notifications`)}
accessibilityHint={
numUnreadNotifications === '' ? '' : `${numUnreadNotifications} unread`
}
count={numUnreadNotifications}
bold={isActive}
onPress={onPress}
/>
)
}
NotificationsMenuItem = React.memo(NotificationsMenuItem)
let FeedsMenuItem = ({
isActive,
onPress,
}: {
isActive: boolean
onPress: () => void
}): React.ReactNode => {
const {_} = useLingui()
const pal = usePalette('default')
return (
<MenuItem
icon={
isActive ? (
<HashtagIcon
strokeWidth={3}
style={pal.text as FontAwesomeIconStyle}
size={24}
/>
) : (
<HashtagIcon
strokeWidth={2}
style={pal.text as FontAwesomeIconStyle}
size={24}
/>
)
}
label={_(msg`Feeds`)}
accessibilityLabel={_(msg`Feeds`)}
accessibilityHint=""
bold={isActive}
onPress={onPress}
/>
)
}
FeedsMenuItem = React.memo(FeedsMenuItem)
let ListsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
const {_} = useLingui()
const pal = usePalette('default')
return (
<MenuItem
icon={<ListIcon strokeWidth={2} style={pal.text} size={26} />}
label={_(msg`Lists`)}
accessibilityLabel={_(msg`Lists`)}
accessibilityHint=""
onPress={onPress}
/>
)
}
ListsMenuItem = React.memo(ListsMenuItem)
let ModerationMenuItem = ({
onPress,
}: {
onPress: () => void
}): React.ReactNode => {
const {_} = useLingui()
const pal = usePalette('default')
return (
<MenuItem
icon={<HandIcon strokeWidth={5} style={pal.text} size={24} />}
label={_(msg`Moderation`)}
accessibilityLabel={_(msg`Moderation`)}
accessibilityHint=""
onPress={onPress}
/>
)
}
ModerationMenuItem = React.memo(ModerationMenuItem)
let ProfileMenuItem = ({
isActive,
onPress,
}: {
isActive: boolean
onPress: () => void
}): React.ReactNode => {
const {_} = useLingui()
const pal = usePalette('default')
return (
<MenuItem
icon={
isActive ? (
<UserIconSolid
style={pal.text as StyleProp<ViewStyle>}
size="26"
strokeWidth={1.5}
/>
) : (
<UserIcon
style={pal.text as StyleProp<ViewStyle>}
size="26"
strokeWidth={1.5}
/>
)
}
label={_(msg`Profile`)}
accessibilityLabel={_(msg`Profile`)}
accessibilityHint=""
onPress={onPress}
/>
)
}
ProfileMenuItem = React.memo(ProfileMenuItem)
let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
const {_} = useLingui()
const pal = usePalette('default')
return (
<MenuItem
icon={
<CogIcon
style={pal.text as StyleProp<ViewStyle>}
size="26"
strokeWidth={1.75}
/>
}
label={_(msg`Settings`)}
accessibilityLabel={_(msg`Settings`)}
accessibilityHint=""
onPress={onPress}
/>
)
}
SettingsMenuItem = React.memo(SettingsMenuItem)
function MenuItem({ function MenuItem({
icon, icon,
label, label,
@ -478,7 +605,7 @@ function MenuItem({
) )
} }
function InviteCodes({style}: {style?: StyleProp<ViewStyle>}) { let InviteCodes = ({}: {}): React.ReactNode => {
const {track} = useAnalytics() const {track} = useAnalytics()
const setDrawerOpen = useSetDrawerOpen() const setDrawerOpen = useSetDrawerOpen()
const pal = usePalette('default') const pal = usePalette('default')
@ -496,7 +623,7 @@ function InviteCodes({style}: {style?: StyleProp<ViewStyle>}) {
return ( return (
<TouchableOpacity <TouchableOpacity
testID="menuItemInviteCodes" testID="menuItemInviteCodes"
style={[styles.inviteCodes, style]} style={styles.inviteCodes}
onPress={onPress} onPress={onPress}
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={_(msg`Invite codes: ${invitesAvailable} available`)} accessibilityLabel={_(msg`Invite codes: ${invitesAvailable} available`)}
@ -526,6 +653,7 @@ function InviteCodes({style}: {style?: StyleProp<ViewStyle>}) {
</TouchableOpacity> </TouchableOpacity>
) )
} }
InviteCodes = React.memo(InviteCodes)
const styles = StyleSheet.create({ const styles = StyleSheet.create({
view: { view: {
@ -595,7 +723,7 @@ const styles = StyleSheet.create({
}, },
inviteCodes: { inviteCodes: {
paddingLeft: 22, paddingLeft: 0,
paddingVertical: 8, paddingVertical: 8,
flexDirection: 'row', flexDirection: 'row',
}, },

View File

@ -11,7 +11,7 @@ import {Button} from '#/view/com/util/forms/Button'
import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import {useCloseAllActiveElements} from '#/state/util' import {useCloseAllActiveElements} from '#/state/util'
export function NavSignupCard() { let NavSignupCard = ({}: {}): React.ReactNode => {
const {_} = useLingui() const {_} = useLingui()
const pal = usePalette('default') const pal = usePalette('default')
const {setShowLoggedOut} = useLoggedOutViewControls() const {setShowLoggedOut} = useLoggedOutViewControls()
@ -59,3 +59,5 @@ export function NavSignupCard() {
</View> </View>
) )
} }
NavSignupCard = React.memo(NavSignupCard)
export {NavSignupCard}