/* * 프로필 사진 선택 — expo-image-picker 래퍼(갤러리/카메라, 1:1 크롭). WISE 모바일 패턴 이식. * 레퍼런스: guardia-messenger/app/uiws/_lib/constants/imagePick.ts. * 방어적 동적 require: 미설치 시 graceful(null 반환). 권한 요청·거부 처리 포함. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any let ImagePicker: any = null; try { // eslint-disable-next-line @typescript-eslint/no-var-requires ImagePicker = require('expo-image-picker'); } catch { ImagePicker = null; } export interface PickedImage { uri: string; fileName: string; mimeType: string; /** base64 원본(백엔드 멀티파트 미지원 시 대체 계약용). quality 절충. */ base64?: string | null; } // eslint-disable-next-line @typescript-eslint/no-explicit-any function toPicked(result: any): PickedImage | null { if (!result || result.canceled) return null; const asset = result.assets?.[0]; if (!asset?.uri) return null; const uri: string = asset.uri; const ext = (uri.split('.').pop() || 'jpg').split('?')[0].toLowerCase(); const mime = asset.mimeType || (ext === 'png' ? 'image/png' : 'image/jpeg'); return { uri, fileName: asset.fileName || `profile.${ext}`, mimeType: mime, base64: asset.base64 ?? null, }; } export function isPickerAvailable(): boolean { return !!ImagePicker; } /** 갤러리에서 1:1 크롭 선택. 권한 거부/취소/미설치 시 null. */ export async function pickFromLibrary(): Promise { if (!ImagePicker) return null; try { const perm = await ImagePicker.requestMediaLibraryPermissionsAsync(); if (!perm?.granted) return null; const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions?.Images, allowsEditing: true, aspect: [1, 1], quality: 0.7, base64: true, }); return toPicked(result); } catch { return null; } } /** 카메라 촬영(1:1 크롭). 권한 거부/취소/미설치 시 null. */ export async function pickFromCamera(): Promise { if (!ImagePicker) return null; try { const perm = await ImagePicker.requestCameraPermissionsAsync(); if (!perm?.granted) return null; const result = await ImagePicker.launchCameraAsync({ allowsEditing: true, aspect: [1, 1], quality: 0.7, base64: true, }); return toPicked(result); } catch { return null; } } /** 권한 상태 조회 — 거부 시 안내 문구 분기용(요청 없이 현 상태만). */ export async function permissionState(): Promise<{ media: boolean; camera: boolean }> { if (!ImagePicker) return { media: false, camera: false }; try { const m = await ImagePicker.getMediaLibraryPermissionsAsync(); const c = await ImagePicker.getCameraPermissionsAsync(); return { media: !!m?.granted, camera: !!c?.granted }; } catch { return { media: false, camera: false }; } } /** PickedImage → multipart FormData(field "file"). RN 멀티파트: { uri, name, type }. */ export function toPhotoFormData(img: PickedImage): FormData { const fd = new FormData(); // eslint-disable-next-line @typescript-eslint/no-explicit-any fd.append('file', { uri: img.uri, name: img.fileName, type: img.mimeType } as any); return fd; }