Files
paseo/packages/app/src/hooks/use-image-attachment-picker.ts
T
Mohamed BoudraandGitHub 21597bdc1b Fix image uploads using the wrong format (#2380)
* fix(app): preserve image attachment MIME types

Image intake discarded clipboard and desktop path metadata, then guessed JPEG when a later file object had no type. Resolve MIME once at the platform boundary and require it downstream.

* fix(app): honor native picker MIME metadata
2026-07-24 21:11:41 +08:00

93 lines
2.8 KiB
TypeScript

import { useCallback, useRef } from "react";
import { Alert } from "react-native";
import * as ImagePicker from "expo-image-picker";
import { useTranslation } from "react-i18next";
import { getDesktopHost, isElectronRuntime } from "@/desktop/host";
import {
normalizePickedImageAssets,
pickImagesWithDesktopDialog,
type PickedImageAttachmentInput,
} from "@/hooks/image-attachment-picker";
import { isWeb } from "@/constants/platform";
interface UseImageAttachmentPickerResult {
pickImages: () => Promise<PickedImageAttachmentInput[] | null>;
}
export function useImageAttachmentPicker(): UseImageAttachmentPickerResult {
const { t } = useTranslation();
const [mediaPermission, requestMediaPermission] = ImagePicker.useMediaLibraryPermissions();
const isPickingRef = useRef(false);
const ensurePermission = useCallback(async () => {
let currentPermission = mediaPermission;
if (
!currentPermission ||
currentPermission.status === ImagePicker.PermissionStatus.UNDETERMINED
) {
currentPermission = await requestMediaPermission();
} else if (!currentPermission.granted) {
currentPermission = await requestMediaPermission();
}
if (!currentPermission?.granted) {
Alert.alert(
t("imageAttachmentPicker.permissionTitle"),
t("imageAttachmentPicker.permissionMessage"),
);
return false;
}
return true;
}, [mediaPermission, requestMediaPermission, t]);
const pickImages = useCallback(async () => {
if (isPickingRef.current) {
return null;
}
isPickingRef.current = true;
try {
if (isWeb && isElectronRuntime()) {
const selectedImages = await pickImagesWithDesktopDialog(getDesktopHost()?.dialog);
if (selectedImages.length === 0) {
return null;
}
return selectedImages;
}
const hasPermission = await ensurePermission();
if (!hasPermission) {
return null;
}
const pendingResult = await ImagePicker.getPendingResultAsync();
if (pendingResult && "canceled" in pendingResult && !pendingResult.canceled) {
return await normalizePickedImageAssets(pendingResult.assets);
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ["images"] as ImagePicker.MediaType[],
allowsMultipleSelection: true,
quality: 0.8,
});
if (result.canceled) {
return null;
}
return await normalizePickedImageAssets(result.assets);
} catch (error) {
console.error("[ImageAttachmentPicker] Failed to pick image:", error);
Alert.alert(t("imageAttachmentPicker.errorTitle"), t("imageAttachmentPicker.failedToSelect"));
return null;
} finally {
isPickingRef.current = false;
}
}, [ensurePermission, t]);
return { pickImages };
}