2020-09-12 15:49:20 +02:00
|
|
|
import {ConnectionHandler} from "../../../ConnectionHandler";
|
2021-03-16 15:14:03 +01:00
|
|
|
import {Registry} from "tc-events";
|
2020-09-12 15:49:20 +02:00
|
|
|
import {FileType} from "../../../file/FileManager";
|
|
|
|
import {CommandResult} from "../../../connection/ServerConnectionDeclaration";
|
|
|
|
import PermissionType from "../../../permission/PermissionType";
|
2021-01-10 17:36:57 +01:00
|
|
|
import {LogCategory, logError, logTrace} from "../../../log";
|
2020-09-12 15:49:20 +02:00
|
|
|
import {Entry, MenuEntry, MenuEntryType, spawn_context_menu} from "../../../ui/elements/ContextMenu";
|
2021-03-18 18:25:20 +01:00
|
|
|
import {getKeyBoard, SpecialKey} from "../../../PPTListener";
|
2021-04-24 13:59:49 +02:00
|
|
|
import {tr, tra} from "../../../i18n/localize";
|
2020-06-10 22:44:50 +02:00
|
|
|
import {
|
|
|
|
FileTransfer,
|
|
|
|
FileTransferState,
|
|
|
|
FileUploadTransfer,
|
|
|
|
TransferProvider,
|
|
|
|
TransferTargetType
|
2020-09-12 15:49:20 +02:00
|
|
|
} from "../../../file/Transfer";
|
|
|
|
import {createErrorModal} from "../../../ui/elements/Modal";
|
2020-12-18 19:18:01 +01:00
|
|
|
import {ErrorCode} from "../../../connection/ErrorCode";
|
2020-06-10 18:13:56 +02:00
|
|
|
import {
|
|
|
|
avatarsPathPrefix,
|
2021-01-10 17:36:57 +01:00
|
|
|
channelPathPrefix,
|
|
|
|
FileBrowserEvents,
|
|
|
|
iconPathPrefix,
|
|
|
|
ListedFileInfo,
|
2020-06-10 22:44:50 +02:00
|
|
|
PathInfo
|
2020-12-18 19:18:01 +01:00
|
|
|
} from "tc-shared/ui/modal/transfer/FileDefinitions";
|
2021-04-24 14:52:08 +02:00
|
|
|
import {promptYesNo} from "tc-shared/ui/modal/yes-no/Controller";
|
2020-06-10 18:13:56 +02:00
|
|
|
|
2020-09-12 15:49:20 +02:00
|
|
|
function parsePath(path: string, connection: ConnectionHandler): PathInfo {
|
|
|
|
if (path === "/" || !path) {
|
2020-06-10 18:13:56 +02:00
|
|
|
return {
|
|
|
|
channel: undefined,
|
|
|
|
channelId: 0,
|
|
|
|
path: "/",
|
|
|
|
type: "root"
|
|
|
|
};
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (path.startsWith("/" + channelPathPrefix)) {
|
2020-06-10 18:13:56 +02:00
|
|
|
const pathParts = path.split("/");
|
|
|
|
|
|
|
|
const channelId = parseInt(pathParts[1].substr(channelPathPrefix.length));
|
2020-09-12 15:49:20 +02:00
|
|
|
if (isNaN(channelId)) {
|
2020-06-10 18:13:56 +02:00
|
|
|
throw tr("Invalid channel id (ID is NaN)");
|
|
|
|
}
|
|
|
|
|
|
|
|
const channel = connection.channelTree.findChannel(channelId);
|
2020-09-12 15:49:20 +02:00
|
|
|
if (!channel) {
|
2020-12-18 19:18:01 +01:00
|
|
|
throw tr("Invalid channel id");
|
2020-06-10 18:13:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
type: "channel",
|
|
|
|
path: "/" + pathParts.slice(2).join("/"),
|
|
|
|
channelId: channelId,
|
|
|
|
channel: channel
|
|
|
|
};
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (path == "/" + iconPathPrefix + "/") {
|
2020-06-10 18:13:56 +02:00
|
|
|
return {
|
|
|
|
type: "icon",
|
|
|
|
path: "/icons/",
|
|
|
|
channelId: 0,
|
|
|
|
channel: undefined
|
|
|
|
};
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (path == "/" + avatarsPathPrefix + "/") {
|
2020-06-10 18:13:56 +02:00
|
|
|
return {
|
|
|
|
type: "avatar",
|
|
|
|
path: "/",
|
|
|
|
channelId: 0,
|
|
|
|
channel: undefined
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
throw tr("Unknown path");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initializeRemoteFileBrowserController(connection: ConnectionHandler, events: Registry<FileBrowserEvents>) {
|
2021-03-23 15:31:59 +01:00
|
|
|
/* currently selected files */
|
|
|
|
let currentPath = "/";
|
|
|
|
let currentPathInfo: PathInfo;
|
|
|
|
let selection: { name: string, type: FileType }[] = [];
|
|
|
|
|
2020-06-10 18:13:56 +02:00
|
|
|
events.on("action_navigate_to", event => {
|
|
|
|
try {
|
|
|
|
|
2021-06-10 16:43:30 +02:00
|
|
|
currentPathInfo = parsePath(event.path, connection);
|
2021-03-23 15:31:59 +01:00
|
|
|
currentPath = event.path;
|
|
|
|
selection = [];
|
|
|
|
|
2020-12-18 19:18:01 +01:00
|
|
|
events.fire_react("notify_current_path", {
|
2020-06-10 18:13:56 +02:00
|
|
|
path: event.path || "/",
|
|
|
|
status: "success",
|
|
|
|
});
|
|
|
|
} catch (error) {
|
2020-12-18 19:18:01 +01:00
|
|
|
events.fire_react("notify_current_path", {
|
2020-06-10 18:13:56 +02:00
|
|
|
path: event.path,
|
|
|
|
status: "error",
|
|
|
|
error: error
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
events.on("query_files", event => {
|
|
|
|
let path: PathInfo;
|
|
|
|
try {
|
|
|
|
path = parsePath(event.path, connection);
|
|
|
|
} catch (error) {
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("query_files_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
path: event.path,
|
|
|
|
status: "error",
|
|
|
|
error: error
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2020-12-03 17:52:20 +01:00
|
|
|
logTrace(LogCategory.FILE_TRANSFER, tr("Requesting a file list for %o"), path);
|
|
|
|
|
2020-06-10 18:13:56 +02:00
|
|
|
let request: Promise<ListedFileInfo[]>;
|
2020-09-12 15:49:20 +02:00
|
|
|
if (path.type === "root") {
|
2020-06-10 18:13:56 +02:00
|
|
|
request = (async () => {
|
|
|
|
const result: ListedFileInfo[] = [];
|
|
|
|
|
|
|
|
result.push({
|
|
|
|
type: FileType.DIRECTORY,
|
|
|
|
name: iconPathPrefix,
|
|
|
|
size: 0,
|
|
|
|
datetime: 0,
|
|
|
|
mode: "normal",
|
|
|
|
virtual: true,
|
|
|
|
path: "/"
|
|
|
|
});
|
|
|
|
|
|
|
|
result.push({
|
|
|
|
type: FileType.DIRECTORY,
|
|
|
|
name: avatarsPathPrefix,
|
|
|
|
size: 0,
|
|
|
|
datetime: 0,
|
|
|
|
mode: "normal",
|
|
|
|
virtual: true,
|
|
|
|
path: "/"
|
|
|
|
});
|
|
|
|
|
|
|
|
const requestArray = connection.channelTree.channels.map(e => {
|
|
|
|
return {
|
|
|
|
request: {
|
|
|
|
path: "/",
|
|
|
|
channelId: e.channelId
|
|
|
|
},
|
|
|
|
name: channelPathPrefix + e.getChannelId(),
|
|
|
|
channel: e
|
|
|
|
}
|
|
|
|
});
|
|
|
|
const channelInfos = await connection.fileManager.requestFileInfo(requestArray.map(e => e.request));
|
2020-09-12 15:49:20 +02:00
|
|
|
for (let index = 0; index < requestArray.length; index++) {
|
2020-06-10 18:13:56 +02:00
|
|
|
const response = channelInfos[index];
|
|
|
|
|
2020-09-12 15:49:20 +02:00
|
|
|
if (response instanceof CommandResult) {
|
2020-12-03 12:09:36 +01:00
|
|
|
/* some kind of error occurred (maybe password set, or non existing) */
|
2020-06-10 18:13:56 +02:00
|
|
|
result.push({
|
|
|
|
type: FileType.DIRECTORY,
|
|
|
|
name: requestArray[index].name,
|
|
|
|
size: 0,
|
|
|
|
datetime: 0,
|
|
|
|
mode: requestArray[index].channel.properties.channel_flag_password ? "password" : "empty",
|
|
|
|
virtual: true,
|
|
|
|
path: "/"
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
result.push({
|
|
|
|
type: FileType.DIRECTORY,
|
|
|
|
name: requestArray[index].name,
|
|
|
|
size: 0,
|
|
|
|
datetime: 0,
|
|
|
|
mode: response.empty ? "empty" : "normal",
|
|
|
|
virtual: true,
|
|
|
|
path: "/"
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
})();
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (path.type === "channel") {
|
2020-06-10 18:13:56 +02:00
|
|
|
request = (async () => {
|
|
|
|
const hash = path.channel.properties.channel_flag_password ? await path.channel.requestChannelPassword(PermissionType.B_FT_IGNORE_PASSWORD) : undefined;
|
|
|
|
return connection.fileManager.requestFileList(path.path, path.channelId, hash?.hash).then(result => result.map(e => {
|
|
|
|
const transfer = connection.fileManager.findTransfer(path.channelId, path.path, e.name);
|
|
|
|
return {
|
|
|
|
datetime: e.datetime,
|
|
|
|
name: e.name,
|
|
|
|
size: e.size,
|
|
|
|
type: e.type,
|
|
|
|
path: event.path,
|
|
|
|
mode: e.empty ? "empty" : "normal",
|
|
|
|
virtual: false,
|
|
|
|
transfer: !transfer ? undefined : {
|
|
|
|
id: transfer.clientTransferId,
|
|
|
|
percent: transfer.isRunning() && transfer.lastProgressInfo() ? transfer.lastProgressInfo().file_current_offset / transfer.lastProgressInfo().file_total_size : 0,
|
|
|
|
status: transfer.isPending() ? "pending" : transfer.isRunning() ? "transferring" : "finished"
|
|
|
|
}
|
|
|
|
} as ListedFileInfo;
|
|
|
|
})).catch(async error => {
|
|
|
|
/* patch for the case that the channel directory hasn't been created yet */
|
2020-09-12 15:49:20 +02:00
|
|
|
if (error instanceof CommandResult) {
|
|
|
|
if (error.id === ErrorCode.FILE_NOT_FOUND && path.path === "/") {
|
2020-06-10 18:13:56 +02:00
|
|
|
return [];
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (error.id === 781) { //Invalid password
|
2020-06-10 18:13:56 +02:00
|
|
|
path.channel.invalidateCachedPassword();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw error;
|
|
|
|
});
|
|
|
|
})();
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (path.type === "icon" || path.type === "avatar") {
|
2020-06-10 18:13:56 +02:00
|
|
|
request = connection.fileManager.requestFileList(path.path, 0).then(result => result.map(e => {
|
|
|
|
return {
|
|
|
|
datetime: e.datetime,
|
|
|
|
name: e.name,
|
|
|
|
size: e.size,
|
|
|
|
type: e.type,
|
|
|
|
mode: e.empty ? "empty" : "normal",
|
|
|
|
path: event.path
|
|
|
|
} as ListedFileInfo;
|
|
|
|
}));
|
|
|
|
} else {
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("query_files_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
path: event.path,
|
|
|
|
status: "error",
|
|
|
|
error: tr("Unknown parsed path type")
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
request.then(files => {
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("query_files_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
path: event.path,
|
|
|
|
status: "success",
|
2020-09-12 15:49:20 +02:00
|
|
|
files: files.map(e => {
|
|
|
|
e.datetime *= 1000;
|
|
|
|
return e;
|
|
|
|
})
|
2020-06-10 18:13:56 +02:00
|
|
|
});
|
|
|
|
}).catch(error => {
|
|
|
|
let message;
|
2020-09-12 15:49:20 +02:00
|
|
|
if (error instanceof CommandResult) {
|
|
|
|
if (error.id === ErrorCode.SERVER_INSUFFICIENT_PERMISSIONS) {
|
2020-06-10 18:13:56 +02:00
|
|
|
const permission = connection.permissions.resolveInfo(error.json["failed_permid"] as number);
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("query_files_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
path: event.path,
|
|
|
|
status: "no-permissions",
|
|
|
|
error: permission ? permission.name : "unknown"
|
|
|
|
});
|
|
|
|
return;
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (error.id === 781) { //Invalid password
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("query_files_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
path: event.path,
|
|
|
|
status: "invalid-password"
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
message = error.message + (error.extra_message ? " (" + error.extra_message + ")" : "");
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (typeof error === "string") {
|
2020-06-10 18:13:56 +02:00
|
|
|
message = error;
|
|
|
|
} else {
|
2021-01-10 17:36:57 +01:00
|
|
|
logError(LogCategory.FILE_TRANSFER, tr("Failed to query channel directory files: %o"), error);
|
2020-06-10 18:13:56 +02:00
|
|
|
message = tr("lookup the console");
|
|
|
|
}
|
|
|
|
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("query_files_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
path: event.path,
|
|
|
|
status: "error",
|
|
|
|
error: message
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
events.on("action_rename_file", event => {
|
2020-09-12 15:49:20 +02:00
|
|
|
if (event.newPath === event.oldPath && event.newName === event.oldName) {
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_rename_file_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
oldPath: event.oldPath,
|
|
|
|
oldName: event.oldName,
|
|
|
|
|
|
|
|
newPath: event.newPath,
|
|
|
|
newName: event.newName,
|
|
|
|
|
|
|
|
status: "no-changes"
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let sourcePath: PathInfo, targetPath: PathInfo;
|
|
|
|
try {
|
|
|
|
sourcePath = parsePath(event.oldPath, connection);
|
2020-12-18 19:18:01 +01:00
|
|
|
if (sourcePath.type !== "channel") {
|
2020-06-10 18:13:56 +02:00
|
|
|
throw tr("Icon/avatars could not be renamed");
|
2020-12-18 19:18:01 +01:00
|
|
|
}
|
2020-06-10 18:13:56 +02:00
|
|
|
} catch (error) {
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_rename_file_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
oldPath: event.oldPath,
|
|
|
|
oldName: event.oldName,
|
|
|
|
status: "error",
|
|
|
|
error: tr("Invalid source path") + " (" + error + ")"
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
targetPath = parsePath(event.newPath, connection);
|
2020-12-18 19:18:01 +01:00
|
|
|
if (sourcePath.type !== "channel") {
|
2020-06-10 18:13:56 +02:00
|
|
|
throw tr("Target path isn't a channel");
|
2020-12-18 19:18:01 +01:00
|
|
|
}
|
2020-06-10 18:13:56 +02:00
|
|
|
} catch (error) {
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_rename_file_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
oldPath: event.oldPath,
|
|
|
|
oldName: event.oldName,
|
|
|
|
status: "error",
|
|
|
|
error: tr("Invalid target path") + " (" + error + ")"
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
(async () => {
|
|
|
|
const sourcePassword = sourcePath.channel.properties.channel_flag_password ? await sourcePath.channel.requestChannelPassword(PermissionType.B_FT_IGNORE_PASSWORD) : undefined;
|
|
|
|
const targetPassword = targetPath.channel.properties.channel_flag_password ? await targetPath.channel.requestChannelPassword(PermissionType.B_FT_IGNORE_PASSWORD) : undefined;
|
|
|
|
return await connection.serverConnection.send_command("ftrenamefile", {
|
|
|
|
cid: sourcePath.channelId,
|
|
|
|
cpw: sourcePassword,
|
|
|
|
tcid: targetPath.channelId,
|
|
|
|
tcpw: targetPassword,
|
|
|
|
oldname: sourcePath.path + event.oldName,
|
|
|
|
newname: targetPath.path + event.newName
|
|
|
|
})
|
|
|
|
})().then(result => {
|
2020-09-12 15:49:20 +02:00
|
|
|
if (result.id !== 0)
|
2020-06-10 18:13:56 +02:00
|
|
|
throw result;
|
|
|
|
|
|
|
|
events.fire("action_rename_file_result", {
|
|
|
|
oldPath: event.oldPath,
|
|
|
|
oldName: event.oldName,
|
|
|
|
status: "success",
|
|
|
|
|
|
|
|
newName: event.newName,
|
|
|
|
newPath: event.newPath
|
|
|
|
});
|
|
|
|
}).catch(error => {
|
|
|
|
let message;
|
2020-09-12 15:49:20 +02:00
|
|
|
if (error instanceof CommandResult) {
|
|
|
|
if (error.id === ErrorCode.SERVER_INSUFFICIENT_PERMISSIONS) {
|
2020-06-10 18:13:56 +02:00
|
|
|
const permission = connection.permissions.resolveInfo(error.json["failed_permid"] as number);
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_rename_file_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
oldPath: event.oldPath,
|
|
|
|
oldName: event.oldName,
|
|
|
|
status: "error",
|
|
|
|
error: tr("Failed on permission ") + (permission ? permission.name : "unknown")
|
|
|
|
});
|
|
|
|
return;
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (error.id === 781) { //Invalid password
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_rename_file_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
oldPath: event.oldPath,
|
|
|
|
oldName: event.oldName,
|
|
|
|
status: "error",
|
|
|
|
error: tr("Invalid channel password")
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
message = error.message + (error.extra_message ? " (" + error.extra_message + ")" : "");
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (typeof error === "string") {
|
2020-06-10 18:13:56 +02:00
|
|
|
message = error;
|
|
|
|
} else {
|
2021-01-10 17:36:57 +01:00
|
|
|
logError(LogCategory.FILE_TRANSFER, tr("Failed to rename/move files: %o"), error);
|
2020-06-10 18:13:56 +02:00
|
|
|
message = tr("lookup the console");
|
|
|
|
}
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_rename_file_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
oldPath: event.oldPath,
|
|
|
|
oldName: event.oldName,
|
|
|
|
status: "error",
|
|
|
|
error: message
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-12-18 19:18:01 +01:00
|
|
|
events.on("query_current_path", () => events.fire_react("notify_current_path", {
|
|
|
|
status: "success",
|
|
|
|
path: currentPath,
|
|
|
|
}));
|
|
|
|
|
2020-06-10 18:13:56 +02:00
|
|
|
events.on("action_rename_file_result", result => {
|
2020-09-12 15:49:20 +02:00
|
|
|
if (result.status !== "success")
|
2020-06-10 18:13:56 +02:00
|
|
|
return;
|
2020-09-12 15:49:20 +02:00
|
|
|
if (result.oldPath !== currentPath)
|
2020-06-10 18:13:56 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
const index = selection.map(e => e.name).findIndex(e => e === result.oldName);
|
2020-09-12 15:49:20 +02:00
|
|
|
if (index !== -1)
|
2020-06-10 18:13:56 +02:00
|
|
|
selection[index].name = result.newName;
|
|
|
|
});
|
|
|
|
|
|
|
|
events.on("action_select_files", event => {
|
2020-09-12 15:49:20 +02:00
|
|
|
if (event.mode === "exclusive") {
|
2020-06-10 18:13:56 +02:00
|
|
|
selection = event.files.slice(0);
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (event.mode === "toggle") {
|
2020-06-10 18:13:56 +02:00
|
|
|
event.files.forEach(e => {
|
|
|
|
const index = selection.map(e => e.name).findIndex(b => b === e.name);
|
2020-09-12 15:49:20 +02:00
|
|
|
if (index === -1)
|
2020-06-10 18:13:56 +02:00
|
|
|
selection.push(e);
|
|
|
|
else
|
|
|
|
selection.splice(index);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/* the selection handler */
|
|
|
|
events.on("action_selection_context_menu", event => {
|
|
|
|
const entries = [] as MenuEntry[];
|
|
|
|
|
2020-09-12 15:49:20 +02:00
|
|
|
if (currentPathInfo.type === "root") {
|
2020-06-10 18:13:56 +02:00
|
|
|
entries.push({
|
|
|
|
type: MenuEntryType.ENTRY,
|
|
|
|
name: tr("Refresh file list"),
|
|
|
|
icon_class: "client-file_refresh"
|
|
|
|
});
|
|
|
|
} else {
|
2021-03-18 18:25:20 +01:00
|
|
|
const forceDelete = getKeyBoard().isKeyPressed(SpecialKey.SHIFT);
|
2020-09-12 15:49:20 +02:00
|
|
|
if (selection.length === 0) {
|
2020-06-10 18:13:56 +02:00
|
|
|
entries.push({
|
|
|
|
type: MenuEntryType.ENTRY,
|
|
|
|
name: tr("Upload"),
|
|
|
|
icon_class: "client-upload",
|
2020-09-12 15:49:20 +02:00
|
|
|
callback: () => events.fire("action_start_upload", {mode: "browse", path: currentPath})
|
2020-06-10 18:13:56 +02:00
|
|
|
});
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (selection.length === 1) {
|
2020-06-10 18:13:56 +02:00
|
|
|
const file = selection[0];
|
2020-09-12 15:49:20 +02:00
|
|
|
if (file.type === FileType.FILE) {
|
2020-06-10 18:13:56 +02:00
|
|
|
entries.push({
|
|
|
|
type: MenuEntryType.ENTRY,
|
|
|
|
name: tr("Download"),
|
|
|
|
icon_class: "client-download",
|
2020-09-12 15:49:20 +02:00
|
|
|
callback: () => events.fire("action_start_download", {
|
|
|
|
files: [{
|
|
|
|
name: file.name,
|
|
|
|
path: currentPath
|
|
|
|
}]
|
|
|
|
})
|
2020-06-10 18:13:56 +02:00
|
|
|
});
|
|
|
|
}
|
2020-09-12 15:49:20 +02:00
|
|
|
if (currentPathInfo.type === "channel") {
|
2020-06-10 18:13:56 +02:00
|
|
|
entries.push({
|
|
|
|
type: MenuEntryType.ENTRY,
|
|
|
|
name: tr("Rename"),
|
|
|
|
icon_class: "client-change_nickname",
|
2020-09-12 15:49:20 +02:00
|
|
|
callback: () => events.fire("action_start_rename", {name: file.name, path: currentPath})
|
2020-06-10 18:13:56 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
entries.push({
|
|
|
|
type: MenuEntryType.ENTRY,
|
|
|
|
name: forceDelete ? tr("Force delete file") : tr("Delete file"),
|
|
|
|
icon_class: "client-delete",
|
2020-09-12 15:49:20 +02:00
|
|
|
callback: () => events.fire("action_delete_file", {
|
|
|
|
mode: forceDelete ? "force" : "ask",
|
|
|
|
files: "selection"
|
|
|
|
})
|
2020-06-10 18:13:56 +02:00
|
|
|
});
|
|
|
|
entries.push(Entry.HR());
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (selection.length > 1) {
|
|
|
|
if (selection.findIndex(e => e.type === FileType.DIRECTORY) === -1) {
|
2020-06-10 18:13:56 +02:00
|
|
|
entries.push({
|
|
|
|
type: MenuEntryType.ENTRY,
|
|
|
|
name: tr("Download"),
|
|
|
|
icon_class: "client-download",
|
2020-09-12 15:49:20 +02:00
|
|
|
callback: () => events.fire("action_start_download", {
|
|
|
|
files: selection.map(file => {
|
|
|
|
return {name: file.name, path: currentPath}
|
|
|
|
})
|
|
|
|
})
|
2020-06-10 18:13:56 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
entries.push({
|
|
|
|
type: MenuEntryType.ENTRY,
|
|
|
|
name: forceDelete ? tr("Force delete files") : tr("Delete files"),
|
|
|
|
icon_class: "client-delete",
|
2020-09-12 15:49:20 +02:00
|
|
|
callback: () => events.fire("action_delete_file", {
|
|
|
|
mode: forceDelete ? "force" : "ask",
|
|
|
|
files: "selection"
|
|
|
|
})
|
2020-06-10 18:13:56 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
entries.push({
|
|
|
|
type: MenuEntryType.ENTRY,
|
|
|
|
name: tr("Refresh file list"),
|
|
|
|
icon_class: "client-file_refresh",
|
2020-09-12 15:49:20 +02:00
|
|
|
callback: () => events.fire("action_navigate_to", {path: currentPath})
|
2020-06-10 18:13:56 +02:00
|
|
|
});
|
|
|
|
entries.push(Entry.HR());
|
|
|
|
entries.push({
|
|
|
|
type: MenuEntryType.ENTRY,
|
|
|
|
name: tr("Create folder"),
|
|
|
|
icon_class: "client-add_folder",
|
2020-09-12 15:49:20 +02:00
|
|
|
callback: () => events.fire("action_start_create_directory", {defaultName: tr("New folder")})
|
2020-06-10 18:13:56 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
spawn_context_menu(event.pageX, event.pageY, ...entries);
|
|
|
|
});
|
|
|
|
|
|
|
|
events.on("action_delete_file", event => {
|
2020-09-12 15:49:20 +02:00
|
|
|
const files = event.files === "selection" ? selection.map(e => {
|
|
|
|
return {path: currentPath, name: e.name}
|
|
|
|
}) : event.files;
|
2020-06-10 18:13:56 +02:00
|
|
|
|
2020-09-12 15:49:20 +02:00
|
|
|
if (event.mode === "ask") {
|
2021-04-24 14:52:08 +02:00
|
|
|
promptYesNo({
|
|
|
|
title: tr("Are you sure?"),
|
|
|
|
question: tra("Do you really want to delete {0} {1}?", files.length, files.length === 1 ? tr("files") : tr("files")),
|
|
|
|
}).then(result => {
|
|
|
|
if(!result) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
events.fire("action_delete_file", {
|
|
|
|
files: files,
|
|
|
|
mode: "force"
|
|
|
|
});
|
2020-06-10 18:13:56 +02:00
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2020-09-12 15:49:20 +02:00
|
|
|
const fileInfos = files.map(e => {
|
|
|
|
return {info: parsePath(e.path, connection), path: e.path, name: e.name}
|
|
|
|
});
|
|
|
|
|
|
|
|
connection.serverConnection.send_command("ftdeletefile", fileInfos.map(e => {
|
|
|
|
return {
|
|
|
|
path: e.info.path,
|
|
|
|
cid: e.info.channelId,
|
2021-02-20 16:57:52 +01:00
|
|
|
cpw: e.info.channel?.getCachedPasswordHash(),
|
2020-09-12 15:49:20 +02:00
|
|
|
name: e.name
|
|
|
|
}
|
|
|
|
})).then(async result => {
|
2020-06-10 18:13:56 +02:00
|
|
|
throw result;
|
|
|
|
}).catch(result => {
|
|
|
|
let message;
|
2020-09-12 15:49:20 +02:00
|
|
|
if (result instanceof CommandResult) {
|
|
|
|
if (result.bulks.length !== fileInfos.length) {
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_delete_file_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
results: fileInfos.map((e) => {
|
|
|
|
return {
|
|
|
|
error: result.bulks.length === 1 ? (result.message + (result.extra_message ? " (" + result.extra_message + ")" : "")) : tr("Response contained invalid bulk length"),
|
|
|
|
path: e.path,
|
|
|
|
name: e.name,
|
|
|
|
status: "error"
|
|
|
|
};
|
|
|
|
})
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let results = [];
|
|
|
|
result.getBulks().forEach((e, index) => {
|
2020-09-12 15:49:20 +02:00
|
|
|
if (e.id === ErrorCode.SERVER_INSUFFICIENT_PERMISSIONS) {
|
2020-06-10 18:13:56 +02:00
|
|
|
const permission = connection.permissions.resolveInfo(e.json["failed_permid"] as number);
|
|
|
|
results.push({
|
|
|
|
path: fileInfos[index].path,
|
|
|
|
name: fileInfos[index].name,
|
|
|
|
status: "error",
|
|
|
|
error: tr("Failed on permission ") + (permission ? permission.name : "unknown")
|
|
|
|
});
|
|
|
|
return;
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (e.id === 781) { //Invalid password
|
2020-06-10 18:13:56 +02:00
|
|
|
results.push({
|
|
|
|
path: fileInfos[index].path,
|
|
|
|
name: fileInfos[index].name,
|
|
|
|
status: "error",
|
|
|
|
error: tr("Invalid channel password")
|
|
|
|
});
|
|
|
|
return;
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (e.id !== 0) {
|
2020-06-10 18:13:56 +02:00
|
|
|
results.push({
|
|
|
|
path: fileInfos[index].path,
|
|
|
|
name: fileInfos[index].name,
|
|
|
|
status: "error",
|
|
|
|
error: e.message + (e.extra_message ? " (" + e.extra_message + ")" : "")
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
results.push({
|
|
|
|
path: fileInfos[index].path,
|
|
|
|
name: fileInfos[index].name,
|
|
|
|
status: "success"
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
});
|
|
|
|
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_delete_file_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
results: results
|
|
|
|
});
|
|
|
|
return;
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (typeof result === "string") {
|
2020-06-10 18:13:56 +02:00
|
|
|
message = result;
|
|
|
|
} else {
|
2021-01-10 17:36:57 +01:00
|
|
|
logError(LogCategory.FILE_TRANSFER, tr("Failed to create directory: %o"), result);
|
2020-06-10 18:13:56 +02:00
|
|
|
message = tr("lookup the console");
|
|
|
|
}
|
|
|
|
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_delete_file_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
results: files.map((e) => {
|
|
|
|
return {
|
|
|
|
error: message,
|
|
|
|
path: e.path,
|
|
|
|
name: e.name,
|
|
|
|
status: "error"
|
|
|
|
};
|
|
|
|
})
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} catch (error) {
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_delete_file_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
results: files.map((e) => {
|
|
|
|
return {
|
|
|
|
error: tr("Failed to parse path for one or more entries ") + " (" + error + ")",
|
|
|
|
path: e.path,
|
|
|
|
name: e.name,
|
|
|
|
status: "error"
|
|
|
|
};
|
|
|
|
})
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
events.on("action_create_directory", event => {
|
|
|
|
let path: PathInfo;
|
|
|
|
try {
|
|
|
|
path = parsePath(event.path, connection);
|
2020-09-12 15:49:20 +02:00
|
|
|
if (path.type !== "channel")
|
2020-06-10 18:13:56 +02:00
|
|
|
throw tr("Directories could only created for channels");
|
|
|
|
} catch (error) {
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_create_directory_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
name: event.name,
|
|
|
|
path: event.path,
|
|
|
|
status: "error",
|
|
|
|
error: tr("Invalid path") + " (" + error + ")"
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//ftcreatedir cid=4 cpw dirname=\/TestDir return_code=1:17
|
|
|
|
connection.serverConnection.send_command("ftcreatedir", {
|
|
|
|
cid: path.channelId,
|
2021-02-20 16:57:52 +01:00
|
|
|
cpw: path.channel.getCachedPasswordHash(),
|
2020-06-10 18:13:56 +02:00
|
|
|
dirname: path.path + event.name
|
|
|
|
}).then(() => {
|
2020-09-12 15:49:20 +02:00
|
|
|
events.fire("action_create_directory_result", {path: event.path, name: event.name, status: "success"});
|
2020-06-10 18:13:56 +02:00
|
|
|
}).catch(error => {
|
|
|
|
let message;
|
2020-09-12 15:49:20 +02:00
|
|
|
if (error instanceof CommandResult) {
|
|
|
|
if (error.id === ErrorCode.SERVER_INSUFFICIENT_PERMISSIONS) {
|
2020-06-10 18:13:56 +02:00
|
|
|
const permission = connection.permissions.resolveInfo(error.json["failed_permid"] as number);
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_create_directory_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
name: event.name,
|
|
|
|
path: event.path,
|
|
|
|
status: "error",
|
|
|
|
error: tr("Failed on permission ") + (permission ? permission.name : "unknown")
|
|
|
|
});
|
|
|
|
return;
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (error.id === 781) { //Invalid password
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_create_directory_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
name: event.name,
|
|
|
|
path: event.path,
|
|
|
|
status: "error",
|
|
|
|
error: tr("Invalid channel password")
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
message = error.message + (error.extra_message ? " (" + error.extra_message + ")" : "");
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (typeof error === "string") {
|
2020-06-10 18:13:56 +02:00
|
|
|
message = error;
|
|
|
|
} else {
|
2021-01-10 17:36:57 +01:00
|
|
|
logError(LogCategory.FILE_TRANSFER, tr("Failed to create directory: %o"), error);
|
2020-06-10 18:13:56 +02:00
|
|
|
message = tr("lookup the console");
|
|
|
|
}
|
2020-11-07 13:16:07 +01:00
|
|
|
events.fire_react("action_create_directory_result", {
|
2020-06-10 18:13:56 +02:00
|
|
|
name: event.name,
|
|
|
|
path: event.path,
|
|
|
|
status: "error",
|
|
|
|
error: message
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-06-11 10:20:00 +02:00
|
|
|
events.on("action_start_download", async event => {
|
|
|
|
for(const file of event.files) {
|
2020-06-10 18:13:56 +02:00
|
|
|
try {
|
2020-06-10 22:44:50 +02:00
|
|
|
let targetSupplier;
|
2020-09-12 15:49:20 +02:00
|
|
|
if (__build.target === "client" && TransferProvider.provider().targetSupported(TransferTargetType.FILE)) {
|
2021-06-11 10:20:00 +02:00
|
|
|
/* Get the target file path before we're actiually starting to download the file */
|
|
|
|
const target = await TransferProvider.provider().createFileTarget(undefined, file.name);
|
2020-06-10 22:44:50 +02:00
|
|
|
targetSupplier = async () => target;
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (TransferProvider.provider().targetSupported(TransferTargetType.DOWNLOAD)) {
|
2020-06-10 22:44:50 +02:00
|
|
|
targetSupplier = async () => await TransferProvider.provider().createDownloadTarget();
|
|
|
|
} else {
|
|
|
|
createErrorModal(tr("Failed to create transfer target"), tr("Failed to create transfer target.\nAll targets are unsupported")).open();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-10 18:13:56 +02:00
|
|
|
const fileName = file.name;
|
|
|
|
const info = parsePath(file.path, connection);
|
|
|
|
const transfer = connection.fileManager.initializeFileDownload({
|
|
|
|
channel: info.channelId,
|
|
|
|
path: info.type === "channel" ? info.path : "",
|
|
|
|
name: info.type === "channel" ? file.name : "/" + file.name,
|
2021-02-20 16:57:52 +01:00
|
|
|
channelPassword: info.channel?.getCachedPasswordHash(),
|
2020-06-10 22:44:50 +02:00
|
|
|
targetSupplier: targetSupplier
|
2020-06-10 18:13:56 +02:00
|
|
|
});
|
|
|
|
transfer.awaitFinished().then(() => {
|
2020-09-12 15:49:20 +02:00
|
|
|
if (transfer.transferState() === FileTransferState.ERRORED) {
|
2021-04-24 13:59:49 +02:00
|
|
|
createErrorModal(tr("Failed to download file"), tra("Failed to download {0}:\n{1}", fileName, transfer.currentErrorMessage())).open();
|
2020-06-10 18:13:56 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (error) {
|
2021-01-10 17:36:57 +01:00
|
|
|
logError(LogCategory.FILE_TRANSFER, tr("Failed to parse path for file download: %s"), error);
|
2020-06-10 18:13:56 +02:00
|
|
|
}
|
2021-06-11 10:20:00 +02:00
|
|
|
}
|
2020-06-10 18:13:56 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
events.on("action_start_upload", event => {
|
2020-09-12 15:49:20 +02:00
|
|
|
if (event.mode === "browse") {
|
2020-06-10 18:13:56 +02:00
|
|
|
const input = document.createElement("input");
|
|
|
|
input.type = "file";
|
|
|
|
input.multiple = true;
|
|
|
|
|
|
|
|
document.body.appendChild(input);
|
|
|
|
input.onchange = () => {
|
2020-09-12 15:49:20 +02:00
|
|
|
if ((input.files?.length | 0) === 0)
|
2020-06-10 18:13:56 +02:00
|
|
|
return;
|
|
|
|
|
2020-09-12 15:49:20 +02:00
|
|
|
events.fire("action_start_upload", {mode: "files", path: event.path, files: [...input.files]});
|
2020-06-10 18:13:56 +02:00
|
|
|
};
|
|
|
|
input.onblur = () => input.remove();
|
|
|
|
setTimeout(() => {
|
2020-09-12 15:49:20 +02:00
|
|
|
input.focus({preventScroll: true});
|
2020-06-10 18:13:56 +02:00
|
|
|
input.click();
|
|
|
|
});
|
|
|
|
return;
|
2020-09-12 15:49:20 +02:00
|
|
|
} else if (event.mode === "files") {
|
2020-06-10 18:13:56 +02:00
|
|
|
const pathInfo = parsePath(event.path, connection);
|
2020-09-12 15:49:20 +02:00
|
|
|
if (pathInfo.type !== "channel") {
|
2021-04-24 13:59:49 +02:00
|
|
|
createErrorModal(tr("Failed to upload file(s)"), tra("Failed to upload files:\nFile uplaod is only supported in channel directories")).open();
|
2020-06-10 18:13:56 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-09-12 15:49:20 +02:00
|
|
|
for (const file of event.files) {
|
2020-06-10 18:13:56 +02:00
|
|
|
const fileName = file.name;
|
|
|
|
const transfer = connection.fileManager.initializeFileUpload({
|
|
|
|
channel: pathInfo.channelId,
|
2021-02-20 16:57:52 +01:00
|
|
|
channelPassword: pathInfo.channel?.getCachedPasswordHash(),
|
2020-06-10 18:13:56 +02:00
|
|
|
name: file.name,
|
|
|
|
path: pathInfo.path,
|
|
|
|
source: async () => TransferProvider.provider().createBrowserFileSource(file)
|
|
|
|
});
|
|
|
|
transfer.awaitFinished().then(() => {
|
2020-09-12 15:49:20 +02:00
|
|
|
if (transfer.transferState() === FileTransferState.ERRORED) {
|
2021-04-24 13:59:49 +02:00
|
|
|
createErrorModal(tr("Failed to upload file"), tra("Failed to upload {0}:\n{1}", fileName, transfer.currentErrorMessage())).open();
|
2020-06-10 18:13:56 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/* transfer status listener */
|
|
|
|
{
|
|
|
|
const listenToTransfer = (transfer: FileTransfer) => {
|
|
|
|
/* We've currently only support for channel files */
|
2020-09-12 15:49:20 +02:00
|
|
|
if (transfer.properties.channel_id === 0)
|
2020-06-10 18:13:56 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
const progressListener = event => events.fire("notify_transfer_progress", {
|
|
|
|
id: transfer.clientTransferId,
|
|
|
|
progress: event.progress.file_current_offset / event.progress.file_total_size,
|
|
|
|
status: "transferring",
|
|
|
|
fileSize: event.progress.file_current_offset
|
|
|
|
});
|
|
|
|
|
|
|
|
transfer.events.on("notify_progress", progressListener);
|
|
|
|
transfer.events.on("notify_state_updated", () => {
|
|
|
|
switch (transfer.transferState()) {
|
|
|
|
case FileTransferState.INITIALIZING:
|
|
|
|
case FileTransferState.PENDING:
|
|
|
|
case FileTransferState.CONNECTING:
|
|
|
|
events.fire("notify_transfer_status", {id: transfer.clientTransferId, status: "pending"});
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FileTransferState.RUNNING:
|
2020-09-12 15:49:20 +02:00
|
|
|
events.fire("notify_transfer_status", {id: transfer.clientTransferId, status: "transferring"});
|
2020-06-10 18:13:56 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FileTransferState.FINISHED:
|
|
|
|
case FileTransferState.CANCELED:
|
2020-09-12 15:49:20 +02:00
|
|
|
events.fire("notify_transfer_status", {
|
|
|
|
id: transfer.clientTransferId,
|
|
|
|
status: "finished",
|
|
|
|
fileSize: transfer.transferProperties().fileSize
|
|
|
|
});
|
2020-06-10 18:13:56 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FileTransferState.ERRORED:
|
2020-09-12 15:49:20 +02:00
|
|
|
events.fire("notify_transfer_status", {id: transfer.clientTransferId, status: "errored"});
|
2020-06-10 18:13:56 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-09-12 15:49:20 +02:00
|
|
|
if (transfer.isFinished()) {
|
2020-06-10 18:13:56 +02:00
|
|
|
unregisterEvents();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
events.fire("notify_transfer_start", {
|
|
|
|
id: transfer.clientTransferId,
|
|
|
|
name: transfer.properties.name,
|
|
|
|
path: "/" + channelPathPrefix + transfer.properties.channel_id + transfer.properties.path,
|
|
|
|
mode: transfer instanceof FileUploadTransfer ? "upload" : "download"
|
|
|
|
});
|
|
|
|
|
|
|
|
const closeListener = () => unregisterEvents();
|
2020-12-18 19:18:01 +01:00
|
|
|
events.on("notify_destroy", closeListener);
|
2020-06-10 18:13:56 +02:00
|
|
|
|
|
|
|
const unregisterEvents = () => {
|
2020-12-18 19:18:01 +01:00
|
|
|
events.off("notify_destroy", closeListener);
|
2020-06-10 18:13:56 +02:00
|
|
|
transfer.events.off("notify_progress", progressListener);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const registeredListener = event => listenToTransfer(event.transfer);
|
|
|
|
connection.fileManager.events.on("notify_transfer_registered", registeredListener);
|
2020-12-18 19:18:01 +01:00
|
|
|
events.on("notify_destroy", () => connection.fileManager.events.off("notify_transfer_registered", registeredListener));
|
2020-06-10 18:13:56 +02:00
|
|
|
|
|
|
|
connection.fileManager.registeredTransfers().forEach(transfer => listenToTransfer(transfer));
|
|
|
|
}
|
|
|
|
}
|