about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/state/models/shell.ts48
-rw-r--r--src/view/com/composer/Autocomplete.tsx (renamed from src/view/com/modals/composer/Autocomplete.tsx)0
-rw-r--r--src/view/com/composer/ComposePost.tsx (renamed from src/view/com/modals/ComposePost.tsx)24
-rw-r--r--src/view/com/modals/Modal.tsx8
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx11
-rw-r--r--src/view/com/post/Post.tsx5
-rw-r--r--src/view/com/posts/FeedItem.tsx6
-rw-r--r--src/view/screens/Home.tsx3
-rw-r--r--src/view/shell/mobile/Composer.tsx83
-rw-r--r--src/view/shell/mobile/index.tsx8
10 files changed, 130 insertions, 66 deletions
diff --git a/src/state/models/shell.ts b/src/state/models/shell.ts
index 8cb0ff9e7..33b8eef36 100644
--- a/src/state/models/shell.ts
+++ b/src/state/models/shell.ts
@@ -27,22 +27,6 @@ export class SharePostModel {
   }
 }
 
-export interface ComposePostModelOpts {
-  replyTo?: Post.PostRef
-  onPost?: () => void
-}
-export class ComposePostModel {
-  name = 'compose-post'
-  replyTo?: Post.PostRef
-  onPost?: () => void
-
-  constructor(opts?: ComposePostModelOpts) {
-    makeAutoObservable(this)
-    this.replyTo = opts?.replyTo
-    this.onPost = opts?.onPost
-  }
-}
-
 export class EditProfileModel {
   name = 'edit-profile'
 
@@ -51,26 +35,22 @@ export class EditProfileModel {
   }
 }
 
+export interface ComposerOpts {
+  replyTo?: Post.PostRef
+  onPost?: () => void
+}
+
 export class ShellModel {
   isModalActive = false
-  activeModal:
-    | LinkActionsModel
-    | SharePostModel
-    | ComposePostModel
-    | EditProfileModel
-    | undefined
+  activeModal: LinkActionsModel | SharePostModel | EditProfileModel | undefined
+  isComposerActive = false
+  composerOpts: ComposerOpts | undefined
 
   constructor() {
     makeAutoObservable(this)
   }
 
-  openModal(
-    modal:
-      | LinkActionsModel
-      | SharePostModel
-      | ComposePostModel
-      | EditProfileModel,
-  ) {
+  openModal(modal: LinkActionsModel | SharePostModel | EditProfileModel) {
     this.isModalActive = true
     this.activeModal = modal
   }
@@ -79,4 +59,14 @@ export class ShellModel {
     this.isModalActive = false
     this.activeModal = undefined
   }
+
+  openComposer(opts: ComposerOpts) {
+    this.isComposerActive = true
+    this.composerOpts = opts
+  }
+
+  closeComposer() {
+    this.isComposerActive = false
+    this.composerOpts = undefined
+  }
 }
diff --git a/src/view/com/modals/composer/Autocomplete.tsx b/src/view/com/composer/Autocomplete.tsx
index 4e4bdfc8e..4e4bdfc8e 100644
--- a/src/view/com/modals/composer/Autocomplete.tsx
+++ b/src/view/com/composer/Autocomplete.tsx
diff --git a/src/view/com/modals/ComposePost.tsx b/src/view/com/composer/ComposePost.tsx
index 806b5d7a0..496b49a9b 100644
--- a/src/view/com/modals/ComposePost.tsx
+++ b/src/view/com/composer/ComposePost.tsx
@@ -1,28 +1,28 @@
 import React, {useEffect, useMemo, useState} from 'react'
-import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'
-import {BottomSheetTextInput} from '@gorhom/bottom-sheet'
+import {StyleSheet, Text, TextInput, TouchableOpacity, View} from 'react-native'
 import LinearGradient from 'react-native-linear-gradient'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import * as GetUserFollows from '../../../third-party/api/src/types/app/bsky/getUserFollows'
-import * as Post from '../../../third-party/api/src/types/app/bsky/post'
-import {Autocomplete} from './composer/Autocomplete'
+import {Autocomplete} from './Autocomplete'
 import Toast from '../util/Toast'
 import ProgressCircle from '../util/ProgressCircle'
 import {useStores} from '../../../state'
 import * as apilib from '../../../state/lib/api'
+import {ComposerOpts} from '../../../state/models/shell'
 import {s, colors, gradients} from '../../lib/styles'
 
 const MAX_TEXT_LENGTH = 256
 const WARNING_TEXT_LENGTH = 200
 const DANGER_TEXT_LENGTH = 255
-export const snapPoints = ['100%']
 
-export function Component({
+export function ComposePost({
   replyTo,
   onPost,
+  onClose,
 }: {
-  replyTo?: Post.PostRef
-  onPost?: () => void
+  replyTo?: ComposerOpts['replyTo']
+  onPost?: ComposerOpts['onPost']
+  onClose: () => void
 }) {
   const store = useStores()
   const [error, setError] = useState('')
@@ -67,7 +67,7 @@ export function Component({
     }
   }
   const onPressCancel = () => {
-    store.shell.closeModal()
+    onClose()
   }
   const onPressPublish = async () => {
     setError('')
@@ -85,7 +85,7 @@ export function Component({
       return
     }
     onPost?.()
-    store.shell.closeModal()
+    onClose()
     Toast.show(`Your ${replyTo ? 'reply' : 'post'} has been published`, {
       duration: Toast.durations.LONG,
       position: Toast.positions.TOP,
@@ -148,7 +148,7 @@ export function Component({
           <Text style={s.red4}>{error}</Text>
         </View>
       )}
-      <BottomSheetTextInput
+      <TextInput
         multiline
         scrollEnabled
         autoFocus
@@ -156,7 +156,7 @@ export function Component({
         placeholder={replyTo ? 'Write your reply' : "What's new?"}
         style={styles.textInput}>
         {textDecorated}
-      </BottomSheetTextInput>
+      </TextInput>
       <View style={[s.flexRow, s.pt10, s.pb10, s.pr5]}>
         <View style={s.flex1} />
         <View>
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index 6282b5af1..02b65a490 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -9,7 +9,6 @@ import * as models from '../../../state/models/shell'
 
 import * as LinkActionsModal from './LinkActions'
 import * as SharePostModal from './SharePost.native'
-import * as ComposePostModal from './ComposePost'
 import * as EditProfile from './EditProfile'
 
 const CLOSED_SNAPPOINTS = ['10%']
@@ -51,13 +50,6 @@ export const Modal = observer(function Modal() {
         {...(store.shell.activeModal as models.SharePostModel)}
       />
     )
-  } else if (store.shell.activeModal?.name === 'compose-post') {
-    snapPoints = ComposePostModal.snapPoints
-    element = (
-      <ComposePostModal.Component
-        {...(store.shell.activeModal as models.ComposePostModel)}
-      />
-    )
   } else if (store.shell.activeModal?.name === 'edit-profile') {
     snapPoints = EditProfile.snapPoints
     element = (
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index 4f0683f09..90cffc029 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -6,7 +6,6 @@ import {AtUri} from '../../../third-party/uri'
 import * as PostType from '../../../third-party/api/src/types/app/bsky/post'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {PostThreadViewPostModel} from '../../../state/models/post-thread-view'
-import {ComposePostModel} from '../../../state/models/shell'
 import {Link} from '../util/Link'
 import {RichText} from '../util/RichText'
 import {PostDropdownBtn} from '../util/DropdownBtn'
@@ -49,12 +48,10 @@ export const PostThreadItem = observer(function PostThreadItem({
   const repostsTitle = 'Reposts of this post'
 
   const onPressReply = () => {
-    store.shell.openModal(
-      new ComposePostModel({
-        replyTo: {uri: item.uri, cid: item.cid},
-        onPost: onPostReply,
-      }),
-    )
+    store.shell.openComposer({
+      replyTo: {uri: item.uri, cid: item.cid},
+      onPost: onPostReply,
+    })
   }
   const onPressToggleRepost = () => {
     item
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index b74bbfc42..b98274c1c 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -11,7 +11,6 @@ import {
 } from 'react-native'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {PostThreadViewModel} from '../../../state/models/post-thread-view'
-import {ComposePostModel} from '../../../state/models/shell'
 import {Link} from '../util/Link'
 import {UserInfoText} from '../util/UserInfoText'
 import {RichText} from '../util/RichText'
@@ -71,9 +70,7 @@ export const Post = observer(function Post({uri}: {uri: string}) {
     replyHref = `/profile/${urip.hostname}/post/${urip.rkey}`
   }
   const onPressReply = () => {
-    store.shell.openModal(
-      new ComposePostModel({replyTo: {uri: item.uri, cid: item.cid}}),
-    )
+    store.shell.openComposer({replyTo: {uri: item.uri, cid: item.cid}})
   }
   const onPressToggleRepost = () => {
     item
diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx
index cfb7d7ed7..e591113d1 100644
--- a/src/view/com/posts/FeedItem.tsx
+++ b/src/view/com/posts/FeedItem.tsx
@@ -5,7 +5,7 @@ import {AtUri} from '../../../third-party/uri'
 import * as PostType from '../../../third-party/api/src/types/app/bsky/post'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {FeedItemModel} from '../../../state/models/feed-view'
-import {ComposePostModel, SharePostModel} from '../../../state/models/shell'
+import {SharePostModel} from '../../../state/models/shell'
 import {Link} from '../util/Link'
 import {PostDropdownBtn} from '../util/DropdownBtn'
 import {UserInfoText} from '../util/UserInfoText'
@@ -40,9 +40,7 @@ export const FeedItem = observer(function FeedItem({
   }, [record.reply])
 
   const onPressReply = () => {
-    store.shell.openModal(
-      new ComposePostModel({replyTo: {uri: item.uri, cid: item.cid}}),
-    )
+    store.shell.openComposer({replyTo: {uri: item.uri, cid: item.cid}})
   }
   const onPressToggleRepost = () => {
     item
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 355a2cec1..178b01dc2 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -6,7 +6,6 @@ import {Feed} from '../com/posts/Feed'
 import {FAB} from '../com/util/FloatingActionButton'
 import {useStores} from '../../state'
 import {FeedModel} from '../../state/models/feed-view'
-import {ComposePostModel} from '../../state/models/shell'
 import {ScreenParams} from '../routes'
 import {s} from '../lib/styles'
 
@@ -46,7 +45,7 @@ export const Home = observer(function Home({
   }, [visible, store])
 
   const onComposePress = () => {
-    store.shell.openModal(new ComposePostModel({onPost: onCreatePost}))
+    store.shell.openComposer({onPost: onCreatePost})
   }
   const onCreatePost = () => {
     defaultFeedView.loadLatest()
diff --git a/src/view/shell/mobile/Composer.tsx b/src/view/shell/mobile/Composer.tsx
new file mode 100644
index 000000000..62bc7304d
--- /dev/null
+++ b/src/view/shell/mobile/Composer.tsx
@@ -0,0 +1,83 @@
+import React, {useEffect} from 'react'
+import {observer} from 'mobx-react-lite'
+import {
+  StyleSheet,
+  Text,
+  TouchableOpacity,
+  TouchableWithoutFeedback,
+  View,
+} from 'react-native'
+import Animated, {
+  useSharedValue,
+  useAnimatedStyle,
+  withTiming,
+  interpolate,
+  Easing,
+} from 'react-native-reanimated'
+import {IconProp} from '@fortawesome/fontawesome-svg-core'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {HomeIcon, UserGroupIcon, BellIcon} from '../../lib/icons'
+import {ComposePost} from '../../com/composer/ComposePost'
+import {useStores} from '../../../state'
+import {ComposerOpts} from '../../../state/models/shell'
+import {s, colors} from '../../lib/styles'
+
+export const Composer = observer(
+  ({
+    active,
+    winHeight,
+    replyTo,
+    onPost,
+    onClose,
+  }: {
+    active: boolean
+    winHeight: number
+    replyTo?: ComposerOpts['replyTo']
+    onPost?: ComposerOpts['onPost']
+    onClose: () => void
+  }) => {
+    const store = useStores()
+    const initInterp = useSharedValue<number>(0)
+
+    useEffect(() => {
+      if (active) {
+        initInterp.value = withTiming(1, {
+          duration: 300,
+          easing: Easing.out(Easing.exp),
+        })
+      } else {
+        initInterp.value = 0
+      }
+    }, [initInterp, active])
+    const wrapperAnimStyle = useAnimatedStyle(() => ({
+      top: interpolate(initInterp.value, [0, 1.0], [winHeight, 0]),
+    }))
+
+    // events
+    // =
+
+    // rendering
+    // =
+
+    if (!active) {
+      return <View />
+    }
+
+    return (
+      <Animated.View style={[styles.wrapper, wrapperAnimStyle]}>
+        <ComposePost replyTo={replyTo} onPost={onPost} onClose={onClose} />
+      </Animated.View>
+    )
+  },
+)
+
+const styles = StyleSheet.create({
+  wrapper: {
+    position: 'absolute',
+    top: 0,
+    bottom: 0,
+    width: '100%',
+    backgroundColor: '#fff',
+    paddingTop: 20,
+  },
+})
diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx
index 7b5dd4e91..60188f89f 100644
--- a/src/view/shell/mobile/index.tsx
+++ b/src/view/shell/mobile/index.tsx
@@ -30,6 +30,7 @@ import {Login} from '../../screens/Login'
 import {Modal} from '../../com/modals/Modal'
 import {MainMenu} from './MainMenu'
 import {TabsSelector} from './TabsSelector'
+import {Composer} from './Composer'
 import {s, colors} from '../../lib/styles'
 import {GridIcon, HomeIcon, BellIcon} from '../../lib/icons'
 
@@ -217,6 +218,13 @@ export const MobileShell: React.FC = observer(() => {
         active={isTabsSelectorActive}
         onClose={() => setTabsSelectorActive(false)}
       />
+      <Composer
+        active={store.shell.isComposerActive}
+        onClose={() => store.shell.closeComposer()}
+        winHeight={winDim.height}
+        replyTo={store.shell.composerOpts?.replyTo}
+        onPost={store.shell.composerOpts?.onPost}
+      />
     </View>
   )
 })