Commit 2822a128 authored by Joel Hockey's avatar Joel Hockey Committed by Chromium LUCI CQ

Move TrashEntry into common and combine with TrashItem

Bug: 953310
Change-Id: I9631628ae0ae8697ffa1df445cda9b84896d2fe0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2567510
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Reviewed-by: default avatarLuciano Pacheco <lucmult@chromium.org>
Cr-Commit-Position: refs/heads/master@{#832691}
parent 9743ad34
......@@ -634,9 +634,9 @@ window.FileOperationManager = class extends EventTarget {
* Restores files from trash.
*
* @param {Array<!{name: string, filesEntry: !Entry, infoEntry: !FileEntry}>}
* trashItems The trash items.
* trashEntries The trash entries.
*/
restoreDeleted(trashItems) {}
restoreDeleted(trashEntries) {}
/**
* Creates a zip file for the selection of files.
......
......@@ -69,9 +69,9 @@ class FileOperationManager extends EventTarget {
* Restores files from trash.
*
* @param {Array<!{name: string, filesEntry: !Entry, infoEntry: !FileEntry}>}
* trashItems The trash items.
* trashEntries The trash entries.
*/
restoreDeleted(trashItems) {}
restoreDeleted(trashEntries) {}
/**
* Creates a zip file for the selection of files.
......
......@@ -25,7 +25,7 @@ class FileOperationProgressEvent extends Event {
/** @public {number} */
this.processedBytes;
/** @public {?Array<!fileOperationUtil.TrashItem>} */
this.trashedItems;
/** @public {?Array<!fileOperationUtil.TrashEntry>} */
this.trashedEntries;
}
}
......@@ -20,4 +20,4 @@ fileOperationUtil.EventRouter.EventType = {};
* infoEntry: !FileEntry
* }}
*/
fileOperationUtil.TrashItem;
fileOperationUtil.TrashEntry;
......@@ -651,7 +651,7 @@ js_library("trash") {
":file_operation_util",
"//ui/file_manager/base/js:volume_manager_types",
"//ui/file_manager/externs:volume_manager",
"//ui/file_manager/file_manager/common/js:trash_config",
"//ui/file_manager/file_manager/common/js:trash",
]
}
......
......@@ -10,7 +10,7 @@
// <include src="../../common/js/lru_cache.js">
// <include src="../../common/js/progress_center_common.js">
// <include src="../../common/js/importer_common.js">
// <include src="../../common/js/trash_config.js">
// <include src="../../common/js/trash.js">
// <include src="metadata_proxy.js">
// <include src="crostini.js">
// <include src="device_handler.js">
......
......@@ -413,7 +413,7 @@ class FileOperationManagerImpl {
totalBytes: 0,
processedBytes: 0,
cancelRequested: false,
trashedItems: [],
trashedEntries: [],
}));
// Obtains entry size and sum them up.
......@@ -494,9 +494,9 @@ class FileOperationManagerImpl {
.removeFileOrDirectory(
assert(this.volumeManager_), task.entries[0],
/*permanentlyDelete=*/ false)
.then(trashItem => {
if (trashItem) {
task.trashedItems.push(trashItem);
.then(trashEntry => {
if (trashEntry) {
task.trashedEntries.push(trashEntry);
}
this.eventRouter_.sendEntryChangedEvent(
util.EntryChangedKind.DELETED, task.entries[0]);
......@@ -531,15 +531,15 @@ class FileOperationManagerImpl {
/**
* Restores files from trash.
*
* @param {Array<!fileOperationUtil.TrashItem>} trashItems The trash items.
* @param {Array<!fileOperationUtil.TrashEntry>} trashEntries The trash items.
*/
restoreDeleted(trashItems) {
restoreDeleted(trashEntries) {
const volumeManager = assert(this.volumeManager_);
while (trashItems.length) {
while (trashEntries.length) {
this.trash_
.restore(
volumeManager,
/** @type {!TrashItem} */ (trashItems.pop()))
/** @type {!TrashEntry} */ (trashEntries.pop()))
.catch(e => console.error('Error restoring deleted file', e));
}
}
......
......@@ -1252,7 +1252,7 @@ fileOperationUtil.ZipTask = class extends fileOperationUtil.Task {
* infoEntry: !FileEntry
* }}
*/
fileOperationUtil.TrashItem;
fileOperationUtil.TrashEntry;
/**
* @typedef {{
......@@ -1262,7 +1262,7 @@ fileOperationUtil.TrashItem;
* totalBytes: number,
* processedBytes: number,
* cancelRequested: boolean,
* trashedItems: Array<!fileOperationUtil.TrashItem>,
* trashedEntries: Array<!fileOperationUtil.TrashEntry>,
* }}
*/
fileOperationUtil.DeleteTask;
......@@ -1389,7 +1389,7 @@ fileOperationUtil.EventRouter = class extends cr.EventTarget {
event.entries = task.entries;
event.totalBytes = task.totalBytes;
event.processedBytes = task.processedBytes;
event.trashedItems = task.trashedItems;
event.trashedEntries = task.trashedEntries;
this.dispatchEvent(event);
}
};
......
......@@ -7,25 +7,6 @@
* https://specifications.freedesktop.org/trash-spec/trashspec-1.0.html
*/
/**
* Result from calling Trash.removeFileOrDirectory().
*/
class TrashItem {
/**
* @param {string} name Name of the file deleted.
* @param {!Entry} filesEntry Trash files entry.
* @param {!FileEntry} infoEntry Trash info entry.
* @param {string=} pathPrefix Optional prefix for 'Path=' in *.trashinfo. For
* crostini, this is the user's homedir (/home/<username>).
*/
constructor(name, filesEntry, infoEntry, pathPrefix = '') {
this.name = name;
this.filesEntry = filesEntry;
this.infoEntry = infoEntry;
this.pathPrefix = pathPrefix;
}
}
/**
* Implementation of trash.
*/
......@@ -86,8 +67,8 @@ class Trash {
* @param {!Entry} entry The entry to remove.
* @param {boolean} permanentlyDelete If true, entry is deleted, else it is
* moved to trash.
* @return {!Promise<!TrashItem|undefined>} Promise which resolves when entry
* is removed, rejects with DOMError.
* @return {!Promise<!TrashEntry|undefined>} Promise which resolves when entry
* is removed, rejects with Error.
*/
removeFileOrDirectory(volumeManager, entry, permanentlyDelete) {
if (!permanentlyDelete) {
......@@ -157,10 +138,11 @@ class Trash {
* @param {!DirectoryEntry} trashInfoDir /.Trash/info directory.
* @param {string} trashInfoName name of the *.trashinfo file.
* @param {string} path path to use in *.trashinfo file.
* @param {!Date} deletionDate deletion date to use in *.trashinfo file.
* @return {!Promise<!FileEntry>}
* @private
*/
async writeTrashInfoFile_(trashInfoDir, trashInfoName, path) {
async writeTrashInfoFile_(trashInfoDir, trashInfoName, path, deletionDate) {
return new Promise((resolve, reject) => {
trashInfoDir.getFile(trashInfoName, {create: true}, infoFile => {
infoFile.createWriter(writer => {
......@@ -169,7 +151,7 @@ class Trash {
};
writer.onerror = reject;
const info = `[Trash Info]\nPath=${path}\nDeletionDate=${
new Date().toISOString()}`;
deletionDate.toISOString()}`;
writer.write(new Blob([info], {type: 'text/plain'}));
}, reject);
}, reject);
......@@ -197,7 +179,7 @@ class Trash {
*
* @param {!Entry} entry The entry to remove.
* @param {!TrashConfig} config trash config for entry.
* @return {!Promise<!TrashItem>}
* @return {!Promise<!TrashEntry>}
* @private
*/
async trashFileOrDirectory_(entry, config) {
......@@ -213,40 +195,41 @@ class Trash {
if (inProgress) {
inProgress.add(trashInfoName);
}
const path = config.pathPrefix + entry.fullPath;
const deletionDate = new Date();
const infoEntry = await this.writeTrashInfoFile_(
trashDirs.info, trashInfoName, config.pathPrefix + entry.fullPath);
trashDirs.info, trashInfoName, path, deletionDate);
const filesEntry = await this.moveTo_(entry, trashDirs.files, name);
return new TrashItem(entry.name, filesEntry, infoEntry, config.pathPrefix);
return new TrashEntry(
entry.name, deletionDate, filesEntry, infoEntry, config.pathPrefix);
}
/**
* Restores the specified trash item.
*
* @param {!VolumeManager} volumeManager
* @param {!TrashItem} trashItem item in trash.
* @param {!TrashEntry} trashEntry entry in trash.
* @return {Promise<void>} Promise which resolves when file is restored.
*/
async restore(volumeManager, trashItem) {
async restore(volumeManager, trashEntry) {
// Read Path from info entry.
const file = await new Promise(
(resolve, reject) => trashItem.infoEntry.file(resolve, reject));
(resolve, reject) => trashEntry.infoEntry.file(resolve, reject));
const text = await file.text();
const found = text.match(/^Path=(.*)/m);
if (!found) {
throw new DOMException(`No Path found to restore in ${
trashItem.infoEntry.fullPath}, text=${text}`);
}
const path = found[1];
if (!path.startsWith(trashItem.pathPrefix)) {
throw new DOMException(`Path does not match expected prefix in ${
trashItem.infoEntry.fullPath}, prefix=${trashItem.pathPrefix}, text=${
text}`);
const path = TrashEntry.parsePath(text);
if (!path) {
throw new Error(`No Path found to restore in ${
trashEntry.infoEntry.fullPath}, text=${text}`);
} else if (!path.startsWith(trashEntry.pathPrefix)) {
throw new Error(`Path does not match expected prefix in ${
trashEntry.infoEntry.fullPath}, prefix=${
trashEntry.pathPrefix}, text=${text}`);
}
const pathNoLeadingSlash = path.substring(trashItem.pathPrefix.length + 1);
const pathNoLeadingSlash = path.substring(trashEntry.pathPrefix.length + 1);
const parts = pathNoLeadingSlash.split('/');
// Move to last directory in path, making sure dirs are created if needed.
let dir = trashItem.filesEntry.filesystem.root;
let dir = trashEntry.filesEntry.filesystem.root;
for (let i = 0; i < parts.length - 1; i++) {
dir = await TrashDirs.getDirectory(dir, parts[i]);
}
......@@ -257,8 +240,8 @@ class Trash {
// when we remove old items.
const name =
await fileOperationUtil.deduplicatePath(dir, parts[parts.length - 1]);
await this.moveTo_(trashItem.filesEntry, dir, name);
await this.permanentlyDeleteFileOrDirectory_(trashItem.infoEntry);
await this.moveTo_(trashEntry.filesEntry, dir, name);
await this.permanentlyDeleteFileOrDirectory_(trashEntry.infoEntry);
}
/**
......
......@@ -236,7 +236,7 @@ async function testCrostiniTrash(done) {
assertEquals(3, Object.keys(fs.entries).length);
// Move /file1 to trash.
const file1TrashItem = await trash.removeFileOrDirectory(
const file1TrashEntry = await trash.removeFileOrDirectory(
volumeManager, file1, deletePermanently);
assertFalse(!!fs.entries['/file1']);
assertTrue(fs.entries['/.local/share/Trash'].isDirectory);
......@@ -252,7 +252,7 @@ async function testCrostiniTrash(done) {
assertEquals(9, Object.keys(fs.entries).length);
// Restore /file1
await trash.restore(volumeManager, assert(file1TrashItem));
await trash.restore(volumeManager, assert(file1TrashEntry));
assertEquals(8, Object.keys(fs.entries).length);
assertTrue(!!fs.entries['/file1']);
......@@ -290,29 +290,29 @@ async function testRestore(done) {
const file3 = MockFileEntry.create(fs, '/dir/file3', null, new Blob(['f3']));
// Move /dir/file1 to trash.
const file1TrashItem = await trash.removeFileOrDirectory(
const file1TrashEntry = await trash.removeFileOrDirectory(
volumeManager, file1, deletePermanently);
assertEquals(9, Object.keys(fs.entries).length);
assertFalse(!!fs.entries['/dir/file1']);
assertEquals('file1', file1TrashItem.name);
assertEquals(fs.entries['/.Trash/files/file1'], file1TrashItem.filesEntry);
assertEquals('file1', file1TrashEntry.name);
assertEquals(fs.entries['/.Trash/files/file1'], file1TrashEntry.filesEntry);
assertEquals(
fs.entries['/.Trash/info/file1.trashinfo'], file1TrashItem.infoEntry);
fs.entries['/.Trash/info/file1.trashinfo'], file1TrashEntry.infoEntry);
// Restore it.
await trash.restore(volumeManager, assert(file1TrashItem));
await trash.restore(volumeManager, assert(file1TrashEntry));
assertEquals(8, Object.keys(fs.entries).length);
assertTrue(!!fs.entries['/dir/file1']);
// Move /dir/file2 to trash, recreate a new /dir/file2,
// original should restore to '/dir/file2 (1)'.
const file2TrashItem = await trash.removeFileOrDirectory(
const file2TrashEntry = await trash.removeFileOrDirectory(
volumeManager, file2, deletePermanently);
assertFalse(!!fs.entries['/dir/file2']);
assertEquals(9, Object.keys(fs.entries).length);
MockFileEntry.create(fs, '/dir/file2', null, new Blob(['f2v2']));
assertEquals(10, Object.keys(fs.entries).length);
await trash.restore(volumeManager, assert(file2TrashItem));
await trash.restore(volumeManager, assert(file2TrashEntry));
assertEquals(9, Object.keys(fs.entries).length);
assertTrue(!!fs.entries['/dir/file2 (1)']);
let text = await fs.entries['/dir/file2'].content.text();
......
......@@ -33,7 +33,7 @@ js_type_check("closure_compile_module") {
":metrics_base",
":mock_entry",
":progress_center_common",
":trash_config",
":trash",
":util",
"//ui/file_manager/base/js:volume_manager_types",
]
......@@ -232,8 +232,16 @@ js_library("mock_entry.m") {
js_library("progress_center_common") {
}
js_library("trash_config") {
deps = [ "//ui/file_manager/base/js:volume_manager_types" ]
js_library("trash") {
deps = [
"//ui/file_manager/base/js:volume_manager_types",
"//ui/file_manager/file_manager/common/js:files_app_entry_types",
]
externs_list = [
"//ui/file_manager/externs/files_app_entry_interfaces.js",
"//ui/file_manager/externs/volume_info.js",
"//ui/file_manager/externs/volume_manager.js",
]
}
js_library("unittest_util") {
......
......@@ -3,7 +3,7 @@
// found in the LICENSE file.
/**
* @fileoverview Trash UI entry types based on
* @fileoverview Trash implementation is based on
* https://specifications.freedesktop.org/trash-spec/trashspec-1.0.html.
*
* When you move /dir/hello.txt to trash, you get:
......@@ -20,34 +20,126 @@
*/
/**
* Used to control the display of items in Trash. Combines the info from both
* .Trash/info and ./Trash/files.
* Configuration for where Trash is stored in a volume.
*/
class TrashConfig {
/**
* @param {VolumeManagerCommon.VolumeType} volumeType
* @param {string} topDir Top directory of volume. Must end with a slash to
* make comparisons simpler.
* @param {string} trashDir Trash directory. Must end with a slash to make
* comparisons simpler.
* @param {boolean=} prefixPathWithRemoteMount Optional, if true, 'Path=' in
* *.trashinfo is prefixed with the volume.remoteMountPath. For crostini,
* this is the user's homedir (/home/<username>).
*/
constructor(volumeType, topDir, trashDir, prefixPathWithRemoteMount = false) {
this.id = `${volumeType}-${topDir}`;
this.volumeType = volumeType;
this.topDir = topDir;
this.trashDir = trashDir;
this.prefixPathWithRemoteMount = prefixPathWithRemoteMount;
this.pathPrefix = '';
}
}
/**
* Volumes supported for Trash, and location of Trash dir. Items will be
* searched in order.
*
* @type {!Array<!TrashConfig>}
*/
TrashConfig.CONFIG = [
// MyFiles/Downloads is a separate volume on a physical device, and doing a
// move from MyFiles/Downloads/<path> to MyFiles/.Trash actually does a
// copy across volumes, so we have a dedicated MyFiles/Downloads/.Trash.
new TrashConfig(
VolumeManagerCommon.VolumeType.DOWNLOADS, '/Downloads/',
'/Downloads/.Trash/'),
new TrashConfig(VolumeManagerCommon.VolumeType.DOWNLOADS, '/', '/.Trash/'),
new TrashConfig(
VolumeManagerCommon.VolumeType.CROSTINI, '/', '/.local/share/Trash/',
/*prefixPathWithRemoteMount=*/ true),
];
/**
* Wrapper for /.Trash/files and /.Trash/info directories.
*/
class TrashDirs {
/**
* @param {!DirectoryEntry} files /.Trash/files directory entry.
* @param {!DirectoryEntry} info /.Trash/info directory entry.
*/
constructor(files, info) {
this.files = files;
this.info = info;
}
/**
* Promise wrapper for FileSystemDirectoryEntry.getDirectory(). Creates dir if
* it does not exist.
*
* @param {!DirectoryEntry} dirEntry current directory.
* @param {string} path name of directory within dirEntry.
* @return {!Promise<!DirectoryEntry>} Promise which resolves with
* <dirEntry>/<path>.
*/
static getDirectory(dirEntry, path) {
return new Promise((resolve, reject) => {
dirEntry.getDirectory(path, {create: true}, resolve, reject);
});
}
/**
* Get trash dirs from file system as specified in config.
*
* @param {!FileSystem} fileSystem File system from volume with trash.
* @param {!TrashConfig} config Config specifying trash dir location.
* @return {!Promise<!TrashDirs>} Promise which resolves with trash dirs.
*/
static async getTrashDirs(fileSystem, config) {
let trashRoot = fileSystem.root;
const parts = config.trashDir.split('/');
for (const part of parts) {
if (part) {
trashRoot = await TrashDirs.getDirectory(trashRoot, part);
}
}
const trashFiles = await TrashDirs.getDirectory(trashRoot, 'files');
const trashInfo = await TrashDirs.getDirectory(trashRoot, 'info');
return new TrashDirs(trashFiles, trashInfo);
}
}
/**
* Represents a file moved to trash. Combines the info from both .Trash/info and
* ./Trash/files.
*
* @implements {FilesAppEntry}
*/
class TrashEntry {
/**
* @param {string} path Path of deleted file from infoEntry.
* @param {string} name Name of the file deleted.
* @param {!Date} deletionDate DeletionDate of deleted file from infoEntry.
* @param {!FileEntry} infoEntry trash info entry.
* @param {!Entry} filesEntry trash files entry.
* @param {string} rootLabel Root label to prefix display name.
* @param {!FileEntry} infoEntry trash info entry.
* @param {string=} pathPrefix Optional prefix for 'Path=' in *.trashinfo. For
* crostini, this is the user's homedir (/home/<username>).
*/
constructor(path, deletionDate, infoEntry, filesEntry, rootLabel) {
/** @private */
this.deletionDate_ = deletionDate;
constructor(name, deletionDate, filesEntry, infoEntry, pathPrefix = '') {
this.name = name;
this.filesEntry = filesEntry;
this.infoEntry = infoEntry;
this.pathPrefix = pathPrefix;
/** @private */
this.infoEntry_ = infoEntry;
/** @private */
this.filesEntry_ = filesEntry;
this.deletionDate_ = deletionDate;
/** @override Entry */
this.filesystem = filesEntry.filesystem;
/** @override Entry */
this.fullPath = path;
this.fullPath = filesEntry.fullPath;
/** @override Entry */
this.isDirectory = filesEntry.isDirectory;
......@@ -55,14 +147,6 @@ class TrashEntry {
/** @override Entry */
this.isFile = filesEntry.isFile;
/**
* Show the root label and the whole Path=<path> from infoEntry as the name.
* This allows users to differentiate deleted files such as /a/hello.txt and
* /b/hello.txt.
* @override Entry
*/
this.name = rootLabel + path.replace(/\//g, '');
/** @override FileEntry */
this.file = filesEntry.file;
......@@ -75,7 +159,7 @@ class TrashEntry {
/** @override Entry */
toURL() {
return 'trash://' + this.infoEntry_.toURL();
return 'trash://' + this.infoEntry.toURL();
}
/**
......@@ -85,7 +169,7 @@ class TrashEntry {
* @override Entry
*/
getMetadata(success, error) {
this.filesEntry_.getMetadata(m => {
this.filesEntry.getMetadata(m => {
success({modificationTime: this.deletionDate_, size: m.size});
}, error);
}
......@@ -104,7 +188,34 @@ class TrashEntry {
getNativeEntry() {
return null;
}
/**
* Parse Path from info entry text, or null if parse fails.
* @param {string} text text of info entry.
* @return {?string} path or null if parse fails.
*/
static parsePath(text) {
const found = text.match(/^Path=(.*)/m);
return found ? found[1] : null;
}
/**
* Parse DeletionDate from info entry text, or null if parse fails.
* @param {string} text text of info entry.
* @return {?Date} deletion date or null if parse fails.
*/
static parseDeletionDate(text) {
const found = text.match(/^DeletionDate=(.*)/m);
if (found) {
const n = Date.parse(found[1]);
if (!Number.isNaN(n)) {
return new Date(n);
}
}
return null;
}
}
/**
* Reads all entries in each of .Trash/info and .Trash/files and produces a
* single stream of TrashEntry.
......@@ -157,9 +268,9 @@ class TrashDirectoryReader {
return error('Ignoring unexpected trash info file');
}
const name = infoEntry.name.substring(0, infoEntry.name.length - 10);
const filesEntry = this.filesEntries_[name];
delete this.filesEntries_[name];
const fileName = infoEntry.name.substring(0, infoEntry.name.length - 10);
const filesEntry = this.filesEntries_[fileName];
delete this.filesEntries_[fileName];
// Ignore any .trashinfo file with no matching file entry.
if (!filesEntry) {
......@@ -169,24 +280,21 @@ class TrashDirectoryReader {
const file =
await new Promise((resolve, reject) => infoEntry.file(resolve, reject));
const text = await file.text();
let found = text.match(/^Path=(.*)/m);
if (!found) {
const path = TrashEntry.parsePath(text);
if (!path) {
return error('Ignoring trash info file with no Path', text);
}
const path = found[1];
found = text.match(/^DeletionDate=(.*)/m);
if (!found) {
return error('Ignoring trash info file with no DeletionDate', text);
}
const d = Date.parse(found[1]);
if (!found) {
const deletionDate = TrashEntry.parseDeletionDate(text);
if (!deletionDate) {
return error('Ignoring trash info file with invalid DeletionDate', text);
}
return new TrashEntry(
path, new Date(d), infoEntry, filesEntry, this.rootLabel_);
// Show the root label and the whole Path=<path> from infoEntry as the name.
// This allows users to differentiate deleted files such as /a/hello.txt and
// /b/hello.txt.
const trashName = this.rootLabel_ + path.replace(/\//g, '');
return new TrashEntry(trashName, deletionDate, filesEntry, infoEntry);
}
/**
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Trash configuration of volumes which have trash, and where it
* is located.
*/
/**
* Configuration for where Trash is stored in a volume.
*/
class TrashConfig {
/**
* @param {VolumeManagerCommon.VolumeType} volumeType
* @param {string} topDir Top directory of volume. Must end with a slash to
* make comparisons simpler.
* @param {string} trashDir Trash directory. Must end with a slash to make
* comparisons simpler.
* @param {boolean=} prefixPathWithRemoteMount Optional, if true, 'Path=' in
* *.trashinfo is prefixed with the volume.remoteMountPath. For crostini,
* this is the user's homedir (/home/<username>).
*/
constructor(volumeType, topDir, trashDir, prefixPathWithRemoteMount = false) {
this.id = `${volumeType}-${topDir}`;
this.volumeType = volumeType;
this.topDir = topDir;
this.trashDir = trashDir;
this.prefixPathWithRemoteMount = prefixPathWithRemoteMount;
this.pathPrefix = '';
}
}
/**
* Volumes supported for Trash, and location of Trash dir. Items will be
* searched in order.
*
* @type {!Array<!TrashConfig>}
*/
TrashConfig.CONFIG = [
// MyFiles/Downloads is a separate volume on a physical device, and doing a
// move from MyFiles/Downloads/<path> to MyFiles/.Trash actually does a
// copy across volumes, so we have a dedicated MyFiles/Downloads/.Trash.
new TrashConfig(
VolumeManagerCommon.VolumeType.DOWNLOADS, '/Downloads/',
'/Downloads/.Trash/'),
new TrashConfig(VolumeManagerCommon.VolumeType.DOWNLOADS, '/', '/.Trash/'),
new TrashConfig(
VolumeManagerCommon.VolumeType.CROSTINI, '/', '/.local/share/Trash/',
/*prefixPathWithRemoteMount=*/ true),
];
/**
* Wrapper for /.Trash/files and /.Trash/info directories.
*/
class TrashDirs {
/**
* @param {!DirectoryEntry} files /.Trash/files directory entry.
* @param {!DirectoryEntry} info /.Trash/info directory entry.
*/
constructor(files, info) {
this.files = files;
this.info = info;
}
/**
* Promise wrapper for FileSystemDirectoryEntry.getDirectory(). Creates dir if
* it does not exist.
*
* @param {!DirectoryEntry} dirEntry current directory.
* @param {string} path name of directory within dirEntry.
* @return {!Promise<!DirectoryEntry>} Promise which resolves with
* <dirEntry>/<path>.
*/
static getDirectory(dirEntry, path) {
return new Promise((resolve, reject) => {
dirEntry.getDirectory(path, {create: true}, resolve, reject);
});
}
/**
* Get trash dirs from file system as specified in config.
*
* @param {!FileSystem} fileSystem File system from volume with trash.
* @param {!TrashConfig} config Config specifying trash dir location.
* @return {!Promise<!TrashDirs>} Promise which resolves with trash dirs.
*/
static async getTrashDirs(fileSystem, config) {
let trashRoot = fileSystem.root;
const parts = config.trashDir.split('/');
for (const part of parts) {
if (part) {
trashRoot = await TrashDirs.getDirectory(trashRoot, part);
}
}
const trashFiles = await TrashDirs.getDirectory(trashRoot, 'files');
const trashInfo = await TrashDirs.getDirectory(trashRoot, 'info');
return new TrashDirs(trashFiles, trashInfo);
}
}
......@@ -85,7 +85,6 @@ js_type_check("closure_compile_module") {
":task_history",
":thumbnail_loader",
":toolbar_controller",
":trash",
":web_store_utils",
":webui_command_extender",
]
......@@ -679,7 +678,7 @@ js_library("navigation_list_model") {
deps = [
":android_app_list_model",
":folder_shortcuts_data_model",
":trash",
"//ui/file_manager/file_manager/common/js:trash",
"//ui/webui/resources/js/cr:event_target",
"//ui/webui/resources/js/cr/ui:array_data_model",
]
......@@ -879,19 +878,6 @@ js_library("toolbar_controller") {
]
}
js_library("trash") {
deps = [
"//ui/file_manager/base/js:volume_manager_types",
"//ui/file_manager/file_manager/common/js:files_app_entry_types",
"//ui/file_manager/file_manager/common/js:trash_config",
]
externs_list = [
"//ui/file_manager/externs/files_app_entry_interfaces.js",
"//ui/file_manager/externs/volume_info.js",
"//ui/file_manager/externs/volume_manager.js",
]
}
js_library("web_store_utils") {
deps = [ ":constants" ]
}
......
......@@ -1197,16 +1197,16 @@ CommandHandler.registerUndoDeleteToast = function(fileManager) {
*/
const onDeleted = (e) => {
if (e.reason === 'BEGIN' || e.reason === 'PROGRESS' ||
!e.trashedItems.length) {
!e.trashedEntries.length) {
return;
}
const message = e.trashedItems.length === 1 ?
strf('UNDO_DELETE_ONE', e.trashedItems[0].name) :
strf('UNDO_DELETE_SOME', e.trashedItems.length);
const message = e.trashedEntries.length === 1 ?
strf('UNDO_DELETE_ONE', e.trashedEntries[0].name) :
strf('UNDO_DELETE_SOME', e.trashedEntries.length);
fileManager.ui.toast.show(message, {
text: str('UNDO_DELETE_ACTION_LABEL'),
callback: () => {
fileManager.fileOperationManager.restoreDeleted(e.trashedItems);
fileManager.fileOperationManager.restoreDeleted(e.trashedEntries);
}
});
};
......
......@@ -87,7 +87,7 @@
// <include src="../../common/js/util.js">
// <include src="../../common/js/progress_center_common.js">
// <include src="../../common/js/importer_common.js">
// <include src="../../common/js/trash_config.js">
// <include src="../../common/js/trash.js">
//
// <include src="constants.js">
// <include src="crossover_search_utils.js">
......@@ -160,7 +160,6 @@
// <include src="toolbar_controller.js">
// <include src="thumbnail_loader.js">
// <include src="list_thumbnail_loader.js">
// <include src="trash.js">
// <include src="providers_model.js">
// <include src="ui/actions_submenu.js">
// <include src="ui/banners.js">
......
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