about summary refs log tree commit diff
path: root/src/view/com/composer
diff options
context:
space:
mode:
authorMary <148872143+mary-ext@users.noreply.github.com>2024-09-24 23:27:40 +0700
committerGitHub <noreply@github.com>2024-09-25 01:27:40 +0900
commitb9516202fa17325a3d54e54372ddd56149be129c (patch)
treee95ff75e6b6e8d6ef3ab6a3ccd34fe293a4a3568 /src/view/com/composer
parented512d6dc5390555232bb4ac3f96f477751c33b1 (diff)
downloadvoidsky-b9516202fa17325a3d54e54372ddd56149be129c.tar.zst
Revamp image editor (#5462)
* new image editor

* Rm react-avatar-editor

---------

Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
Diffstat (limited to 'src/view/com/composer')
-rw-r--r--src/view/com/composer/photos/EditImageDialog.tsx14
-rw-r--r--src/view/com/composer/photos/EditImageDialog.web.tsx105
-rw-r--r--src/view/com/composer/photos/Gallery.tsx34
3 files changed, 138 insertions, 15 deletions
diff --git a/src/view/com/composer/photos/EditImageDialog.tsx b/src/view/com/composer/photos/EditImageDialog.tsx
new file mode 100644
index 000000000..4263587fd
--- /dev/null
+++ b/src/view/com/composer/photos/EditImageDialog.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+
+import {ComposerImage} from '#/state/gallery'
+import * as Dialog from '#/components/Dialog'
+
+export type EditImageDialogProps = {
+  control: Dialog.DialogOuterProps['control']
+  image: ComposerImage
+  onChange: (next: ComposerImage) => void
+}
+
+export const EditImageDialog = ({}: EditImageDialogProps): React.ReactNode => {
+  return null
+}
diff --git a/src/view/com/composer/photos/EditImageDialog.web.tsx b/src/view/com/composer/photos/EditImageDialog.web.tsx
new file mode 100644
index 000000000..0afb83ed9
--- /dev/null
+++ b/src/view/com/composer/photos/EditImageDialog.web.tsx
@@ -0,0 +1,105 @@
+import 'react-image-crop/dist/ReactCrop.css'
+
+import React from 'react'
+import {View} from 'react-native'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import ReactCrop, {PercentCrop} from 'react-image-crop'
+
+import {
+  ImageSource,
+  ImageTransformation,
+  manipulateImage,
+} from '#/state/gallery'
+import {atoms as a} from '#/alf'
+import {Button, ButtonText} from '#/components/Button'
+import * as Dialog from '#/components/Dialog'
+import {Text} from '#/components/Typography'
+import {EditImageDialogProps} from './EditImageDialog'
+
+export const EditImageDialog = (props: EditImageDialogProps) => {
+  return (
+    <Dialog.Outer control={props.control}>
+      <EditImageInner key={props.image.source.id} {...props} />
+    </Dialog.Outer>
+  )
+}
+
+const EditImageInner = ({control, image, onChange}: EditImageDialogProps) => {
+  const {_} = useLingui()
+
+  const source = image.source
+
+  const initialCrop = getInitialCrop(source, image.manips)
+  const [crop, setCrop] = React.useState(initialCrop)
+
+  const isEmpty = !crop || (crop.width || crop.height) === 0
+  const isNew = initialCrop ? true : !isEmpty
+
+  const onPressSubmit = React.useCallback(async () => {
+    const result = await manipulateImage(image, {
+      crop:
+        crop && (crop.width || crop.height) !== 0
+          ? {
+              originX: (crop.x * source.width) / 100,
+              originY: (crop.y * source.height) / 100,
+              width: (crop.width * source.width) / 100,
+              height: (crop.height * source.height) / 100,
+            }
+          : undefined,
+    })
+
+    onChange(result)
+    control.close()
+  }, [crop, image, source, control, onChange])
+
+  return (
+    <Dialog.Inner label={_(msg`Edit image`)}>
+      <Dialog.Close />
+
+      <Text style={[a.text_2xl, a.font_bold, a.leading_tight, a.pb_sm]}>
+        <Trans>Edit image</Trans>
+      </Text>
+
+      <View style={[a.align_center]}>
+        <ReactCrop
+          crop={crop}
+          onChange={(_pixelCrop, percentCrop) => setCrop(percentCrop)}
+          className="ReactCrop--no-animate">
+          <img src={source.path} style={{maxHeight: `50vh`}} />
+        </ReactCrop>
+      </View>
+
+      <View style={[a.mt_md, a.gap_md]}>
+        <Button
+          disabled={!isNew}
+          label={_(msg`Save`)}
+          size="large"
+          color="primary"
+          variant="solid"
+          onPress={onPressSubmit}>
+          <ButtonText>
+            <Trans>Save</Trans>
+          </ButtonText>
+        </Button>
+      </View>
+    </Dialog.Inner>
+  )
+}
+
+const getInitialCrop = (
+  source: ImageSource,
+  manips: ImageTransformation | undefined,
+): PercentCrop | undefined => {
+  const initialArea = manips?.crop
+
+  if (initialArea) {
+    return {
+      unit: '%',
+      x: (initialArea.originX / source.width) * 100,
+      y: (initialArea.originY / source.height) * 100,
+      width: (initialArea.width / source.width) * 100,
+      height: (initialArea.height / source.height) * 100,
+    }
+  }
+}
diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx
index 83c1e3c80..369f08d74 100644
--- a/src/view/com/composer/photos/Gallery.tsx
+++ b/src/view/com/composer/photos/Gallery.tsx
@@ -21,6 +21,7 @@ import {ComposerImage, cropImage} from '#/state/gallery'
 import {Text} from '#/view/com/util/text/Text'
 import {useTheme} from '#/alf'
 import * as Dialog from '#/components/Dialog'
+import {EditImageDialog} from './EditImageDialog'
 import {ImageAltTextDialog} from './ImageAltTextDialog'
 
 const IMAGE_GAP = 8
@@ -144,12 +145,15 @@ const GalleryItem = ({
   const t = useTheme()
 
   const altTextControl = Dialog.useDialogControl()
+  const editControl = Dialog.useDialogControl()
 
   const onImageEdit = () => {
     if (isNative) {
       cropImage(image).then(next => {
         onChange(next)
       })
+    } else {
+      editControl.open()
     }
   }
 
@@ -185,21 +189,15 @@ const GalleryItem = ({
         </Text>
       </TouchableOpacity>
       <View style={imageControlsStyle}>
-        {isNative && (
-          <TouchableOpacity
-            testID="editPhotoButton"
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Edit image`)}
-            accessibilityHint=""
-            onPress={onImageEdit}
-            style={styles.imageControl}>
-            <FontAwesomeIcon
-              icon="pen"
-              size={12}
-              style={{color: colors.white}}
-            />
-          </TouchableOpacity>
-        )}
+        <TouchableOpacity
+          testID="editPhotoButton"
+          accessibilityRole="button"
+          accessibilityLabel={_(msg`Edit image`)}
+          accessibilityHint=""
+          onPress={onImageEdit}
+          style={styles.imageControl}>
+          <FontAwesomeIcon icon="pen" size={12} style={{color: colors.white}} />
+        </TouchableOpacity>
         <TouchableOpacity
           testID="removePhotoButton"
           accessibilityRole="button"
@@ -237,6 +235,12 @@ const GalleryItem = ({
         image={image}
         onChange={onChange}
       />
+
+      <EditImageDialog
+        control={editControl}
+        image={image}
+        onChange={onChange}
+      />
     </View>
   )
 }