Remove unnecessary state update for reply gate (#4897)
* Move mobile compose prompt to inner component * Make canReply computed * Use same clamp we use elsewherezio/stable
parent
2174feed44
commit
4b71950d99
|
@ -1,11 +1,14 @@
|
||||||
import React, {useEffect, useRef} from 'react'
|
import React, {useRef} from 'react'
|
||||||
import {useWindowDimensions, View} from 'react-native'
|
import {StyleSheet, useWindowDimensions, View} from 'react-native'
|
||||||
import {runOnJS} from 'react-native-reanimated'
|
import {runOnJS} from 'react-native-reanimated'
|
||||||
|
import Animated from 'react-native-reanimated'
|
||||||
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import {AppBskyFeedDefs} from '@atproto/api'
|
import {AppBskyFeedDefs} from '@atproto/api'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped'
|
import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped'
|
||||||
|
import {clamp} from '#/lib/numbers'
|
||||||
import {ScrollProvider} from '#/lib/ScrollContext'
|
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||||
import {isAndroid, isNative, isWeb} from '#/platform/detection'
|
import {isAndroid, isNative, isWeb} from '#/platform/detection'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
|
@ -22,6 +25,7 @@ import {
|
||||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
|
import {useMinimalShellFabTransform} from 'lib/hooks/useMinimalShellTransform'
|
||||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||||
|
@ -82,11 +86,9 @@ const keyExtractor = (item: RowItem) => {
|
||||||
|
|
||||||
export function PostThread({
|
export function PostThread({
|
||||||
uri,
|
uri,
|
||||||
onCanReply,
|
|
||||||
onPressReply,
|
onPressReply,
|
||||||
}: {
|
}: {
|
||||||
uri: string | undefined
|
uri: string | undefined
|
||||||
onCanReply: (canReply: boolean) => void
|
|
||||||
onPressReply: () => unknown
|
onPressReply: () => unknown
|
||||||
}) {
|
}) {
|
||||||
const {hasSession, currentAccount} = useSession()
|
const {hasSession, currentAccount} = useSession()
|
||||||
|
@ -210,14 +212,6 @@ export function PostThread({
|
||||||
return null
|
return null
|
||||||
}, [thread, skeleton?.highlightedPost, isThreadError, _, threadError])
|
}, [thread, skeleton?.highlightedPost, isThreadError, _, threadError])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (error) {
|
|
||||||
onCanReply(false)
|
|
||||||
} else if (rootPost) {
|
|
||||||
onCanReply(!rootPost.viewer?.replyDisabled)
|
|
||||||
}
|
|
||||||
}, [rootPost, onCanReply, error])
|
|
||||||
|
|
||||||
// construct content
|
// construct content
|
||||||
const posts = React.useMemo(() => {
|
const posts = React.useMemo(() => {
|
||||||
if (!skeleton) return []
|
if (!skeleton) return []
|
||||||
|
@ -313,6 +307,7 @@ export function PostThread({
|
||||||
setMaxReplies(prev => prev + 50)
|
setMaxReplies(prev => prev + 50)
|
||||||
}, [isFetching, maxReplies, posts.length])
|
}, [isFetching, maxReplies, posts.length])
|
||||||
|
|
||||||
|
const canReply = !error && rootPost && !rootPost.viewer?.replyDisabled
|
||||||
const hasParents =
|
const hasParents =
|
||||||
skeleton?.highlightedPost?.type === 'post' &&
|
skeleton?.highlightedPost?.type === 'post' &&
|
||||||
(skeleton.highlightedPost.ctx.isParentLoading ||
|
(skeleton.highlightedPost.ctx.isParentLoading ||
|
||||||
|
@ -473,10 +468,30 @@ export function PostThread({
|
||||||
sideBorders={false}
|
sideBorders={false}
|
||||||
/>
|
/>
|
||||||
</ScrollProvider>
|
</ScrollProvider>
|
||||||
|
{isMobile && canReply && hasSession && (
|
||||||
|
<MobileComposePrompt onPressReply={onPressReply} />
|
||||||
|
)}
|
||||||
</CenteredView>
|
</CenteredView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function MobileComposePrompt({onPressReply}: {onPressReply: () => unknown}) {
|
||||||
|
const safeAreaInsets = useSafeAreaInsets()
|
||||||
|
const fabMinimalShellTransform = useMinimalShellFabTransform()
|
||||||
|
return (
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.prompt,
|
||||||
|
fabMinimalShellTransform,
|
||||||
|
{
|
||||||
|
bottom: clamp(safeAreaInsets.bottom, 15, 30),
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<ComposePrompt onPressCompose={onPressReply} />
|
||||||
|
</Animated.View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function isThreadPost(v: unknown): v is ThreadPost {
|
function isThreadPost(v: unknown): v is ThreadPost {
|
||||||
return !!v && typeof v === 'object' && 'type' in v && v.type === 'post'
|
return !!v && typeof v === 'object' && 'type' in v && v.type === 'post'
|
||||||
}
|
}
|
||||||
|
@ -622,3 +637,12 @@ function hasBranchingReplies(node?: ThreadNode) {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
prompt: {
|
||||||
|
// @ts-ignore web-only
|
||||||
|
position: isWeb ? 'fixed' : 'absolute',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
|
@ -1,39 +1,26 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import Animated from 'react-native-reanimated'
|
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
|
||||||
import {useFocusEffect} from '@react-navigation/native'
|
import {useFocusEffect} from '@react-navigation/native'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
import {clamp} from 'lodash'
|
|
||||||
|
|
||||||
import {isWeb} from '#/platform/detection'
|
|
||||||
import {
|
import {
|
||||||
RQKEY as POST_THREAD_RQKEY,
|
RQKEY as POST_THREAD_RQKEY,
|
||||||
ThreadNode,
|
ThreadNode,
|
||||||
} from '#/state/queries/post-thread'
|
} from '#/state/queries/post-thread'
|
||||||
import {useSession} from '#/state/session'
|
|
||||||
import {useSetMinimalShellMode} from '#/state/shell'
|
import {useSetMinimalShellMode} from '#/state/shell'
|
||||||
import {useComposerControls} from '#/state/shell/composer'
|
import {useComposerControls} from '#/state/shell/composer'
|
||||||
import {useMinimalShellFabTransform} from 'lib/hooks/useMinimalShellTransform'
|
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|
||||||
import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
|
import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
|
||||||
import {makeRecordUri} from 'lib/strings/url-helpers'
|
import {makeRecordUri} from 'lib/strings/url-helpers'
|
||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
import {ComposePrompt} from 'view/com/composer/Prompt'
|
|
||||||
import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread'
|
import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread'
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'>
|
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'>
|
||||||
export function PostThreadScreen({route}: Props) {
|
export function PostThreadScreen({route}: Props) {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const {hasSession} = useSession()
|
|
||||||
const fabMinimalShellTransform = useMinimalShellFabTransform()
|
|
||||||
const setMinimalShellMode = useSetMinimalShellMode()
|
const setMinimalShellMode = useSetMinimalShellMode()
|
||||||
const {openComposer} = useComposerControls()
|
const {openComposer} = useComposerControls()
|
||||||
const safeAreaInsets = useSafeAreaInsets()
|
|
||||||
const {name, rkey} = route.params
|
const {name, rkey} = route.params
|
||||||
const {isMobile} = useWebMediaQueries()
|
|
||||||
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
||||||
const [canReply, setCanReply] = React.useState(false)
|
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
React.useCallback(() => {
|
React.useCallback(() => {
|
||||||
|
@ -67,33 +54,8 @@ export function PostThreadScreen({route}: Props) {
|
||||||
return (
|
return (
|
||||||
<View style={s.hContentRegion}>
|
<View style={s.hContentRegion}>
|
||||||
<View style={s.flex1}>
|
<View style={s.flex1}>
|
||||||
<PostThreadComponent
|
<PostThreadComponent uri={uri} onPressReply={onPressReply} />
|
||||||
uri={uri}
|
|
||||||
onPressReply={onPressReply}
|
|
||||||
onCanReply={setCanReply}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
{isMobile && canReply && hasSession && (
|
|
||||||
<Animated.View
|
|
||||||
style={[
|
|
||||||
styles.prompt,
|
|
||||||
fabMinimalShellTransform,
|
|
||||||
{
|
|
||||||
bottom: clamp(safeAreaInsets.bottom, 15, 30),
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
<ComposePrompt onPressCompose={onPressReply} />
|
|
||||||
</Animated.View>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
prompt: {
|
|
||||||
// @ts-ignore web-only
|
|
||||||
position: isWeb ? 'fixed' : 'absolute',
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
Loading…
Reference in New Issue