about summary refs log tree commit diff
path: root/src/view/screens/PostThread.tsx
blob: 0bdd062698ca0c8e9b5b9dc5be938e69ddb99c7f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import React, {useMemo} from 'react'
import {InteractionManager, StyleSheet, View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
import {observer} from 'mobx-react-lite'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
import {makeRecordUri} from 'lib/strings/url-helpers'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {ViewHeader} from '../com/util/ViewHeader'
import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread'
import {ComposePrompt} from 'view/com/composer/Prompt'
import {PostThreadModel} from 'state/models/content/post-thread'
import {useStores} from 'state/index'
import {s} from 'lib/styles'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {clamp} from 'lodash'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {logger} from '#/logger'
import {useMinimalShellMode, useSetMinimalShellMode} from '#/state/shell'

const SHELL_FOOTER_HEIGHT = 44

type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'>
export const PostThreadScreen = withAuthRequired(
  observer(function PostThreadScreenImpl({route}: Props) {
    const store = useStores()
    const minimalShellMode = useMinimalShellMode()
    const setMinimalShellMode = useSetMinimalShellMode()
    const safeAreaInsets = useSafeAreaInsets()
    const {name, rkey} = route.params
    const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
    const view = useMemo<PostThreadModel>(
      () => new PostThreadModel(store, {uri}),
      [store, uri],
    )
    const {isMobile} = useWebMediaQueries()

    useFocusEffect(
      React.useCallback(() => {
        setMinimalShellMode(false)
        const threadCleanup = view.registerListeners()

        InteractionManager.runAfterInteractions(() => {
          if (!view.hasLoaded && !view.isLoading) {
            view.setup().catch(err => {
              logger.error('Failed to fetch thread', {error: err})
            })
          }
        })

        return () => {
          threadCleanup()
        }
      }, [view, setMinimalShellMode]),
    )

    const onPressReply = React.useCallback(() => {
      if (!view.thread) {
        return
      }
      store.shell.openComposer({
        replyTo: {
          uri: view.thread.post.uri,
          cid: view.thread.post.cid,
          text: view.thread.postRecord?.text as string,
          author: {
            handle: view.thread.post.author.handle,
            displayName: view.thread.post.author.displayName,
            avatar: view.thread.post.author.avatar,
          },
        },
        onPost: () => view.refresh(),
      })
    }, [view, store])

    return (
      <View style={s.hContentRegion}>
        {isMobile && <ViewHeader title="Post" />}
        <View style={s.flex1}>
          <PostThreadComponent
            uri={uri}
            view={view}
            onPressReply={onPressReply}
            treeView={!!store.preferences.thread.lab_treeViewEnabled}
          />
        </View>
        {isMobile && !minimalShellMode && (
          <View
            style={[
              styles.prompt,
              {
                bottom:
                  SHELL_FOOTER_HEIGHT + clamp(safeAreaInsets.bottom, 15, 30),
              },
            ]}>
            <ComposePrompt onPressCompose={onPressReply} />
          </View>
        )}
      </View>
    )
  }),
)

const styles = StyleSheet.create({
  prompt: {
    position: 'absolute',
    left: 0,
    right: 0,
  },
})