Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update react-navigation (#5967)

authored by

dan and committed by
GitHub
f9389634 42c4da1e

+292 -255
+5 -5
package.json
··· 94 94 "@react-native-async-storage/async-storage": "2.1.2", 95 95 "@react-native-menu/menu": "^1.2.3", 96 96 "@react-native-picker/picker": "2.11.0", 97 - "@react-navigation/bottom-tabs": "^6.5.20", 98 - "@react-navigation/drawer": "^6.6.15", 99 - "@react-navigation/native": "^6.1.17", 100 - "@react-navigation/native-stack": "^6.9.26", 97 + "@react-navigation/bottom-tabs": "^7.3.13", 98 + "@react-navigation/drawer": "^7.3.12", 99 + "@react-navigation/native": "^7.1.9", 100 + "@react-navigation/native-stack": "^7.3.13", 101 101 "@sentry/react-native": "~6.10.0", 102 102 "@tanstack/query-async-storage-persister": "^5.25.0", 103 103 "@tanstack/react-query": "^5.8.1", ··· 198 198 "react-native-reanimated": "~3.17.5", 199 199 "react-native-root-siblings": "^4.1.1", 200 200 "react-native-safe-area-context": "5.4.0", 201 - "react-native-screens": "~4.10.0", 201 + "react-native-screens": "^4.11.1", 202 202 "react-native-svg": "15.11.2", 203 203 "react-native-uitextview": "^1.4.0", 204 204 "react-native-url-polyfill": "^1.3.0",
+42 -62
src/Navigation.tsx
··· 103 103 import {Wizard} from '#/screens/StarterPack/Wizard' 104 104 import TopicScreen from '#/screens/Topic' 105 105 import {VideoFeed} from '#/screens/VideoFeed' 106 - import {useTheme} from '#/alf' 106 + import {type Theme, useTheme} from '#/alf' 107 107 import { 108 108 EmailDialogScreenID, 109 109 useEmailDialogControl, ··· 127 127 /** 128 128 * These "common screens" are reused across stacks. 129 129 */ 130 - function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { 130 + function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) { 131 131 const title = (page: MessageDescriptor) => 132 132 bskyTitle(i18n._(page), unreadCountLabel) 133 133 ··· 500 500 <Tab.Screen name="HomeTab" getComponent={() => HomeTabNavigator} /> 501 501 <Tab.Screen name="SearchTab" getComponent={() => SearchTabNavigator} /> 502 502 <Tab.Screen 503 + name="MessagesTab" 504 + getComponent={() => MessagesTabNavigator} 505 + /> 506 + <Tab.Screen 503 507 name="NotificationsTab" 504 508 getComponent={() => NotificationsTabNavigator} 505 509 /> ··· 507 511 name="MyProfileTab" 508 512 getComponent={() => MyProfileTabNavigator} 509 513 /> 510 - <Tab.Screen 511 - name="MessagesTab" 512 - getComponent={() => MessagesTabNavigator} 513 - /> 514 514 </Tab.Navigator> 515 515 ) 516 516 } 517 517 518 + function screenOptions(t: Theme) { 519 + return { 520 + fullScreenGestureEnabled: true, 521 + headerShown: false, 522 + contentStyle: t.atoms.bg, 523 + } as const 524 + } 525 + 518 526 function HomeTabNavigator() { 519 527 const t = useTheme() 520 528 521 529 return ( 522 - <HomeTab.Navigator 523 - screenOptions={{ 524 - animationDuration: 285, 525 - gestureEnabled: true, 526 - fullScreenGestureEnabled: true, 527 - headerShown: false, 528 - contentStyle: t.atoms.bg, 529 - }}> 530 + <HomeTab.Navigator screenOptions={screenOptions(t)} initialRouteName="Home"> 530 531 <HomeTab.Screen name="Home" getComponent={() => HomeScreen} /> 531 532 <HomeTab.Screen name="Start" getComponent={() => HomeScreen} /> 532 - {commonScreens(HomeTab)} 533 + {commonScreens(HomeTab as typeof Flat)} 533 534 </HomeTab.Navigator> 534 535 ) 535 536 } ··· 538 539 const t = useTheme() 539 540 return ( 540 541 <SearchTab.Navigator 541 - screenOptions={{ 542 - animationDuration: 285, 543 - gestureEnabled: true, 544 - fullScreenGestureEnabled: true, 545 - headerShown: false, 546 - contentStyle: t.atoms.bg, 547 - }}> 542 + screenOptions={screenOptions(t)} 543 + initialRouteName="Search"> 548 544 <SearchTab.Screen name="Search" getComponent={() => SearchScreen} /> 549 - {commonScreens(SearchTab as typeof HomeTab)} 545 + {commonScreens(SearchTab as typeof Flat)} 550 546 </SearchTab.Navigator> 551 547 ) 552 548 } ··· 555 551 const t = useTheme() 556 552 return ( 557 553 <NotificationsTab.Navigator 558 - screenOptions={{ 559 - animationDuration: 285, 560 - gestureEnabled: true, 561 - fullScreenGestureEnabled: true, 562 - headerShown: false, 563 - contentStyle: t.atoms.bg, 564 - }}> 554 + screenOptions={screenOptions(t)} 555 + initialRouteName="Notifications"> 565 556 <NotificationsTab.Screen 566 557 name="Notifications" 567 558 getComponent={() => NotificationsScreen} 568 559 options={{requireAuth: true}} 569 560 /> 570 - {commonScreens(NotificationsTab as typeof HomeTab)} 561 + {commonScreens(NotificationsTab as typeof Flat)} 571 562 </NotificationsTab.Navigator> 572 563 ) 573 564 } ··· 576 567 const t = useTheme() 577 568 return ( 578 569 <MyProfileTab.Navigator 579 - screenOptions={{ 580 - animationDuration: 285, 581 - gestureEnabled: true, 582 - fullScreenGestureEnabled: true, 583 - headerShown: false, 584 - contentStyle: t.atoms.bg, 585 - }}> 570 + screenOptions={screenOptions(t)} 571 + initialRouteName="MyProfile"> 586 572 <MyProfileTab.Screen 587 - // @ts-ignore // TODO: fix this broken type in ProfileScreen 588 - name="MyProfile" 573 + // MyProfile is not in AllNavigationParams - asserting as Profile at least 574 + // gives us typechecking for initialParams -sfn 575 + name={'MyProfile' as 'Profile'} 589 576 getComponent={() => ProfileScreen} 590 - initialParams={{ 591 - name: 'me', 592 - hideBackButton: true, 593 - }} 577 + initialParams={{name: 'me', hideBackButton: true}} 594 578 /> 595 - {commonScreens(MyProfileTab as typeof HomeTab)} 579 + {commonScreens(MyProfileTab as unknown as typeof Flat)} 596 580 </MyProfileTab.Navigator> 597 581 ) 598 582 } ··· 601 585 const t = useTheme() 602 586 return ( 603 587 <MessagesTab.Navigator 604 - screenOptions={{ 605 - animationDuration: 285, 606 - gestureEnabled: true, 607 - fullScreenGestureEnabled: true, 608 - headerShown: false, 609 - contentStyle: t.atoms.bg, 610 - }}> 588 + screenOptions={screenOptions(t)} 589 + initialRouteName="Messages"> 611 590 <MessagesTab.Screen 612 591 name="Messages" 613 592 getComponent={() => MessagesScreen} ··· 616 595 animationTypeForReplace: route.params?.animation ?? 'push', 617 596 })} 618 597 /> 619 - {commonScreens(MessagesTab as typeof HomeTab)} 598 + {commonScreens(MessagesTab as typeof Flat)} 620 599 </MessagesTab.Navigator> 621 600 ) 622 601 } ··· 634 613 return ( 635 614 <Flat.Navigator 636 615 screenListeners={screenListeners} 637 - screenOptions={{ 638 - animationDuration: 285, 639 - gestureEnabled: true, 640 - fullScreenGestureEnabled: true, 641 - headerShown: false, 642 - contentStyle: t.atoms.bg, 643 - }}> 616 + screenOptions={screenOptions(t)}> 644 617 <Flat.Screen 645 618 name="Home" 646 619 getComponent={() => HomeScreen} ··· 666 639 getComponent={() => HomeScreen} 667 640 options={{title: title(msg`Home`)}} 668 641 /> 669 - {commonScreens(Flat as typeof HomeTab, numUnread)} 642 + {commonScreens(Flat, numUnread)} 670 643 </Flat.Navigator> 671 644 ) 672 645 } ··· 773 746 logModuleInitTime() 774 747 onReady() 775 748 logger.metric('router:navigate', {}, {statsig: false}) 776 - }}> 749 + }} 750 + // WARNING: Implicit navigation to nested navigators is depreciated in React Navigation 7.x 751 + // However, there's a fair amount of places we do that, especially in when popping to the top of stacks. 752 + // See BottomBar.tsx for an example of how to handle nested navigators in the tabs correctly. 753 + // I'm scared of missing a spot (esp. with push notifications etc) so let's enable this legacy behaviour for now. 754 + // We will need to confirm we handle nested navigators correctly by the time we migrate to React Navigation 8.x 755 + // -sfn 756 + navigationInChildEnabled> 777 757 {children} 778 758 </NavigationContainer> 779 759 </>
+26 -10
src/components/Link.tsx
··· 1 - import React from 'react' 1 + import React, {useMemo} from 'react' 2 2 import {type GestureResponderEvent} from 'react-native' 3 3 import {sanitizeUrl} from '@braintree/sanitize-url' 4 - import {StackActions, useLinkProps} from '@react-navigation/native' 4 + import { 5 + type LinkProps as RNLinkProps, 6 + StackActions, 7 + useLinkBuilder, 8 + } from '@react-navigation/native' 5 9 6 10 import {BSKY_DOWNLOAD_URL} from '#/lib/constants' 7 11 import {useNavigationDeduped} from '#/lib/hooks/useNavigationDeduped' ··· 28 32 */ 29 33 export {useButtonContext as useLinkContext} from '#/components/Button' 30 34 31 - type BaseLinkProps = Pick< 32 - Parameters<typeof useLinkProps<AllNavigatorParams>>[0], 33 - 'to' 34 - > & { 35 + type BaseLinkProps = { 35 36 testID?: string 37 + 38 + to: RNLinkProps<AllNavigatorParams> | string 36 39 37 40 /** 38 41 * The React Navigation `StackAction` to perform when the link is pressed. ··· 92 95 shouldProxy?: boolean 93 96 }) { 94 97 const navigation = useNavigationDeduped() 95 - const {href} = useLinkProps<AllNavigatorParams>({ 96 - to: 97 - typeof to === 'string' ? convertBskyAppUrlIfNeeded(sanitizeUrl(to)) : to, 98 - }) 98 + const {buildHref} = useLinkBuilder() 99 + const href = useMemo(() => { 100 + return typeof to === 'string' 101 + ? convertBskyAppUrlIfNeeded(sanitizeUrl(to)) 102 + : to.screen 103 + ? buildHref(to.screen, to.params) 104 + : to.href 105 + ? convertBskyAppUrlIfNeeded(sanitizeUrl(to.href)) 106 + : undefined 107 + }, [to, buildHref]) 108 + 109 + if (!href) { 110 + throw new Error( 111 + 'Link `to` prop must be a string or an object with `screen` and `params` properties', 112 + ) 113 + } 114 + 99 115 const isExternal = isExternalUrl(href) 100 116 const {openModal, closeModal} = useModalControls() 101 117 const openLink = useOpenLink()
+9 -38
src/lib/hooks/useNavigationDeduped.ts
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {useNavigation} from '@react-navigation/core' 3 - import {NavigationState} from '@react-navigation/native' 4 - import type {NavigationAction} from '@react-navigation/routers' 5 3 6 4 import {useDedupe} from '#/lib/hooks/useDedupe' 7 - import {AllNavigatorParams, NavigationProp} from '#/lib/routes/types' 5 + import {type NavigationProp} from '#/lib/routes/types' 8 6 9 7 export type DebouncedNavigationProp = Pick< 10 8 NavigationProp, ··· 22 20 const navigation = useNavigation<NavigationProp>() 23 21 const dedupe = useDedupe() 24 22 25 - return React.useMemo( 26 - (): DebouncedNavigationProp => ({ 27 - // Types from @react-navigation/routers/lib/typescript/src/StackRouter.ts 28 - push: <RouteName extends keyof AllNavigatorParams>( 29 - ...args: undefined extends AllNavigatorParams[RouteName] 30 - ? 31 - | [screen: RouteName] 32 - | [screen: RouteName, params: AllNavigatorParams[RouteName]] 33 - : [screen: RouteName, params: AllNavigatorParams[RouteName]] 34 - ) => { 23 + return useMemo<DebouncedNavigationProp>( 24 + () => ({ 25 + push: (...args: Parameters<typeof navigation.push>) => { 35 26 dedupe(() => navigation.push(...args)) 36 27 }, 37 - // Types from @react-navigation/core/src/types.tsx 38 - navigate: <RouteName extends keyof AllNavigatorParams>( 39 - ...args: RouteName extends unknown 40 - ? undefined extends AllNavigatorParams[RouteName] 41 - ? 42 - | [screen: RouteName] 43 - | [screen: RouteName, params: AllNavigatorParams[RouteName]] 44 - : [screen: RouteName, params: AllNavigatorParams[RouteName]] 45 - : never 46 - ) => { 28 + navigate: (...args: Parameters<typeof navigation.navigate>) => { 47 29 dedupe(() => navigation.navigate(...args)) 48 30 }, 49 - // Types from @react-navigation/routers/lib/typescript/src/StackRouter.ts 50 - replace: <RouteName extends keyof AllNavigatorParams>( 51 - ...args: undefined extends AllNavigatorParams[RouteName] 52 - ? 53 - | [screen: RouteName] 54 - | [screen: RouteName, params: AllNavigatorParams[RouteName]] 55 - : [screen: RouteName, params: AllNavigatorParams[RouteName]] 56 - ) => { 31 + replace: (...args: Parameters<typeof navigation.replace>) => { 57 32 dedupe(() => navigation.replace(...args)) 58 33 }, 59 - dispatch: ( 60 - action: 61 - | NavigationAction 62 - | ((state: NavigationState) => NavigationAction), 63 - ) => { 64 - dedupe(() => navigation.dispatch(action)) 34 + dispatch: (...args: Parameters<typeof navigation.dispatch>) => { 35 + dedupe(() => navigation.dispatch(...args)) 65 36 }, 66 37 popToTop: () => { 67 38 dedupe(() => navigation.popToTop())
+1 -1
src/lib/routes/types.ts
··· 92 92 } 93 93 94 94 export type MyProfileTabNavigatorParams = CommonNavigatorParams & { 95 - MyProfile: undefined 95 + MyProfile: {name: 'me'; hideBackButton: true} 96 96 } 97 97 98 98 export type MessagesTabNavigatorParams = CommonNavigatorParams & {
+44 -39
src/view/com/util/Link.tsx
··· 1 - import React, {ComponentProps, memo, useMemo} from 'react' 1 + import {memo, useCallback, useMemo} from 'react' 2 2 import { 3 - GestureResponderEvent, 3 + type GestureResponderEvent, 4 4 Platform, 5 5 Pressable, 6 - StyleProp, 7 - TextProps, 8 - TextStyle, 9 - TouchableOpacity, 6 + type StyleProp, 7 + type TextProps, 8 + type TextStyle, 9 + type TouchableOpacity, 10 10 View, 11 - ViewStyle, 11 + type ViewStyle, 12 12 } from 'react-native' 13 13 import {sanitizeUrl} from '@braintree/sanitize-url' 14 - import {StackActions, useLinkProps} from '@react-navigation/native' 14 + import {StackActions} from '@react-navigation/native' 15 15 16 16 import { 17 - DebouncedNavigationProp, 17 + type DebouncedNavigationProp, 18 18 useNavigationDeduped, 19 19 } from '#/lib/hooks/useNavigationDeduped' 20 20 import {useOpenLink} from '#/lib/hooks/useOpenLink' ··· 24 24 isExternalUrl, 25 25 linkRequiresWarning, 26 26 } from '#/lib/strings/url-helpers' 27 - import {TypographyVariant} from '#/lib/ThemeContext' 27 + import {type TypographyVariant} from '#/lib/ThemeContext' 28 28 import {isAndroid, isWeb} from '#/platform/detection' 29 29 import {emitSoftReset} from '#/state/events' 30 30 import {useModalControls} from '#/state/modals' ··· 38 38 | React.MouseEvent<HTMLAnchorElement, MouseEvent> 39 39 | GestureResponderEvent 40 40 41 - interface Props extends ComponentProps<typeof TouchableOpacity> { 41 + interface Props extends React.ComponentProps<typeof TouchableOpacity> { 42 42 testID?: string 43 43 style?: StyleProp<ViewStyle> 44 44 href?: string ··· 47 47 hoverStyle?: StyleProp<ViewStyle> 48 48 noFeedback?: boolean 49 49 asAnchor?: boolean 50 - dataSet?: Object | undefined 50 + dataSet?: any 51 51 anchorNoUnderline?: boolean 52 52 navigationAction?: 'push' | 'replace' | 'navigate' 53 53 onPointerEnter?: () => void ··· 69 69 onBeforePress, 70 70 accessibilityActions, 71 71 onAccessibilityAction, 72 + dataSet: dataSetProp, 72 73 ...props 73 74 }: Props) { 74 75 const t = useTheme() ··· 77 78 const anchorHref = asAnchor ? sanitizeUrl(href) : undefined 78 79 const openLink = useOpenLink() 79 80 80 - const onPress = React.useCallback( 81 + const onPress = useCallback( 81 82 (e?: Event) => { 82 83 onBeforePress?.() 83 84 if (typeof href === 'string') { ··· 99 100 {name: 'activate', label: title}, 100 101 ] 101 102 103 + const dataSet = useMemo(() => { 104 + const ds = {...dataSetProp} 105 + if (anchorNoUnderline) { 106 + ds.noUnderline = 1 107 + } 108 + return ds 109 + }, [dataSetProp, anchorNoUnderline]) 110 + 102 111 if (noFeedback) { 103 112 return ( 104 113 <WebAuxClickWrapper> ··· 129 138 ) 130 139 } 131 140 132 - if (anchorNoUnderline) { 133 - // @ts-ignore web only -prf 134 - props.dataSet = props.dataSet || {} 135 - // @ts-ignore web only -prf 136 - props.dataSet.noUnderline = 1 137 - } 138 - 139 - if (title && !props.accessibilityLabel) { 140 - props.accessibilityLabel = title 141 - } 142 - 143 141 const Com = props.hoverStyle ? PressableWithHover : Pressable 144 142 return ( 145 143 <Com ··· 148 146 onPress={onPress} 149 147 accessible={accessible} 150 148 accessibilityRole="link" 149 + accessibilityLabel={props.accessibilityLabel ?? title} 150 + accessibilityHint={props.accessibilityHint} 151 151 // @ts-ignore web only -prf 152 152 href={anchorHref} 153 + dataSet={dataSet} 153 154 {...props}> 154 155 {children ? children : <Text>{title || 'link'}</Text>} 155 156 </Com> ··· 164 165 text, 165 166 numberOfLines, 166 167 lineHeight, 167 - dataSet, 168 + dataSet: dataSetProp, 168 169 title, 169 - onPress, 170 + onPress: onPressProp, 170 171 onBeforePress, 171 172 disableMismatchWarning, 172 173 navigationAction, 173 174 anchorNoUnderline, 174 - ...orgProps 175 + ...props 175 176 }: { 176 177 testID?: string 177 178 type?: TypographyVariant ··· 187 188 anchorNoUnderline?: boolean 188 189 onBeforePress?: () => void 189 190 } & TextProps) { 190 - const {...props} = useLinkProps({to: sanitizeUrl(href)}) 191 191 const navigation = useNavigationDeduped() 192 192 const {openModal, closeModal} = useModalControls() 193 193 const openLink = useOpenLink() ··· 196 196 console.error('Unable to detect mismatching label') 197 197 } 198 198 199 - if (anchorNoUnderline) { 200 - dataSet = dataSet ?? {} 201 - dataSet.noUnderline = 1 202 - } 199 + const dataSet = useMemo(() => { 200 + const ds = {...dataSetProp} 201 + if (anchorNoUnderline) { 202 + ds.noUnderline = 1 203 + } 204 + return ds 205 + }, [dataSetProp, anchorNoUnderline]) 203 206 204 - props.onPress = React.useCallback( 207 + const onPress = useCallback( 205 208 (e?: Event) => { 206 209 const requiresWarning = 207 210 !disableMismatchWarning && ··· 224 227 return 225 228 } 226 229 onBeforePress?.() 227 - if (onPress) { 230 + if (onPressProp) { 228 231 e?.preventDefault?.() 229 - // @ts-ignore function signature differs by platform -prf 230 - return onPress() 232 + // @ts-expect-error function signature differs by platform -prf 233 + return onPressProp() 231 234 } 232 235 return onPressInner( 233 236 closeModal, ··· 240 243 }, 241 244 [ 242 245 onBeforePress, 243 - onPress, 246 + onPressProp, 244 247 closeModal, 245 248 openModal, 246 249 navigation, ··· 273 276 title={title} 274 277 // @ts-ignore web only -prf 275 278 hrefAttrs={hrefAttrs} // hack to get open in new tab to work on safari. without this, safari will open in a new window 276 - {...props} 277 - {...orgProps}> 279 + onPress={onPress} 280 + accessibilityRole="link" 281 + href={convertBskyAppUrlIfNeeded(sanitizeUrl(href))} 282 + {...props}> 278 283 {text} 279 284 </Text> 280 285 )
+18 -4
src/view/shell/Drawer.tsx
··· 160 160 // = 161 161 162 162 const onPressTab = React.useCallback( 163 - (tab: string) => { 163 + (tab: 'Home' | 'Search' | 'Messages' | 'Notifications' | 'MyProfile') => { 164 164 const state = navigation.getState() 165 165 setDrawerOpen(false) 166 166 if (isWeb) { ··· 168 168 if (tab === 'MyProfile') { 169 169 navigation.navigate('Profile', {name: currentAccount!.handle}) 170 170 } else { 171 - // @ts-ignore must be Home, Search, Notifications, or MyProfile 171 + // @ts-expect-error struggles with string unions, apparently 172 172 navigation.navigate(tab) 173 173 } 174 174 } else { ··· 176 176 if (tabState === TabState.InsideAtRoot) { 177 177 emitSoftReset() 178 178 } else if (tabState === TabState.Inside) { 179 - navigation.dispatch(StackActions.popToTop()) 179 + // find the correct navigator in which to pop-to-top 180 + const target = state.routes.find(route => route.name === `${tab}Tab`) 181 + ?.state?.key 182 + if (target) { 183 + // if we found it, trigger pop-to-top 184 + navigation.dispatch({ 185 + ...StackActions.popToTop(), 186 + target, 187 + }) 188 + } else { 189 + // fallback: reset navigation 190 + navigation.reset({ 191 + index: 0, 192 + routes: [{name: `${tab}Tab`}], 193 + }) 194 + } 180 195 } else { 181 - // @ts-ignore must be Home, Search, Notifications, or MyProfile 182 196 navigation.navigate(`${tab}Tab`) 183 197 } 184 198 }
+30 -22
src/view/shell/bottom-bar/BottomBar.tsx
··· 1 - import React, {type ComponentProps} from 'react' 1 + import {useCallback} from 'react' 2 2 import {type GestureResponderEvent, View} from 'react-native' 3 3 import Animated from 'react-native-reanimated' 4 4 import {useSafeAreaInsets} from 'react-native-safe-area-context' ··· 52 52 import {useDemoMode} from '#/storage/hooks/demo-mode' 53 53 import {styles} from './BottomBarStyles' 54 54 55 - type TabOptions = 56 - | 'Home' 57 - | 'Search' 58 - | 'Notifications' 59 - | 'MyProfile' 60 - | 'Feeds' 61 - | 'Messages' 55 + type TabOptions = 'Home' | 'Search' | 'Messages' | 'Notifications' | 'MyProfile' 62 56 63 57 export function BottomBar({navigation}: BottomTabBarProps) { 64 58 const {hasSession, currentAccount} = useSession() ··· 81 75 const gate = useGate() 82 76 const iconWidth = 28 83 77 84 - const showSignIn = React.useCallback(() => { 78 + const showSignIn = useCallback(() => { 85 79 closeAllActiveElements() 86 80 requestSwitchToAccount({requestedAccount: 'none'}) 87 81 }, [requestSwitchToAccount, closeAllActiveElements]) 88 82 89 - const showCreateAccount = React.useCallback(() => { 83 + const showCreateAccount = useCallback(() => { 90 84 closeAllActiveElements() 91 85 requestSwitchToAccount({requestedAccount: 'new'}) 92 86 // setShowLoggedOut(true) 93 87 }, [requestSwitchToAccount, closeAllActiveElements]) 94 88 95 - const onPressTab = React.useCallback( 89 + const onPressTab = useCallback( 96 90 (tab: TabOptions) => { 97 91 const state = navigation.getState() 98 92 const tabState = getTabState(state, tab) 99 93 if (tabState === TabState.InsideAtRoot) { 100 94 emitSoftReset() 101 95 } else if (tabState === TabState.Inside) { 102 - dedupe(() => navigation.dispatch(StackActions.popToTop())) 96 + // find the correct navigator in which to pop-to-top 97 + const target = state.routes.find(route => route.name === `${tab}Tab`) 98 + ?.state?.key 99 + dedupe(() => { 100 + if (target) { 101 + // if we found it, trigger pop-to-top 102 + navigation.dispatch({ 103 + ...StackActions.popToTop(), 104 + target, 105 + }) 106 + } else { 107 + // fallback: reset navigation 108 + navigation.reset({ 109 + index: 0, 110 + routes: [{name: `${tab}Tab`}], 111 + }) 112 + } 113 + }) 103 114 } else { 104 115 dedupe(() => navigation.navigate(`${tab}Tab`)) 105 116 } 106 117 }, 107 118 [navigation, dedupe], 108 119 ) 109 - const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab]) 110 - const onPressSearch = React.useCallback( 111 - () => onPressTab('Search'), 112 - [onPressTab], 113 - ) 114 - const onPressNotifications = React.useCallback( 120 + const onPressHome = useCallback(() => onPressTab('Home'), [onPressTab]) 121 + const onPressSearch = useCallback(() => onPressTab('Search'), [onPressTab]) 122 + const onPressNotifications = useCallback( 115 123 () => onPressTab('Notifications'), 116 124 [onPressTab], 117 125 ) 118 - const onPressProfile = React.useCallback(() => { 126 + const onPressProfile = useCallback(() => { 119 127 onPressTab('MyProfile') 120 128 }, [onPressTab]) 121 - const onPressMessages = React.useCallback(() => { 129 + const onPressMessages = useCallback(() => { 122 130 onPressTab('Messages') 123 131 }, [onPressTab]) 124 132 125 - const onLongPressProfile = React.useCallback(() => { 133 + const onLongPressProfile = useCallback(() => { 126 134 playHaptic() 127 135 accountSwitchControl.open() 128 136 }, [accountSwitchControl, playHaptic]) ··· 361 369 362 370 interface BtnProps 363 371 extends Pick< 364 - ComponentProps<typeof PressableScale>, 372 + React.ComponentProps<typeof PressableScale>, 365 373 | 'accessible' 366 374 | 'accessibilityRole' 367 375 | 'accessibilityHint'
+37 -11
src/view/shell/createNativeStackNavigatorWithAuth.tsx
··· 1 1 import * as React from 'react' 2 2 import {View} from 'react-native' 3 - // Based on @react-navigation/native-stack/src/createNativeStackNavigator.ts 3 + // Based on @react-navigation/native-stack/src/navigators/createNativeStackNavigator.ts 4 4 // MIT License 5 5 // Copyright (c) 2017 React Navigation Contributors 6 6 import { 7 7 createNavigatorFactory, 8 8 type EventArg, 9 + type NavigatorTypeBagBase, 9 10 type ParamListBase, 10 11 type StackActionHelpers, 11 12 StackActions, 12 13 type StackNavigationState, 13 14 StackRouter, 14 15 type StackRouterOptions, 16 + type StaticConfig, 17 + type TypedNavigator, 15 18 useNavigationBuilder, 16 19 } from '@react-navigation/native' 20 + import {NativeStackView} from '@react-navigation/native-stack' 17 21 import { 18 22 type NativeStackNavigationEventMap, 19 23 type NativeStackNavigationOptions, 24 + type NativeStackNavigationProp, 25 + type NativeStackNavigatorProps, 20 26 } from '@react-navigation/native-stack' 21 - import {NativeStackView} from '@react-navigation/native-stack' 22 - import {type NativeStackNavigatorProps} from '@react-navigation/native-stack/src/types' 23 27 24 28 import {PWI_ENABLED} from '#/lib/build-flags' 25 29 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' ··· 48 52 id, 49 53 initialRouteName, 50 54 children, 55 + layout, 51 56 screenListeners, 52 57 screenOptions, 58 + screenLayout, 53 59 ...rest 54 60 }: NativeStackNavigatorProps) { 55 61 // --- this is copy and pasted from the original native stack navigator --- 56 - const {state, descriptors, navigation, NavigationContent} = 62 + const {state, describe, descriptors, navigation, NavigationContent} = 57 63 useNavigationBuilder< 58 64 StackNavigationState<ParamListBase>, 59 65 StackRouterOptions, ··· 64 70 id, 65 71 initialRouteName, 66 72 children, 73 + layout, 67 74 screenListeners, 68 75 screenOptions, 76 + screenLayout, 69 77 }) 78 + 70 79 React.useEffect( 71 80 () => 72 81 // @ts-expect-error: there may not be a tab navigator in parent ··· 148 157 {...rest} 149 158 state={state} 150 159 navigation={navigation} 151 - descriptors={newDescriptors} 160 + descriptors={descriptors} 161 + describe={describe} 152 162 /> 153 163 </View> 154 164 {isWeb && ( ··· 161 171 ) 162 172 } 163 173 164 - export const createNativeStackNavigatorWithAuth = createNavigatorFactory< 165 - StackNavigationState<ParamListBase>, 166 - NativeStackNavigationOptionsWithAuth, 167 - NativeStackNavigationEventMap, 168 - typeof NativeStackNavigator 169 - >(NativeStackNavigator) 174 + export function createNativeStackNavigatorWithAuth< 175 + const ParamList extends ParamListBase, 176 + const NavigatorID extends string | undefined = undefined, 177 + const TypeBag extends NavigatorTypeBagBase = { 178 + ParamList: ParamList 179 + NavigatorID: NavigatorID 180 + State: StackNavigationState<ParamList> 181 + ScreenOptions: NativeStackNavigationOptionsWithAuth 182 + EventMap: NativeStackNavigationEventMap 183 + NavigationList: { 184 + [RouteName in keyof ParamList]: NativeStackNavigationProp< 185 + ParamList, 186 + RouteName, 187 + NavigatorID 188 + > 189 + } 190 + Navigator: typeof NativeStackNavigator 191 + }, 192 + const Config extends StaticConfig<TypeBag> = StaticConfig<TypeBag>, 193 + >(config?: Config): TypedNavigator<TypeBag, Config> { 194 + return createNavigatorFactory(NativeStackNavigator)(config) 195 + }
+8 -8
src/view/shell/desktop/LeftNav.tsx
··· 1 - import React from 'react' 1 + import {useCallback, useMemo, useState} from 'react' 2 2 import {StyleSheet, View} from 'react-native' 3 3 import {type AppBskyActorDefs} from '@atproto/api' 4 4 import {msg, plural, Trans} from '@lingui/macro' 5 5 import {useLingui} from '@lingui/react' 6 6 import { 7 - useLinkProps, 7 + useLinkTo, 8 8 useNavigation, 9 9 useNavigationState, 10 10 } from '@react-navigation/native' ··· 326 326 const {_} = useLingui() 327 327 const {currentAccount} = useSession() 328 328 const {leftNavMinimal} = useLayoutBreakpoints() 329 - const [pathName] = React.useMemo(() => router.matchPath(href), [href]) 329 + const [pathName] = useMemo(() => router.matchPath(href), [href]) 330 330 const currentRouteInfo = useNavigationState(state => { 331 331 if (!state) { 332 332 return {name: 'Home'} ··· 339 339 (currentRouteInfo.params as CommonNavigatorParams['Profile']).name === 340 340 currentAccount?.handle 341 341 : isTab(currentRouteInfo.name, pathName) 342 - const {onPress} = useLinkProps({to: href}) 343 - const onPressWrapped = React.useCallback( 342 + const linkTo = useLinkTo() 343 + const onPressWrapped = useCallback( 344 344 (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => { 345 345 if (e.ctrlKey || e.metaKey || e.altKey) { 346 346 return ··· 349 349 if (isCurrent) { 350 350 emitSoftReset() 351 351 } else { 352 - onPress() 352 + linkTo(href) 353 353 } 354 354 }, 355 - [onPress, isCurrent], 355 + [linkTo, href, isCurrent], 356 356 ) 357 357 358 358 return ( ··· 468 468 const {openComposer} = useOpenComposer() 469 469 const {_} = useLingui() 470 470 const {leftNavMinimal} = useLayoutBreakpoints() 471 - const [isFetchingHandle, setIsFetchingHandle] = React.useState(false) 471 + const [isFetchingHandle, setIsFetchingHandle] = useState(false) 472 472 const fetchHandle = useFetchHandle() 473 473 474 474 const getProfileHandle = async () => {
+72 -55
yarn.lock
··· 6289 6289 invariant "^2.2.4" 6290 6290 nullthrows "^1.1.1" 6291 6291 6292 - "@react-navigation/bottom-tabs@^6.5.20": 6293 - version "6.5.20" 6294 - resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-6.5.20.tgz#5335e75b02c527ef0569bd97d4f9185d65616e49" 6295 - integrity sha512-ow6Z06iS4VqBO8d7FP+HsGjJLWt2xTWIvuWjpoCvsM/uQXzCRDIjBv9HaKcXbF0yTW7IMir0oDAbU5PFzEDdgA== 6292 + "@react-navigation/bottom-tabs@^7.3.13": 6293 + version "7.3.14" 6294 + resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-7.3.14.tgz#9ee02baea86ab24abe267726665bc69c6df0bf4c" 6295 + integrity sha512-s2qinJggS2HYZdCOey9A+fN+bNpWeEKwiL/FjAVOTcv+uofxPWN6CtEZUZGPEjfRjis/srURBmCmpNZSI6sQ9Q== 6296 6296 dependencies: 6297 - "@react-navigation/elements" "^1.3.30" 6297 + "@react-navigation/elements" "^2.4.3" 6298 6298 color "^4.2.3" 6299 - warn-once "^0.1.0" 6300 6299 6301 - "@react-navigation/core@^6.4.16": 6302 - version "6.4.16" 6303 - resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-6.4.16.tgz#f9369a134805174536b9aa0f0f483b930511caf9" 6304 - integrity sha512-UDTJBsHxnzgFETR3ZxhctP+RWr4SkyeZpbhpkQoIGOuwSCkt1SE0qjU48/u6r6w6XlX8OqVudn1Ab0QFXTHxuQ== 6300 + "@react-navigation/core@^7.10.0": 6301 + version "7.10.0" 6302 + resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-7.10.0.tgz#8205ea6b84ce34b2fc2c196701b4cd9b434211b9" 6303 + integrity sha512-qZBA5gGm+9liT4+EHk+kl9apwvqh7HqhLF1XeX6SQRmC/n2QI0u1B8OevKc+EPUDEM9Od15IuwT/GRbSs7/Umw== 6305 6304 dependencies: 6306 - "@react-navigation/routers" "^6.1.9" 6305 + "@react-navigation/routers" "^7.4.0" 6307 6306 escape-string-regexp "^4.0.0" 6308 - nanoid "^3.1.23" 6307 + nanoid "^3.3.11" 6309 6308 query-string "^7.1.3" 6310 - react-is "^16.13.0" 6311 - use-latest-callback "^0.1.9" 6309 + react-is "^19.1.0" 6310 + use-latest-callback "^0.2.3" 6311 + use-sync-external-store "^1.5.0" 6312 6312 6313 - "@react-navigation/drawer@^6.6.15": 6314 - version "6.6.15" 6315 - resolved "https://registry.yarnpkg.com/@react-navigation/drawer/-/drawer-6.6.15.tgz#fcedba68f735103dbc035911f5959ce926081d62" 6316 - integrity sha512-GLkFQNxjtmxB/qXSHmu1DfoB89jCzW64tmX68iPndth+9U+0IP27GcCCaMZxQfwj+nI8Kn2zlTlXAZDIIHE+DQ== 6313 + "@react-navigation/drawer@^7.3.12": 6314 + version "7.4.1" 6315 + resolved "https://registry.yarnpkg.com/@react-navigation/drawer/-/drawer-7.4.1.tgz#50517d8c57f09cdbfc20a485c47016066b918e76" 6316 + integrity sha512-kj5wL31smDLw/6l+0KPR5cjaOZg6oHJCl3RPQonFPuYolUPZBVnuS++uvlifWcD/mqdGmhl3rgLTircRH4vQ7Q== 6317 6317 dependencies: 6318 - "@react-navigation/elements" "^1.3.30" 6318 + "@react-navigation/elements" "^2.4.3" 6319 6319 color "^4.2.3" 6320 - warn-once "^0.1.0" 6320 + react-native-drawer-layout "^4.1.10" 6321 + use-latest-callback "^0.2.3" 6321 6322 6322 - "@react-navigation/elements@^1.3.30": 6323 - version "1.3.30" 6324 - resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.30.tgz#a81371f599af1070b12014f05d6c09b1a611fd9a" 6325 - integrity sha512-plhc8UvCZs0UkV+sI+3bisIyn78wz9O/BiWZXpounu72k/R/Sj5PuZYFJ1fi6psvriUveMCGh4LeZckAZu2qiQ== 6323 + "@react-navigation/elements@^2.4.3": 6324 + version "2.4.3" 6325 + resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-2.4.3.tgz#cc1dde4c98739d35a0c9c23872316063962cfaee" 6326 + integrity sha512-psoNmnZ0DQIt9nxxPITVLtYW04PGCAfnmd/Pcd3yhiBs93aj+HYKH+SDZDpUnXMf3BN7Wvo4+jPI+/Xjqb+m9w== 6327 + dependencies: 6328 + color "^4.2.3" 6326 6329 6327 - "@react-navigation/native-stack@^6.9.26": 6328 - version "6.9.26" 6329 - resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-6.9.26.tgz#90facf7783c9927f094bc9f01c613af75b6c241e" 6330 - integrity sha512-++dueQ+FDj2XkZ902DVrK79ub1vp19nSdAZWxKRgd6+Bc0Niiesua6rMCqymYOVaYh+dagwkA9r00bpt/U5WLw== 6330 + "@react-navigation/native-stack@^7.3.13": 6331 + version "7.3.14" 6332 + resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-7.3.14.tgz#d1c90f2e50cd13bbced923991cf2faee8083f725" 6333 + integrity sha512-45Sf7ReqSCIySXS5nrKtLGmNlFXm5x+u32YQMwKDONCqVGOBCfo4ryKqeQq1EMJ7Py6IDyOwHMhA+jhNOxnfPw== 6331 6334 dependencies: 6332 - "@react-navigation/elements" "^1.3.30" 6333 - warn-once "^0.1.0" 6335 + "@react-navigation/elements" "^2.4.3" 6336 + warn-once "^0.1.1" 6334 6337 6335 - "@react-navigation/native@^6.1.17": 6336 - version "6.1.17" 6337 - resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-6.1.17.tgz#439f15a99809d26ea4682d2a3766081cf2ca31cf" 6338 - integrity sha512-mer3OvfwWOHoUSMJyLa4vnBH3zpFmCwuzrBPlw7feXklurr/ZDiLjLxUScOot6jLRMz/67GyilEYMmP99LL0RQ== 6338 + "@react-navigation/native@^7.1.9": 6339 + version "7.1.10" 6340 + resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-7.1.10.tgz#768f674f7c09b6a57215762052aa62a7dc107402" 6341 + integrity sha512-Ug4IML0DkAxZTMF/E7lyyLXSclkGAYElY2cxZWITwfBjtlVeda0NjsdnTWY5EGjnd7bwvhTIUC+CO6qSlrDn5A== 6339 6342 dependencies: 6340 - "@react-navigation/core" "^6.4.16" 6343 + "@react-navigation/core" "^7.10.0" 6341 6344 escape-string-regexp "^4.0.0" 6342 6345 fast-deep-equal "^3.1.3" 6343 - nanoid "^3.1.23" 6346 + nanoid "^3.3.11" 6347 + use-latest-callback "^0.2.3" 6344 6348 6345 - "@react-navigation/routers@^6.1.9": 6346 - version "6.1.9" 6347 - resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-6.1.9.tgz#73f5481a15a38e36592a0afa13c3c064b9f90bed" 6348 - integrity sha512-lTM8gSFHSfkJvQkxacGM6VJtBt61ip2XO54aNfswD+KMw6eeZ4oehl7m0me3CR9hnDE4+60iAZR8sAhvCiI3NA== 6349 + "@react-navigation/routers@^7.4.0": 6350 + version "7.4.0" 6351 + resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-7.4.0.tgz#5bace799713ac163310c18711b98dfbe418c6b36" 6352 + integrity sha512-th5THnuWKJlmr7GGHiicy979di11ycDWub9iIXbEDvQwmwmsRzppmVbfs2nD8bC/MgyMgqWu/gxfys+HqN+kcw== 6349 6353 dependencies: 6350 - nanoid "^3.1.23" 6354 + nanoid "^3.3.11" 6351 6355 6352 6356 "@remirror/core-constants@3.0.0": 6353 6357 version "3.0.0" ··· 14962 14966 object-assign "^4.0.1" 14963 14967 thenify-all "^1.0.0" 14964 14968 14965 - nanoid@^3.1.23, nanoid@^3.3.1, nanoid@^3.3.6: 14969 + nanoid@^3.3.1, nanoid@^3.3.6: 14966 14970 version "3.3.6" 14967 14971 resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" 14968 14972 integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== 14973 + 14974 + nanoid@^3.3.11: 14975 + version "3.3.11" 14976 + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" 14977 + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== 14969 14978 14970 14979 nanoid@^3.3.7: 14971 14980 version "3.3.7" ··· 16727 16736 resolved "https://registry.yarnpkg.com/react-image-crop/-/react-image-crop-11.0.7.tgz#25f3d37ccbb65a05d19d23b4740a5912835c741e" 16728 16737 integrity sha512-ZciKWHDYzmm366JDL18CbrVyjnjH0ojufGDmScfS4ZUqLHg4nm6ATY+K62C75W4ZRNt4Ii+tX0bSjNk9LQ2xzQ== 16729 16738 16730 - react-is@19, react-is@^19.0.0: 16739 + react-is@19, react-is@^19.0.0, react-is@^19.1.0: 16731 16740 version "19.1.0" 16732 16741 resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.1.0.tgz#805bce321546b7e14c084989c77022351bbdd11b" 16733 16742 integrity sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg== 16734 16743 16735 - react-is@^16.13.0, react-is@^16.13.1, react-is@^16.7.0: 16744 + react-is@^16.13.1, react-is@^16.7.0: 16736 16745 version "16.13.1" 16737 16746 resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" 16738 16747 integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== ··· 16763 16772 integrity sha512-6vnIE+WHABSeHCaYP6l3O1BOEhWxKH6nHAdV7n/wKn/sciZ64zPPp2NUdEUf1m7g4uuzlLbjgr+6uDt89q2DOg== 16764 16773 dependencies: 16765 16774 dotenv "^16.4.5" 16775 + 16776 + react-native-drawer-layout@^4.1.10: 16777 + version "4.1.10" 16778 + resolved "https://registry.yarnpkg.com/react-native-drawer-layout/-/react-native-drawer-layout-4.1.10.tgz#9007cb747767ca8e1c9c3337671ad35ed95ad4d9" 16779 + integrity sha512-wejQo0F+EffCkOkRh+DP6ENWMB+aWEHkXV8Pd564PmtoySZLUsV/ksYrh/mrufh7T7EuvGT8+fNHz7mMRYftWg== 16780 + dependencies: 16781 + use-latest-callback "^0.2.3" 16766 16782 16767 16783 react-native-drawer-layout@^4.1.6: 16768 16784 version "4.1.7" ··· 16806 16822 dependencies: 16807 16823 "@dominicstop/ts-event-emitter" "^1.1.0" 16808 16824 16809 - react-native-is-edge-to-edge@1.1.7: 16825 + react-native-is-edge-to-edge@1.1.7, react-native-is-edge-to-edge@^1.1.7: 16810 16826 version "1.1.7" 16811 16827 resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.7.tgz#28947688f9fafd584e73a4f935ea9603bd9b1939" 16812 16828 integrity sha512-EH6i7E8epJGIcu7KpfXYXiV2JFIYITtq+rVS8uEb+92naMRBdxhTuS8Wn2Q7j9sqyO0B+Xbaaf9VdipIAmGW4w== ··· 16875 16891 resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-5.4.0.tgz#04b51940408c114f75628a12a93569d30c525454" 16876 16892 integrity sha512-JaEThVyJcLhA+vU0NU8bZ0a1ih6GiF4faZ+ArZLqpYbL6j7R3caRqj+mE3lEtKCuHgwjLg3bCxLL1GPUJZVqUA== 16877 16893 16878 - react-native-screens@~4.10.0: 16879 - version "4.10.0" 16880 - resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.10.0.tgz#40634aead590c6b7034ded6a9f92465d1d611906" 16881 - integrity sha512-Tw21NGuXm3PbiUGtZd0AnXirUixaAbPXDjNR0baBH7/WJDaDTTELLcQ7QRXuqAWbmr/EVCrKj1348ei1KFIr8A== 16894 + react-native-screens@^4.11.1: 16895 + version "4.11.1" 16896 + resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.11.1.tgz#7d0f3d313d8ddc1e55437c5e038f15f8805dc991" 16897 + integrity sha512-F0zOzRVa3ptZfLpD0J8ROdo+y1fEPw+VBFq1MTY/iyDu08al7qFUO5hLMd+EYMda5VXGaTFCa8q7bOppUszhJw== 16882 16898 dependencies: 16883 16899 react-freeze "^1.0.0" 16900 + react-native-is-edge-to-edge "^1.1.7" 16884 16901 warn-once "^0.1.0" 16885 16902 16886 16903 react-native-svg@15.11.2: ··· 19388 19405 resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" 19389 19406 integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== 19390 19407 19391 - use-latest-callback@^0.1.9: 19392 - version "0.1.9" 19393 - resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.1.9.tgz#10191dc54257e65a8e52322127643a8940271e2a" 19394 - integrity sha512-CL/29uS74AwreI/f2oz2hLTW7ZqVeV5+gxFeGudzQrgkCytrHw33G4KbnQOrRlAEzzAFXi7dDLMC9zhWcVpzmw== 19395 - 19396 19408 use-latest-callback@^0.2.3: 19397 19409 version "0.2.3" 19398 19410 resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.2.3.tgz#2d644d3063040b9bc2d4c55bb525a13ae3de9e16" ··· 19425 19437 version "1.2.2" 19426 19438 resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" 19427 19439 integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== 19440 + 19441 + use-sync-external-store@^1.5.0: 19442 + version "1.5.0" 19443 + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#55122e2a3edd2a6c106174c27485e0fd59bcfca0" 19444 + integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A== 19428 19445 19429 19446 util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: 19430 19447 version "1.0.2" ··· 19527 19544 dependencies: 19528 19545 makeerror "1.0.12" 19529 19546 19530 - warn-once@0.1.1, warn-once@^0.1.0: 19547 + warn-once@0.1.1, warn-once@^0.1.0, warn-once@^0.1.1: 19531 19548 version "0.1.1" 19532 19549 resolved "https://registry.yarnpkg.com/warn-once/-/warn-once-0.1.1.tgz#952088f4fb56896e73fd4e6a3767272a3fccce43" 19533 19550 integrity sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==