about summary refs log tree commit diff
path: root/src/view/com/modals
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/modals')
-rw-r--r--src/view/com/modals/ComposePost.tsx165
-rw-r--r--src/view/com/modals/Modal.tsx23
2 files changed, 186 insertions, 2 deletions
diff --git a/src/view/com/modals/ComposePost.tsx b/src/view/com/modals/ComposePost.tsx
new file mode 100644
index 000000000..253db3771
--- /dev/null
+++ b/src/view/com/modals/ComposePost.tsx
@@ -0,0 +1,165 @@
+import React, {useState} from 'react'
+import {
+  KeyboardAvoidingView,
+  StyleSheet,
+  Text,
+  TextInput,
+  TouchableOpacity,
+  View,
+} from 'react-native'
+import {BottomSheetTextInput} from '@gorhom/bottom-sheet'
+import LinearGradient from 'react-native-linear-gradient'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import Toast from '../util/Toast'
+import ProgressCircle from '../util/ProgressCircle'
+import {useStores} from '../../../state'
+import * as apilib from '../../../state/lib/api'
+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({replyTo}: {replyTo?: string}) {
+  const store = useStores()
+  const [text, setText] = useState('')
+  const [error, setError] = useState('')
+
+  const onChangeText = (newText: string) => {
+    if (newText.length > MAX_TEXT_LENGTH) {
+      setText(newText.slice(0, MAX_TEXT_LENGTH))
+    } else {
+      setText(newText)
+    }
+  }
+  const onPressCancel = () => {
+    store.shell.closeModal()
+  }
+  const onPressPublish = async () => {
+    setError('')
+    if (text.trim().length === 0) {
+      setError('Did you want to say anything?')
+      return false
+    }
+    try {
+      await apilib.post(store.api, 'alice.com', text, replyTo)
+    } catch (e: any) {
+      console.error(`Failed to create post: ${e.toString()}`)
+      setError(
+        'Post failed to upload. Please check your Internet connection and try again.',
+      )
+      return
+    }
+    store.shell.closeModal()
+    Toast.show(`Your ${replyTo ? 'reply' : 'post'} has been published`, {
+      duration: Toast.durations.LONG,
+      position: Toast.positions.TOP,
+      shadow: true,
+      animation: true,
+      hideOnPress: true,
+    })
+  }
+
+  const progressColor =
+    text.length > DANGER_TEXT_LENGTH
+      ? '#e60000'
+      : text.length > WARNING_TEXT_LENGTH
+      ? '#f7c600'
+      : undefined
+
+  return (
+    <View style={styles.outer}>
+      <View style={styles.topbar}>
+        <TouchableOpacity onPress={onPressCancel}>
+          <Text style={[s.blue3, s.f16]}>Cancel</Text>
+        </TouchableOpacity>
+        <View style={s.flex1} />
+        <TouchableOpacity onPress={onPressPublish}>
+          <LinearGradient
+            colors={[gradients.primary.start, gradients.primary.end]}
+            start={{x: 0, y: 0}}
+            end={{x: 1, y: 1}}
+            style={styles.postBtn}>
+            <Text style={[s.white, s.f16, s.semiBold]}>Post</Text>
+          </LinearGradient>
+        </TouchableOpacity>
+      </View>
+      {error !== '' && (
+        <View style={styles.errorLine}>
+          <View style={styles.errorIcon}>
+            <FontAwesomeIcon
+              icon="exclamation"
+              style={{color: colors.red4}}
+              size={10}
+            />
+          </View>
+          <Text style={s.red4}>{error}</Text>
+        </View>
+      )}
+      <BottomSheetTextInput
+        multiline
+        scrollEnabled
+        autoFocus
+        onChangeText={(text: string) => onChangeText(text)}
+        value={text}
+        placeholder={replyTo ? 'Write your reply' : "What's new?"}
+        style={styles.textInput}
+      />
+      <View style={[s.flexRow, s.pt10, s.pb10, s.pr5]}>
+        <View style={s.flex1} />
+        <View>
+          <ProgressCircle
+            color={progressColor}
+            progress={text.length / MAX_TEXT_LENGTH}
+          />
+        </View>
+      </View>
+    </View>
+  )
+}
+
+const styles = StyleSheet.create({
+  outer: {
+    flexDirection: 'column',
+    backgroundColor: '#fff',
+    padding: 15,
+    height: '100%',
+  },
+  topbar: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    paddingTop: 10,
+    paddingBottom: 5,
+    paddingHorizontal: 5,
+  },
+  postBtn: {
+    borderRadius: 20,
+    paddingHorizontal: 20,
+    paddingVertical: 6,
+  },
+  errorLine: {
+    flexDirection: 'row',
+    backgroundColor: colors.red1,
+    borderRadius: 6,
+    paddingHorizontal: 8,
+    paddingVertical: 6,
+    marginVertical: 6,
+  },
+  errorIcon: {
+    borderWidth: 1,
+    borderColor: colors.red4,
+    color: colors.red4,
+    borderRadius: 30,
+    width: 16,
+    height: 16,
+    alignItems: 'center',
+    justifyContent: 'center',
+    marginRight: 5,
+  },
+  textInput: {
+    flex: 1,
+    padding: 5,
+    fontSize: 18,
+  },
+})
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index dc5b719bc..6e0846000 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -5,8 +5,11 @@ import BottomSheet from '@gorhom/bottom-sheet'
 import {useStores} from '../../../state'
 import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
 
+import * as models from '../../../state/models/shell'
+
 import * as LinkActionsModal from './LinkActions'
 import * as SharePostModal from './SharePost.native'
+import * as ComposePostModal from './ComposePost'
 
 export const Modal = observer(function Modal() {
   const store = useStores()
@@ -28,10 +31,25 @@ export const Modal = observer(function Modal() {
   let snapPoints, element
   if (store.shell.activeModal?.name === 'link-actions') {
     snapPoints = LinkActionsModal.snapPoints
-    element = <LinkActionsModal.Component {...store.shell.activeModal} />
+    element = (
+      <LinkActionsModal.Component
+        {...(store.shell.activeModal as models.LinkActionsModel)}
+      />
+    )
   } else if (store.shell.activeModal?.name === 'share-post') {
     snapPoints = SharePostModal.snapPoints
-    element = <SharePostModal.Component {...store.shell.activeModal} />
+    element = (
+      <SharePostModal.Component
+        {...(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 {
     return <View />
   }
@@ -41,6 +59,7 @@ export const Modal = observer(function Modal() {
       ref={bottomSheetRef}
       snapPoints={snapPoints}
       enablePanDownToClose
+      keyboardBehavior="fillParent"
       backdropComponent={createCustomBackdrop(onClose)}
       onChange={onShareBottomSheetChange}>
       {element}