Replace the navigation model with react-navigation

This commit is contained in:
Paul Frazee
2023-03-10 01:01:51 -06:00
parent bf1f743382
commit 3b0d72b776
22 changed files with 249 additions and 776 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ export function Component({}: {}) {
token: confirmCode,
})
Toast.show('Your account has been deleted')
store.nav.tab.fixedTabReset()
// store.nav.tab.fixedTabReset() TODO
store.session.clear()
store.shell.closeModal()
} catch (e: any) {
+10 -4
View File
@@ -7,6 +7,7 @@ import {
StyleSheet,
ViewStyle,
} from 'react-native'
import {useNavigation} from '@react-navigation/native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
import {CenteredView, FlatList} from '../util/Views'
@@ -18,10 +19,10 @@ import {FeedModel} from 'state/models/feed-view'
import {FeedItem} from './FeedItem'
import {OnScrollCb} from 'lib/hooks/useOnMainScroll'
import {s} from 'lib/styles'
import {useStores} from 'state/index'
import {useAnalytics} from 'lib/analytics'
import {usePalette} from 'lib/hooks/usePalette'
import {MagnifyingGlassIcon} from 'lib/icons'
import {NavigationProp} from 'lib/routes/types'
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
const ERROR_FEED_ITEM = {_reactKey: '__error__'}
@@ -47,9 +48,9 @@ export const Feed = observer(function Feed({
}) {
const pal = usePalette('default')
const palInverted = usePalette('inverted')
const store = useStores()
const {track} = useAnalytics()
const [isRefreshing, setIsRefreshing] = React.useState(false)
const navigation = useNavigation<NavigationProp>()
const data = React.useMemo(() => {
let feedItems: any[] = []
@@ -112,7 +113,12 @@ export const Feed = observer(function Feed({
<Button
type="inverted"
style={styles.emptyBtn}
onPress={() => store.nav.navigate('/search')}>
onPress={
() =>
navigation.navigate(
'SearchTab',
) /* TODO make sure it goes to root of the tab */
}>
<Text type="lg-medium" style={palInverted.text}>
Find accounts
</Text>
@@ -134,7 +140,7 @@ export const Feed = observer(function Feed({
}
return <FeedItem item={item} showFollowBtn={showPostFollowBtn} />
},
[feed, onPressTryAgain, showPostFollowBtn, pal, palInverted, store.nav],
[feed, onPressTryAgain, showPostFollowBtn, pal, palInverted, navigation],
)
const FeedFooter = React.useCallback(
+26 -23
View File
@@ -12,6 +12,7 @@ import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {useNavigation} from '@react-navigation/native'
import {BlurView} from '../util/BlurView'
import {ProfileViewModel} from 'state/models/profile-view'
import {useStores} from 'state/index'
@@ -28,6 +29,7 @@ import {UserAvatar} from '../util/UserAvatar'
import {UserBanner} from '../util/UserBanner'
import {usePalette} from 'lib/hooks/usePalette'
import {useAnalytics} from 'lib/analytics'
import {NavigationProp} from 'lib/routes/types'
const BACK_HITSLOP = {left: 30, top: 30, right: 30, bottom: 30}
@@ -40,16 +42,17 @@ export const ProfileHeader = observer(function ProfileHeader({
}) {
const pal = usePalette('default')
const store = useStores()
const navigation = useNavigation<NavigationProp>()
const {track} = useAnalytics()
const onPressBack = () => {
store.nav.tab.goBack()
}
const onPressAvi = () => {
const onPressBack = React.useCallback(() => {
navigation.goBack()
}, [navigation])
const onPressAvi = React.useCallback(() => {
if (view.avatar) {
store.shell.openLightbox(new ProfileImageLightbox(view))
}
}
const onPressToggleFollow = () => {
}, [store, view])
const onPressToggleFollow = React.useCallback(() => {
view?.toggleFollowing().then(
() => {
Toast.show(
@@ -60,28 +63,28 @@ export const ProfileHeader = observer(function ProfileHeader({
},
err => store.log.error('Failed to toggle follow', err),
)
}
const onPressEditProfile = () => {
}, [view, store])
const onPressEditProfile = React.useCallback(() => {
track('ProfileHeader:EditProfileButtonClicked')
store.shell.openModal({
name: 'edit-profile',
profileView: view,
onUpdate: onRefreshAll,
})
}
const onPressFollowers = () => {
}, [track, store, view, onRefreshAll])
const onPressFollowers = React.useCallback(() => {
track('ProfileHeader:FollowersButtonClicked')
store.nav.navigate(`/profile/${view.handle}/followers`)
}
const onPressFollows = () => {
navigation.push('ProfileFollowers', {name: view.handle})
}, [track, navigation, view])
const onPressFollows = React.useCallback(() => {
track('ProfileHeader:FollowsButtonClicked')
store.nav.navigate(`/profile/${view.handle}/follows`)
}
const onPressShare = () => {
navigation.push('ProfileFollows', {name: view.handle})
}, [track, navigation, view])
const onPressShare = React.useCallback(() => {
track('ProfileHeader:ShareButtonClicked')
Share.share({url: toShareUrl(`/profile/${view.handle}`)})
}
const onPressMuteAccount = async () => {
}, [track, view])
const onPressMuteAccount = React.useCallback(async () => {
track('ProfileHeader:MuteAccountButtonClicked')
try {
await view.muteAccount()
@@ -90,8 +93,8 @@ export const ProfileHeader = observer(function ProfileHeader({
store.log.error('Failed to mute account', e)
Toast.show(`There was an issue! ${e.toString()}`)
}
}
const onPressUnmuteAccount = async () => {
}, [track, view, store])
const onPressUnmuteAccount = React.useCallback(async () => {
track('ProfileHeader:UnmuteAccountButtonClicked')
try {
await view.unmuteAccount()
@@ -100,14 +103,14 @@ export const ProfileHeader = observer(function ProfileHeader({
store.log.error('Failed to unmute account', e)
Toast.show(`There was an issue! ${e.toString()}`)
}
}
const onPressReportAccount = () => {
}, [track, view, store])
const onPressReportAccount = React.useCallback(() => {
track('ProfileHeader:ReportAccountButtonClicked')
store.shell.openModal({
name: 'report-account',
did: view.did,
})
}
}, [track, store, view])
// loading
// =
+77 -37
View File
@@ -2,6 +2,8 @@ import React from 'react'
import {observer} from 'mobx-react-lite'
import {
Linking,
GestureResponderEvent,
Platform,
StyleProp,
TouchableWithoutFeedback,
TouchableOpacity,
@@ -9,11 +11,18 @@ import {
View,
ViewStyle,
} from 'react-native'
import {useLinkProps, useNavigation} from '@react-navigation/native'
import {Text} from './text/Text'
import {TypographyVariant} from 'lib/ThemeContext'
import {NavigationProp} from 'lib/routes/types'
import {matchPath} from 'view/screens'
import {useStores, RootStoreModel} from 'state/index'
import {convertBskyAppUrlIfNeeded} from 'lib/strings/url-helpers'
type Event =
| React.MouseEvent<HTMLAnchorElement, MouseEvent>
| GestureResponderEvent
export const Link = observer(function Link({
style,
href,
@@ -27,35 +36,30 @@ export const Link = observer(function Link({
children?: React.ReactNode
noFeedback?: boolean
}) {
let {...props} = useLinkProps({to: href})
const store = useStores()
const onPress = () => {
if (href) {
handleLink(store, href, false)
}
}
const onLongPress = () => {
if (href) {
handleLink(store, href, true)
}
}
const navigation = useNavigation<NavigationProp>()
props.onPress = React.useCallback(
(e?: Event) => {
if (typeof href === 'string') {
return onPressInner(store, navigation, href, e)
}
},
[store, navigation, href],
)
if (noFeedback) {
return (
<TouchableWithoutFeedback
onPress={onPress}
onLongPress={onLongPress}
delayPressIn={50}>
<View style={style}>
<TouchableWithoutFeedback delayPressIn={50} {...props}>
<View style={style} {...props}>
{children ? children : <Text>{title || 'link'}</Text>}
</View>
</TouchableWithoutFeedback>
)
}
return (
<TouchableOpacity
onPress={onPress}
onLongPress={onLongPress}
delayPressIn={50}
style={style}>
<TouchableOpacity delayPressIn={50} style={style} {...props}>
{children ? children : <Text>{title || 'link'}</Text>}
</TouchableOpacity>
)
@@ -72,29 +76,65 @@ export const TextLink = observer(function TextLink({
href: string
text: string
}) {
const {...props} = useLinkProps({to: href})
const store = useStores()
const onPress = () => {
handleLink(store, href, false)
}
const onLongPress = () => {
handleLink(store, href, true)
}
const navigation = useNavigation<NavigationProp>()
props.onPress = React.useCallback(
(e?: Event) => {
return onPressInner(store, navigation, href, e)
},
[store, navigation, href],
)
return (
<Text type={type} style={style} onPress={onPress} onLongPress={onLongPress}>
<Text type={type} style={style} {...props}>
{text}
</Text>
)
})
function handleLink(store: RootStoreModel, href: string, longPress: boolean) {
href = convertBskyAppUrlIfNeeded(href)
if (href.startsWith('http')) {
Linking.openURL(href)
} else if (longPress) {
store.shell.closeModal() // close any active modals
store.nav.newTab(href)
} else {
store.shell.closeModal() // close any active modals
store.nav.navigate(href)
// NOTE
// we can't use the onPress given by useLinkProps because it will
// match most paths to the HomeTab routes while we actually want to
// preserve the tab the app is currently in
//
// we also have some additional behaviors - closing the current modal,
// converting bsky urls, and opening http/s links in the system browser
//
// this method copies from the onPress implementation but adds our
// needed customizations
// -prf
function onPressInner(
store: RootStoreModel,
navigation: NavigationProp,
href: string,
e?: Event,
) {
let shouldHandle = false
if (Platform.OS !== 'web' || !e) {
shouldHandle = e ? !e.defaultPrevented : true
} else if (
!e.defaultPrevented && // onPress prevented default
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && // ignore clicks with modifier keys
(e.button == null || e.button === 0) && // ignore everything but left clicks
[undefined, null, '', 'self'].includes(e.currentTarget?.target) // let browser handle "target=_blank" etc.
) {
e.preventDefault()
shouldHandle = true
}
if (shouldHandle) {
href = convertBskyAppUrlIfNeeded(href)
if (href.startsWith('http')) {
Linking.openURL(href)
} else {
store.shell.closeModal() // close any active modals
const {name, params} = matchPath(href)
// @ts-ignore we're not able to type check on this one -prf
navigation.push(name, params)
}
}
}
+14 -7
View File
@@ -2,6 +2,7 @@ import React from 'react'
import {observer} from 'mobx-react-lite'
import {Animated, StyleSheet, TouchableOpacity, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {useNavigation, DrawerActions} from '@react-navigation/native'
import {UserAvatar} from './UserAvatar'
import {Text} from './text/Text'
import {useStores} from 'state/index'
@@ -9,6 +10,7 @@ import {usePalette} from 'lib/hooks/usePalette'
import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
import {useAnalytics} from 'lib/analytics'
import {isDesktopWeb} from '../../../platform/detection'
import {NavigationProp} from 'lib/routes/types'
const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
@@ -23,17 +25,22 @@ export const ViewHeader = observer(function ViewHeader({
}) {
const pal = usePalette('default')
const store = useStores()
const navigation = useNavigation<NavigationProp>()
const {track} = useAnalytics()
const onPressBack = () => {
store.nav.tab.goBack()
}
const onPressMenu = () => {
const onPressBack = React.useCallback(() => {
navigation.goBack()
}, [navigation])
const onPressMenu = React.useCallback(() => {
track('ViewHeader:MenuButtonClicked')
store.shell.setMainMenuOpen(true)
}
navigation.dispatch(DrawerActions.openDrawer())
}, [track, navigation])
if (typeof canGoBack === 'undefined') {
canGoBack = store.nav.tab.canGoBack
canGoBack = navigation.canGoBack()
}
if (isDesktopWeb) {
return <></>
}
@@ -17,7 +17,6 @@ import {Button, ButtonType} from './Button'
import {colors} from 'lib/styles'
import {toShareUrl} from 'lib/strings/url-helpers'
import {useStores} from 'state/index'
import {TABS_ENABLED} from 'lib/build-flags'
import {usePalette} from 'lib/hooks/usePalette'
import {useTheme} from 'lib/ThemeContext'
@@ -138,15 +137,6 @@ export function PostDropdownBtn({
const store = useStores()
const dropdownItems: DropdownItem[] = [
TABS_ENABLED
? {
icon: ['far', 'clone'],
label: 'Open in new tab',
onPress() {
store.nav.newTab(itemHref)
},
}
: undefined,
{
icon: 'language',
label: 'Translate...',