From 29863ca89d6e72cfe782f8d6c9c92c8724fcf51d Mon Sep 17 00:00:00 2001 From: Hailey Date: Mon, 5 May 2025 08:54:08 -0700 Subject: [PATCH] type fixes --- src/lib/media/manip.web.ts | 14 +++++++----- src/lib/media/picker.e2e.tsx | 7 +++--- src/lib/media/picker.web.tsx | 18 +++++++-------- src/lib/media/types.ts | 7 ------ .../Profile/Header/EditProfileDialog.tsx | 10 ++++----- src/state/modals/index.tsx | 4 ++-- src/state/queries/list.ts | 22 +++++++++---------- src/state/queries/profile.ts | 6 ++--- src/view/com/modals/CreateOrEditList.tsx | 6 ++--- src/view/com/modals/CropImage.web.tsx | 6 ++--- src/view/com/modals/EditProfile.tsx | 10 ++++----- src/view/com/util/UserAvatar.tsx | 12 +++++----- 12 files changed, 58 insertions(+), 64 deletions(-) diff --git a/src/lib/media/manip.web.ts b/src/lib/media/manip.web.ts index ffef7314d7..ffcf0c5335 100644 --- a/src/lib/media/manip.web.ts +++ b/src/lib/media/manip.web.ts @@ -1,12 +1,11 @@ -import {Image as RNImage} from 'react-native-image-crop-picker' - -import {Dimensions} from './types' +import {type PickerImage} from './picker.shared' +import {type Dimensions} from './types' import {blobToDataUri, getDataUriSize} from './util' export async function compressIfNeeded( - img: RNImage, + img: PickerImage, maxSize: number, -): Promise { +): Promise { if (img.size < maxSize) { return img } @@ -69,7 +68,10 @@ interface DoResizeOpts { maxSize: number } -async function doResize(dataUri: string, opts: DoResizeOpts): Promise { +async function doResize( + dataUri: string, + opts: DoResizeOpts, +): Promise { let newDataUri let minQualityPercentage = 0 diff --git a/src/lib/media/picker.e2e.tsx b/src/lib/media/picker.e2e.tsx index 6c1e0a3bb5..a2a9357ec2 100644 --- a/src/lib/media/picker.e2e.tsx +++ b/src/lib/media/picker.e2e.tsx @@ -3,11 +3,10 @@ import { getInfoAsync, readDirectoryAsync, } from 'expo-file-system' -import ExpoImageCropTool from 'expo-image-crop-tool' +import ExpoImageCropTool, {type OpenCropperOptions} from 'expo-image-crop-tool' import {compressIfNeeded} from './manip' import {type PickerImage} from './picker.shared' -import {type CropperOptions} from './types' async function getFile() { const imagesDir = documentDirectory! @@ -43,10 +42,10 @@ export async function openCamera(): Promise { return await getFile() } -export async function openCropper(opts: CropperOptions) { +export async function openCropper(opts: OpenCropperOptions) { const item = await ExpoImageCropTool.openCropperAsync({ ...opts, - forceJpg: true, // ios only + format: 'jpeg', }) return { diff --git a/src/lib/media/picker.web.tsx b/src/lib/media/picker.web.tsx index 2b4a38e10e..b7d0d6f06d 100644 --- a/src/lib/media/picker.web.tsx +++ b/src/lib/media/picker.web.tsx @@ -1,8 +1,10 @@ /// +import {type OpenCropperOptions} from 'expo-image-crop-tool' + import {unstable__openModal} from '#/state/modals' import {type PickerImage} from './picker.shared' -import {type CameraOpts, type CropperOptions} from './types' +import {type CameraOpts} from './types' export {openPicker, type PickerImage as RNImage} from './picker.shared' @@ -11,18 +13,16 @@ export async function openCamera(_opts: CameraOpts): Promise { throw new Error('TODO') } -export async function openCropper(opts: CropperOptions): Promise { +export async function openCropper( + opts: OpenCropperOptions, +): Promise { // TODO handle more opts return new Promise((resolve, reject) => { unstable__openModal({ name: 'crop-image', - uri: opts.path, - dimensions: - opts.width && opts.height - ? {width: opts.width, height: opts.height} - : undefined, - aspect: opts.webAspectRatio, - circular: opts.webCircularCrop, + uri: opts.imageUri, + aspect: opts.aspectRatio, + circular: opts.shape === 'circle', onSelect: (img?: PickerImage) => { if (img) { resolve(img) diff --git a/src/lib/media/types.ts b/src/lib/media/types.ts index ec94256ea1..c083093ac7 100644 --- a/src/lib/media/types.ts +++ b/src/lib/media/types.ts @@ -1,5 +1,3 @@ -import {openCropper} from 'react-native-image-crop-picker' - export interface Dimensions { width: number height: number @@ -17,8 +15,3 @@ export interface CameraOpts { freeStyleCropEnabled?: boolean cropperCircleOverlay?: boolean } - -export type CropperOptions = Parameters[0] & { - webAspectRatio?: number - webCircularCrop?: boolean -} diff --git a/src/screens/Profile/Header/EditProfileDialog.tsx b/src/screens/Profile/Header/EditProfileDialog.tsx index a0e24d78a8..8a9f0d5404 100644 --- a/src/screens/Profile/Header/EditProfileDialog.tsx +++ b/src/screens/Profile/Header/EditProfileDialog.tsx @@ -1,12 +1,12 @@ import {useCallback, useEffect, useState} from 'react' import {Dimensions, View} from 'react-native' -import {type Image as RNImage} from 'react-native-image-crop-picker' import {type AppBskyActorDefs} from '@atproto/api' import {msg, Plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {urls} from '#/lib/constants' import {compressIfNeeded} from '#/lib/media/manip' +import {type PickerImage} from '#/lib/media/picker.shared' import {cleanError} from '#/lib/strings/errors' import {useWarnMaxGraphemeCount} from '#/lib/strings/helpers' import {logger} from '#/logger' @@ -127,10 +127,10 @@ function DialogInner({ profile.avatar, ) const [newUserBanner, setNewUserBanner] = useState< - RNImage | undefined | null + PickerImage | undefined | null >() const [newUserAvatar, setNewUserAvatar] = useState< - RNImage | undefined | null + PickerImage | undefined | null >() const dirty = @@ -144,7 +144,7 @@ function DialogInner({ }, [dirty, setDirty]) const onSelectNewAvatar = useCallback( - async (img: RNImage | null) => { + async (img: PickerImage | null) => { setImageError('') if (img === null) { setNewUserAvatar(null) @@ -163,7 +163,7 @@ function DialogInner({ ) const onSelectNewBanner = useCallback( - async (img: RNImage | null) => { + async (img: PickerImage | null) => { setImageError('') if (!img) { setNewUserBanner(null) diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx index f79f6213ff..3e738898a3 100644 --- a/src/state/modals/index.tsx +++ b/src/state/modals/index.tsx @@ -1,8 +1,8 @@ import React from 'react' -import {type Image as RNImage} from 'react-native-image-crop-picker' import {type AppBskyActorDefs, type AppBskyGraphDefs} from '@atproto/api' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' +import {type PickerImage} from '#/lib/media/picker.shared' export interface EditProfileModal { name: 'edit-profile' @@ -32,7 +32,7 @@ export interface CropImageModal { dimensions?: {width: number; height: number} aspect?: number circular?: boolean - onSelect: (img?: RNImage) => void + onSelect: (img?: PickerImage) => void } export interface DeleteAccountModal { diff --git a/src/state/queries/list.ts b/src/state/queries/list.ts index 260a0bf2ce..38e8cd62d8 100644 --- a/src/state/queries/list.ts +++ b/src/state/queries/list.ts @@ -1,20 +1,20 @@ -import {Image as RNImage} from 'react-native-image-crop-picker' import { - $Typed, - AppBskyGraphDefs, - AppBskyGraphGetList, - AppBskyGraphList, + type $Typed, + type AppBskyGraphDefs, + type AppBskyGraphGetList, + type AppBskyGraphList, AtUri, - BskyAgent, - ComAtprotoRepoApplyWrites, - Facet, - Un$Typed, + type BskyAgent, + type ComAtprotoRepoApplyWrites, + type Facet, + type Un$Typed, } from '@atproto/api' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import chunk from 'lodash.chunk' import {uploadBlob} from '#/lib/api' import {until} from '#/lib/async/until' +import {type PickerImage} from '#/lib/media/picker.shared' import {STALE} from '#/state/queries' import {useAgent, useSession} from '../session' import {invalidate as invalidateMyLists} from './my-lists' @@ -47,7 +47,7 @@ export interface ListCreateMutateParams { name: string description: string descriptionFacets: Facet[] | undefined - avatar: RNImage | null | undefined + avatar: PickerImage | null | undefined } export function useListCreateMutation() { const {currentAccount} = useSession() @@ -115,7 +115,7 @@ export interface ListMetadataMutateParams { name: string description: string descriptionFacets: Facet[] | undefined - avatar: RNImage | null | undefined + avatar: PickerImage | null | undefined } export function useListMetadataMutation() { const {currentAccount} = useSession() diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts index 609a62e25a..9f40ab7f61 100644 --- a/src/state/queries/profile.ts +++ b/src/state/queries/profile.ts @@ -1,5 +1,4 @@ import {useCallback} from 'react' -import {type Image as RNImage} from 'react-native-image-crop-picker' import { type AppBskyActorDefs, type AppBskyActorGetProfile, @@ -21,6 +20,7 @@ import { import {uploadBlob} from '#/lib/api' import {until} from '#/lib/async/until' import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue' +import {type PickerImage} from '#/lib/media/picker.shared' import {logEvent, type LogEvents, toClout} from '#/lib/statsig/statsig' import {type Shadow} from '#/state/cache/types' import {STALE} from '#/state/queries' @@ -131,8 +131,8 @@ interface ProfileUpdateParams { | (( existing: Un$Typed, ) => Un$Typed) - newUserAvatar?: RNImage | undefined | null - newUserBanner?: RNImage | undefined | null + newUserAvatar?: PickerImage | undefined | null + newUserBanner?: PickerImage | undefined | null checkCommitted?: (res: AppBskyActorGetProfile.Response) => boolean } export function useProfileUpdateMutation() { diff --git a/src/view/com/modals/CreateOrEditList.tsx b/src/view/com/modals/CreateOrEditList.tsx index 0e4e23b97f..a7eae15ddb 100644 --- a/src/view/com/modals/CreateOrEditList.tsx +++ b/src/view/com/modals/CreateOrEditList.tsx @@ -8,7 +8,6 @@ import { TouchableOpacity, View, } from 'react-native' -import {type Image as RNImage} from 'react-native-image-crop-picker' import {LinearGradient} from 'expo-linear-gradient' import {type AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api' import {msg, Trans} from '@lingui/macro' @@ -17,6 +16,7 @@ import {useLingui} from '@lingui/react' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {compressIfNeeded} from '#/lib/media/manip' +import {type PickerImage} from '#/lib/media/picker.shared' import {cleanError, isNetworkError} from '#/lib/strings/errors' import {enforceLen} from '#/lib/strings/helpers' import {richTextToString} from '#/lib/strings/rich-text-helpers' @@ -95,7 +95,7 @@ export function Component({ const isDescriptionOver = graphemeLength > MAX_DESCRIPTION const [avatar, setAvatar] = useState(list?.avatar) - const [newAvatar, setNewAvatar] = useState() + const [newAvatar, setNewAvatar] = useState() const onDescriptionChange = useCallback( (newText: string) => { @@ -112,7 +112,7 @@ export function Component({ }, [closeModal]) const onSelectNewAvatar = useCallback( - async (img: RNImage | null) => { + async (img: PickerImage | null) => { if (!img) { setNewAvatar(null) setAvatar(undefined) diff --git a/src/view/com/modals/CropImage.web.tsx b/src/view/com/modals/CropImage.web.tsx index 41ca306573..78c0466f0b 100644 --- a/src/view/com/modals/CropImage.web.tsx +++ b/src/view/com/modals/CropImage.web.tsx @@ -1,14 +1,14 @@ import React from 'react' import {StyleSheet, TouchableOpacity, View} from 'react-native' -import {Image as RNImage} from 'react-native-image-crop-picker' import {manipulateAsync, SaveFormat} from 'expo-image-manipulator' import {LinearGradient} from 'expo-linear-gradient' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import ReactCrop, {PercentCrop} from 'react-image-crop' +import ReactCrop, {type PercentCrop} from 'react-image-crop' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' +import {type PickerImage} from '#/lib/media/picker.shared' import {getDataUriSize} from '#/lib/media/util' import {gradients, s} from '#/lib/styles' import {useModalControls} from '#/state/modals' @@ -25,7 +25,7 @@ export function Component({ uri: string aspect?: number circular?: boolean - onSelect: (img?: RNImage) => void + onSelect: (img?: PickerImage) => void }) { const pal = usePalette('default') const {_} = useLingui() diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx index ebc1281a33..cb1552fe57 100644 --- a/src/view/com/modals/EditProfile.tsx +++ b/src/view/com/modals/EditProfile.tsx @@ -8,7 +8,6 @@ import { TouchableOpacity, View, } from 'react-native' -import {type Image as RNImage} from 'react-native-image-crop-picker' import Animated, {FadeOut} from 'react-native-reanimated' import {LinearGradient} from 'expo-linear-gradient' import {type AppBskyActorDefs} from '@atproto/api' @@ -18,6 +17,7 @@ import {useLingui} from '@lingui/react' import {MAX_DESCRIPTION, MAX_DISPLAY_NAME, urls} from '#/lib/constants' import {usePalette} from '#/lib/hooks/usePalette' import {compressIfNeeded} from '#/lib/media/manip' +import {type PickerImage} from '#/lib/media/picker.shared' import {cleanError} from '#/lib/strings/errors' import {enforceLen} from '#/lib/strings/helpers' import {colors, gradients, s} from '#/lib/styles' @@ -67,16 +67,16 @@ export function Component({ profile.avatar, ) const [newUserBanner, setNewUserBanner] = useState< - RNImage | undefined | null + PickerImage | undefined | null >() const [newUserAvatar, setNewUserAvatar] = useState< - RNImage | undefined | null + PickerImage | undefined | null >() const onPressCancel = () => { closeModal() } const onSelectNewAvatar = useCallback( - async (img: RNImage | null) => { + async (img: PickerImage | null) => { setImageError('') if (img === null) { setNewUserAvatar(null) @@ -95,7 +95,7 @@ export function Component({ ) const onSelectNewBanner = useCallback( - async (img: RNImage | null) => { + async (img: PickerImage | null) => { setImageError('') if (!img) { setNewUserBanner(null) diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx index 06fe0f50d0..fe40b0aa6b 100644 --- a/src/view/com/util/UserAvatar.tsx +++ b/src/view/com/util/UserAvatar.tsx @@ -2,13 +2,13 @@ import React, {memo, useMemo} from 'react' import { Image, Pressable, - StyleProp, + type StyleProp, StyleSheet, View, - ViewStyle, + type ViewStyle, } from 'react-native' import Svg, {Circle, Path, Rect} from 'react-native-svg' -import {ModerationUI} from '@atproto/api' +import {type ModerationUI} from '@atproto/api' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -37,12 +37,12 @@ import {Link} from '#/components/Link' import {MediaInsetBorder} from '#/components/MediaInsetBorder' import * as Menu from '#/components/Menu' import {ProfileHoverCard} from '#/components/ProfileHoverCard' -import * as bsky from '#/types/bsky' +import type * as bsky from '#/types/bsky' import { openCamera, openCropper, openPicker, - RNImage, + type RNImage, } from '../../../lib/media/picker' export type UserAvatarType = 'user' | 'algo' | 'list' | 'labeler' @@ -346,7 +346,7 @@ let EditableUserAvatar = ({ }) onSelectNewAvatar(croppedImage) }, - isIOS ? 500 : 0, + isIOS ? 500 : 500, ) } catch (e: any) { // Don't log errors for cancelling selection to sentry on ios or android