New navigation model (#1)
* Flatten all routing into a single stack * Replace router with custom implementation * Add shell header and titles * Add tab selector * Add back/forward history menus on longpress * Fix: don't modify state during render * Add refresh() to navigation and reroute navigations to the current location to refresh instead of add to history * Cache screens during navigation to maintain scroll position and improve load-time for renders
This commit is contained in:
parent
d1470bad66
commit
97f52b6a03
57 changed files with 1382 additions and 1159 deletions
|
@ -1,18 +1,11 @@
|
|||
import React, {useRef} from 'react'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {Text, View, FlatList} from 'react-native'
|
||||
import {OnNavigateContent} from '../../routes/types'
|
||||
import {FeedViewModel, FeedViewItemModel} from '../../../state/models/feed-view'
|
||||
import {FeedItem} from './FeedItem'
|
||||
import {ShareModal} from '../modals/SharePost'
|
||||
|
||||
export const Feed = observer(function Feed({
|
||||
feed,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
feed: FeedViewModel
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) {
|
||||
export const Feed = observer(function Feed({feed}: {feed: FeedViewModel}) {
|
||||
const shareSheetRef = useRef<{open: (_uri: string) => void}>()
|
||||
|
||||
const onPressShare = (uri: string) => {
|
||||
|
@ -23,11 +16,7 @@ export const Feed = observer(function Feed({
|
|||
// renderItem function renders components that follow React performance best practices
|
||||
// like PureComponent, shouldComponentUpdate, etc
|
||||
const renderItem = ({item}: {item: FeedViewItemModel}) => (
|
||||
<FeedItem
|
||||
item={item}
|
||||
onNavigateContent={onNavigateContent}
|
||||
onPressShare={onPressShare}
|
||||
/>
|
||||
<FeedItem item={item} onPressShare={onPressShare} />
|
||||
)
|
||||
const onRefresh = () => {
|
||||
feed.refresh().catch(err => console.error('Failed to refresh', err))
|
||||
|
|
|
@ -3,39 +3,31 @@ import {observer} from 'mobx-react-lite'
|
|||
import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native'
|
||||
import {bsky, AdxUri} from '@adxp/mock-api'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {OnNavigateContent} from '../../routes/types'
|
||||
import {FeedViewItemModel} from '../../../state/models/feed-view'
|
||||
import {s} from '../../lib/styles'
|
||||
import {ago} from '../../lib/strings'
|
||||
import {AVIS} from '../../lib/assets'
|
||||
import {useStores} from '../../../state'
|
||||
|
||||
export const FeedItem = observer(function FeedItem({
|
||||
item,
|
||||
onNavigateContent,
|
||||
onPressShare,
|
||||
}: {
|
||||
item: FeedViewItemModel
|
||||
onNavigateContent: OnNavigateContent
|
||||
onPressShare: (_uri: string) => void
|
||||
}) {
|
||||
const store = useStores()
|
||||
const record = item.record as unknown as bsky.Post.Record
|
||||
|
||||
const onPressOuter = () => {
|
||||
const urip = new AdxUri(item.uri)
|
||||
onNavigateContent('PostThread', {
|
||||
name: item.author.name,
|
||||
recordKey: urip.recordKey,
|
||||
})
|
||||
store.nav.navigate(`/profile/${item.author.name}/post/${urip.recordKey}`)
|
||||
}
|
||||
const onPressAuthor = () => {
|
||||
onNavigateContent('Profile', {
|
||||
name: item.author.name,
|
||||
})
|
||||
store.nav.navigate(`/profile/${item.author.name}`)
|
||||
}
|
||||
const onPressReply = () => {
|
||||
onNavigateContent('Composer', {
|
||||
replyTo: item.uri,
|
||||
})
|
||||
store.nav.navigate('/composer')
|
||||
}
|
||||
const onPressToggleRepost = () => {
|
||||
item
|
||||
|
@ -137,8 +129,11 @@ export const FeedItem = observer(function FeedItem({
|
|||
|
||||
const styles = StyleSheet.create({
|
||||
outer: {
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: '#e8e8e8',
|
||||
// borderWidth: 1,
|
||||
// borderColor: '#e8e8e8',
|
||||
borderRadius: 10,
|
||||
margin: 2,
|
||||
marginBottom: 0,
|
||||
backgroundColor: '#fff',
|
||||
padding: 10,
|
||||
},
|
||||
|
@ -175,6 +170,7 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
postText: {
|
||||
paddingBottom: 5,
|
||||
fontFamily: 'Helvetica Neue',
|
||||
},
|
||||
ctrls: {
|
||||
flexDirection: 'row',
|
||||
|
|
|
@ -1,27 +1,10 @@
|
|||
import React, {
|
||||
forwardRef,
|
||||
useState,
|
||||
useMemo,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
} from 'react'
|
||||
import {
|
||||
Button,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import BottomSheet, {BottomSheetBackdropProps} from '@gorhom/bottom-sheet'
|
||||
import Animated, {
|
||||
Extrapolate,
|
||||
interpolate,
|
||||
useAnimatedStyle,
|
||||
} from 'react-native-reanimated'
|
||||
import React, {forwardRef, useState, useImperativeHandle, useRef} from 'react'
|
||||
import {Button, StyleSheet, Text, TouchableOpacity, View} from 'react-native'
|
||||
import BottomSheet from '@gorhom/bottom-sheet'
|
||||
import Toast from '../util/Toast'
|
||||
import Clipboard from '@react-native-clipboard/clipboard'
|
||||
import {s} from '../../lib/styles'
|
||||
import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
|
||||
|
||||
export const ShareModal = forwardRef(function ShareModal({}: {}, ref) {
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false)
|
||||
|
@ -33,14 +16,13 @@ export const ShareModal = forwardRef(function ShareModal({}: {}, ref) {
|
|||
console.log('sharing', uri)
|
||||
setUri(uri)
|
||||
setIsOpen(true)
|
||||
bottomSheetRef.current?.expand()
|
||||
},
|
||||
}))
|
||||
|
||||
const onPressCopy = () => {
|
||||
Clipboard.setString(uri)
|
||||
console.log('showing')
|
||||
console.log(Toast)
|
||||
console.log(Toast.show)
|
||||
Toast.show('Link copied', {
|
||||
position: Toast.positions.TOP,
|
||||
})
|
||||
|
@ -55,50 +37,25 @@ export const ShareModal = forwardRef(function ShareModal({}: {}, ref) {
|
|||
bottomSheetRef.current?.close()
|
||||
}
|
||||
|
||||
const CustomBackdrop = ({animatedIndex, style}: BottomSheetBackdropProps) => {
|
||||
// animated variables
|
||||
const opacity = useAnimatedStyle(() => ({
|
||||
opacity: interpolate(
|
||||
animatedIndex.value, // current snap index
|
||||
[-1, 0], // input range
|
||||
[0, 0.5], // output range
|
||||
Extrapolate.CLAMP,
|
||||
),
|
||||
}))
|
||||
|
||||
const containerStyle = useMemo(
|
||||
() => [style, {backgroundColor: '#000'}, opacity],
|
||||
[style, opacity],
|
||||
)
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={onClose}>
|
||||
<Animated.View style={containerStyle} />
|
||||
</TouchableWithoutFeedback>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{isOpen && (
|
||||
<BottomSheet
|
||||
ref={bottomSheetRef}
|
||||
snapPoints={['50%']}
|
||||
enablePanDownToClose
|
||||
backdropComponent={CustomBackdrop}
|
||||
onChange={onShareBottomSheetChange}>
|
||||
<View>
|
||||
<Text style={[s.textCenter, s.bold, s.mb10]}>Share this post</Text>
|
||||
<Text style={[s.textCenter, s.mb10]}>{uri}</Text>
|
||||
<Button title="Copy to clipboard" onPress={onPressCopy} />
|
||||
<View style={s.p10}>
|
||||
<TouchableOpacity onPress={onClose} style={styles.closeBtn}>
|
||||
<Text style={s.textCenter}>Close</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</BottomSheet>
|
||||
)}
|
||||
</>
|
||||
<BottomSheet
|
||||
ref={bottomSheetRef}
|
||||
index={-1}
|
||||
snapPoints={['50%']}
|
||||
enablePanDownToClose
|
||||
backdropComponent={isOpen ? createCustomBackdrop(onClose) : undefined}
|
||||
onChange={onShareBottomSheetChange}>
|
||||
<View>
|
||||
<Text style={[s.textCenter, s.bold, s.mb10]}>Share this post</Text>
|
||||
<Text style={[s.textCenter, s.mb10]}>{uri}</Text>
|
||||
<Button title="Copy to clipboard" onPress={onPressCopy} />
|
||||
<View style={s.p10}>
|
||||
<TouchableOpacity onPress={onClose} style={styles.closeBtn}>
|
||||
<Text style={s.textCenter}>Close</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</BottomSheet>
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {Text, View, FlatList} from 'react-native'
|
||||
import {OnNavigateContent} from '../../routes/types'
|
||||
import {
|
||||
NotificationsViewModel,
|
||||
NotificationsViewItemModel,
|
||||
|
@ -10,17 +9,15 @@ import {FeedItem} from './FeedItem'
|
|||
|
||||
export const Feed = observer(function Feed({
|
||||
view,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
view: NotificationsViewModel
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) {
|
||||
// TODO optimize renderItem or FeedItem, we're getting this notice from RN: -prf
|
||||
// VirtualizedList: You have a large list that is slow to update - make sure your
|
||||
// renderItem function renders components that follow React performance best practices
|
||||
// like PureComponent, shouldComponentUpdate, etc
|
||||
const renderItem = ({item}: {item: NotificationsViewItemModel}) => (
|
||||
<FeedItem item={item} onNavigateContent={onNavigateContent} />
|
||||
<FeedItem item={item} />
|
||||
)
|
||||
const onRefresh = () => {
|
||||
view.refresh().catch(err => console.error('Failed to refresh', err))
|
||||
|
|
|
@ -3,44 +3,34 @@ import {observer} from 'mobx-react-lite'
|
|||
import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native'
|
||||
import {AdxUri} from '@adxp/mock-api'
|
||||
import {FontAwesomeIcon, Props} from '@fortawesome/react-native-fontawesome'
|
||||
import {OnNavigateContent} from '../../routes/types'
|
||||
import {NotificationsViewItemModel} from '../../../state/models/notifications-view'
|
||||
import {s} from '../../lib/styles'
|
||||
import {ago} from '../../lib/strings'
|
||||
import {AVIS} from '../../lib/assets'
|
||||
import {PostText} from '../post/PostText'
|
||||
import {Post} from '../post/Post'
|
||||
import {useStores} from '../../../state'
|
||||
|
||||
export const FeedItem = observer(function FeedItem({
|
||||
item,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
item: NotificationsViewItemModel
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) {
|
||||
const store = useStores()
|
||||
|
||||
const onPressOuter = () => {
|
||||
if (item.isLike || item.isRepost) {
|
||||
const urip = new AdxUri(item.subjectUri)
|
||||
onNavigateContent('PostThread', {
|
||||
name: urip.host,
|
||||
recordKey: urip.recordKey,
|
||||
})
|
||||
store.nav.navigate(`/profile/${urip.host}/post/${urip.recordKey}`)
|
||||
} else if (item.isFollow) {
|
||||
onNavigateContent('Profile', {
|
||||
name: item.author.name,
|
||||
})
|
||||
store.nav.navigate(`/profile/${item.author.name}`)
|
||||
} else if (item.isReply) {
|
||||
const urip = new AdxUri(item.uri)
|
||||
onNavigateContent('PostThread', {
|
||||
name: urip.host,
|
||||
recordKey: urip.recordKey,
|
||||
})
|
||||
store.nav.navigate(`/profile/${urip.host}/post/${urip.recordKey}`)
|
||||
}
|
||||
}
|
||||
const onPressAuthor = () => {
|
||||
onNavigateContent('Profile', {
|
||||
name: item.author.name,
|
||||
})
|
||||
store.nav.navigate(`/profile/${item.author.name}`)
|
||||
}
|
||||
|
||||
let action = ''
|
||||
|
@ -92,7 +82,7 @@ export const FeedItem = observer(function FeedItem({
|
|||
</View>
|
||||
{item.isReply ? (
|
||||
<View style={s.pt5}>
|
||||
<Post uri={item.uri} onNavigateContent={onNavigateContent} />
|
||||
<Post uri={item.uri} />
|
||||
</View>
|
||||
) : (
|
||||
<></>
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {OnNavigateContent} from '../../routes/types'
|
||||
import {
|
||||
LikedByViewModel,
|
||||
LikedByViewItemModel,
|
||||
|
@ -18,13 +17,7 @@ import {useStores} from '../../../state'
|
|||
import {s} from '../../lib/styles'
|
||||
import {AVIS} from '../../lib/assets'
|
||||
|
||||
export const PostLikedBy = observer(function PostLikedBy({
|
||||
uri,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
uri: string
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) {
|
||||
export const PostLikedBy = observer(function PostLikedBy({uri}: {uri: string}) {
|
||||
const store = useStores()
|
||||
const [view, setView] = useState<LikedByViewModel | undefined>()
|
||||
|
||||
|
@ -66,7 +59,7 @@ export const PostLikedBy = observer(function PostLikedBy({
|
|||
// loaded
|
||||
// =
|
||||
const renderItem = ({item}: {item: LikedByViewItemModel}) => (
|
||||
<LikedByItem item={item} onNavigateContent={onNavigateContent} />
|
||||
<LikedByItem item={item} />
|
||||
)
|
||||
return (
|
||||
<View>
|
||||
|
@ -79,17 +72,10 @@ export const PostLikedBy = observer(function PostLikedBy({
|
|||
)
|
||||
})
|
||||
|
||||
const LikedByItem = ({
|
||||
item,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
item: LikedByViewItemModel
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) => {
|
||||
const LikedByItem = ({item}: {item: LikedByViewItemModel}) => {
|
||||
const store = useStores()
|
||||
const onPressOuter = () => {
|
||||
onNavigateContent('Profile', {
|
||||
name: item.name,
|
||||
})
|
||||
store.nav.navigate(`/profile/${item.name}`)
|
||||
}
|
||||
return (
|
||||
<TouchableOpacity style={styles.outer} onPress={onPressOuter}>
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {OnNavigateContent} from '../../routes/types'
|
||||
import {
|
||||
RepostedByViewModel,
|
||||
RepostedByViewItemModel,
|
||||
|
@ -20,10 +19,8 @@ import {AVIS} from '../../lib/assets'
|
|||
|
||||
export const PostRepostedBy = observer(function PostRepostedBy({
|
||||
uri,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
uri: string
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) {
|
||||
const store = useStores()
|
||||
const [view, setView] = useState<RepostedByViewModel | undefined>()
|
||||
|
@ -68,7 +65,7 @@ export const PostRepostedBy = observer(function PostRepostedBy({
|
|||
// loaded
|
||||
// =
|
||||
const renderItem = ({item}: {item: RepostedByViewItemModel}) => (
|
||||
<RepostedByItem item={item} onNavigateContent={onNavigateContent} />
|
||||
<RepostedByItem item={item} />
|
||||
)
|
||||
return (
|
||||
<View>
|
||||
|
@ -81,17 +78,10 @@ export const PostRepostedBy = observer(function PostRepostedBy({
|
|||
)
|
||||
})
|
||||
|
||||
const RepostedByItem = ({
|
||||
item,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
item: RepostedByViewItemModel
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) => {
|
||||
const RepostedByItem = ({item}: {item: RepostedByViewItemModel}) => {
|
||||
const store = useStores()
|
||||
const onPressOuter = () => {
|
||||
onNavigateContent('Profile', {
|
||||
name: item.name,
|
||||
})
|
||||
store.nav.navigate(`/profile/${item.name}`)
|
||||
}
|
||||
return (
|
||||
<TouchableOpacity style={styles.outer} onPress={onPressOuter}>
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import React, {useState, useEffect, useRef} from 'react'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {ActivityIndicator, FlatList, Text, View} from 'react-native'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {OnNavigateContent} from '../../routes/types'
|
||||
import {
|
||||
PostThreadViewModel,
|
||||
PostThreadViewPostModel,
|
||||
|
@ -14,13 +12,7 @@ import {s} from '../../lib/styles'
|
|||
|
||||
const UPDATE_DELAY = 2e3 // wait 2s before refetching the thread for updates
|
||||
|
||||
export const PostThread = observer(function PostThread({
|
||||
uri,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
uri: string
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) {
|
||||
export const PostThread = observer(function PostThread({uri}: {uri: string}) {
|
||||
const store = useStores()
|
||||
const [view, setView] = useState<PostThreadViewModel | undefined>()
|
||||
const [lastUpdate, setLastUpdate] = useState<number>(Date.now())
|
||||
|
@ -37,12 +29,13 @@ export const PostThread = observer(function PostThread({
|
|||
newView.setup().catch(err => console.error('Failed to fetch thread', err))
|
||||
}, [uri, view?.params.uri, store])
|
||||
|
||||
useFocusEffect(() => {
|
||||
if (Date.now() - lastUpdate > UPDATE_DELAY) {
|
||||
view?.update()
|
||||
setLastUpdate(Date.now())
|
||||
}
|
||||
})
|
||||
// TODO
|
||||
// useFocusEffect(() => {
|
||||
// if (Date.now() - lastUpdate > UPDATE_DELAY) {
|
||||
// view?.update()
|
||||
// setLastUpdate(Date.now())
|
||||
// }
|
||||
// })
|
||||
|
||||
const onPressShare = (uri: string) => {
|
||||
shareSheetRef.current?.open(uri)
|
||||
|
@ -79,11 +72,7 @@ export const PostThread = observer(function PostThread({
|
|||
// =
|
||||
const posts = view.thread ? Array.from(flattenThread(view.thread)) : []
|
||||
const renderItem = ({item}: {item: PostThreadViewPostModel}) => (
|
||||
<PostThreadItem
|
||||
item={item}
|
||||
onNavigateContent={onNavigateContent}
|
||||
onPressShare={onPressShare}
|
||||
/>
|
||||
<PostThreadItem item={item} onPressShare={onPressShare} />
|
||||
)
|
||||
return (
|
||||
<View style={s.h100pct}>
|
||||
|
|
|
@ -3,11 +3,11 @@ import {observer} from 'mobx-react-lite'
|
|||
import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native'
|
||||
import {bsky, AdxUri} from '@adxp/mock-api'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {OnNavigateContent} from '../../routes/types'
|
||||
import {PostThreadViewPostModel} from '../../../state/models/post-thread-view'
|
||||
import {s} from '../../lib/styles'
|
||||
import {ago, pluralize} from '../../lib/strings'
|
||||
import {AVIS} from '../../lib/assets'
|
||||
import {useStores} from '../../../state'
|
||||
|
||||
function iter<T>(n: number, fn: (_i: number) => T): Array<T> {
|
||||
const arr: T[] = []
|
||||
|
@ -19,46 +19,36 @@ function iter<T>(n: number, fn: (_i: number) => T): Array<T> {
|
|||
|
||||
export const PostThreadItem = observer(function PostThreadItem({
|
||||
item,
|
||||
onNavigateContent,
|
||||
onPressShare,
|
||||
}: {
|
||||
item: PostThreadViewPostModel
|
||||
onNavigateContent: OnNavigateContent
|
||||
onPressShare: (_uri: string) => void
|
||||
}) {
|
||||
const store = useStores()
|
||||
const record = item.record as unknown as bsky.Post.Record
|
||||
const hasEngagement = item.likeCount || item.repostCount
|
||||
|
||||
const onPressOuter = () => {
|
||||
const urip = new AdxUri(item.uri)
|
||||
onNavigateContent('PostThread', {
|
||||
name: item.author.name,
|
||||
recordKey: urip.recordKey,
|
||||
})
|
||||
store.nav.navigate(`/profile/${item.author.name}/post/${urip.recordKey}`)
|
||||
}
|
||||
const onPressAuthor = () => {
|
||||
onNavigateContent('Profile', {
|
||||
name: item.author.name,
|
||||
})
|
||||
store.nav.navigate(`/profile/${item.author.name}`)
|
||||
}
|
||||
const onPressLikes = () => {
|
||||
const urip = new AdxUri(item.uri)
|
||||
onNavigateContent('PostLikedBy', {
|
||||
name: item.author.name,
|
||||
recordKey: urip.recordKey,
|
||||
})
|
||||
store.nav.navigate(
|
||||
`/profile/${item.author.name}/post/${urip.recordKey}/liked-by`,
|
||||
)
|
||||
}
|
||||
const onPressReposts = () => {
|
||||
const urip = new AdxUri(item.uri)
|
||||
onNavigateContent('PostRepostedBy', {
|
||||
name: item.author.name,
|
||||
recordKey: urip.recordKey,
|
||||
})
|
||||
store.nav.navigate(
|
||||
`/profile/${item.author.name}/post/${urip.recordKey}/reposted-by`,
|
||||
)
|
||||
}
|
||||
const onPressReply = () => {
|
||||
onNavigateContent('Composer', {
|
||||
replyTo: item.uri,
|
||||
})
|
||||
store.nav.navigate(`/composer?replyTo=${item.uri}`)
|
||||
}
|
||||
const onPressToggleRepost = () => {
|
||||
item
|
||||
|
@ -227,6 +217,7 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
postText: {
|
||||
paddingBottom: 5,
|
||||
fontFamily: 'Helvetica Neue',
|
||||
},
|
||||
expandedInfo: {
|
||||
flexDirection: 'row',
|
||||
|
|
|
@ -10,20 +10,13 @@ import {
|
|||
View,
|
||||
} from 'react-native'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {OnNavigateContent} from '../../routes/types'
|
||||
import {PostThreadViewModel} from '../../../state/models/post-thread-view'
|
||||
import {useStores} from '../../../state'
|
||||
import {s} from '../../lib/styles'
|
||||
import {ago} from '../../lib/strings'
|
||||
import {AVIS} from '../../lib/assets'
|
||||
|
||||
export const Post = observer(function Post({
|
||||
uri,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
uri: string
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) {
|
||||
export const Post = observer(function Post({uri}: {uri: string}) {
|
||||
const store = useStores()
|
||||
const [view, setView] = useState<PostThreadViewModel | undefined>()
|
||||
|
||||
|
@ -63,20 +56,13 @@ export const Post = observer(function Post({
|
|||
|
||||
const onPressOuter = () => {
|
||||
const urip = new AdxUri(item.uri)
|
||||
onNavigateContent('PostThread', {
|
||||
name: item.author.name,
|
||||
recordKey: urip.recordKey,
|
||||
})
|
||||
store.nav.navigate(`/profile/${item.author.name}/post/${urip.recordKey}`)
|
||||
}
|
||||
const onPressAuthor = () => {
|
||||
onNavigateContent('Profile', {
|
||||
name: item.author.name,
|
||||
})
|
||||
store.nav.navigate(`/profile/${item.author.name}`)
|
||||
}
|
||||
const onPressReply = () => {
|
||||
onNavigateContent('Composer', {
|
||||
replyTo: item.uri,
|
||||
})
|
||||
store.nav.navigate(`/composer?replyTo=${item.uri}`)
|
||||
}
|
||||
const onPressToggleRepost = () => {
|
||||
item
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {OnNavigateContent} from '../../routes/types'
|
||||
import {
|
||||
UserFollowersViewModel,
|
||||
FollowerItem,
|
||||
|
@ -20,10 +19,8 @@ import {AVIS} from '../../lib/assets'
|
|||
|
||||
export const ProfileFollowers = observer(function ProfileFollowers({
|
||||
name,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
name: string
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) {
|
||||
const store = useStores()
|
||||
const [view, setView] = useState<UserFollowersViewModel | undefined>()
|
||||
|
@ -67,9 +64,7 @@ export const ProfileFollowers = observer(function ProfileFollowers({
|
|||
|
||||
// loaded
|
||||
// =
|
||||
const renderItem = ({item}: {item: FollowerItem}) => (
|
||||
<User item={item} onNavigateContent={onNavigateContent} />
|
||||
)
|
||||
const renderItem = ({item}: {item: FollowerItem}) => <User item={item} />
|
||||
return (
|
||||
<View>
|
||||
<FlatList
|
||||
|
@ -81,17 +76,10 @@ export const ProfileFollowers = observer(function ProfileFollowers({
|
|||
)
|
||||
})
|
||||
|
||||
const User = ({
|
||||
item,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
item: FollowerItem
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) => {
|
||||
const User = ({item}: {item: FollowerItem}) => {
|
||||
const store = useStores()
|
||||
const onPressOuter = () => {
|
||||
onNavigateContent('Profile', {
|
||||
name: item.name,
|
||||
})
|
||||
store.nav.navigate(`/profile/${item.name}`)
|
||||
}
|
||||
return (
|
||||
<TouchableOpacity style={styles.outer} onPress={onPressOuter}>
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {OnNavigateContent} from '../../routes/types'
|
||||
import {
|
||||
UserFollowsViewModel,
|
||||
FollowItem,
|
||||
|
@ -20,10 +19,8 @@ import {AVIS} from '../../lib/assets'
|
|||
|
||||
export const ProfileFollows = observer(function ProfileFollows({
|
||||
name,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
name: string
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) {
|
||||
const store = useStores()
|
||||
const [view, setView] = useState<UserFollowsViewModel | undefined>()
|
||||
|
@ -67,9 +64,7 @@ export const ProfileFollows = observer(function ProfileFollows({
|
|||
|
||||
// loaded
|
||||
// =
|
||||
const renderItem = ({item}: {item: FollowItem}) => (
|
||||
<User item={item} onNavigateContent={onNavigateContent} />
|
||||
)
|
||||
const renderItem = ({item}: {item: FollowItem}) => <User item={item} />
|
||||
return (
|
||||
<View>
|
||||
<FlatList
|
||||
|
@ -81,17 +76,10 @@ export const ProfileFollows = observer(function ProfileFollows({
|
|||
)
|
||||
})
|
||||
|
||||
const User = ({
|
||||
item,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
item: FollowItem
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) => {
|
||||
const User = ({item}: {item: FollowItem}) => {
|
||||
const store = useStores()
|
||||
const onPressOuter = () => {
|
||||
onNavigateContent('Profile', {
|
||||
name: item.name,
|
||||
})
|
||||
store.nav.navigate(`/profile/${item.name}`)
|
||||
}
|
||||
return (
|
||||
<TouchableOpacity style={styles.outer} onPress={onPressOuter}>
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {OnNavigateContent} from '../../routes/types'
|
||||
import {ProfileViewModel} from '../../../state/models/profile-view'
|
||||
import {useStores} from '../../../state'
|
||||
import {pluralize} from '../../lib/strings'
|
||||
|
@ -19,10 +18,8 @@ import Toast from '../util/Toast'
|
|||
|
||||
export const ProfileHeader = observer(function ProfileHeader({
|
||||
user,
|
||||
onNavigateContent,
|
||||
}: {
|
||||
user: string
|
||||
onNavigateContent: OnNavigateContent
|
||||
}) {
|
||||
const store = useStores()
|
||||
const [view, setView] = useState<ProfileViewModel | undefined>()
|
||||
|
@ -55,10 +52,10 @@ export const ProfileHeader = observer(function ProfileHeader({
|
|||
)
|
||||
}
|
||||
const onPressFollowers = () => {
|
||||
onNavigateContent('ProfileFollowers', {name: user})
|
||||
store.nav.navigate(`/profile/${user}/followers`)
|
||||
}
|
||||
const onPressFollows = () => {
|
||||
onNavigateContent('ProfileFollows', {name: user})
|
||||
store.nav.navigate(`/profile/${user}/follows`)
|
||||
}
|
||||
|
||||
// loading
|
||||
|
|
36
src/view/com/util/BottomSheetCustomBackdrop.tsx
Normal file
36
src/view/com/util/BottomSheetCustomBackdrop.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import React, {useMemo} from 'react'
|
||||
import {GestureResponderEvent, TouchableWithoutFeedback} from 'react-native'
|
||||
import {BottomSheetBackdropProps} from '@gorhom/bottom-sheet'
|
||||
import Animated, {
|
||||
Extrapolate,
|
||||
interpolate,
|
||||
useAnimatedStyle,
|
||||
} from 'react-native-reanimated'
|
||||
|
||||
export function createCustomBackdrop(
|
||||
onClose?: ((event: GestureResponderEvent) => void) | undefined,
|
||||
): React.FC<BottomSheetBackdropProps> {
|
||||
const CustomBackdrop = ({animatedIndex, style}: BottomSheetBackdropProps) => {
|
||||
// animated variables
|
||||
const opacity = useAnimatedStyle(() => ({
|
||||
opacity: interpolate(
|
||||
animatedIndex.value, // current snap index
|
||||
[-1, 0], // input range
|
||||
[0, 0.5], // output range
|
||||
Extrapolate.CLAMP,
|
||||
),
|
||||
}))
|
||||
|
||||
const containerStyle = useMemo(
|
||||
() => [style, {backgroundColor: '#000'}, opacity],
|
||||
[style, opacity],
|
||||
)
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={onClose}>
|
||||
<Animated.View style={containerStyle} />
|
||||
</TouchableWithoutFeedback>
|
||||
)
|
||||
}
|
||||
return CustomBackdrop
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue