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/EditProfile.tsx10
-rw-r--r--src/view/com/modals/Modal.web.tsx7
-rw-r--r--src/view/com/modals/crop-image/CropImage.tsx11
-rw-r--r--src/view/com/modals/crop-image/CropImage.web.tsx164
4 files changed, 187 insertions, 5 deletions
diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx
index 1c139e9bd..12b72a399 100644
--- a/src/view/com/modals/EditProfile.tsx
+++ b/src/view/com/modals/EditProfile.tsx
@@ -8,7 +8,7 @@ import {
 } from 'react-native'
 import LinearGradient from 'react-native-linear-gradient'
 import {ScrollView, TextInput} from './util'
-import {Image as PickedImage} from '../util/images/ImageCropPicker'
+import {PickedMedia} from '../util/images/image-crop-picker/ImageCropPicker'
 import {Text} from '../util/text/Text'
 import {ErrorMessage} from '../util/error/ErrorMessage'
 import {useStores} from '../../../state'
@@ -48,12 +48,12 @@ export function Component({
   const [userAvatar, setUserAvatar] = useState<string | undefined>(
     profileView.avatar,
   )
-  const [newUserBanner, setNewUserBanner] = useState<PickedImage | undefined>()
-  const [newUserAvatar, setNewUserAvatar] = useState<PickedImage | undefined>()
+  const [newUserBanner, setNewUserBanner] = useState<PickedMedia | undefined>()
+  const [newUserAvatar, setNewUserAvatar] = useState<PickedMedia | undefined>()
   const onPressCancel = () => {
     store.shell.closeModal()
   }
-  const onSelectNewAvatar = async (img: PickedImage) => {
+  const onSelectNewAvatar = async (img: PickedMedia) => {
     try {
       const finalImg = await compressIfNeeded(img, 300000)
       setNewUserAvatar(finalImg)
@@ -62,7 +62,7 @@ export function Component({
       setError(e.message || e.toString())
     }
   }
-  const onSelectNewBanner = async (img: PickedImage) => {
+  const onSelectNewBanner = async (img: PickedMedia) => {
     try {
       const finalImg = await compressIfNeeded(img, 500000)
       setNewUserBanner(finalImg)
diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx
index 25493312d..44ea95f07 100644
--- a/src/view/com/modals/Modal.web.tsx
+++ b/src/view/com/modals/Modal.web.tsx
@@ -11,6 +11,7 @@ import * as EditProfileModal from './EditProfile'
 import * as ServerInputModal from './ServerInput'
 import * as ReportPostModal from './ReportPost'
 import * as ReportAccountModal from './ReportAccount'
+import * as CropImageModal from './crop-image/CropImage.web'
 
 export const Modal = observer(function Modal() {
   const store = useStores()
@@ -50,6 +51,12 @@ export const Modal = observer(function Modal() {
     element = <ReportPostModal.Component />
   } else if (store.shell.activeModal?.name === 'report-account') {
     element = <ReportAccountModal.Component />
+  } else if (store.shell.activeModal?.name === 'crop-image') {
+    element = (
+      <CropImageModal.Component
+        {...(store.shell.activeModal as models.CropImageModal)}
+      />
+    )
   } else {
     return null
   }
diff --git a/src/view/com/modals/crop-image/CropImage.tsx b/src/view/com/modals/crop-image/CropImage.tsx
new file mode 100644
index 000000000..9ac3f277f
--- /dev/null
+++ b/src/view/com/modals/crop-image/CropImage.tsx
@@ -0,0 +1,11 @@
+/**
+ * NOTE
+ * This modal is used only in the web build
+ * Native uses a third-party library
+ */
+
+export const snapPoints = ['0%']
+
+export function Component() {
+  return null
+}
diff --git a/src/view/com/modals/crop-image/CropImage.web.tsx b/src/view/com/modals/crop-image/CropImage.web.tsx
new file mode 100644
index 000000000..1f234c4a6
--- /dev/null
+++ b/src/view/com/modals/crop-image/CropImage.web.tsx
@@ -0,0 +1,164 @@
+import React from 'react'
+import {StyleSheet, TouchableOpacity, View} from 'react-native'
+import ImageEditor from 'react-avatar-editor'
+import {Slider} from '@miblanchard/react-native-slider'
+import LinearGradient from 'react-native-linear-gradient'
+import {Text} from '../../util/text/Text'
+import {PickedMedia} from '../../util/images/image-crop-picker/types'
+import {s, gradients} from '../../../lib/styles'
+import {useStores} from '../../../../state'
+import {usePalette} from '../../../lib/hooks/usePalette'
+import {SquareIcon, RectWideIcon, RectTallIcon} from '../../../lib/icons'
+
+enum AspectRatio {
+  Square = 'square',
+  Wide = 'wide',
+  Tall = 'tall',
+}
+interface Dim {
+  width: number
+  height: number
+}
+const DIMS: Record<string, Dim> = {
+  [AspectRatio.Square]: {width: 1000, height: 1000},
+  [AspectRatio.Wide]: {width: 1000, height: 750},
+  [AspectRatio.Tall]: {width: 750, height: 1000},
+}
+
+export const snapPoints = ['0%']
+
+export function Component({
+  uri,
+  onSelect,
+}: {
+  uri: string
+  onSelect: (img?: PickedMedia) => void
+}) {
+  const store = useStores()
+  const pal = usePalette('default')
+  const [as, setAs] = React.useState<AspectRatio>(AspectRatio.Square)
+  const [scale, setScale] = React.useState<number>(1)
+
+  const doSetAs = (v: AspectRatio) => () => setAs(v)
+
+  const onPressCancel = () => {
+    onSelect(undefined)
+    store.shell.closeModal()
+  }
+  const onPressDone = () => {
+    console.log('TODO')
+    onSelect(undefined) // TODO
+    store.shell.closeModal()
+  }
+
+  let cropperStyle
+  if (as === AspectRatio.Square) {
+    cropperStyle = styles.cropperSquare
+  } else if (as === AspectRatio.Wide) {
+    cropperStyle = styles.cropperWide
+  } else if (as === AspectRatio.Tall) {
+    cropperStyle = styles.cropperTall
+  }
+  return (
+    <View>
+      <View style={[styles.cropper, cropperStyle]}>
+        <ImageEditor
+          style={styles.imageEditor}
+          image={uri}
+          width={DIMS[as].width}
+          height={DIMS[as].height}
+          scale={scale}
+        />
+      </View>
+      <View style={styles.ctrls}>
+        <Slider
+          value={scale}
+          onValueChange={(v: number | number[]) =>
+            setScale(Array.isArray(v) ? v[0] : v)
+          }
+          minimumValue={1}
+          maximumValue={3}
+          containerStyle={styles.slider}
+        />
+        <TouchableOpacity onPress={doSetAs(AspectRatio.Wide)}>
+          <RectWideIcon
+            size={24}
+            style={as === AspectRatio.Wide ? s.blue3 : undefined}
+          />
+        </TouchableOpacity>
+        <TouchableOpacity onPress={doSetAs(AspectRatio.Tall)}>
+          <RectTallIcon
+            size={24}
+            style={as === AspectRatio.Tall ? s.blue3 : undefined}
+          />
+        </TouchableOpacity>
+        <TouchableOpacity onPress={doSetAs(AspectRatio.Square)}>
+          <SquareIcon
+            size={24}
+            style={as === AspectRatio.Square ? s.blue3 : undefined}
+          />
+        </TouchableOpacity>
+      </View>
+      <View style={styles.btns}>
+        <TouchableOpacity onPress={onPressCancel}>
+          <Text type="xl" style={pal.link}>
+            Cancel
+          </Text>
+        </TouchableOpacity>
+        <View style={s.flex1} />
+        <TouchableOpacity onPress={onPressDone}>
+          <LinearGradient
+            colors={[gradients.blueLight.start, gradients.blueLight.end]}
+            start={{x: 0, y: 0}}
+            end={{x: 1, y: 1}}
+            style={[styles.btn]}>
+            <Text type="xl-medium" style={s.white}>
+              Done
+            </Text>
+          </LinearGradient>
+        </TouchableOpacity>
+      </View>
+    </View>
+  )
+}
+
+const styles = StyleSheet.create({
+  cropper: {
+    marginLeft: 'auto',
+    marginRight: 'auto',
+  },
+  cropperSquare: {
+    width: 400,
+    height: 400,
+  },
+  cropperWide: {
+    width: 400,
+    height: 300,
+  },
+  cropperTall: {
+    width: 300,
+    height: 400,
+  },
+  imageEditor: {
+    maxWidth: '100%',
+  },
+  ctrls: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    marginTop: 10,
+  },
+  slider: {
+    flex: 1,
+    marginRight: 10,
+  },
+  btns: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    marginTop: 10,
+  },
+  btn: {
+    borderRadius: 4,
+    paddingVertical: 8,
+    paddingHorizontal: 24,
+  },
+})