From 65a152e51822f50ac67ebbcfb68a65e1b65a56df Mon Sep 17 00:00:00 2001 From: Dhruwang Jariwala <67850763+Dhruwang@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:43:05 +0530 Subject: [PATCH] fix: survey background image upload issue (#2222) Co-authored-by: pandeymangg --- .../edit/components/AnimatedSurveyBg.tsx | 4 +- .../edit/components/ColorSurveyBg.tsx | 6 +-- .../edit/components/ImageSurveyBg.tsx | 20 ++------ .../edit/components/SurveyBgSelectorTab.tsx | 6 +-- packages/ui/FileInput/index.tsx | 44 +++++------------ packages/ui/FileInput/lib/fileUpload.ts | 47 ++++++++++++++++++- 6 files changed, 71 insertions(+), 56 deletions(-) diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/AnimatedSurveyBg.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/AnimatedSurveyBg.tsx index c136644c12..0fb3bd846a 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/AnimatedSurveyBg.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/AnimatedSurveyBg.tsx @@ -5,7 +5,7 @@ interface AnimatedSurveyBgProps { background: string; } -export default function AnimatedSurveyBg({ handleBgChange, background }: AnimatedSurveyBgProps) { +export const AnimatedSurveyBg = ({ handleBgChange, background }: AnimatedSurveyBgProps) => { const [animation, setAnimation] = useState(background || "/animated-bgs/4K/1_4k.mp4"); const [hoveredVideo, setHoveredVideo] = useState(null); @@ -103,4 +103,4 @@ export default function AnimatedSurveyBg({ handleBgChange, background }: Animate ); -} +}; diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/ColorSurveyBg.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/ColorSurveyBg.tsx index 69e6eb812a..e16f8401e8 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/ColorSurveyBg.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/ColorSurveyBg.tsx @@ -2,13 +2,13 @@ import { useState } from "react"; import { ColorPicker } from "@formbricks/ui/ColorPicker"; -interface ColorSurveyBgBgProps { +interface ColorSurveyBgProps { handleBgChange: (bg: string, bgType: string) => void; colours: string[]; background: string; } -export default function ColorSurveyBg({ handleBgChange, colours, background }: ColorSurveyBgBgProps) { +export const ColorSurveyBg = ({ handleBgChange, colours, background }: ColorSurveyBgProps) => { const [color, setColor] = useState(background || "#ffff"); const handleBg = (x: string) => { @@ -35,4 +35,4 @@ export default function ColorSurveyBg({ handleBgChange, colours, background }: C ); -} +}; diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/ImageSurveyBg.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/ImageSurveyBg.tsx index c216a07b9c..a741c120fb 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/ImageSurveyBg.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/ImageSurveyBg.tsx @@ -1,23 +1,12 @@ import FileInput from "@formbricks/ui/FileInput"; -interface ImageSurveyBgBgProps { +interface ImageSurveyBgProps { environmentId: string; handleBgChange: (url: string, bgType: string) => void; background: string; } -export default function ImageSurveyBg({ environmentId, handleBgChange, background }: ImageSurveyBgBgProps) { - const isUrl = (str: string) => { - try { - new URL(str); - return true; - } catch (error) { - return false; - } - }; - - const fileUrl = isUrl(background ?? "") ? background ?? "" : ""; - +export const ImageSurveyBg = ({ environmentId, handleBgChange, background }: ImageSurveyBgProps) => { return (
@@ -32,9 +21,10 @@ export default function ImageSurveyBg({ environmentId, handleBgChange, backgroun handleBgChange("", "image"); } }} - fileUrl={fileUrl} + fileUrl={background} + maxSizeInMB={2} />
); -} +}; diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyBgSelectorTab.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyBgSelectorTab.tsx index 6cebc035df..3295c88931 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyBgSelectorTab.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyBgSelectorTab.tsx @@ -2,9 +2,9 @@ import { useEffect, useState } from "react"; import { TSurvey } from "@formbricks/types/surveys"; -import AnimatedSurveyBg from "./AnimatedSurveyBg"; -import ColorSurveyBg from "./ColorSurveyBg"; -import ImageSurveyBg from "./ImageSurveyBg"; +import { AnimatedSurveyBg } from "./AnimatedSurveyBg"; +import { ColorSurveyBg } from "./ColorSurveyBg"; +import { ImageSurveyBg } from "./ImageSurveyBg"; interface SurveyBgSelectorTabProps { localSurvey: TSurvey; diff --git a/packages/ui/FileInput/index.tsx b/packages/ui/FileInput/index.tsx index 0b58225eb8..3cd0f4a0aa 100644 --- a/packages/ui/FileInput/index.tsx +++ b/packages/ui/FileInput/index.tsx @@ -9,7 +9,7 @@ import toast from "react-hot-toast"; import { cn } from "@formbricks/lib/cn"; import { TAllowedFileExtension } from "@formbricks/types/common"; -import { uploadFile } from "./lib/fileUpload"; +import { getAllowedFiles, uploadFile } from "./lib/fileUpload"; const allowedFileTypesForPreview = ["png", "jpeg", "jpg", "webp"]; const isImage = (name: string) => { @@ -23,6 +23,7 @@ interface FileInputProps { fileUrl?: string | string[]; multiple?: boolean; imageFit?: "cover" | "contain"; + maxSizeInMB?: number; } interface SelectedFile { @@ -39,6 +40,7 @@ const FileInput: React.FC = ({ fileUrl, multiple = false, imageFit = "cover", + maxSizeInMB, }) => { const [selectedFiles, setSelectedFiles] = useState([]); @@ -48,19 +50,9 @@ const FileInput: React.FC = ({ toast.error("Only one file is allowed"); } - const allowedFiles = files.filter( - (file) => - file && - file.type && - allowedFileExtensions.includes(file.name.split(".").pop() as TAllowedFileExtension) - ); - - if (allowedFiles.length < files.length) { - if (allowedFiles.length === 0) { - toast.error("No files are supported"); - return; - } - toast.error("Some files are not supported"); + const allowedFiles = getAllowedFiles(files, allowedFileExtensions, maxSizeInMB); + if (allowedFiles.length === 0) { + return; } setSelectedFiles( @@ -85,7 +77,7 @@ const FileInput: React.FC = ({ const uploadedUrls: string[] = []; uploadedFiles.forEach((file) => { if (file.status === "fulfilled") { - uploadedUrls.push(file.value.url); + uploadedUrls.push(encodeURI(file.value.url)); } }); @@ -125,21 +117,9 @@ const FileInput: React.FC = ({ }; const handleUploadMore = async (files: File[]) => { - let filesToUpload: File[] = files; - - const allowedFiles = filesToUpload.filter( - (file) => - file && - file.type && - allowedFileExtensions.includes(file.name.split(".").pop() as TAllowedFileExtension) - ); - - if (allowedFiles.length < filesToUpload.length) { - if (allowedFiles.length === 0) { - toast.error("No files are supported"); - return; - } - toast.error("Some files are not supported"); + const allowedFiles = getAllowedFiles(files, allowedFileExtensions, maxSizeInMB); + if (allowedFiles.length === 0) { + return; } setSelectedFiles((prevFiles) => [ @@ -165,7 +145,7 @@ const FileInput: React.FC = ({ const uploadedUrls: string[] = []; uploadedFiles.forEach((file) => { if (file.status === "fulfilled") { - uploadedUrls.push(file.value.url); + uploadedUrls.push(encodeURI(file.value.url)); } }); @@ -199,6 +179,7 @@ const FileInput: React.FC = ({ src={file.url} alt={file.name} fill + sizes="100%" style={{ objectFit: "cover" }} quality={100} className={!file.uploaded ? "opacity-50" : ""} @@ -253,6 +234,7 @@ const FileInput: React.FC = ({ src={selectedFiles[0].url} alt={selectedFiles[0].name} fill + sizes="100%" style={{ objectFit: imageFit }} quality={100} className={!selectedFiles[0].uploaded ? "opacity-50" : ""} diff --git a/packages/ui/FileInput/lib/fileUpload.ts b/packages/ui/FileInput/lib/fileUpload.ts index 992f56e906..0b87282daf 100644 --- a/packages/ui/FileInput/lib/fileUpload.ts +++ b/packages/ui/FileInput/lib/fileUpload.ts @@ -1,6 +1,10 @@ "use client"; -const uploadFile = async ( +import { toast } from "react-hot-toast"; + +import { TAllowedFileExtension } from "@formbricks/types/common"; + +export const uploadFile = async ( file: File | Blob, allowedFileExtensions: string[] | undefined, environmentId: string | undefined @@ -93,4 +97,43 @@ const uploadFile = async ( } }; -export { uploadFile }; +export const getAllowedFiles = ( + files: File[], + allowedFileExtensions: string[], + maxSizeInMB?: number +): File[] => { + const sizeExceedFiles: string[] = []; + const unsupportedExtensionFiles: string[] = []; + + const allowedFiles = files.filter((file) => { + if (!file || !file.type) { + return false; + } + + const extension = file.name.split(".").pop(); + const fileSizeInMB = file.size / 1000000; // Kb -> Mb + + if (!allowedFileExtensions.includes(extension as TAllowedFileExtension)) { + unsupportedExtensionFiles.push(file.name); + return false; // Exclude file if extension not allowed + } else if (maxSizeInMB && fileSizeInMB > maxSizeInMB) { + sizeExceedFiles.push(file.name); + return false; // Exclude files larger than the maximum size + } + + return true; + }); + + // Constructing toast messages based on the issues found + let toastMessage = ""; + if (sizeExceedFiles.length > 0) { + toastMessage += `Files exceeding size limit (${maxSizeInMB} MB): ${sizeExceedFiles.join(", ")}. `; + } + if (unsupportedExtensionFiles.length > 0) { + toastMessage += `Unsupported file types: ${unsupportedExtensionFiles.join(", ")}.`; + } + if (toastMessage) { + toast.error(toastMessage); + } + return allowedFiles; +};