Remove unnecessary state update for reply gate (#4897)

* Move mobile compose prompt to inner component

* Make canReply computed

* Use same clamp we use elsewhere
zio/stable
dan 2024-08-08 17:05:51 +01:00 committed by GitHub
parent 2174feed44
commit 4b71950d99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 38 additions and 52 deletions

View File

@ -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,
},
})

View File

@ -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,
},
})