Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HOLD for payment 2023-08-14] [$1000] Web - Appropriate message is not shown when drag&drop a folder. #22306

Closed
1 of 6 tasks
kbecciv opened this issue Jul 5, 2023 · 44 comments
Assignees
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 External Added to denote the issue can be worked on by a contributor

Comments

@kbecciv
Copy link

kbecciv commented Jul 5, 2023

If you haven’t already, check out our contributing guidelines for onboarding and email [email protected] to request to join our Slack channel!


Action Performed:

  1. Open any chat.
  2. Drag & drop a folder a size less than 24MB and Greater than 240bytes.

Expected Result:

We should show an error message that uploading folders is not supported.

Actual Result:

We show the error: The attachment size must be greater than 240 bytes.

Workaround:

Unknown

Platforms:

Which of our officially supported platforms is this issue occurring on?

  • Android / native
  • Android / Chrome
  • iOS / native
  • iOS / Safari
  • MacOS / Chrome / Safari
  • MacOS / Desktop

Version Number: 1.3.36-4
Reproducible in staging?: n/a
Reproducible in production?: n/a
If this was caught during regression testing, add the test name, ID and link from TestRail:
Email or phone of affected tester (no customers):
Logs: https://stackoverflow.com/c/expensify/questions/4856
Notes/Photos/Videos: Any additional supporting documentation

Test58_Dragdropfolder-1.mp4

Expensify/Expensify Issue URL:
Issue reported by: @daveSeife
Slack conversation: https://expensify.slack.com/archives/C049HHMV9SM/p1688504637490979

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~012e40ccd48a35f18f
  • Upwork Job ID: 1678535202129506304
  • Last Price Increase: 2023-07-17
@kbecciv kbecciv added Daily KSv2 Needs Reproduction Reproducible steps needed Bug Something is broken. Auto assigns a BugZero manager. labels Jul 5, 2023
@melvin-bot
Copy link

melvin-bot bot commented Jul 5, 2023

Triggered auto assignment to @sakluger (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details.

@melvin-bot
Copy link

melvin-bot bot commented Jul 5, 2023

Bug0 Triage Checklist (Main S/O)

  • This "bug" occurs on a supported platform (ensure Platforms in OP are ✅)
  • This bug is not a duplicate report (check E/App issues and #expensify-bugs)
    • If it is, comment with a link to the original report, close the issue and add any novel details to the original issue instead
  • This bug is reproducible using the reproduction steps in the OP. S/O
    • If the reproduction steps are clear and you're unable to reproduce the bug, check with the reporter and QA first, then close the issue.
    • If the reproduction steps aren't clear and you determine the correct steps, please update the OP.
  • This issue is filled out as thoroughly and clearly as possible
    • Pay special attention to the title, results, platforms where the bug occurs, and if the bug happens on staging/production.
  • I have reviewed and subscribed to the linked Slack conversation to ensure Slack/Github stay in sync

@PrashantMangukiya
Copy link
Contributor

PrashantMangukiya commented Jul 6, 2023

Proposal

Posting proposal early as per new guidelines

Please re-state the problem that we are trying to solve in this issue.

Appropriate message is not shown when drag&drop a folder.

What is the root cause of that problem?

When we drag file it will have file object info as shown here in debug.

Image

When we drag folder it will have file object info as shown here in debug.

Folder

As we can see for the folder type property is empty string and size belongs to folder size.

So when we drop content, we just check size but we do not have any checking to detect that given item is folder or file, so it will just check size and show message accordingly.

Here is the code we are checking for file size etc.

const isValidFile = useCallback(
(_file) => {
const {fileExtension} = FileUtils.splitExtensionFromFileName(lodashGet(_file, 'name', ''));
if (_.contains(CONST.API_ATTACHMENT_VALIDATIONS.UNALLOWED_EXTENSIONS, fileExtension.toLowerCase())) {
const invalidReason = props.translate('attachmentPicker.notAllowedExtension');
setIsAttachmentInvalid(true);
setAttachmentInvalidReasonTitle(props.translate('attachmentPicker.wrongFileType'));
setAttachmentInvalidReason(invalidReason);
return false;
}
if (lodashGet(_file, 'size', 0) > CONST.API_ATTACHMENT_VALIDATIONS.MAX_SIZE) {
setIsAttachmentInvalid(true);
setAttachmentInvalidReasonTitle(props.translate('attachmentPicker.attachmentTooLarge'));
setAttachmentInvalidReason(props.translate('attachmentPicker.sizeExceeded'));
return false;
}
if (lodashGet(_file, 'size', 0) < CONST.API_ATTACHMENT_VALIDATIONS.MIN_SIZE) {
setIsAttachmentInvalid(true);
setAttachmentInvalidReasonTitle(props.translate('attachmentPicker.attachmentTooSmall'));
setAttachmentInvalidReason(props.translate('attachmentPicker.sizeNotMet'));
return false;
}
return true;
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[props.translate],
);

What changes do you think we should make in order to solve the problem?

We need to add checking that dropped item is folder or not. So if dropped item is folder it will have file.type as empty, so we can put condition as shown code below:

const isValidFile = useCallback( 
     (_file) => { 
        // *** ADD THIS CONDITION ***
        // If dropped item is folder then show error.
        if (_file.type === '') {
            setIsAttachmentInvalid(true);
            setAttachmentInvalidReasonTitle(props.translate('attachmentPicker.folderNotAllowed'));
            setAttachmentInvalidReason(props.translate('attachmentPicker.folderNotAllowedMessage'));
            return false;
        }
        ...
     }, 
     // eslint-disable-next-line react-hooks/exhaustive-deps 
     [props.translate], 
 ); 

We have to also add corresponding translation for en and es.

en.js

attachmentPicker: {
    ...
    folderNotAllowed: 'Folder not allowed.', // *** ADD THIS ***
    folderNotAllowedMessage: 'Uploading folders is not allowed.', // *** ADD THIS ***
},

es.js

attachmentPicker: {
    ...
    folderNotAllowed: 'Carpeta no permitida.',
    folderNotAllowedMessage: 'No se permite subir carpetas.',
},

Note: We can adjust the message text copy as per need. At present I put folder not allowed message for explanation purpose.

What alternative solutions did you explore? (Optional)

None

Result
22306-FolderNotAllowed.mov

@melvin-bot melvin-bot bot added the Overdue label Jul 10, 2023
@sakluger
Copy link
Contributor

I agree that this is a bug, we do not support folder upload and should return an error if a user tries uploading a folder via drag and drop.

@melvin-bot melvin-bot bot removed the Overdue label Jul 10, 2023
@sakluger sakluger added External Added to denote the issue can be worked on by a contributor and removed Needs Reproduction Reproducible steps needed labels Jul 10, 2023
@melvin-bot melvin-bot bot changed the title Web - Appropriate message is not shown when drag&drop a folder. [$1000] Web - Appropriate message is not shown when drag&drop a folder. Jul 10, 2023
@melvin-bot
Copy link

melvin-bot bot commented Jul 10, 2023

Job added to Upwork: https://www.upwork.com/jobs/~012e40ccd48a35f18f

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label Jul 10, 2023
@melvin-bot
Copy link

melvin-bot bot commented Jul 10, 2023

Current assignee @sakluger is eligible for the External assigner, not assigning anyone new.

@melvin-bot
Copy link

melvin-bot bot commented Jul 10, 2023

Triggered auto assignment to Contributor-plus team member for initial proposal review - @abdulrahuman5196 (External)

@alitoshmatov
Copy link
Contributor

Proposal

Please re-state the problem that we are trying to solve in this issue.

Web - Appropriate message is not shown when drag&drop a folder

What is the root cause of that problem?

We are not checking for file if it is a folder or not.
Folders have type as an empty string
Screenshot 2023-07-11 at 11 00 53 AM

What changes do you think we should make in order to solve the problem?

There are also other files where type is empty, for example: .c files.
Screenshot 2023-07-11 at 11 09 15 AM

if type is empty the backend also rejects this file, and sends an error.
Screenshot 2023-07-11 at 11 11 50 AM

So there is no point of sending files where type is empty. We can add additional condition !lodashGet(_file, 'type') here to check if type is empty:

if (_.contains(CONST.API_ATTACHMENT_VALIDATIONS.UNALLOWED_EXTENSIONS, fileExtension.toLowerCase())) {

This way, we can prevent folders and any other files which backend would respond with an error.
Also we can update this error string to be something like: This attachment type is not allowed, this way we mean all file types as well as folders.

notAllowedExtension: 'This filetype is not allowed',

What alternative solutions did you explore? (Optional)

@Charan-hs
Copy link
Contributor

Charan-hs commented Jul 11, 2023

Proposal

Please re-state the problem that we are trying to solve in this issue.

Web - Appropriate message is not shown when drag&drop a folder

What is the root cause of that problem?

The issue was caused due to "IsValidFile" function not checking the file type before checking for other attributes.

What changes do you think we should make in order to solve the problem?

Found some viable solutions.

  1. Basic check of file with type checking. We are getting file details through props, now we can apply a check i.e. if the type property is empty then we can rise an error that "Is file is Not Supported". But this can block any file without any type property for example, the User wants to upload a raw image file but this file doesn't have a type property so this file can't be uploaded. In this hypothetical scenario, it can rise a concerning Issue.

  2. Advanced solution is to check the file type with a inbuilt function i.e. "webkitGetAsEntry". This function returns an object with these properties

{ 
  fullPath : "string",
  isDirectory:boolean,
  isFile:boolean,
  name: "string"
}

so with the help of this object, we can easily distinguish files uploaded, whether it's a folder or image, or a file without any type.

Here are some example file uploaded and type checking.

  1. Image file uploaded
{ 
  fullPath : "/Screenshot 2023-04-16 at 11.48.02 AM.png",
  isDirectory: false,
  isFile: true,
  name: "Screenshot 2023-04-16 at 11.48.02 AM.png"
}
image type
  1. Folder uploaded
{ 
  fullPath : "/test folder",
  isDirectory: true,
  isFile: false,
  name: "test folder"
}

file type
  1. File without type property
{ 
  fullPath : "/sample.c",
  isDirectory: false,
  isFile: true,
  name: "sample.c"
}

general file without type

so using this Method we can check file type if it's a folder we can rise a Error to the user "Folder can't be Uploaded" and If it's a File without a type property then we can add a type to that file then Upload***

Detailed steps

1. Changes in Drag and Drop Component.

  • Adding webkitdirectory attribute to that doumentElement.

  • Current File

addEventListeners() {
this.dropZone = document.getElementById(this.props.dropZoneId);
this.dropZoneRect = this.calculateDropZoneClientReact();
document.addEventListener('dragover', this.dropZoneDragListener);
document.addEventListener('dragenter', this.dropZoneDragListener);
document.addEventListener('dragleave', this.dropZoneDragListener);
document.addEventListener('drop', this.dropZoneDragListener);
window.addEventListener('resize', this.throttledDragNDropWindowResizeListener);
}
removeEventListeners() {
document.removeEventListener('dragover', this.dropZoneDragListener);
document.removeEventListener('dragenter', this.dropZoneDragListener);
document.removeEventListener('dragleave', this.dropZoneDragListener);
document.removeEventListener('drop', this.dropZoneDragListener);
window.removeEventListener('resize', this.throttledDragNDropWindowResizeListener);
}

  • changes needed i.e adding webkitdirectory attribute to Entire Document
   addEventListeners() {
        this.dropZone = document.getElementById(this.props.dropZoneId);
        document.documentElement.setAttribute('webkitdirectory',"")
        this.dropZoneRect = this.calculateDropZoneClientReact();
        document.addEventListener('dragover', this.dropZoneDragListener);
        document.addEventListener('dragenter', this.dropZoneDragListener);
        document.addEventListener('dragleave', this.dropZoneDragListener);
        document.addEventListener('drop', this.dropZoneDragListener);
        window.addEventListener('resize', this.throttledDragNDropWindowResizeListener);
        
    }

    removeEventListeners() {
        document.documentElement.removeAttribute('webkitdirectory')
        document.removeEventListener('dragover', this.dropZoneDragListener);
        document.removeEventListener('dragenter', this.dropZoneDragListener);
        document.removeEventListener('dragleave', this.dropZoneDragListener);
        document.removeEventListener('drop', this.dropZoneDragListener);
        window.removeEventListener('resize', this.throttledDragNDropWindowResizeListener);
    }

2. OnDropChanges

  • Currently
    const file = lodashGet(e, ['dataTransfer', 'files', 0]);
    displayFileInModal(file);
  • Changes
    Here we will get items from dataTransfer object then pass it on to displayFileInModal
    we can get File from calling data.getAsFile()
 const data = lodashGet(e, ['dataTransfer', 'items', 0]);                                                
displayFileInModal(data);

3. IsValidFile

here we can Check File type then throw an Error "File Type Not Supported"

 if (typeof _data.webkitGetAsEntry === 'function') {
                const entry = _data.webkitGetAsEntry();
                checkFileorFolder(entry);
               // entry Object has isDirectory property
            }

What alternative solutions did you explore? (Optional)

One more suggestion is rather than adding EventListeners to entire document and then narrowing down to upload section. just add EventListeners to that View.

@melvin-bot
Copy link

melvin-bot bot commented Jul 11, 2023

📣 @Charan-hs! 📣
Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork.
Please follow these steps:

  1. Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
  2. Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
  3. Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.
    Screen Shot 2022-11-16 at 4 42 54 PM
    Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>

@Charan-hs
Copy link
Contributor

Contributor details
Your Expensify account email: [email protected]
Upwork Profile Link: https://www.upwork.com/freelancers/~01a9705854f8d26ed1

@melvin-bot
Copy link

melvin-bot bot commented Jul 12, 2023

✅ Contributor details stored successfully. Thank you for contributing to Expensify!

@melvin-bot melvin-bot bot added the Overdue label Jul 13, 2023
@abdulrahuman5196
Copy link
Contributor

Will check on this in morning

@melvin-bot melvin-bot bot removed the Overdue label Jul 14, 2023
@abdulrahuman5196
Copy link
Contributor

On @PrashantMangukiya 's proposal here #22306 (comment), the root cause is correct but I am not aligned with the solution to check and consider empty file type as a folder, since there could be file with empty file type.

@abdulrahuman5196
Copy link
Contributor

@alitoshmatov 's proposal #22306 (comment) here mentions to block all empty file types since backend doesn't allow it. I don't think we should block files without extension, I think should be raised as a separate bug on why backend doesn't allow it, maybe we can confirm the same with internal engineer on if this is the expected behaviour.

@abdulrahuman5196
Copy link
Contributor

@Charan-hs 's proposal here #22306 (comment) uses webkitGetAsEntry which provides way of checking if the drop item is a file or folder which seems good IMO. Could you provide information on how you would implement the same in expensify app codebase?

@Charan-hs
Copy link
Contributor

@abdulrahuman5196

Hi, we can implement proposed changes i.e using webkitGetAsEntry.

1. Changes in Drag and Drop Component (with two different ways).

  • Considering the suggested changes i.e adding eventListeners to dropZone rather than to entire document and then adding webkitdirectory attribute to that dropZone.

  • Current File

addEventListeners() {
this.dropZone = document.getElementById(this.props.dropZoneId);
this.dropZoneRect = this.calculateDropZoneClientReact();
document.addEventListener('dragover', this.dropZoneDragListener);
document.addEventListener('dragenter', this.dropZoneDragListener);
document.addEventListener('dragleave', this.dropZoneDragListener);
document.addEventListener('drop', this.dropZoneDragListener);
window.addEventListener('resize', this.throttledDragNDropWindowResizeListener);
}
removeEventListeners() {
document.removeEventListener('dragover', this.dropZoneDragListener);
document.removeEventListener('dragenter', this.dropZoneDragListener);
document.removeEventListener('dragleave', this.dropZoneDragListener);
document.removeEventListener('drop', this.dropZoneDragListener);
window.removeEventListener('resize', this.throttledDragNDropWindowResizeListener);
}

  • With suggested changes.
addEventListeners() {
        
        this.dropZone = document.getElementById(this.props.dropZoneId);
        this.dropZone.setAttribute("webkitdirectory","")
        this.dropZoneRect = this.calculateDropZoneClientReact();
        this.dropZone.addEventListener('dragover', this.dropZoneDragListener);
        this.dropZone.addEventListener('dragenter', this.dropZoneDragListener);
        this.dropZone.addEventListener('dragleave', this.dropZoneDragListener);
        this.dropZone.addEventListener('drop', this.dropZoneDragListener);
        window.addEventListener('resize', this.throttledDragNDropWindowResizeListener);
        
    }

    removeEventListeners() {
        this.dropZone = document.getElementById(this.props.dropZoneId);
        this.dropZone.removeAttribute("webkitdirectory")
        this.dropZone.removeEventListener('dragover', this.dropZoneDragListener);
        this.dropZone.removeEventListener('dragenter', this.dropZoneDragListener);
        this.dropZone.removeEventListener('dragleave', this.dropZoneDragListener);
        this.dropZone.removeEventListener('drop', this.dropZoneDragListener);
        window.removeEventListener('resize', this.throttledDragNDropWindowResizeListener);
    }
  • without suggested changes i.e adding webkitdirectory attribute to Entire Document
   addEventListeners() {
        this.dropZone = document.getElementById(this.props.dropZoneId);
        document.documentElement.setAttribute('webkitdirectory',"")
        this.dropZoneRect = this.calculateDropZoneClientReact();
        document.addEventListener('dragover', this.dropZoneDragListener);
        document.addEventListener('dragenter', this.dropZoneDragListener);
        document.addEventListener('dragleave', this.dropZoneDragListener);
        document.addEventListener('drop', this.dropZoneDragListener);
        window.addEventListener('resize', this.throttledDragNDropWindowResizeListener);
        
    }

    removeEventListeners() {
        document.documentElement.removeAttribute('webkitdirectory')
        document.removeEventListener('dragover', this.dropZoneDragListener);
        document.removeEventListener('dragenter', this.dropZoneDragListener);
        document.removeEventListener('dragleave', this.dropZoneDragListener);
        document.removeEventListener('drop', this.dropZoneDragListener);
        window.removeEventListener('resize', this.throttledDragNDropWindowResizeListener);
    }

2. OnDropChanges

  • Currently
    const file = lodashGet(e, ['dataTransfer', 'files', 0]);
    displayFileInModal(file);
  • Changes
    Here we will get items from dataTransfer object then pass it on to displayFileInModal
    we can get File from calling data.getAsFile()
 const data = lodashGet(e, ['dataTransfer', 'items', 0]);                                                
displayFileInModal(data);

3. IsValidFile

here we can Check File type then throw an Error "File Type Not Supported"

 if (typeof _data.webkitGetAsEntry === 'function') {
                const entry = _data.webkitGetAsEntry();
                checkFileorFolder(entry);
               // entry Object has isDirectory property
            }

In near future we can get files from the folder then Upload using

const entryContent = await readEntryContentAsync(entry);

@melvin-bot melvin-bot bot added Weekly KSv2 and removed Weekly KSv2 labels Jul 21, 2023
@Charan-hs
Copy link
Contributor

@abdulrahuman5196 @luacmartins Created a PR #23321
Tested All the scenarios in Chrome, and Safari for Drag and drop and Add attachments from + icon.

@alitoshmatov
Copy link
Contributor

I think should be raised as a separate bug on why backend doesn't allow it, maybe we can confirm the same with internal engineer on if this is the expected behaviour.

@abdulrahuman5196
Can you raise an issue for this or should I report it in slack?

@luacmartins luacmartins added the Waiting for copy User facing verbiage needs polishing label Jul 26, 2023
@melvin-bot
Copy link

melvin-bot bot commented Jul 26, 2023

Triggered auto assignment to @LLPeckham (Waiting for copy), see https://stackoverflow.com/c/expensify/questions/7025/ for more details.

@melvin-bot melvin-bot bot added Daily KSv2 and removed Weekly KSv2 labels Jul 26, 2023
@luacmartins
Copy link
Contributor

@LLPeckham can you please review the copy below? This is an error message we'll display to users when they try to upload an entire folder in NewDot

Uploading folder is not allowed.

@LLPeckham
Copy link
Contributor

@luacmartins -- What about: Uploading a folder is not allowed. Try a different file. -- LMK if it's too long adding the "try a different file" line - just thought it could be good to give them a next step after we tell them a folder is not allowed.

@luacmartins
Copy link
Contributor

Sounds good! Thank you!

@sakluger sakluger removed the Waiting for copy User facing verbiage needs polishing label Aug 1, 2023
@sakluger
Copy link
Contributor

sakluger commented Aug 1, 2023

Looks like the PR is continuing to make progress.

@luacmartins
Copy link
Contributor

PR is in review

@melvin-bot
Copy link

melvin-bot bot commented Aug 3, 2023

Based on my calculations, the pull request did not get merged within 3 working days of assignment. Please, check out my computations here:

  • when @Charan-hs got assigned: 2023-07-20 18:59:25 Z
  • when the PR got merged: 2023-08-03 16:40:05 UTC
  • days elapsed: 9

On to the next one 🚀

@sakluger
Copy link
Contributor

sakluger commented Aug 9, 2023

Hey @luacmartins I'm a bit confused with the PR, why is it showing the deploy messages twice?

@abdulrahuman5196
Copy link
Contributor

abdulrahuman5196 commented Aug 9, 2023

@sakluger I think there has been some issue with melvin. This change was pushed to production on Aug 7 #23321 (comment). This issue should be made to hold for payment on Aug 14. Can we kindly change it manually?

@sakluger sakluger changed the title [$1000] Web - Appropriate message is not shown when drag&drop a folder. [HOLD for payment 2023-08-14] [$1000] Web - Appropriate message is not shown when drag&drop a folder. Aug 10, 2023
@sakluger sakluger added Awaiting Payment Auto-added when associated PR is deployed to production Weekly KSv2 and removed Reviewing Has a PR in review Daily KSv2 labels Aug 10, 2023
@abdulrahuman5196
Copy link
Contributor

The PR that introduced the bug has been identified. Link to the PR:
The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake. Link to comment:
A discussion in #expensify-bugs has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner. Link to discussion:

Not a regression. Folder uploaded was not supported and we wheren't showing proper message from implementation itself.

Determine if we should create a regression test for this bug.
If we decide to create a regression test for the bug, please propose the regression test steps to ensure the same bug will not reach production again.

No. Minor bug to show proper error mesage in case of folders, so a regression test won't be beneficial.

@Charan-hs
Copy link
Contributor

@abdulrahuman5196 Error message has been approved here #22306 (comment)

@sakluger sakluger added Daily KSv2 and removed Weekly KSv2 labels Aug 14, 2023
@sakluger
Copy link
Contributor

All paid up! Thanks everyone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 External Added to denote the issue can be worked on by a contributor
Projects
None yet
Development

No branches or pull requests

8 participants