Merge branch 'bluesky-social:main' into patch-3

This commit is contained in:
Minseo Lee 2024-03-02 13:04:51 +09:00 committed by GitHub
commit ab2b454be8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 1299 additions and 527 deletions

View file

@ -18,6 +18,8 @@ import {Mark} from '@tiptap/core'
import {Plugin, PluginKey} from '@tiptap/pm/state'
import {Node as ProsemirrorNode} from '@tiptap/pm/model'
import {Decoration, DecorationSet} from '@tiptap/pm/view'
import {URL_REGEX} from '@atproto/api'
import {isValidDomain} from 'lib/strings/url-helpers'
export const LinkDecorator = Mark.create({
@ -78,8 +80,7 @@ function linkDecorator() {
function iterateUris(str: string, cb: (from: number, to: number) => void) {
let match
const re =
/(^|\s|\()((https?:\/\/[\S]+)|((?<domain>[a-z][a-z0-9]*(\.[a-z0-9]+)+)[\S]*))/gim
const re = URL_REGEX
while ((match = re.exec(str))) {
let uri = match[2]
if (!uri.startsWith('http')) {

View file

@ -18,28 +18,36 @@ import {Mark} from '@tiptap/core'
import {Plugin, PluginKey} from '@tiptap/pm/state'
import {Node as ProsemirrorNode} from '@tiptap/pm/model'
import {Decoration, DecorationSet} from '@tiptap/pm/view'
import {TAG_REGEX, TRAILING_PUNCTUATION_REGEX} from '@atproto/api'
function getDecorations(doc: ProsemirrorNode) {
const decorations: Decoration[] = []
doc.descendants((node, pos) => {
if (node.isText && node.text) {
const regex = /(?:^|\s)(#[^\d\s]\S*)(?=\s)?/g
const regex = TAG_REGEX
const textContent = node.textContent
let match
while ((match = regex.exec(textContent))) {
const [matchedString, tag] = match
const [matchedString, _, tag] = match
if (tag.length > 66) continue
if (!tag || tag.replace(TRAILING_PUNCTUATION_REGEX, '').length > 64)
continue
const [trailingPunc = ''] = tag.match(/\p{P}+$/u) || []
const [trailingPunc = ''] = tag.match(TRAILING_PUNCTUATION_REGEX) || []
const matchedFrom = match.index + matchedString.indexOf(tag)
const matchedTo = matchedFrom + (tag.length - trailingPunc.length)
const from = match.index + matchedString.indexOf(tag)
const to = from + (tag.length - trailingPunc.length)
/*
* The match is exclusive of `#` so we need to adjust the start of the
* highlight by -1 to include the `#`
*/
const start = pos + matchedFrom - 1
const end = pos + matchedTo
decorations.push(
Decoration.inline(pos + from, pos + to, {
Decoration.inline(start, end, {
class: 'autolink',
}),
)

View file

@ -52,7 +52,7 @@ export function HomeHeader(
)
return (
<HomeHeaderLayout>
<HomeHeaderLayout tabBarAnchor={props.tabBarAnchor}>
<TabBar
key={items.join(',')}
onPressSelected={props.onPressSelected}

View file

@ -1,13 +1,10 @@
import React from 'react'
import {StyleSheet, View} from 'react-native'
import Animated from 'react-native-reanimated'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile'
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
import {useShellLayout} from '#/state/shell/shell-layout'
import {Logo} from '#/view/icons/Logo'
import {Link, TextLink} from '../util/Link'
import {Link} from '../util/Link'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
@ -16,41 +13,42 @@ import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'
import {CogIcon} from '#/lib/icons'
export function HomeHeaderLayout({children}: {children: React.ReactNode}) {
export function HomeHeaderLayout(props: {
children: React.ReactNode
tabBarAnchor: JSX.Element | null | undefined
}) {
const {isMobile} = useWebMediaQueries()
if (isMobile) {
return <HomeHeaderLayoutMobile>{children}</HomeHeaderLayoutMobile>
return <HomeHeaderLayoutMobile {...props} />
} else {
return <HomeHeaderLayoutTablet>{children}</HomeHeaderLayoutTablet>
return <HomeHeaderLayoutDesktopAndTablet {...props} />
}
}
function HomeHeaderLayoutTablet({children}: {children: React.ReactNode}) {
function HomeHeaderLayoutDesktopAndTablet({
children,
tabBarAnchor,
}: {
children: React.ReactNode
tabBarAnchor: JSX.Element | null | undefined
}) {
const pal = usePalette('default')
const {headerMinimalShellTransform} = useMinimalShellMode()
const {headerHeight} = useShellLayout()
const {_} = useLingui()
return (
// @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf
<Animated.View
style={[pal.view, pal.border, styles.tabBar, headerMinimalShellTransform]}
onLayout={e => {
headerHeight.value = e.nativeEvent.layout.height
}}>
<View style={[pal.view, styles.topBar]}>
<TextLink
type="title-lg"
<>
<View style={[pal.view, pal.border, styles.bar, styles.topBar]}>
<Link
href="/settings/following-feed"
hitSlop={10}
accessibilityRole="button"
accessibilityLabel={_(msg`Following Feed Preferences`)}
accessibilityHint=""
text={
<FontAwesomeIcon
icon="sliders"
style={pal.textLight as FontAwesomeIconStyle}
/>
}
/>
accessibilityHint="">
<FontAwesomeIcon
icon="sliders"
style={pal.textLight as FontAwesomeIconStyle}
/>
</Link>
<Logo width={28} />
<Link
href="/settings/saved-feeds"
@ -61,32 +59,38 @@ function HomeHeaderLayoutTablet({children}: {children: React.ReactNode}) {
<CogIcon size={22} strokeWidth={2} style={pal.textLight} />
</Link>
</View>
{children}
</Animated.View>
{tabBarAnchor}
<View style={[pal.view, pal.border, styles.bar, styles.tabBar]}>
{children}
</View>
</>
)
}
const styles = StyleSheet.create({
bar: {
// @ts-ignore Web only
left: 'calc(50% - 300px)',
width: 600,
borderLeftWidth: 1,
borderRightWidth: 1,
},
topBar: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 18,
paddingVertical: 8,
marginTop: 8,
width: '100%',
paddingTop: 16,
paddingBottom: 8,
},
tabBar: {
// @ts-ignore Web only
position: 'sticky',
zIndex: 1,
// @ts-ignore Web only -prf
left: 'calc(50% - 300px)',
width: 600,
top: 0,
flexDirection: 'column',
alignItems: 'center',
borderLeftWidth: 1,
borderRightWidth: 1,
zIndex: 1,
},
})

View file

@ -23,6 +23,7 @@ export function HomeHeaderLayoutMobile({
children,
}: {
children: React.ReactNode
tabBarAnchor: JSX.Element | null | undefined
}) {
const pal = usePalette('default')
const {_} = useLingui()

View file

@ -159,7 +159,7 @@ export const TextLink = memo(function TextLink({
dataSet,
title,
onPress,
warnOnMismatchingLabel,
disableMismatchWarning,
navigationAction,
...orgProps
}: {
@ -172,7 +172,7 @@ export const TextLink = memo(function TextLink({
lineHeight?: number
dataSet?: any
title?: string
warnOnMismatchingLabel?: boolean
disableMismatchWarning?: boolean
navigationAction?: 'push' | 'replace' | 'navigate'
} & TextProps) {
const {...props} = useLinkProps({to: sanitizeUrl(href)})
@ -180,14 +180,14 @@ export const TextLink = memo(function TextLink({
const {openModal, closeModal} = useModalControls()
const openLink = useOpenLink()
if (warnOnMismatchingLabel && typeof text !== 'string') {
if (!disableMismatchWarning && typeof text !== 'string') {
console.error('Unable to detect mismatching label')
}
props.onPress = React.useCallback(
(e?: Event) => {
const requiresWarning =
warnOnMismatchingLabel &&
!disableMismatchWarning &&
linkRequiresWarning(href, typeof text === 'string' ? text : '')
if (requiresWarning) {
e?.preventDefault?.()
@ -227,7 +227,7 @@ export const TextLink = memo(function TextLink({
navigation,
href,
text,
warnOnMismatchingLabel,
disableMismatchWarning,
navigationAction,
openLink,
],

View file

@ -13,11 +13,13 @@ import Animated from 'react-native-reanimated'
import {useSetDrawerOpen} from '#/state/shell'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useTheme} from '#/alf'
const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
export function ViewHeader({
title,
subtitle,
canGoBack,
showBackButton = true,
hideOnScroll,
@ -26,6 +28,7 @@ export function ViewHeader({
renderButton,
}: {
title: string
subtitle?: string
canGoBack?: boolean
showBackButton?: boolean
hideOnScroll?: boolean
@ -39,6 +42,7 @@ export function ViewHeader({
const navigation = useNavigation<NavigationProp>()
const {track} = useAnalytics()
const {isDesktop, isTablet} = useWebMediaQueries()
const t = useTheme()
const onPressBack = React.useCallback(() => {
if (navigation.canGoBack()) {
@ -71,42 +75,60 @@ export function ViewHeader({
return (
<Container hideOnScroll={hideOnScroll || false} showBorder={showBorder}>
{showBackButton ? (
<TouchableOpacity
testID="viewHeaderDrawerBtn"
onPress={canGoBack ? onPressBack : onPressMenu}
hitSlop={BACK_HITSLOP}
style={canGoBack ? styles.backBtn : styles.backBtnWide}
accessibilityRole="button"
accessibilityLabel={canGoBack ? _(msg`Back`) : _(msg`Menu`)}
accessibilityHint={
canGoBack ? '' : _(msg`Access navigation links and settings`)
}>
{canGoBack ? (
<FontAwesomeIcon
size={18}
icon="angle-left"
style={[styles.backIcon, pal.text]}
/>
) : !isTablet ? (
<FontAwesomeIcon
size={18}
icon="bars"
style={[styles.backIcon, pal.textLight]}
/>
<View style={{flex: 1}}>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
{showBackButton ? (
<TouchableOpacity
testID="viewHeaderDrawerBtn"
onPress={canGoBack ? onPressBack : onPressMenu}
hitSlop={BACK_HITSLOP}
style={canGoBack ? styles.backBtn : styles.backBtnWide}
accessibilityRole="button"
accessibilityLabel={canGoBack ? _(msg`Back`) : _(msg`Menu`)}
accessibilityHint={
canGoBack ? '' : _(msg`Access navigation links and settings`)
}>
{canGoBack ? (
<FontAwesomeIcon
size={18}
icon="angle-left"
style={[styles.backIcon, pal.text]}
/>
) : !isTablet ? (
<FontAwesomeIcon
size={18}
icon="bars"
style={[styles.backIcon, pal.textLight]}
/>
) : null}
</TouchableOpacity>
) : null}
</TouchableOpacity>
) : null}
<View style={styles.titleContainer} pointerEvents="none">
<Text type="title" style={[pal.text, styles.title]}>
{title}
</Text>
<View style={styles.titleContainer} pointerEvents="none">
<Text type="title" style={[pal.text, styles.title]}>
{title}
</Text>
</View>
{renderButton ? (
renderButton()
) : showBackButton ? (
<View style={canGoBack ? styles.backBtn : styles.backBtnWide} />
) : null}
</View>
{subtitle ? (
<View
style={[styles.titleContainer, {marginTop: -3}]}
pointerEvents="none">
<Text
style={[
pal.text,
styles.subtitle,
t.atoms.text_contrast_medium,
]}>
{subtitle}
</Text>
</View>
) : undefined}
</View>
{renderButton ? (
renderButton()
) : showBackButton ? (
<View style={canGoBack ? styles.backBtn : styles.backBtnWide} />
) : null}
</Container>
)
}
@ -185,7 +207,6 @@ function Container({
const styles = StyleSheet.create({
header: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 12,
paddingVertical: 6,
width: '100%',
@ -207,12 +228,14 @@ const styles = StyleSheet.create({
titleContainer: {
marginLeft: 'auto',
marginRight: 'auto',
paddingRight: 10,
alignItems: 'center',
},
title: {
fontWeight: 'bold',
},
subtitle: {
fontSize: 13,
},
backBtn: {
width: 30,
height: 30,

View file

@ -46,7 +46,7 @@ export function ContentHider({
)
}
const isMute = moderation.cause?.type === 'muted'
const isMute = ['muted', 'muted-word'].includes(moderation.cause?.type || '')
const desc = describeModerationCause(moderation.cause, 'content')
return (
<View testID={testID} style={[styles.outer, style]}>

View file

@ -47,7 +47,7 @@ export function PostHider({
)
}
const isMute = moderation.cause?.type === 'muted'
const isMute = ['muted', 'muted-word'].includes(moderation.cause?.type || '')
const desc = describeModerationCause(moderation.cause, 'content')
return !override ? (
<Pressable

View file

@ -114,7 +114,6 @@ export function RichText({
href={link.uri}
style={[style, lineHeightStyle, pal.link, {pointerEvents: 'auto'}]}
dataSet={WORD_WRAP}
warnOnMismatchingLabel
selectable={selectable}
/>,
)

View file

@ -123,8 +123,7 @@ function HomeScreenReady({
return (
<HomeHeader
key="FEEDS_TAB_BAR"
selectedPage={props.selectedPage}
onSelect={props.onSelect}
{...props}
testID="homeScreenFeedTabs"
onPressSelected={onPressSelected}
feeds={pinnedFeedInfos}

View file

@ -491,6 +491,8 @@ const styles = StyleSheet.create({
container: {
flexDirection: 'column',
height: '100%',
// @ts-ignore Web-only.
overflowAnchor: 'none', // Fixes jumps when switching tabs while scrolled down.
},
loading: {
paddingVertical: 10,

View file

@ -60,7 +60,7 @@ import {
import {logger} from '#/logger'
import {useAnalytics} from '#/lib/analytics/analytics'
import {listenSoftReset} from '#/state/events'
import {atoms as a} from '#/alf'
import {atoms as a, useTheme} from '#/alf'
const SECTION_TITLES_CURATE = ['Posts', 'About']
const SECTION_TITLES_MOD = ['About']
@ -699,6 +699,7 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
ref,
) {
const pal = usePalette('default')
const t = useTheme()
const {_} = useLingui()
const {isMobile} = useWebMediaQueries()
const {currentAccount} = useSession()
@ -792,7 +793,7 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
paddingBottom: isMobile ? 14 : 18,
},
]}>
<Text type="lg-bold">
<Text type="lg-bold" style={t.atoms.text}>
<Trans>Users</Trans>
</Text>
{isOwner && (
@ -817,14 +818,18 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
</View>
)
}, [
pal,
list,
isMobile,
pal.border,
pal.textLight,
pal.colors.link,
pal.link,
descriptionRT,
isCurateList,
isOwner,
onPressAddUser,
list.creator,
t.atoms.text,
_,
onPressAddUser,
])
const renderEmptyState = useCallback(() => {

View file

@ -4,7 +4,7 @@ import {View} from 'react-native'
import {useTheme, atoms as a} from '#/alf'
import {ButtonText} from '#/components/Button'
import {InlineLink, Link} from '#/components/Link'
import {H1, H3, Text} from '#/components/Typography'
import {H1, Text} from '#/components/Typography'
export function Links() {
const t = useTheme()
@ -13,31 +13,19 @@ export function Links() {
<H1>Links</H1>
<View style={[a.gap_md, a.align_start]}>
<InlineLink
to="https://bsky.social"
warnOnMismatchingTextChild
style={[a.text_md]}>
External
<InlineLink to="https://google.com" style={[a.text_lg]}>
https://google.com
</InlineLink>
<InlineLink to="https://bsky.social" style={[a.text_md, t.atoms.text]}>
<H3>External with custom children</H3>
<InlineLink to="https://google.com" style={[a.text_lg]}>
External with custom children (google.com)
</InlineLink>
<InlineLink
to="https://bsky.social"
style={[a.text_md, t.atoms.text_contrast_low]}>
External with custom children
Internal (bsky.social)
</InlineLink>
<InlineLink
to="https://bsky.social"
warnOnMismatchingTextChild
style={[a.text_lg]}>
https://bsky.social
</InlineLink>
<InlineLink
to="https://bsky.app/profile/bsky.app"
warnOnMismatchingTextChild
style={[a.text_md]}>
Internal
<InlineLink to="https://bsky.app/profile/bsky.app" style={[a.text_md]}>
Internal (bsky.app)
</InlineLink>
<Link

View file

@ -1,7 +1,6 @@
import React from 'react'
import {View} from 'react-native'
import * as tokens from '#/alf/tokens'
import {atoms as a, useTheme} from '#/alf'
export function Palette() {
@ -28,79 +27,79 @@ export function Palette() {
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_25},
{height: 60, backgroundColor: t.palette.primary_25},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_50},
{height: 60, backgroundColor: t.palette.primary_50},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_100},
{height: 60, backgroundColor: t.palette.primary_100},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_200},
{height: 60, backgroundColor: t.palette.primary_200},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_300},
{height: 60, backgroundColor: t.palette.primary_300},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_400},
{height: 60, backgroundColor: t.palette.primary_400},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_500},
{height: 60, backgroundColor: t.palette.primary_500},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_600},
{height: 60, backgroundColor: t.palette.primary_600},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_700},
{height: 60, backgroundColor: t.palette.primary_700},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_800},
{height: 60, backgroundColor: t.palette.primary_800},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_900},
{height: 60, backgroundColor: t.palette.primary_900},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_950},
{height: 60, backgroundColor: t.palette.primary_950},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.blue_975},
{height: 60, backgroundColor: t.palette.primary_975},
]}
/>
</View>
@ -108,153 +107,159 @@ export function Palette() {
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_25},
{height: 60, backgroundColor: t.palette.positive_25},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_50},
{height: 60, backgroundColor: t.palette.positive_50},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_100},
{height: 60, backgroundColor: t.palette.positive_100},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_200},
{height: 60, backgroundColor: t.palette.positive_200},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_300},
{height: 60, backgroundColor: t.palette.positive_300},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_400},
{height: 60, backgroundColor: t.palette.positive_400},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_500},
{height: 60, backgroundColor: t.palette.positive_500},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_600},
{height: 60, backgroundColor: t.palette.positive_600},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_700},
{height: 60, backgroundColor: t.palette.positive_700},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_800},
{height: 60, backgroundColor: t.palette.positive_800},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_900},
{height: 60, backgroundColor: t.palette.positive_900},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_950},
{height: 60, backgroundColor: t.palette.positive_950},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.green_975},
{height: 60, backgroundColor: t.palette.positive_975},
]}
/>
</View>
<View style={[a.flex_row, a.gap_md]}>
<View
style={[a.flex_1, {height: 60, backgroundColor: tokens.color.red_25}]}
/>
<View
style={[a.flex_1, {height: 60, backgroundColor: tokens.color.red_50}]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.red_100},
{height: 60, backgroundColor: t.palette.negative_25},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.red_200},
{height: 60, backgroundColor: t.palette.negative_50},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.red_300},
{height: 60, backgroundColor: t.palette.negative_100},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.red_400},
{height: 60, backgroundColor: t.palette.negative_200},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.red_500},
{height: 60, backgroundColor: t.palette.negative_300},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.red_600},
{height: 60, backgroundColor: t.palette.negative_400},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.red_700},
{height: 60, backgroundColor: t.palette.negative_500},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.red_800},
{height: 60, backgroundColor: t.palette.negative_600},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.red_900},
{height: 60, backgroundColor: t.palette.negative_700},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.red_950},
{height: 60, backgroundColor: t.palette.negative_800},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: tokens.color.red_975},
{height: 60, backgroundColor: t.palette.negative_900},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: t.palette.negative_950},
]}
/>
<View
style={[
a.flex_1,
{height: 60, backgroundColor: t.palette.negative_975},
]}
/>
</View>

View file

@ -200,10 +200,10 @@ function ComposeBtn() {
const fetchHandle = useFetchHandle()
const getProfileHandle = async () => {
const {routes} = getState()
const currentRoute = routes[routes.length - 1]
const routes = getState()?.routes
const currentRoute = routes?.[routes?.length - 1]
if (currentRoute.name === 'Profile') {
if (currentRoute?.name === 'Profile') {
let handle: string | undefined = (
currentRoute.params as CommonNavigatorParams['Profile']
).name