feat: Support HEIC format for images

This commit is contained in:
Paribesh01
2025-02-07 00:43:23 +05:30
parent bb6df783ab
commit 423466d30a
10 changed files with 68 additions and 18 deletions

View File

@@ -114,7 +114,7 @@ export const EditWelcomeCard = ({
<div className="mt-3 flex w-full items-center justify-center">
<FileInput
id="welcome-card-image"
allowedFileExtensions={["png", "jpeg", "jpg", "webp"]}
allowedFileExtensions={["png", "jpeg", "jpg", "webp", "heic"]}
environmentId={environmentId}
onFileUpload={(url: string[]) => {
updateSurvey({ fileUrl: url[0] });

View File

@@ -16,7 +16,7 @@ export const UploadImageSurveyBg = ({
<div className="flex w-full items-center justify-center">
<FileInput
id="survey-bg-file-input"
allowedFileExtensions={["png", "jpeg", "jpg", "webp"]}
allowedFileExtensions={["png", "jpeg", "jpg", "webp", "heic"]}
environmentId={environmentId}
onFileUpload={(url: string[]) => {
if (url.length > 0) {

View File

@@ -131,7 +131,7 @@ export const PictureSelectionForm = ({
<div className="mt-3 flex w-full items-center justify-center">
<FileInput
id="choices-file-input"
allowedFileExtensions={["png", "jpeg", "jpg", "webp"]}
allowedFileExtensions={["png", "jpeg", "jpg", "webp", "heic"]}
environmentId={environmentId}
onFileUpload={handleFileInputChanges}
fileUrl={question?.choices?.map((choice) => choice.imageUrl)}

View File

@@ -138,7 +138,7 @@ export const EditLogo = ({ project, environmentId, isReadOnly }: EditLogoProps)
) : (
<FileInput
id="logo-input"
allowedFileExtensions={["png", "jpeg", "jpg", "webp"]}
allowedFileExtensions={["png", "jpeg", "jpg", "webp", "heic"]}
environmentId={environmentId}
onFileUpload={(files: string[]) => {
setLogoUrl(files[0]);

View File

@@ -307,7 +307,7 @@ export const QuestionFormInput = ({
{showImageUploader && id === "headline" && (
<FileInput
id="question-image"
allowedFileExtensions={["png", "jpeg", "jpg", "webp"]}
allowedFileExtensions={["png", "jpeg", "jpg", "webp", "heic"]}
environmentId={localSurvey.environmentId}
onFileUpload={(url: string[] | undefined, fileType: "image" | "video") => {
if (url) {

View File

@@ -68,7 +68,7 @@ export const FileInput = ({
toast.error(t("common.only_one_file_allowed"));
}
const allowedFiles = getAllowedFiles(files, allowedFileExtensions, maxSizeInMB);
const allowedFiles = await getAllowedFiles(files, allowedFileExtensions, maxSizeInMB);
if (allowedFiles.length === 0) {
return;
@@ -137,7 +137,7 @@ export const FileInput = ({
};
const handleUploadMore = async (files: File[]) => {
const allowedFiles = getAllowedFiles(files, allowedFileExtensions, maxSizeInMB);
const allowedFiles = await getAllowedFiles(files, allowedFileExtensions, maxSizeInMB);
if (allowedFiles.length === 0) {
return;
}

View File

@@ -0,0 +1,26 @@
"use server";
export const convertHeicToJpeg = async (file: File): Promise<File | null> => {
if (!file || !file.name.endsWith(".heic")) return file;
try {
const convert = (await import("heic-convert")).default;
// Convert File to Buffer
const arrayBuffer = await file.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const convertedBuffer = await convert({
buffer,
format: "JPEG",
quality: 0.9,
});
// Convert back to File
return new File([convertedBuffer], file.name.replace(/\.heic$/, ".jpg"), {
type: "image/jpeg",
});
} catch (error) {
console.error("Error converting HEIC to JPEG:", error);
return null;
}
};

View File

@@ -2,6 +2,7 @@
import { toast } from "react-hot-toast";
import { TAllowedFileExtension } from "@formbricks/types/common";
import { convertHeicToJpeg } from "./action";
export const uploadFile = async (
file: File | Blob,
@@ -96,32 +97,53 @@ export const uploadFile = async (
}
};
export const getAllowedFiles = (
export const getAllowedFiles = async (
files: File[],
allowedFileExtensions: string[],
maxSizeInMB?: number
): File[] => {
): Promise<File[]> => {
const sizeExceedFiles: string[] = [];
const unsupportedExtensionFiles: string[] = [];
const convertedFiles: File[] = [];
const allowedFiles = files.filter((file) => {
for (const file of files) {
if (!file || !file.type) {
return false;
continue;
}
const extension = file.name.split(".").pop();
const fileSizeInMB = file.size / 1000000; // Kb -> Mb
const extension = file.name.split(".").pop()?.toLowerCase();
const fileSizeInMB = file.size / 1000000;
console.log("going inside");
if (extension === "heic") {
try {
console.log("converting heic file", file.name);
const convertedFile = await convertHeicToJpeg(file);
if (convertedFile) {
console.log("converted file", convertedFile);
convertedFiles.push(
new File([convertedFile], file.name.replace(/\.heic$/i, ".jpg"), {
type: "image/jpeg",
})
);
}
continue;
} catch (error) {
console.error("Error converting HEIC file:", error);
unsupportedExtensionFiles.push(file.name);
continue;
}
}
if (!allowedFileExtensions.includes(extension as TAllowedFileExtension)) {
unsupportedExtensionFiles.push(file.name);
return false; // Exclude file if extension not allowed
continue;
} else if (maxSizeInMB && fileSizeInMB > maxSizeInMB) {
sizeExceedFiles.push(file.name);
return false; // Exclude files larger than the maximum size
continue;
}
return true;
});
convertedFiles.push(file);
}
// Constructing toast messages based on the issues found
let toastMessage = "";
@@ -134,7 +156,7 @@ export const getAllowedFiles = (
if (toastMessage) {
toast.error(toastMessage);
}
return allowedFiles;
return convertedFiles;
};
export const checkForYoutubePrivacyMode = (url: string): boolean => {

View File

@@ -80,6 +80,7 @@
"file-loader": "6.2.0",
"framer-motion": "11.15.0",
"googleapis": "144.0.0",
"heic-convert": "2.1.0",
"https-proxy-agent": "7.0.6",
"jiti": "2.4.1",
"jsonwebtoken": "9.0.2",

View File

@@ -19,6 +19,7 @@ export const ZPlacement = z.enum(["bottomLeft", "bottomRight", "topLeft", "topRi
export type TPlacement = z.infer<typeof ZPlacement>;
export const ZAllowedFileExtension = z.enum([
"heic",
"png",
"jpeg",
"jpg",