about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/alf/util/useColorModeTheme.ts6
-rw-r--r--src/view/com/composer/Composer.tsx51
-rw-r--r--src/view/com/composer/KeyboardAccessory.tsx34
-rw-r--r--src/view/shell/Composer.tsx104
4 files changed, 99 insertions, 96 deletions
diff --git a/src/alf/util/useColorModeTheme.ts b/src/alf/util/useColorModeTheme.ts
index 4f8921bf9..301c993dd 100644
--- a/src/alf/util/useColorModeTheme.ts
+++ b/src/alf/util/useColorModeTheme.ts
@@ -1,10 +1,10 @@
 import React from 'react'
 import {ColorSchemeName, useColorScheme} from 'react-native'
+import * as SystemUI from 'expo-system-ui'
 
-import {useThemePrefs} from 'state/shell'
 import {isWeb} from 'platform/detection'
-import {ThemeName, light, dark, dim} from '#/alf/themes'
-import * as SystemUI from 'expo-system-ui'
+import {useThemePrefs} from 'state/shell'
+import {dark, dim, light, ThemeName} from '#/alf/themes'
 
 export function useColorModeTheme(): ThemeName {
   const colorScheme = useColorScheme()
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index 12e57c411..5746454c2 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -1,7 +1,13 @@
-import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'
+import React, {
+  useCallback,
+  useEffect,
+  useImperativeHandle,
+  useMemo,
+  useRef,
+  useState,
+} from 'react'
 import {
   ActivityIndicator,
-  BackHandler,
   Keyboard,
   ScrollView,
   StyleSheet,
@@ -79,6 +85,10 @@ import {TextInput, TextInputRef} from './text-input/TextInput'
 import {ThreadgateBtn} from './threadgate/ThreadgateBtn'
 import {useExternalLinkFetch} from './useExternalLinkFetch'
 
+type CancelRef = {
+  onPressCancel: () => void
+}
+
 type Props = ComposerOpts
 export const ComposePost = observer(function ComposePost({
   replyTo,
@@ -88,7 +98,10 @@ export const ComposePost = observer(function ComposePost({
   openPicker,
   text: initText,
   imageUris: initImageUris,
-}: Props) {
+  cancelRef,
+}: Props & {
+  cancelRef?: React.RefObject<CancelRef>
+}) {
   const {currentAccount} = useSession()
   const agent = useAgent()
   const {data: currentProfile} = useProfileQuery({did: currentAccount!.did})
@@ -145,7 +158,7 @@ export const ComposePost = observer(function ComposePost({
     () => ({
       paddingBottom:
         isAndroid || (isIOS && !isKeyboardVisible) ? insets.bottom : 0,
-      paddingTop: isAndroid ? insets.top : isMobile ? 15 : 0,
+      paddingTop: isMobile && isWeb ? 15 : insets.top,
     }),
     [insets, isKeyboardVisible, isMobile],
   )
@@ -167,23 +180,8 @@ export const ComposePost = observer(function ComposePost({
     discardPromptControl,
     onClose,
   ])
-  // android back button
-  useEffect(() => {
-    if (!isAndroid) {
-      return
-    }
-    const backHandler = BackHandler.addEventListener(
-      'hardwareBackPress',
-      () => {
-        onPressCancel()
-        return true
-      },
-    )
 
-    return () => {
-      backHandler.remove()
-    }
-  }, [onPressCancel])
+  useImperativeHandle(cancelRef, () => ({onPressCancel}))
 
   // listen to escape key on desktop web
   const onEscape = useCallback(
@@ -583,19 +581,18 @@ export const ComposePost = observer(function ComposePost({
   )
 })
 
+export function useComposerCancelRef() {
+  return useRef<CancelRef>(null)
+}
+
 const styles = StyleSheet.create({
-  outer: {
-    flexDirection: 'column',
-    flex: 1,
-    height: '100%',
-  },
   topbar: {
     flexDirection: 'row',
     alignItems: 'center',
-    paddingTop: 6,
+    marginTop: -14,
     paddingBottom: 4,
     paddingHorizontal: 20,
-    height: 55,
+    height: 50,
     gap: 4,
   },
   topbarDesktop: {
diff --git a/src/view/com/composer/KeyboardAccessory.tsx b/src/view/com/composer/KeyboardAccessory.tsx
new file mode 100644
index 000000000..983a87dae
--- /dev/null
+++ b/src/view/com/composer/KeyboardAccessory.tsx
@@ -0,0 +1,34 @@
+import React from 'react'
+import {View} from 'react-native'
+import {KeyboardStickyView} from 'react-native-keyboard-controller'
+import {useSafeAreaInsets} from 'react-native-safe-area-context'
+
+import {isWeb} from '#/platform/detection'
+import {atoms as a, useTheme} from '#/alf'
+
+export function KeyboardAccessory({children}: {children: React.ReactNode}) {
+  const t = useTheme()
+  const {bottom} = useSafeAreaInsets()
+
+  const style = [
+    a.flex_row,
+    a.py_xs,
+    a.pl_sm,
+    a.pr_xl,
+    a.align_center,
+    a.border_t,
+    t.atoms.border_contrast_medium,
+    t.atoms.bg,
+  ]
+
+  // todo: when iPad support is added, it should also not use the KeyboardStickyView
+  if (isWeb) {
+    return <View style={style}>{children}</View>
+  }
+
+  return (
+    <KeyboardStickyView offset={{closed: -bottom}} style={style}>
+      {children}
+    </KeyboardStickyView>
+  )
+}
diff --git a/src/view/shell/Composer.tsx b/src/view/shell/Composer.tsx
index 1937fcb6e..17348a30c 100644
--- a/src/view/shell/Composer.tsx
+++ b/src/view/shell/Composer.tsx
@@ -1,77 +1,49 @@
-import React, {useEffect} from 'react'
+import React from 'react'
+import {Modal, View} from 'react-native'
 import {observer} from 'mobx-react-lite'
-import {Animated, Easing, Platform, StyleSheet, View} from 'react-native'
-import {ComposePost} from '../com/composer/Composer'
+
+import {Provider as LegacyModalProvider} from '#/state/modals'
 import {useComposerState} from 'state/shell/composer'
-import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
-import {usePalette} from 'lib/hooks/usePalette'
+import {ModalsContainer as LegacyModalsContainer} from '#/view/com/modals/Modal'
+import {useTheme} from '#/alf'
+import {
+  Outlet as PortalOutlet,
+  Provider as PortalProvider,
+} from '#/components/Portal'
+import {ComposePost, useComposerCancelRef} from '../com/composer/Composer'
 
-export const Composer = observer(function ComposerImpl({
-  winHeight,
-}: {
+export const Composer = observer(function ComposerImpl({}: {
   winHeight: number
 }) {
+  const t = useTheme()
   const state = useComposerState()
-  const pal = usePalette('default')
-  const initInterp = useAnimatedValue(0)
-
-  useEffect(() => {
-    if (state) {
-      Animated.timing(initInterp, {
-        toValue: 1,
-        duration: 300,
-        easing: Easing.out(Easing.exp),
-        useNativeDriver: true,
-      }).start()
-    } else {
-      initInterp.setValue(0)
-    }
-  }, [initInterp, state])
-  const wrapperAnimStyle = {
-    transform: [
-      {
-        translateY: initInterp.interpolate({
-          inputRange: [0, 1],
-          outputRange: [winHeight, 0],
-        }),
-      },
-    ],
-  }
-
-  // rendering
-  // =
-
-  if (!state) {
-    return <View />
-  }
+  const ref = useComposerCancelRef()
 
   return (
-    <Animated.View
-      style={[styles.wrapper, pal.view, wrapperAnimStyle]}
+    <Modal
       aria-modal
-      accessibilityViewIsModal>
-      <ComposePost
-        replyTo={state.replyTo}
-        onPost={state.onPost}
-        quote={state.quote}
-        mention={state.mention}
-        text={state.text}
-        imageUris={state.imageUris}
-      />
-    </Animated.View>
+      accessibilityViewIsModal
+      visible={!!state}
+      presentationStyle="overFullScreen"
+      animationType="slide"
+      onRequestClose={() => ref.current?.onPressCancel()}>
+      <View style={[t.atoms.bg, {flex: 1}]}>
+        <LegacyModalProvider>
+          <PortalProvider>
+            <ComposePost
+              cancelRef={ref}
+              replyTo={state?.replyTo}
+              onPost={state?.onPost}
+              quote={state?.quote}
+              mention={state?.mention}
+              text={state?.text}
+              imageUris={state?.imageUris}
+            />
+            <LegacyModalsContainer />
+            <PortalOutlet />
+          </PortalProvider>
+        </LegacyModalProvider>
+      </View>
+    </Modal>
   )
 })
-
-const styles = StyleSheet.create({
-  wrapper: {
-    position: 'absolute',
-    top: 0,
-    bottom: 0,
-    width: '100%',
-    ...Platform.select({
-      ios: {
-        paddingTop: 24,
-      },
-    }),
-  },
-})