224 lines
No EOL
6 KiB
TypeScript
224 lines
No EOL
6 KiB
TypeScript
import {spawnReactModal} from "tc-shared/ui/react-elements/Modal";
|
|
import * as React from "react";
|
|
import {FileType} from "tc-shared/file/FileManager";
|
|
import {Registry} from "tc-shared/events";
|
|
import {FileBrowser, NavigationBar} from "tc-shared/ui/modal/transfer/FileBrowser";
|
|
import {TransferInfo, TransferInfoEvents} from "tc-shared/ui/modal/transfer/TransferInfo";
|
|
import {initializeRemoteFileBrowserController} from "tc-shared/ui/modal/transfer/RemoteFileBrowserController";
|
|
import {ChannelEntry} from "tc-shared/tree/Channel";
|
|
import {initializeTransferInfoController} from "tc-shared/ui/modal/transfer/TransferInfoController";
|
|
import {Translatable} from "tc-shared/ui/react-elements/i18n";
|
|
import {InternalModal} from "tc-shared/ui/react-elements/internal-modal/Controller";
|
|
import {server_connections} from "tc-shared/ConnectionManager";
|
|
|
|
const cssStyle = require("./ModalFileTransfer.scss");
|
|
export const channelPathPrefix = tr("Channel") + " ";
|
|
export const iconPathPrefix = tr("Icons");
|
|
export const avatarsPathPrefix = tr("Avatars");
|
|
export const FileTransferUrlMediaType = "application/x-teaspeak-ft-urls";
|
|
|
|
export type TransferStatus = "pending" | "transferring" | "finished" | "errored" | "none";
|
|
export type FileMode = "password" | "empty" | "create" | "creating" | "normal" | "uploading";
|
|
|
|
export type ListedFileInfo = {
|
|
path: string;
|
|
name: string;
|
|
type: FileType;
|
|
|
|
datetime: number;
|
|
size: number;
|
|
|
|
virtual: boolean;
|
|
mode: FileMode;
|
|
|
|
transfer?: {
|
|
id: number;
|
|
direction: "upload" | "download";
|
|
status: TransferStatus;
|
|
percent: number;
|
|
} | undefined
|
|
};
|
|
|
|
export type PathInfo = {
|
|
channelId: number;
|
|
channel: ChannelEntry;
|
|
|
|
path: string;
|
|
type: "icon" | "avatar" | "channel" | "root";
|
|
}
|
|
|
|
export interface FileBrowserEvents {
|
|
query_files: { path: string },
|
|
query_files_result: {
|
|
path: string,
|
|
status: "success" | "timeout" | "error" | "no-permissions" | "invalid-password",
|
|
|
|
error?: string,
|
|
files?: ListedFileInfo[]
|
|
},
|
|
|
|
action_navigate_to: {
|
|
path: string
|
|
},
|
|
action_navigate_to_result: {
|
|
path: string,
|
|
status: "success" | "timeout" | "error";
|
|
error?: string;
|
|
pathInfo?: PathInfo
|
|
}
|
|
|
|
action_delete_file: {
|
|
files: {
|
|
path: string,
|
|
name: string
|
|
}[] | "selection";
|
|
mode: "force" | "ask";
|
|
},
|
|
action_delete_file_result: {
|
|
results: {
|
|
path: string,
|
|
name: string,
|
|
status: "success" | "timeout" | "error";
|
|
error?: string;
|
|
}[],
|
|
},
|
|
|
|
action_start_create_directory: {
|
|
defaultName: string
|
|
},
|
|
action_create_directory: {
|
|
path: string,
|
|
name: string
|
|
},
|
|
action_create_directory_result: {
|
|
path: string,
|
|
name: string,
|
|
status: "success" | "timeout" | "error";
|
|
|
|
error?: string;
|
|
},
|
|
|
|
action_rename_file: {
|
|
oldPath: string,
|
|
oldName: string,
|
|
|
|
newPath: string;
|
|
newName: string
|
|
},
|
|
action_rename_file_result: {
|
|
oldPath: string,
|
|
oldName: string,
|
|
status: "success" | "timeout" | "error" | "no-changes";
|
|
|
|
newPath?: string,
|
|
newName?: string,
|
|
error?: string;
|
|
},
|
|
|
|
action_start_rename: {
|
|
path: string;
|
|
name: string;
|
|
},
|
|
|
|
action_select_files: {
|
|
files: {
|
|
name: string,
|
|
type: FileType
|
|
}[]
|
|
mode: "exclusive" | "toggle"
|
|
},
|
|
action_selection_context_menu: {
|
|
pageX: number,
|
|
pageY: number
|
|
},
|
|
|
|
action_start_download: {
|
|
files: {
|
|
path: string,
|
|
name: string
|
|
}[]
|
|
},
|
|
action_start_upload: {
|
|
path: string;
|
|
mode: "files" | "browse";
|
|
|
|
files?: File[];
|
|
},
|
|
|
|
notify_transfer_start: {
|
|
path: string;
|
|
name: string;
|
|
|
|
id: number;
|
|
mode: "upload" | "download";
|
|
},
|
|
|
|
notify_transfer_status: {
|
|
id: number;
|
|
status: TransferStatus;
|
|
fileSize?: number;
|
|
},
|
|
notify_transfer_progress: {
|
|
id: number;
|
|
progress: number;
|
|
fileSize: number;
|
|
status: TransferStatus
|
|
}
|
|
|
|
|
|
notify_modal_closed: {},
|
|
notify_drag_ended: {},
|
|
|
|
/* Attention: Only use in sync mode! */
|
|
notify_drag_started: {
|
|
event: DragEvent
|
|
}
|
|
}
|
|
|
|
|
|
class FileTransferModal extends InternalModal {
|
|
readonly remoteBrowseEvents = new Registry<FileBrowserEvents>();
|
|
readonly transferInfoEvents = new Registry<TransferInfoEvents>();
|
|
|
|
private readonly defaultChannelId;
|
|
|
|
constructor(defaultChannelId: number) {
|
|
super();
|
|
|
|
this.defaultChannelId = defaultChannelId;
|
|
|
|
this.remoteBrowseEvents.enableDebug("remote-file-browser");
|
|
this.transferInfoEvents.enableDebug("transfer-info");
|
|
|
|
initializeRemoteFileBrowserController(server_connections.active_connection(), this.remoteBrowseEvents);
|
|
initializeTransferInfoController(server_connections.active_connection(), this.transferInfoEvents);
|
|
}
|
|
|
|
protected onInitialize() {
|
|
const path = this.defaultChannelId ? "/" + channelPathPrefix + this.defaultChannelId + "/" : "/";
|
|
this.remoteBrowseEvents.fire("action_navigate_to", {path: path});
|
|
}
|
|
|
|
protected onDestroy() {
|
|
this.remoteBrowseEvents.fire("notify_modal_closed");
|
|
this.transferInfoEvents.fire("notify_modal_closed");
|
|
}
|
|
|
|
title() {
|
|
return <Translatable>File Browser</Translatable>;
|
|
}
|
|
|
|
renderBody() {
|
|
const path = this.defaultChannelId ? "/" + channelPathPrefix + this.defaultChannelId + "/" : "/";
|
|
return <div className={cssStyle.container}>
|
|
<NavigationBar events={this.remoteBrowseEvents} currentPath={path}/>
|
|
<FileBrowser events={this.remoteBrowseEvents} currentPath={path}/>
|
|
<TransferInfo events={this.transferInfoEvents}/>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
export function spawnFileTransferModal(channel: number) {
|
|
const modal = spawnReactModal(FileTransferModal, channel);
|
|
modal.show();
|
|
} |