type fixes

This commit is contained in:
Hailey
2025-05-05 08:54:08 -07:00
parent d36837dcf0
commit 29863ca89d
12 changed files with 58 additions and 64 deletions
+8 -6
View File
@@ -1,12 +1,11 @@
import {Image as RNImage} from 'react-native-image-crop-picker' import {type PickerImage} from './picker.shared'
import {type Dimensions} from './types'
import {Dimensions} from './types'
import {blobToDataUri, getDataUriSize} from './util' import {blobToDataUri, getDataUriSize} from './util'
export async function compressIfNeeded( export async function compressIfNeeded(
img: RNImage, img: PickerImage,
maxSize: number, maxSize: number,
): Promise<RNImage> { ): Promise<PickerImage> {
if (img.size < maxSize) { if (img.size < maxSize) {
return img return img
} }
@@ -69,7 +68,10 @@ interface DoResizeOpts {
maxSize: number maxSize: number
} }
async function doResize(dataUri: string, opts: DoResizeOpts): Promise<RNImage> { async function doResize(
dataUri: string,
opts: DoResizeOpts,
): Promise<PickerImage> {
let newDataUri let newDataUri
let minQualityPercentage = 0 let minQualityPercentage = 0
+3 -4
View File
@@ -3,11 +3,10 @@ import {
getInfoAsync, getInfoAsync,
readDirectoryAsync, readDirectoryAsync,
} from 'expo-file-system' } 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 {compressIfNeeded} from './manip'
import {type PickerImage} from './picker.shared' import {type PickerImage} from './picker.shared'
import {type CropperOptions} from './types'
async function getFile() { async function getFile() {
const imagesDir = documentDirectory! const imagesDir = documentDirectory!
@@ -43,10 +42,10 @@ export async function openCamera(): Promise<PickerImage> {
return await getFile() return await getFile()
} }
export async function openCropper(opts: CropperOptions) { export async function openCropper(opts: OpenCropperOptions) {
const item = await ExpoImageCropTool.openCropperAsync({ const item = await ExpoImageCropTool.openCropperAsync({
...opts, ...opts,
forceJpg: true, // ios only format: 'jpeg',
}) })
return { return {
+9 -9
View File
@@ -1,8 +1,10 @@
/// <reference lib="dom" /> /// <reference lib="dom" />
import {type OpenCropperOptions} from 'expo-image-crop-tool'
import {unstable__openModal} from '#/state/modals' import {unstable__openModal} from '#/state/modals'
import {type PickerImage} from './picker.shared' 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' export {openPicker, type PickerImage as RNImage} from './picker.shared'
@@ -11,18 +13,16 @@ export async function openCamera(_opts: CameraOpts): Promise<PickerImage> {
throw new Error('TODO') throw new Error('TODO')
} }
export async function openCropper(opts: CropperOptions): Promise<PickerImage> { export async function openCropper(
opts: OpenCropperOptions,
): Promise<PickerImage> {
// TODO handle more opts // TODO handle more opts
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
unstable__openModal({ unstable__openModal({
name: 'crop-image', name: 'crop-image',
uri: opts.path, uri: opts.imageUri,
dimensions: aspect: opts.aspectRatio,
opts.width && opts.height circular: opts.shape === 'circle',
? {width: opts.width, height: opts.height}
: undefined,
aspect: opts.webAspectRatio,
circular: opts.webCircularCrop,
onSelect: (img?: PickerImage) => { onSelect: (img?: PickerImage) => {
if (img) { if (img) {
resolve(img) resolve(img)
-7
View File
@@ -1,5 +1,3 @@
import {openCropper} from 'react-native-image-crop-picker'
export interface Dimensions { export interface Dimensions {
width: number width: number
height: number height: number
@@ -17,8 +15,3 @@ export interface CameraOpts {
freeStyleCropEnabled?: boolean freeStyleCropEnabled?: boolean
cropperCircleOverlay?: boolean cropperCircleOverlay?: boolean
} }
export type CropperOptions = Parameters<typeof openCropper>[0] & {
webAspectRatio?: number
webCircularCrop?: boolean
}
@@ -1,12 +1,12 @@
import {useCallback, useEffect, useState} from 'react' import {useCallback, useEffect, useState} from 'react'
import {Dimensions, View} from 'react-native' import {Dimensions, View} from 'react-native'
import {type Image as RNImage} from 'react-native-image-crop-picker'
import {type AppBskyActorDefs} from '@atproto/api' import {type AppBskyActorDefs} from '@atproto/api'
import {msg, Plural, Trans} from '@lingui/macro' import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {urls} from '#/lib/constants' import {urls} from '#/lib/constants'
import {compressIfNeeded} from '#/lib/media/manip' import {compressIfNeeded} from '#/lib/media/manip'
import {type PickerImage} from '#/lib/media/picker.shared'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {useWarnMaxGraphemeCount} from '#/lib/strings/helpers' import {useWarnMaxGraphemeCount} from '#/lib/strings/helpers'
import {logger} from '#/logger' import {logger} from '#/logger'
@@ -127,10 +127,10 @@ function DialogInner({
profile.avatar, profile.avatar,
) )
const [newUserBanner, setNewUserBanner] = useState< const [newUserBanner, setNewUserBanner] = useState<
RNImage | undefined | null PickerImage | undefined | null
>() >()
const [newUserAvatar, setNewUserAvatar] = useState< const [newUserAvatar, setNewUserAvatar] = useState<
RNImage | undefined | null PickerImage | undefined | null
>() >()
const dirty = const dirty =
@@ -144,7 +144,7 @@ function DialogInner({
}, [dirty, setDirty]) }, [dirty, setDirty])
const onSelectNewAvatar = useCallback( const onSelectNewAvatar = useCallback(
async (img: RNImage | null) => { async (img: PickerImage | null) => {
setImageError('') setImageError('')
if (img === null) { if (img === null) {
setNewUserAvatar(null) setNewUserAvatar(null)
@@ -163,7 +163,7 @@ function DialogInner({
) )
const onSelectNewBanner = useCallback( const onSelectNewBanner = useCallback(
async (img: RNImage | null) => { async (img: PickerImage | null) => {
setImageError('') setImageError('')
if (!img) { if (!img) {
setNewUserBanner(null) setNewUserBanner(null)
+2 -2
View File
@@ -1,8 +1,8 @@
import React from 'react' import React from 'react'
import {type Image as RNImage} from 'react-native-image-crop-picker'
import {type AppBskyActorDefs, type AppBskyGraphDefs} from '@atproto/api' import {type AppBskyActorDefs, type AppBskyGraphDefs} from '@atproto/api'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {type PickerImage} from '#/lib/media/picker.shared'
export interface EditProfileModal { export interface EditProfileModal {
name: 'edit-profile' name: 'edit-profile'
@@ -32,7 +32,7 @@ export interface CropImageModal {
dimensions?: {width: number; height: number} dimensions?: {width: number; height: number}
aspect?: number aspect?: number
circular?: boolean circular?: boolean
onSelect: (img?: RNImage) => void onSelect: (img?: PickerImage) => void
} }
export interface DeleteAccountModal { export interface DeleteAccountModal {
+11 -11
View File
@@ -1,20 +1,20 @@
import {Image as RNImage} from 'react-native-image-crop-picker'
import { import {
$Typed, type $Typed,
AppBskyGraphDefs, type AppBskyGraphDefs,
AppBskyGraphGetList, type AppBskyGraphGetList,
AppBskyGraphList, type AppBskyGraphList,
AtUri, AtUri,
BskyAgent, type BskyAgent,
ComAtprotoRepoApplyWrites, type ComAtprotoRepoApplyWrites,
Facet, type Facet,
Un$Typed, type Un$Typed,
} from '@atproto/api' } from '@atproto/api'
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import chunk from 'lodash.chunk' import chunk from 'lodash.chunk'
import {uploadBlob} from '#/lib/api' import {uploadBlob} from '#/lib/api'
import {until} from '#/lib/async/until' import {until} from '#/lib/async/until'
import {type PickerImage} from '#/lib/media/picker.shared'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
import {useAgent, useSession} from '../session' import {useAgent, useSession} from '../session'
import {invalidate as invalidateMyLists} from './my-lists' import {invalidate as invalidateMyLists} from './my-lists'
@@ -47,7 +47,7 @@ export interface ListCreateMutateParams {
name: string name: string
description: string description: string
descriptionFacets: Facet[] | undefined descriptionFacets: Facet[] | undefined
avatar: RNImage | null | undefined avatar: PickerImage | null | undefined
} }
export function useListCreateMutation() { export function useListCreateMutation() {
const {currentAccount} = useSession() const {currentAccount} = useSession()
@@ -115,7 +115,7 @@ export interface ListMetadataMutateParams {
name: string name: string
description: string description: string
descriptionFacets: Facet[] | undefined descriptionFacets: Facet[] | undefined
avatar: RNImage | null | undefined avatar: PickerImage | null | undefined
} }
export function useListMetadataMutation() { export function useListMetadataMutation() {
const {currentAccount} = useSession() const {currentAccount} = useSession()
+3 -3
View File
@@ -1,5 +1,4 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {type Image as RNImage} from 'react-native-image-crop-picker'
import { import {
type AppBskyActorDefs, type AppBskyActorDefs,
type AppBskyActorGetProfile, type AppBskyActorGetProfile,
@@ -21,6 +20,7 @@ import {
import {uploadBlob} from '#/lib/api' import {uploadBlob} from '#/lib/api'
import {until} from '#/lib/async/until' import {until} from '#/lib/async/until'
import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue' import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue'
import {type PickerImage} from '#/lib/media/picker.shared'
import {logEvent, type LogEvents, toClout} from '#/lib/statsig/statsig' import {logEvent, type LogEvents, toClout} from '#/lib/statsig/statsig'
import {type Shadow} from '#/state/cache/types' import {type Shadow} from '#/state/cache/types'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
@@ -131,8 +131,8 @@ interface ProfileUpdateParams {
| (( | ((
existing: Un$Typed<AppBskyActorProfile.Record>, existing: Un$Typed<AppBskyActorProfile.Record>,
) => Un$Typed<AppBskyActorProfile.Record>) ) => Un$Typed<AppBskyActorProfile.Record>)
newUserAvatar?: RNImage | undefined | null newUserAvatar?: PickerImage | undefined | null
newUserBanner?: RNImage | undefined | null newUserBanner?: PickerImage | undefined | null
checkCommitted?: (res: AppBskyActorGetProfile.Response) => boolean checkCommitted?: (res: AppBskyActorGetProfile.Response) => boolean
} }
export function useProfileUpdateMutation() { export function useProfileUpdateMutation() {
+3 -3
View File
@@ -8,7 +8,6 @@ import {
TouchableOpacity, TouchableOpacity,
View, View,
} from 'react-native' } from 'react-native'
import {type Image as RNImage} from 'react-native-image-crop-picker'
import {LinearGradient} from 'expo-linear-gradient' import {LinearGradient} from 'expo-linear-gradient'
import {type AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api' import {type AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
@@ -17,6 +16,7 @@ import {useLingui} from '@lingui/react'
import {usePalette} from '#/lib/hooks/usePalette' import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {compressIfNeeded} from '#/lib/media/manip' import {compressIfNeeded} from '#/lib/media/manip'
import {type PickerImage} from '#/lib/media/picker.shared'
import {cleanError, isNetworkError} from '#/lib/strings/errors' import {cleanError, isNetworkError} from '#/lib/strings/errors'
import {enforceLen} from '#/lib/strings/helpers' import {enforceLen} from '#/lib/strings/helpers'
import {richTextToString} from '#/lib/strings/rich-text-helpers' import {richTextToString} from '#/lib/strings/rich-text-helpers'
@@ -95,7 +95,7 @@ export function Component({
const isDescriptionOver = graphemeLength > MAX_DESCRIPTION const isDescriptionOver = graphemeLength > MAX_DESCRIPTION
const [avatar, setAvatar] = useState<string | undefined>(list?.avatar) const [avatar, setAvatar] = useState<string | undefined>(list?.avatar)
const [newAvatar, setNewAvatar] = useState<RNImage | undefined | null>() const [newAvatar, setNewAvatar] = useState<PickerImage | undefined | null>()
const onDescriptionChange = useCallback( const onDescriptionChange = useCallback(
(newText: string) => { (newText: string) => {
@@ -112,7 +112,7 @@ export function Component({
}, [closeModal]) }, [closeModal])
const onSelectNewAvatar = useCallback( const onSelectNewAvatar = useCallback(
async (img: RNImage | null) => { async (img: PickerImage | null) => {
if (!img) { if (!img) {
setNewAvatar(null) setNewAvatar(null)
setAvatar(undefined) setAvatar(undefined)
+3 -3
View File
@@ -1,14 +1,14 @@
import React from 'react' import React from 'react'
import {StyleSheet, TouchableOpacity, View} from 'react-native' 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 {manipulateAsync, SaveFormat} from 'expo-image-manipulator'
import {LinearGradient} from 'expo-linear-gradient' import {LinearGradient} from 'expo-linear-gradient'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' 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 {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {type PickerImage} from '#/lib/media/picker.shared'
import {getDataUriSize} from '#/lib/media/util' import {getDataUriSize} from '#/lib/media/util'
import {gradients, s} from '#/lib/styles' import {gradients, s} from '#/lib/styles'
import {useModalControls} from '#/state/modals' import {useModalControls} from '#/state/modals'
@@ -25,7 +25,7 @@ export function Component({
uri: string uri: string
aspect?: number aspect?: number
circular?: boolean circular?: boolean
onSelect: (img?: RNImage) => void onSelect: (img?: PickerImage) => void
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')
const {_} = useLingui() const {_} = useLingui()
+5 -5
View File
@@ -8,7 +8,6 @@ import {
TouchableOpacity, TouchableOpacity,
View, View,
} from 'react-native' } from 'react-native'
import {type Image as RNImage} from 'react-native-image-crop-picker'
import Animated, {FadeOut} from 'react-native-reanimated' import Animated, {FadeOut} from 'react-native-reanimated'
import {LinearGradient} from 'expo-linear-gradient' import {LinearGradient} from 'expo-linear-gradient'
import {type AppBskyActorDefs} from '@atproto/api' 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 {MAX_DESCRIPTION, MAX_DISPLAY_NAME, urls} from '#/lib/constants'
import {usePalette} from '#/lib/hooks/usePalette' import {usePalette} from '#/lib/hooks/usePalette'
import {compressIfNeeded} from '#/lib/media/manip' import {compressIfNeeded} from '#/lib/media/manip'
import {type PickerImage} from '#/lib/media/picker.shared'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {enforceLen} from '#/lib/strings/helpers' import {enforceLen} from '#/lib/strings/helpers'
import {colors, gradients, s} from '#/lib/styles' import {colors, gradients, s} from '#/lib/styles'
@@ -67,16 +67,16 @@ export function Component({
profile.avatar, profile.avatar,
) )
const [newUserBanner, setNewUserBanner] = useState< const [newUserBanner, setNewUserBanner] = useState<
RNImage | undefined | null PickerImage | undefined | null
>() >()
const [newUserAvatar, setNewUserAvatar] = useState< const [newUserAvatar, setNewUserAvatar] = useState<
RNImage | undefined | null PickerImage | undefined | null
>() >()
const onPressCancel = () => { const onPressCancel = () => {
closeModal() closeModal()
} }
const onSelectNewAvatar = useCallback( const onSelectNewAvatar = useCallback(
async (img: RNImage | null) => { async (img: PickerImage | null) => {
setImageError('') setImageError('')
if (img === null) { if (img === null) {
setNewUserAvatar(null) setNewUserAvatar(null)
@@ -95,7 +95,7 @@ export function Component({
) )
const onSelectNewBanner = useCallback( const onSelectNewBanner = useCallback(
async (img: RNImage | null) => { async (img: PickerImage | null) => {
setImageError('') setImageError('')
if (!img) { if (!img) {
setNewUserBanner(null) setNewUserBanner(null)
+6 -6
View File
@@ -2,13 +2,13 @@ import React, {memo, useMemo} from 'react'
import { import {
Image, Image,
Pressable, Pressable,
StyleProp, type StyleProp,
StyleSheet, StyleSheet,
View, View,
ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import Svg, {Circle, Path, Rect} from 'react-native-svg' 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 {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@@ -37,12 +37,12 @@ import {Link} from '#/components/Link'
import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import * as Menu from '#/components/Menu' import * as Menu from '#/components/Menu'
import {ProfileHoverCard} from '#/components/ProfileHoverCard' import {ProfileHoverCard} from '#/components/ProfileHoverCard'
import * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
import { import {
openCamera, openCamera,
openCropper, openCropper,
openPicker, openPicker,
RNImage, type RNImage,
} from '../../../lib/media/picker' } from '../../../lib/media/picker'
export type UserAvatarType = 'user' | 'algo' | 'list' | 'labeler' export type UserAvatarType = 'user' | 'algo' | 'list' | 'labeler'
@@ -346,7 +346,7 @@ let EditableUserAvatar = ({
}) })
onSelectNewAvatar(croppedImage) onSelectNewAvatar(croppedImage)
}, },
isIOS ? 500 : 0, isIOS ? 500 : 500,
) )
} catch (e: any) { } catch (e: any) {
// Don't log errors for cancelling selection to sentry on ios or android // Don't log errors for cancelling selection to sentry on ios or android