From ae26963b45082b842803076a5b55532031f9f69a Mon Sep 17 00:00:00 2001
From: Piotr P <piotr.palek@housecallpro.com>
Date: Sat, 27 Apr 2024 14:21:13 +0200
Subject: eslint autofixes

---
 src/lib/media/picker.web.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'src/lib/media/picker.web.tsx')

diff --git a/src/lib/media/picker.web.tsx b/src/lib/media/picker.web.tsx
index 995a0c95f..7e2562e29 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'
 
-- 
cgit 1.4.1


From ebd333b331d7103322d4e0a1c6f5035c0b22bdd9 Mon Sep 17 00:00:00 2001
From: Piotr P <piotr.palek@housecallpro.com>
Date: Sat, 27 Apr 2024 14:21:52 +0200
Subject: Fix cropper crashing after clicking cancel

---
 src/lib/media/picker.web.tsx | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

(limited to 'src/lib/media/picker.web.tsx')

diff --git a/src/lib/media/picker.web.tsx b/src/lib/media/picker.web.tsx
index 7e2562e29..70e28838b 100644
--- a/src/lib/media/picker.web.tsx
+++ b/src/lib/media/picker.web.tsx
@@ -13,15 +13,13 @@ 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,
       onSelect: (img?: RNImage) => {
         if (img) {
           resolve(img)
-        } else {
-          reject(new Error('Canceled'))
         }
       },
     })
-- 
cgit 1.4.1


From bc956803b800715ae2ebbc2105b44c9dc335863b Mon Sep 17 00:00:00 2001
From: Piotr P <piotr.palek@housecallpro.com>
Date: Sat, 27 Apr 2024 14:23:11 +0200
Subject: allow for custom cropper aspect ration based on image

---
 src/lib/media/picker.web.tsx                     |  4 ++++
 src/state/modals/index.tsx                       |  1 +
 src/view/com/modals/crop-image/CropImage.web.tsx | 30 ++++++++++++++++++++----
 3 files changed, 30 insertions(+), 5 deletions(-)

(limited to 'src/lib/media/picker.web.tsx')

diff --git a/src/lib/media/picker.web.tsx b/src/lib/media/picker.web.tsx
index 70e28838b..fde6a64ac 100644
--- a/src/lib/media/picker.web.tsx
+++ b/src/lib/media/picker.web.tsx
@@ -17,6 +17,10 @@ export async function openCropper(opts: CropperOptions): Promise<RNImage> {
     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)
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..a359ed44d 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}
         />
-- 
cgit 1.4.1


From fe82257801f49b3d7aceca9dd611ef4005da48ce Mon Sep 17 00:00:00 2001
From: Paul Frazee <pfrazee@gmail.com>
Date: Mon, 6 May 2024 15:44:19 -0700
Subject: Dont leave promise unresolved

---
 src/lib/media/picker.web.tsx     |  4 +++-
 src/view/com/util/UserAvatar.tsx | 23 +++++++++++++++--------
 src/view/com/util/UserBanner.tsx | 23 +++++++++++++++--------
 3 files changed, 33 insertions(+), 17 deletions(-)

(limited to 'src/lib/media/picker.web.tsx')

diff --git a/src/lib/media/picker.web.tsx b/src/lib/media/picker.web.tsx
index fde6a64ac..8782e1457 100644
--- a/src/lib/media/picker.web.tsx
+++ b/src/lib/media/picker.web.tsx
@@ -13,7 +13,7 @@ export async function openCamera(_opts: CameraOpts): Promise<RNImage> {
 
 export async function openCropper(opts: CropperOptions): Promise<RNImage> {
   // TODO handle more opts
-  return new Promise(resolve => {
+  return new Promise((resolve, reject) => {
     unstable__openModal({
       name: 'crop-image',
       uri: opts.path,
@@ -24,6 +24,8 @@ export async function openCropper(opts: CropperOptions): Promise<RNImage> {
       onSelect: (img?: RNImage) => {
         if (img) {
           resolve(img)
+        } else {
+          reject(new Error('Canceled'))
         }
       },
     })
diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx
index 118e2ce2b..45327669b 100644
--- a/src/view/com/util/UserAvatar.tsx
+++ b/src/view/com/util/UserAvatar.tsx
@@ -8,6 +8,7 @@ import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useQueryClient} from '@tanstack/react-query'
 
+import {logger} from '#/logger'
 import {usePalette} from 'lib/hooks/usePalette'
 import {
   useCameraPermission,
@@ -282,15 +283,21 @@ let EditableUserAvatar = ({
       return
     }
 
-    const croppedImage = await openCropper({
-      mediaType: 'photo',
-      cropperCircleOverlay: true,
-      height: item.height,
-      width: item.width,
-      path: item.path,
-    })
+    try {
+      const croppedImage = await openCropper({
+        mediaType: 'photo',
+        cropperCircleOverlay: true,
+        height: item.height,
+        width: item.width,
+        path: item.path,
+      })
 
-    onSelectNewAvatar(croppedImage)
+      onSelectNewAvatar(croppedImage)
+    } catch (e: any) {
+      if (!String(e).includes('Canceled')) {
+        logger.error('Failed to crop banner', {error: e})
+      }
+    }
   }, [onSelectNewAvatar, requestPhotoAccessIfNeeded])
 
   const onRemoveAvatar = React.useCallback(() => {
diff --git a/src/view/com/util/UserBanner.tsx b/src/view/com/util/UserBanner.tsx
index f08044ec3..93ea32750 100644
--- a/src/view/com/util/UserBanner.tsx
+++ b/src/view/com/util/UserBanner.tsx
@@ -6,6 +6,7 @@ import {ModerationUI} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
+import {logger} from '#/logger'
 import {usePalette} from 'lib/hooks/usePalette'
 import {
   useCameraPermission,
@@ -64,14 +65,20 @@ export function UserBanner({
       return
     }
 
-    onSelectNewBanner?.(
-      await openCropper({
-        mediaType: 'photo',
-        path: items[0].path,
-        width: 3000,
-        height: 1000,
-      }),
-    )
+    try {
+      onSelectNewBanner?.(
+        await openCropper({
+          mediaType: 'photo',
+          path: items[0].path,
+          width: 3000,
+          height: 1000,
+        }),
+      )
+    } catch (e: any) {
+      if (!String(e).includes('Canceled')) {
+        logger.error('Failed to crop banner', {error: e})
+      }
+    }
   }, [onSelectNewBanner, requestPhotoAccessIfNeeded])
 
   const onRemoveBanner = React.useCallback(() => {
-- 
cgit 1.4.1

