From 43c93a597ab150d173affc0b03e6a04e73645e08 Mon Sep 17 00:00:00 2001
From: Valentin Yanakiev <valentin.yanakiev@gmail.com>
Date: Fri, 13 Dec 2024 13:54:00 +0200
Subject: [PATCH] Filter file extensions robustly

---
 src/core/ui/upload/FileUpload/FileUpload.tsx | 51 +++++++++++++++++++-
 1 file changed, 50 insertions(+), 1 deletion(-)

diff --git a/src/core/ui/upload/FileUpload/FileUpload.tsx b/src/core/ui/upload/FileUpload/FileUpload.tsx
index f6f8032843..9c0c4977d6 100644
--- a/src/core/ui/upload/FileUpload/FileUpload.tsx
+++ b/src/core/ui/upload/FileUpload/FileUpload.tsx
@@ -10,6 +10,7 @@ import {
 } from '@/core/apollo/generated/apollo-hooks';
 import UploadButton from '@/core/ui/button/UploadButton';
 import { useNotification } from '@/core/ui/notifications/useNotification';
+import { useMemo } from 'react';
 
 const DEFAULT_REFERENCE_TYPE = 'reference';
 
@@ -37,6 +38,54 @@ const FileUploadButton = ({
 
   const maxFileSizeMb = storageConfig.maxFileSize ? storageConfig.maxFileSize / bytesInMegabyte : 0;
 
+  // NOTE: Some browsers fail to map mime types to extensions correctly. Hence the need of a mapping of
+  // file types to extensions explicitly, so we can have a filtered list when selecting files on the client.
+  // Extended mimeTypeToExtensionMap
+  const mimeTypeToExtensionMap: { [mimeType: string]: string } = {
+    // PDF
+    'application/pdf': '.pdf',
+
+    // Excel
+    'application/vnd.ms-excel': '.xls',
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '.xlsx',
+    'application/vnd.oasis.opendocument.spreadsheet': '.ods',
+
+    // Word
+    'application/msword': '.doc',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx',
+    'application/vnd.oasis.opendocument.text': '.odt',
+
+    // Images
+    'image/bmp': '.bmp',
+    'image/jpg': '.jpg',
+    'image/jpeg': '.jpg,.jpeg',
+    'image/x-png': '.png',
+    'image/png': '.png',
+    'image/gif': '.gif',
+    'image/webp': '.webp',
+    'image/svg+xml': '.svg',
+    'image/avif': '.avif',
+
+    // PowerPoint
+    'application/vnd.ms-powerpoint': '.ppt',
+    'application/vnd.openxmlformats-officedocument.presentationml.presentation': '.pptx',
+    'application/vnd.ms-powerpoint.presentation.macroEnabled.12': '.pptm',
+    'application/vnd.openxmlformats-officedocument.presentationml.slideshow': '.ppsx',
+    'application/vnd.ms-powerpoint.slideshow.macroEnabled.12': '.ppsm',
+    'application/vnd.openxmlformats-officedocument.presentationml.template': '.potx',
+    'application/vnd.ms-powerpoint.template.macroEnabled.12': '.potm',
+    'application/vnd.oasis.opendocument.presentation': '.odp',
+  };
+
+  const allowedMimeTypes = storageConfig.allowedMimeTypes;
+
+  const allowedExtensions = useMemo(() => {
+    return allowedMimeTypes
+      .map(mimeType => mimeTypeToExtensionMap[mimeType])
+      .filter(Boolean)
+      .join(',');
+  }, [allowedMimeTypes]);
+
   const [uploadFileOnReference, { loading: loadingOnReference }] = useUploadFileOnReferenceMutation({
     onCompleted: data => {
       notify(t('components.file-upload.file-upload-success'), 'success');
@@ -103,7 +152,7 @@ const FileUploadButton = ({
     <UploadButton
       icon={loading ? <CircularProgress size={20} /> : <AttachFileIcon />}
       disabled={loading}
-      allowedTypes={storageConfig.allowedMimeTypes}
+      allowedTypes={allowedExtensions.split(',')}
       onFileSelected={file => {
         handleSubmit(file);
         onChange?.(file.name);