about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2024-05-06 15:30:54 -0700
committerPaul Frazee <pfrazee@gmail.com>2024-05-06 15:30:54 -0700
commite1940983a3b02e06ddd209c836648d993c281679 (patch)
tree1be4b1cbf161708acd13878aa182915bff516146
parent136b0e12abe4c4c00c15374c018dee36c522ec38 (diff)
parentbf7aee19b8fc0dfb31376f814d883b654b5db8a1 (diff)
downloadvoidsky-e1940983a3b02e06ddd209c836648d993c281679.tar.zst
Merge branch 'fix-banner-cropper' of https://github.com/piotrpalek/social-app into piotrpalek-fix-banner-cropper
-rw-r--r--src/lib/media/picker.tsx3
-rw-r--r--src/lib/media/picker.web.tsx11
-rw-r--r--src/state/modals/index.tsx1
-rw-r--r--src/view/com/modals/crop-image/CropImage.web.tsx94
-rw-r--r--src/view/com/modals/crop-image/cropImageUtil.ts13
-rw-r--r--src/view/com/util/UserBanner.tsx20
6 files changed, 92 insertions, 50 deletions
diff --git a/src/lib/media/picker.tsx b/src/lib/media/picker.tsx
index bf531c981..37e01e67f 100644
--- a/src/lib/media/picker.tsx
+++ b/src/lib/media/picker.tsx
@@ -1,8 +1,9 @@
 import {
+  Image as RNImage,
   openCamera as openCameraFn,
   openCropper as openCropperFn,
-  Image as RNImage,
 } from 'react-native-image-crop-picker'
+
 import {CameraOpts, CropperOptions} from './types'
 export {openPicker} from './picker.shared'
 
diff --git a/src/lib/media/picker.web.tsx b/src/lib/media/picker.web.tsx
index 995a0c95f..fde6a64ac 100644
--- a/src/lib/media/picker.web.tsx
+++ b/src/lib/media/picker.web.tsx
@@ -1,7 +1,8 @@
 /// <reference lib="dom" />
 
-import {CameraOpts, CropperOptions} from './types'
 import {Image as RNImage} from 'react-native-image-crop-picker'
+
+import {CameraOpts, CropperOptions} from './types'
 export {openPicker} from './picker.shared'
 import {unstable__openModal} from '#/state/modals'
 
@@ -12,15 +13,17 @@ export async function openCamera(_opts: CameraOpts): Promise<RNImage> {
 
 export async function openCropper(opts: CropperOptions): Promise<RNImage> {
   // TODO handle more opts
-  return new Promise((resolve, reject) => {
+  return new Promise(resolve => {
     unstable__openModal({
       name: 'crop-image',
       uri: opts.path,
+      dimensions:
+        opts.height && opts.width
+          ? {width: opts.width, height: opts.height}
+          : undefined,
       onSelect: (img?: RNImage) => {
         if (img) {
           resolve(img)
-        } else {
-          reject(new Error('Canceled'))
         }
       },
     })
diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx
index 0f61a9711..cf82bcd07 100644
--- a/src/state/modals/index.tsx
+++ b/src/state/modals/index.tsx
@@ -47,6 +47,7 @@ export interface EditImageModal {
 export interface CropImageModal {
   name: 'crop-image'
   uri: string
+  dimensions?: {width: number; height: number}
   onSelect: (img?: RNImage) => void
 }
 
diff --git a/src/view/com/modals/crop-image/CropImage.web.tsx b/src/view/com/modals/crop-image/CropImage.web.tsx
index 79ff5a02a..10cae2f17 100644
--- a/src/view/com/modals/crop-image/CropImage.web.tsx
+++ b/src/view/com/modals/crop-image/CropImage.web.tsx
@@ -14,11 +14,13 @@ import {Dimensions} from 'lib/media/types'
 import {getDataUriSize} from 'lib/media/util'
 import {gradients, s} from 'lib/styles'
 import {Text} from 'view/com/util/text/Text'
+import {calculateDimensions} from './cropImageUtil'
 
 enum AspectRatio {
   Square = 'square',
   Wide = 'wide',
   Tall = 'tall',
+  Custom = 'custom',
 }
 
 const DIMS: Record<string, Dimensions> = {
@@ -31,17 +33,24 @@ export const snapPoints = ['0%']
 
 export function Component({
   uri,
+  dimensions,
   onSelect,
 }: {
   uri: string
+  dimensions?: Dimensions
   onSelect: (img?: RNImage) => void
 }) {
   const {closeModal} = useModalControls()
   const pal = usePalette('default')
   const {_} = useLingui()
-  const [as, setAs] = React.useState<AspectRatio>(AspectRatio.Square)
+  const defaultAspectStyle = dimensions
+    ? AspectRatio.Custom
+    : AspectRatio.Square
+  const [as, setAs] = React.useState<AspectRatio>(defaultAspectStyle)
   const [scale, setScale] = React.useState<number>(1)
   const editorRef = React.useRef<ImageEditor>(null)
+  const imageEditorWidth = dimensions ? dimensions.width : DIMS[as].width
+  const imageEditorHeight = dimensions ? dimensions.height : DIMS[as].height
 
   const doSetAs = (v: AspectRatio) => () => setAs(v)
 
@@ -57,8 +66,8 @@ export function Component({
         path: dataUri,
         mime: 'image/jpeg',
         size: getDataUriSize(dataUri),
-        width: DIMS[as].width,
-        height: DIMS[as].height,
+        width: imageEditorWidth,
+        height: imageEditorHeight,
       })
     } else {
       onSelect(undefined)
@@ -73,7 +82,18 @@ export function Component({
     cropperStyle = styles.cropperWide
   } else if (as === AspectRatio.Tall) {
     cropperStyle = styles.cropperTall
+  } else if (as === AspectRatio.Custom) {
+    const cropperDimensions = calculateDimensions(
+      550,
+      imageEditorHeight,
+      imageEditorWidth,
+    )
+    cropperStyle = {
+      width: cropperDimensions.width,
+      height: cropperDimensions.height,
+    }
   }
+
   return (
     <View>
       <View style={[styles.cropper, pal.borderDark, cropperStyle]}>
@@ -81,8 +101,8 @@ export function Component({
           ref={editorRef}
           style={styles.imageEditor}
           image={uri}
-          width={DIMS[as].width}
-          height={DIMS[as].height}
+          width={imageEditorWidth}
+          height={imageEditorHeight}
           scale={scale}
           border={0}
         />
@@ -97,36 +117,40 @@ export function Component({
           maximumValue={3}
           containerStyle={styles.slider}
         />
-        <TouchableOpacity
-          onPress={doSetAs(AspectRatio.Wide)}
-          accessibilityRole="button"
-          accessibilityLabel={_(msg`Wide`)}
-          accessibilityHint={_(msg`Sets image aspect ratio to wide`)}>
-          <RectWideIcon
-            size={24}
-            style={as === AspectRatio.Wide ? s.blue3 : pal.text}
-          />
-        </TouchableOpacity>
-        <TouchableOpacity
-          onPress={doSetAs(AspectRatio.Tall)}
-          accessibilityRole="button"
-          accessibilityLabel={_(msg`Tall`)}
-          accessibilityHint={_(msg`Sets image aspect ratio to tall`)}>
-          <RectTallIcon
-            size={24}
-            style={as === AspectRatio.Tall ? s.blue3 : pal.text}
-          />
-        </TouchableOpacity>
-        <TouchableOpacity
-          onPress={doSetAs(AspectRatio.Square)}
-          accessibilityRole="button"
-          accessibilityLabel={_(msg`Square`)}
-          accessibilityHint={_(msg`Sets image aspect ratio to square`)}>
-          <SquareIcon
-            size={24}
-            style={as === AspectRatio.Square ? s.blue3 : pal.text}
-          />
-        </TouchableOpacity>
+        {as === AspectRatio.Custom ? null : (
+          <>
+            <TouchableOpacity
+              onPress={doSetAs(AspectRatio.Wide)}
+              accessibilityRole="button"
+              accessibilityLabel={_(msg`Wide`)}
+              accessibilityHint={_(msg`Sets image aspect ratio to wide`)}>
+              <RectWideIcon
+                size={24}
+                style={as === AspectRatio.Wide ? s.blue3 : pal.text}
+              />
+            </TouchableOpacity>
+            <TouchableOpacity
+              onPress={doSetAs(AspectRatio.Tall)}
+              accessibilityRole="button"
+              accessibilityLabel={_(msg`Tall`)}
+              accessibilityHint={_(msg`Sets image aspect ratio to tall`)}>
+              <RectTallIcon
+                size={24}
+                style={as === AspectRatio.Tall ? s.blue3 : pal.text}
+              />
+            </TouchableOpacity>
+            <TouchableOpacity
+              onPress={doSetAs(AspectRatio.Square)}
+              accessibilityRole="button"
+              accessibilityLabel={_(msg`Square`)}
+              accessibilityHint={_(msg`Sets image aspect ratio to square`)}>
+              <SquareIcon
+                size={24}
+                style={as === AspectRatio.Square ? s.blue3 : pal.text}
+              />
+            </TouchableOpacity>
+          </>
+        )}
       </View>
       <View style={styles.btns}>
         <TouchableOpacity
diff --git a/src/view/com/modals/crop-image/cropImageUtil.ts b/src/view/com/modals/crop-image/cropImageUtil.ts
new file mode 100644
index 000000000..303d15ba5
--- /dev/null
+++ b/src/view/com/modals/crop-image/cropImageUtil.ts
@@ -0,0 +1,13 @@
+export const calculateDimensions = (
+  maxWidth: number,
+  originalHeight: number,
+  originalWidth: number,
+) => {
+  const aspectRatio = originalWidth / originalHeight
+  const newHeight = maxWidth / aspectRatio
+  const newWidth = maxWidth
+  return {
+    width: newWidth,
+    height: newHeight,
+  }
+}
diff --git a/src/view/com/util/UserBanner.tsx b/src/view/com/util/UserBanner.tsx
index 4d73b853b..f08044ec3 100644
--- a/src/view/com/util/UserBanner.tsx
+++ b/src/view/com/util/UserBanner.tsx
@@ -1,29 +1,29 @@
 import React from 'react'
 import {StyleSheet, TouchableOpacity, View} from 'react-native'
-import {ModerationUI} from '@atproto/api'
+import {Image as RNImage} from 'react-native-image-crop-picker'
 import {Image} from 'expo-image'
-import {useLingui} from '@lingui/react'
+import {ModerationUI} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
 
-import {colors} from 'lib/styles'
-import {useTheme} from 'lib/ThemeContext'
-import {useTheme as useAlfTheme, tokens} from '#/alf'
-import {openCamera, openCropper, openPicker} from '../../../lib/media/picker'
+import {usePalette} from 'lib/hooks/usePalette'
 import {
-  usePhotoLibraryPermission,
   useCameraPermission,
+  usePhotoLibraryPermission,
 } from 'lib/hooks/usePermissions'
-import {usePalette} from 'lib/hooks/usePalette'
+import {colors} from 'lib/styles'
+import {useTheme} from 'lib/ThemeContext'
 import {isAndroid, isNative} from 'platform/detection'
-import {Image as RNImage} from 'react-native-image-crop-picker'
 import {EventStopper} from 'view/com/util/EventStopper'
-import * as Menu from '#/components/Menu'
+import {tokens, useTheme as useAlfTheme} from '#/alf'
 import {
   Camera_Filled_Stroke2_Corner0_Rounded as CameraFilled,
   Camera_Stroke2_Corner0_Rounded as Camera,
 } from '#/components/icons/Camera'
 import {StreamingLive_Stroke2_Corner0_Rounded as Library} from '#/components/icons/StreamingLive'
 import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
+import * as Menu from '#/components/Menu'
+import {openCamera, openCropper, openPicker} from '../../../lib/media/picker'
 
 export function UserBanner({
   type,