Commit 7978ba54 authored by Joel Hockey's avatar Joel Hockey Committed by Commit Bot

FilesApp do not show confirm delete dialog if using trash

Bug: 953310
Change-Id: I04762056fb288807d9dc7a8fce717f331d43784a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2504750Reviewed-by: default avatarNoel Gordon <noel@chromium.org>
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Auto-Submit: Joel Hockey <joelhockey@chromium.org>
Cr-Commit-Position: refs/heads/master@{#822057}
parent 2e0721b4
...@@ -49,10 +49,19 @@ class FileOperationManager extends EventTarget { ...@@ -49,10 +49,19 @@ class FileOperationManager extends EventTarget {
*/ */
paste(sourceEntries, targetEntry, isMove, opt_taskId) {} paste(sourceEntries, targetEntry, isMove, opt_taskId) {}
/**
* Returns true if all entries will use trash for delete.
*
* @param {!VolumeManager} volumeManager
* @param {!Array<!Entry>} entries The entries.
* @return {boolean}
*/
willUseTrash(volumeManager, entries) {}
/** /**
* Schedules the files deletion. * Schedules the files deletion.
* *
* @param {Array<Entry>} entries The entries. * @param {!Array<!Entry>} entries The entries.
*/ */
deleteEntries(entries) {} deleteEntries(entries) {}
......
...@@ -387,10 +387,22 @@ class FileOperationManagerImpl { ...@@ -387,10 +387,22 @@ class FileOperationManagerImpl {
nextTask.run(onEntryChanged, onTaskProgress, onTaskSuccess, onTaskError); nextTask.run(onEntryChanged, onTaskProgress, onTaskSuccess, onTaskError);
} }
/**
* Returns true if all entries will use trash for delete.
*
* @param {!VolumeManager} volumeManager
* @param {!Array<!Entry>} entries The entries.
* @return {boolean}
*/
willUseTrash(volumeManager, entries) {
return entries.every(
entry => this.trash_.shouldMoveToTrash(volumeManager, entry));
}
/** /**
* Schedules the files deletion. * Schedules the files deletion.
* *
* @param {Array<Entry>} entries The entries. * @param {!Array<!Entry>} entries The entries.
*/ */
deleteEntries(entries) { deleteEntries(entries) {
const task = const task =
......
...@@ -93,6 +93,7 @@ class MockFileOperationManager extends cr.EventTarget { ...@@ -93,6 +93,7 @@ class MockFileOperationManager extends cr.EventTarget {
hasQueuedTasks() {} hasQueuedTasks() {}
filterSameDirectoryEntry() {} filterSameDirectoryEntry() {}
willUseTrash() {}
deleteEntries() {} deleteEntries() {}
restoreDeleted() {} restoreDeleted() {}
zipSelection() {} zipSelection() {}
......
...@@ -84,9 +84,8 @@ class Trash { ...@@ -84,9 +84,8 @@ class Trash {
* @param {!Entry} entry The entry to remove. * @param {!Entry} entry The entry to remove.
* @return {?TrashConfig} Valid TrashConfig if item should be moved to trash, * @return {?TrashConfig} Valid TrashConfig if item should be moved to trash,
* else null if item should be permanently deleted. * else null if item should be permanently deleted.
* @private
*/ */
shouldMoveToTrash_(volumeManager, entry) { shouldMoveToTrash(volumeManager, entry) {
const info = volumeManager.getLocationInfo(entry); const info = volumeManager.getLocationInfo(entry);
if (!loadTimeData.getBoolean('FILES_TRASH_ENABLED') || !info) { if (!loadTimeData.getBoolean('FILES_TRASH_ENABLED') || !info) {
return null; return null;
...@@ -118,7 +117,7 @@ class Trash { ...@@ -118,7 +117,7 @@ class Trash {
*/ */
removeFileOrDirectory(volumeManager, entry, permanentlyDelete) { removeFileOrDirectory(volumeManager, entry, permanentlyDelete) {
if (!permanentlyDelete) { if (!permanentlyDelete) {
const config = this.shouldMoveToTrash_(volumeManager, entry); const config = this.shouldMoveToTrash(volumeManager, entry);
if (config) { if (config) {
return this.trashFileOrDirectory_(entry, config); return this.trashFileOrDirectory_(entry, config);
} }
......
...@@ -162,8 +162,6 @@ async function testMyFilesTrash(done) { ...@@ -162,8 +162,6 @@ async function testMyFilesTrash(done) {
/** /**
* Test that Downloads has its own /Downloads/.Trash since it is a separate * Test that Downloads has its own /Downloads/.Trash since it is a separate
* mount on a device and we don't want move to trash to be a copy operation. * mount on a device and we don't want move to trash to be a copy operation.
*
* @suppress {accessControls} Access shouldMoveToTrash_().
*/ */
async function testDownloadsHasOwnTrash(done) { async function testDownloadsHasOwnTrash(done) {
const trash = new Trash(); const trash = new Trash();
...@@ -197,14 +195,14 @@ async function testDownloadsHasOwnTrash(done) { ...@@ -197,14 +195,14 @@ async function testDownloadsHasOwnTrash(done) {
// Delete /Downloads/.Trash/files/file2. // Delete /Downloads/.Trash/files/file2.
const file2Trashed = fs.entries['/Downloads/.Trash/files/file2']; const file2Trashed = fs.entries['/Downloads/.Trash/files/file2'];
assertFalse(!!trash.shouldMoveToTrash_(volumeManager, file2Trashed)); assertFalse(!!trash.shouldMoveToTrash(volumeManager, file2Trashed));
await trash.removeFileOrDirectory( await trash.removeFileOrDirectory(
volumeManager, file2Trashed, deletePermanently); volumeManager, file2Trashed, deletePermanently);
assertEquals(11, Object.keys(fs.entries).length); assertEquals(11, Object.keys(fs.entries).length);
// Delete /Downloads/.Trash. // Delete /Downloads/.Trash.
const downloadsTrash = fs.entries['/Downloads/.Trash']; const downloadsTrash = fs.entries['/Downloads/.Trash'];
assertFalse(!!trash.shouldMoveToTrash_(volumeManager, downloadsTrash)); assertFalse(!!trash.shouldMoveToTrash(volumeManager, downloadsTrash));
await trash.removeFileOrDirectory( await trash.removeFileOrDirectory(
volumeManager, downloadsTrash, deletePermanently); volumeManager, downloadsTrash, deletePermanently);
assertEquals(7, Object.keys(fs.entries).length); assertEquals(7, Object.keys(fs.entries).length);
...@@ -214,8 +212,6 @@ async function testDownloadsHasOwnTrash(done) { ...@@ -214,8 +212,6 @@ async function testDownloadsHasOwnTrash(done) {
/** /**
* Test crostini trash in .local/share/Trash. * Test crostini trash in .local/share/Trash.
*
* @suppress {accessControls} Access shouldMoveToTrash_().
*/ */
async function testCrostiniTrash(done) { async function testCrostiniTrash(done) {
const trash = new Trash(); const trash = new Trash();
...@@ -252,14 +248,14 @@ async function testCrostiniTrash(done) { ...@@ -252,14 +248,14 @@ async function testCrostiniTrash(done) {
// Move /file2 to trash, then delete /.local/share/Trash/files/file2. // Move /file2 to trash, then delete /.local/share/Trash/files/file2.
await trash.removeFileOrDirectory(volumeManager, file2, deletePermanently); await trash.removeFileOrDirectory(volumeManager, file2, deletePermanently);
const file2Trashed = fs.entries['/.local/share/Trash/files/file2']; const file2Trashed = fs.entries['/.local/share/Trash/files/file2'];
assertFalse(!!trash.shouldMoveToTrash_(volumeManager, file2Trashed)); assertFalse(!!trash.shouldMoveToTrash(volumeManager, file2Trashed));
await trash.removeFileOrDirectory( await trash.removeFileOrDirectory(
volumeManager, file2Trashed, deletePermanently); volumeManager, file2Trashed, deletePermanently);
assertEquals(8, Object.keys(fs.entries).length); assertEquals(8, Object.keys(fs.entries).length);
// Delete /.local/share/Trash. // Delete /.local/share/Trash.
const crostiniTrash = fs.entries['/.local/share/Trash']; const crostiniTrash = fs.entries['/.local/share/Trash'];
assertFalse(!!trash.shouldMoveToTrash_(volumeManager, crostiniTrash)); assertFalse(!!trash.shouldMoveToTrash(volumeManager, crostiniTrash));
await trash.removeFileOrDirectory( await trash.removeFileOrDirectory(
volumeManager, crostiniTrash, deletePermanently); volumeManager, crostiniTrash, deletePermanently);
assertEquals(4, Object.keys(fs.entries).length); assertEquals(4, Object.keys(fs.entries).length);
......
...@@ -1112,6 +1112,13 @@ CommandHandler.COMMANDS_['delete'] = new class extends Command { ...@@ -1112,6 +1112,13 @@ CommandHandler.COMMANDS_['delete'] = new class extends Command {
return; return;
} }
// We show undo toast rather than dialog for entries which will use trash.
if (fileManager.fileOperationManager.willUseTrash(
fileManager.volumeManager, entries)) {
fileManager.fileOperationManager.deleteEntries(entries);
return;
}
const message = entries.length === 1 ? const message = entries.length === 1 ?
strf('GALLERY_CONFIRM_DELETE_ONE', entries[0].name) : strf('GALLERY_CONFIRM_DELETE_ONE', entries[0].name) :
strf('GALLERY_CONFIRM_DELETE_SOME', entries.length); strf('GALLERY_CONFIRM_DELETE_SOME', entries.length);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
/** /**
* Delete files in MyFiles and ensure they are moved to /.Trash. * Delete files in MyFiles and ensure they are moved to /.Trash.
* Then delete items from /.Trash/files and /.Trash/info, then delete /.Trash.
*/ */
testcase.trashMoveToTrash = async () => { testcase.trashMoveToTrash = async () => {
const appId = await setupAndWaitUntilReady( const appId = await setupAndWaitUntilReady(
...@@ -15,12 +16,8 @@ testcase.trashMoveToTrash = async () => { ...@@ -15,12 +16,8 @@ testcase.trashMoveToTrash = async () => {
await remoteCall.waitAndClickElement( await remoteCall.waitAndClickElement(
appId, '#file-list [file-name="hello.txt"]'); appId, '#file-list [file-name="hello.txt"]');
// Delete item and confirm delete. // Delete item and wait for it to be removed (no dialog).
await remoteCall.waitAndClickElement(appId, '#delete-button'); await remoteCall.waitAndClickElement(appId, '#delete-button');
await remoteCall.waitAndClickElement(
appId, '.files-confirm-dialog .cr-dialog-ok');
// Wait for completion of file deletion.
await remoteCall.waitForElementLost( await remoteCall.waitForElementLost(
appId, '#file-list [file-name="hello.txt"]'); appId, '#file-list [file-name="hello.txt"]');
...@@ -42,15 +39,49 @@ testcase.trashMoveToTrash = async () => { ...@@ -42,15 +39,49 @@ testcase.trashMoveToTrash = async () => {
// Navigate to /My files/Downloads/.Trash/files. // Navigate to /My files/Downloads/.Trash/files.
await navigateWithDirectoryTree(appId, '/My files/Downloads/.Trash/files'); await navigateWithDirectoryTree(appId, '/My files/Downloads/.Trash/files');
// Ensure hello.txt exists. // Select hello.txt.
await remoteCall.waitForElement(appId, '#file-list [file-name="hello.txt"]'); await remoteCall.waitAndClickElement(
appId, '#file-list [file-name="hello.txt"]');
// Navigate to /My files/.Trash/files. // Delete item and confirm delete (dialog shown).
await remoteCall.waitAndClickElement(appId, '#delete-button');
await remoteCall.waitAndClickElement(
appId, '.files-confirm-dialog .cr-dialog-ok');
// Wait for completion of file deletion.
await remoteCall.waitForElementLost(
appId, '#file-list [file-name="hello.txt"]');
// Navigate to /My files/Downloads/.Trash/info.
await navigateWithDirectoryTree(appId, '/My files/Downloads/.Trash/info'); await navigateWithDirectoryTree(appId, '/My files/Downloads/.Trash/info');
// Ensure hello.txt.trashinfo exists. // Select hello.txt.trashinfo.
await remoteCall.waitForElement( await remoteCall.waitAndClickElement(
appId, '#file-list [file-name="hello.txt.trashinfo"]');
// Delete item and confirm delete (dialog shown).
await remoteCall.waitAndClickElement(appId, '#delete-button');
await remoteCall.waitAndClickElement(
appId, '.files-confirm-dialog .cr-dialog-ok');
// Wait for completion of file deletion.
await remoteCall.waitForElementLost(
appId, '#file-list [file-name="hello.txt.trashinfo"]'); appId, '#file-list [file-name="hello.txt.trashinfo"]');
// Navigate to /My files/Downloads.
await navigateWithDirectoryTree(appId, '/My files/Downloads');
// Select .Trash.
await remoteCall.waitAndClickElement(
appId, '#file-list [file-name=".Trash"]');
// Delete item and confirm delete (dialog shown).
await remoteCall.waitAndClickElement(appId, '#delete-button');
await remoteCall.waitAndClickElement(
appId, '.files-confirm-dialog .cr-dialog-ok');
// Wait for completion of file deletion.
await remoteCall.waitForElementLost(appId, '#file-list [file-name=".Trash"]');
}; };
/** /**
...@@ -64,12 +95,8 @@ testcase.trashRestore = async () => { ...@@ -64,12 +95,8 @@ testcase.trashRestore = async () => {
await remoteCall.waitAndClickElement( await remoteCall.waitAndClickElement(
appId, '#file-list [file-name="hello.txt"]'); appId, '#file-list [file-name="hello.txt"]');
// Delete item and confirm delete. // Delete item and wait for it to be removed (no dialog).
await remoteCall.waitAndClickElement(appId, '#delete-button'); await remoteCall.waitAndClickElement(appId, '#delete-button');
await remoteCall.waitAndClickElement(
appId, '.files-confirm-dialog .cr-dialog-ok');
// Wait for file to be removed from list.
await remoteCall.waitForElementLost( await remoteCall.waitForElementLost(
appId, '#file-list [file-name="hello.txt"]'); appId, '#file-list [file-name="hello.txt"]');
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment