@@ -27,6 +27,25 @@ import type IconAsset from '@src/types/utils/IconAsset';
2727import launchCamera from './launchCamera/launchCamera' ;
2828import type AttachmentPickerProps from './types' ;
2929
30+ const EXTENSION_TO_NATIVE_TYPE : Record < string , string > = {
31+ pdf : String ( types . pdf ) ,
32+ doc : String ( types . doc ) ,
33+ docx : String ( types . docx ) ,
34+ zip : String ( types . zip ) ,
35+ txt : String ( types . plainText ) ,
36+ json : String ( types . json ) ,
37+ xls : String ( types . xls ) ,
38+ xlsx : String ( types . xlsx ) ,
39+ jpg : String ( types . images ) ,
40+ jpeg : String ( types . images ) ,
41+ png : String ( types . images ) ,
42+ gif : String ( types . images ) ,
43+ heif : String ( types . images ) ,
44+ heic : String ( types . images ) ,
45+ tif : String ( types . images ) ,
46+ tiff : String ( types . images ) ,
47+ } ;
48+
3049type LocalCopy = {
3150 name : string | null ;
3251 uri : string ;
@@ -121,8 +140,10 @@ function AttachmentPicker({
121140 shouldHideCameraOption = false ,
122141 shouldValidateImage = true ,
123142 shouldHideGalleryOption = false ,
143+ acceptedFileTypes,
124144 fileLimit = 1 ,
125145 onOpenPicker,
146+ shouldSkipAttachmentTypeModal = false ,
126147} : AttachmentPickerProps ) {
127148 const icons = useMemoizedLazyExpensifyIcons ( [ 'Camera' , 'Gallery' , 'Paperclip' ] ) ;
128149 const styles = useThemeStyles ( ) ;
@@ -251,8 +272,22 @@ function AttachmentPicker({
251272 * Launch the DocumentPicker. Results are in the same format as ImagePicker
252273 */
253274 const showDocumentPicker = useCallback ( async ( ) : Promise < LocalCopy [ ] > => {
275+ let pickerTypes : string [ ] ;
276+ if ( acceptedFileTypes && acceptedFileTypes . length > 0 ) {
277+ const mappedTypes = acceptedFileTypes . reduce < string [ ] > ( ( result , extension ) => {
278+ const nativeType = EXTENSION_TO_NATIVE_TYPE [ String ( extension ) ] ;
279+ if ( nativeType !== undefined && ! result . includes ( nativeType ) ) {
280+ result . push ( nativeType ) ;
281+ }
282+ return result ;
283+ } , [ ] ) ;
284+ pickerTypes = mappedTypes . length > 0 ? mappedTypes : [ types . allFiles ] ;
285+ } else {
286+ pickerTypes = [ type === CONST . ATTACHMENT_PICKER_TYPE . IMAGE ? types . images : types . allFiles ] ;
287+ }
288+
254289 const pickedFiles = await pick ( {
255- type : [ type === CONST . ATTACHMENT_PICKER_TYPE . IMAGE ? types . images : types . allFiles ] ,
290+ type : pickerTypes ,
256291 allowMultiSelection : fileLimit !== 1 ,
257292 } ) ;
258293
@@ -280,7 +315,7 @@ function AttachmentPicker({
280315 type : file . type ,
281316 } ;
282317 } ) ;
283- } , [ fileLimit , type ] ) ;
318+ } , [ acceptedFileTypes , fileLimit , type ] ) ;
284319
285320 const menuItemData : Item [ ] = useMemo ( ( ) => {
286321 const data : Item [ ] = [
@@ -336,19 +371,6 @@ function AttachmentPicker({
336371 [ showGeneralAlert , showImageCorruptionAlert , translate ] ,
337372 ) ;
338373
339- /**
340- * Opens the attachment modal
341- *
342- * @param onPickedHandler A callback that will be called with the selected attachment
343- * @param onCanceledHandler A callback that will be called without a selected attachment
344- */
345- const open = ( onPickedHandler : ( files : FileObject [ ] ) => void , onCanceledHandler : ( ) => void = ( ) => { } , onClosedHandler : ( ) => void = ( ) => { } ) => {
346- completeAttachmentSelection . current = onPickedHandler ;
347- onCanceled . current = onCanceledHandler ;
348- onClosed . current = onClosedHandler ;
349- setIsVisible ( true ) ;
350- } ;
351-
352374 /**
353375 * Closes the attachment modal
354376 */
@@ -442,6 +464,33 @@ function AttachmentPicker({
442464 [ handleImageProcessingError , shouldValidateImage , showGeneralAlert , showImageCorruptionAlert ] ,
443465 ) ;
444466
467+ /**
468+ * Opens the attachment modal, or directly launches the document picker when shouldSkipAttachmentTypeModal is true.
469+ */
470+ const open = ( onPickedHandler : ( files : FileObject [ ] ) => void , onCanceledHandler : ( ) => void = ( ) => { } , onClosedHandler : ( ) => void = ( ) => { } ) => {
471+ completeAttachmentSelection . current = onPickedHandler ;
472+ onCanceled . current = onCanceledHandler ;
473+ onClosed . current = onClosedHandler ;
474+
475+ if ( shouldSkipAttachmentTypeModal ) {
476+ onOpenPicker ?.( ) ;
477+ showDocumentPicker ( )
478+ . catch ( ( error : Error ) => {
479+ if ( JSON . stringify ( error ) . includes ( 'OPERATION_CANCELED' ) ) {
480+ return ;
481+ }
482+ showGeneralAlert ( error . message ) ;
483+ throw error ;
484+ } )
485+ . then ( ( result ) => pickAttachment ( result ) )
486+ . catch ( console . error )
487+ . finally ( ( ) => onClosedHandler ( ) ) ;
488+ return ;
489+ }
490+
491+ setIsVisible ( true ) ;
492+ } ;
493+
445494 /**
446495 * Setup native attachment selection to start after this popover closes
447496 *
0 commit comments