From e23fc0a2d9e5360956ffbde40e0ae088848460a5 Mon Sep 17 00:00:00 2001 From: hexxa Date: Mon, 13 Dec 2021 14:21:51 +0800 Subject: [PATCH] fix(fe/errors): introduce errors and make functions return status --- src/client/web/src/components/browser.tsx | 964 ------------------ .../web/src/components/layout/table.tsx | 59 +- src/client/web/src/components/panel_files.tsx | 8 +- .../web/src/components/panel_uploadings.tsx | 24 +- .../web/src/components/state_updater.ts | 41 +- src/client/web/src/i18n/en_US.ts | 2 + src/client/web/src/i18n/zh_CN.ts | 2 + src/client/web/src/worker/upload_mgr.ts | 37 +- 8 files changed, 67 insertions(+), 1070 deletions(-) delete mode 100644 src/client/web/src/components/browser.tsx diff --git a/src/client/web/src/components/browser.tsx b/src/client/web/src/components/browser.tsx deleted file mode 100644 index 2738b09..0000000 --- a/src/client/web/src/components/browser.tsx +++ /dev/null @@ -1,964 +0,0 @@ -import * as React from "react"; -import * as ReactDOM from "react-dom"; -import { List, Map, Set } from "immutable"; -import FileSize from "filesize"; - -import { RiFolder2Fill } from "@react-icons/all-files/ri/RiFolder2Fill"; -import { RiHomeSmileFill } from "@react-icons/all-files/ri/RiHomeSmileFill"; -import { RiFile2Fill } from "@react-icons/all-files/ri/RiFile2Fill"; -import { RiShareBoxLine } from "@react-icons/all-files/ri/RiShareBoxLine"; -import { RiFolderSharedFill } from "@react-icons/all-files/ri/RiFolderSharedFill"; -import { RiUploadCloudFill } from "@react-icons/all-files/ri/RiUploadCloudFill"; -import { RiUploadCloudLine } from "@react-icons/all-files/ri/RiUploadCloudLine"; -import { RiEmotionSadLine } from "@react-icons/all-files/ri/RiEmotionSadLine"; - -import { alertMsg, confirmMsg } from "../common/env"; -import { updater } from "./state_updater"; -import { ICoreState, MsgProps, UIProps } from "./core_state"; -import { LoginProps } from "./pane_login"; -import { MetadataResp, roleVisitor, roleAdmin } from "../client"; -import { Up } from "../worker/upload_mgr"; -import { UploadEntry, UploadState } from "../worker/interface"; -import { Flexbox } from "./layout/flexbox"; - -// export interface Item { -// name: string; -// size: number; -// modTime: string; -// isDir: boolean; -// selected: boolean; -// sha1: string; -// } - -// export interface BrowserProps { -// tab: string; - -// dirPath: List; -// isSharing: boolean; -// items: List; - -// uploadings: List; -// uploadFiles: List; -// uploadValue: string; - -// sharings: List; -// } - -// export interface Props { -// browser: BrowserProps; -// msg: MsgProps; -// login: LoginProps; -// ui: UIProps; -// update?: (updater: (prevState: ICoreState) => ICoreState) => void; -// } - -export function getItemPath(dirPath: string, itemName: string): string { - return dirPath.endsWith("/") - ? `${dirPath}${itemName}` - : `${dirPath}/${itemName}`; -} - -// export interface State { -// newFolderName: string; -// selectedSrc: string; -// selectedItems: Map; -// showDetail: Set; -// } - -// export class Browser extends React.Component { -// private uploadInput: Element | Text; -// private assignInput: (input: Element) => void; -// private onClickUpload: () => void; - -// constructor(p: Props) { -// super(p); -// this.state = { -// newFolderName: "", -// selectedSrc: "", -// selectedItems: Map(), -// showDetail: Set(), -// }; - -// Up().setStatusCb(this.updateProgress); -// this.uploadInput = undefined; -// this.assignInput = (input) => { -// this.uploadInput = ReactDOM.findDOMNode(input); -// }; -// this.onClickUpload = () => { -// const uploadInput = this.uploadInput as HTMLButtonElement; -// uploadInput.click(); -// }; -// } - -// onNewFolderNameChange = (ev: React.ChangeEvent) => { -// this.setState({ newFolderName: ev.target.value }); -// }; - -// addUploads = (event: React.ChangeEvent) => { -// if (event.target.files.length > 1000) { -// alertMsg(this.props.msg.pkg.get("err.tooManyUploads")); -// return; -// } - -// let fileList = List(); -// for (let i = 0; i < event.target.files.length; i++) { -// fileList = fileList.push(event.target.files[i]); -// } -// updater().addUploads(fileList); -// this.props.update(updater().updateBrowser); -// }; - -// deleteUpload = (filePath: string): Promise => { -// return updater() -// .deleteUpload(filePath) -// .then((ok: boolean) => { -// if (!ok) { -// alertMsg(this.props.msg.pkg.get("browser.upload.del.fail")); -// } -// return updater().refreshUploadings(); -// }) -// .then(() => { -// return updater().self(); -// }) -// .then(() => { -// this.props.update(updater().updateBrowser); -// this.props.update(updater().updateLogin); -// }); -// }; - -// stopUploading = (filePath: string) => { -// updater().stopUploading(filePath); -// this.props.update(updater().updateBrowser); -// }; - -// onMkDir = () => { -// if (this.state.newFolderName === "") { -// alertMsg(this.props.msg.pkg.get("browser.folder.add.fail")); -// return; -// } - -// const dirPath = getItemPath( -// this.props.browser.dirPath.join("/"), -// this.state.newFolderName -// ); -// updater() -// .mkDir(dirPath) -// .then(() => { -// this.setState({ newFolderName: "" }); -// return updater().setItems(this.props.browser.dirPath); -// }) -// .then(() => { -// this.props.update(updater().updateBrowser); -// }); -// }; - -// delete = () => { -// // TODO: selected should be cleaned after change the cwd -// if (this.props.browser.dirPath.join("/") !== this.state.selectedSrc) { -// alertMsg(this.props.msg.pkg.get("browser.del.fail")); -// this.setState({ -// selectedSrc: this.props.browser.dirPath.join("/"), -// selectedItems: Map(), -// }); -// return; -// } else { -// const filesToDel = this.state.selectedItems.keySeq().join(", "); -// if (!confirmMsg(`${this.props.msg.pkg.get("op.confirm")} [${this.state.selectedItems.size}]: ${filesToDel}`)) { -// return; -// } -// } - -// updater() -// .delete( -// this.props.browser.dirPath, -// this.props.browser.items, -// this.state.selectedItems -// ) -// .then(() => { -// return updater().self(); -// }) -// .then(() => { -// this.props.update(updater().updateBrowser); -// this.props.update(updater().updateLogin); -// this.setState({ -// selectedSrc: "", -// selectedItems: Map(), -// }); -// }); -// }; - -// moveHere = () => { -// const oldDir = this.state.selectedSrc; -// const newDir = this.props.browser.dirPath.join("/"); -// if (oldDir === newDir) { -// alertMsg(this.props.msg.pkg.get("browser.move.fail")); -// return; -// } - -// updater() -// .moveHere( -// this.state.selectedSrc, -// this.props.browser.dirPath.join("/"), -// this.state.selectedItems -// ) -// .then(() => { -// this.props.update(updater().updateBrowser); -// this.setState({ -// selectedSrc: "", -// selectedItems: Map(), -// }); -// }); -// }; - -// gotoChild = async (childDirName: string) => { -// return this.chdir(this.props.browser.dirPath.push(childDirName)); -// }; - -// chdir = async (dirPath: List) => { -// if (dirPath === this.props.browser.dirPath) { -// return; -// } else if (this.props.login.userRole !== roleAdmin && dirPath.size <= 1) { -// alertMsg(this.props.msg.pkg.get("unauthed")); -// return; -// } - -// return updater() -// .setItems(dirPath) -// .then(() => { -// return updater().listSharings(); -// }) -// .then(() => { -// return updater().isSharing(dirPath.join("/")); -// }) -// .then(() => { -// this.props.update(updater().updateBrowser); -// }); -// }; - -// updateProgress = async ( -// infos: Map, -// refresh: boolean -// ) => { -// updater().setUploadings(infos); -// let errCount = 0; -// infos.valueSeq().forEach((entry: UploadEntry) => { -// errCount += entry.state === UploadState.Error ? 1 : 0; -// }); - -// if (infos.size === 0 || infos.size === errCount) { -// // refresh used space -// updater() -// .self() -// .then(() => { -// this.props.update(updater().updateLogin); -// }); -// } - -// if (refresh) { -// updater() -// .setItems(this.props.browser.dirPath) -// .then(() => { -// this.props.update(updater().updateBrowser); -// }); -// } else { -// this.props.update(updater().updateBrowser); -// } -// }; - -// select = (itemName: string) => { -// const selectedItems = this.state.selectedItems.has(itemName) -// ? this.state.selectedItems.delete(itemName) -// : this.state.selectedItems.set(itemName, true); - -// this.setState({ -// selectedSrc: this.props.browser.dirPath.join("/"), -// selectedItems: selectedItems, -// }); -// }; - -// selectAll = () => { -// let newSelected = Map(); -// const someSelected = this.state.selectedItems.size === 0 ? true : false; -// if (someSelected) { -// this.props.browser.items.forEach((item) => { -// newSelected = newSelected.set(item.name, true); -// }); -// } else { -// this.props.browser.items.forEach((item) => { -// newSelected = newSelected.delete(item.name); -// }); -// } - -// this.setState({ -// selectedSrc: this.props.browser.dirPath.join("/"), -// selectedItems: newSelected, -// }); -// }; - -// addSharing = async () => { -// return updater() -// .addSharing() -// .then((ok) => { -// if (!ok) { -// alertMsg(this.props.msg.pkg.get("browser.share.add.fail")); -// } else { -// updater().setSharing(true); -// return this.listSharings(); -// } -// }) -// .then(() => { -// this.props.update(updater().updateBrowser); -// }); -// }; - -// deleteSharing = async (dirPath: string) => { -// return updater() -// .deleteSharing(dirPath) -// .then((ok) => { -// if (!ok) { -// alertMsg(this.props.msg.pkg.get("browser.share.del.fail")); -// } else { -// updater().setSharing(false); -// return this.listSharings(); -// } -// }) -// .then(() => { -// this.props.update(updater().updateBrowser); -// }); -// }; - -// listSharings = async () => { -// return updater() -// .listSharings() -// .then((ok) => { -// if (ok) { -// this.props.update(updater().updateBrowser); -// } -// }); -// }; - -// setTab = (tabName: string) => { -// updater().setTab(tabName); -// this.props.update(updater().updateBrowser); -// }; - -// toggleDetail = (name: string) => { -// const showDetail = this.state.showDetail.has(name) -// ? this.state.showDetail.delete(name) -// : this.state.showDetail.add(name); -// this.setState({ showDetail }); -// }; - -// generateHash = async (filePath: string): Promise => { -// alertMsg(this.props.msg.pkg.get("refresh-hint")); -// return updater().generateHash(filePath); -// }; - -// render() { -// const showOp = this.props.login.userRole === roleVisitor ? "hidden" : ""; -// const breadcrumb = this.props.browser.dirPath.map( -// (pathPart: string, key: number) => { -// return ( -// -// ); -// } -// ); - -// const nameWidthClass = `item-name item-name-${ -// this.props.ui.isVertical ? "vertical" : "horizontal" -// } pointer`; - -// const ops = ( -//
-//
-// -// -//
- -//
-// -// -//
-//
-// ); - -// const sortedItems = this.props.browser.items.sort( -// (item1: MetadataResp, item2: MetadataResp) => { -// if (item1.isDir && !item2.isDir) { -// return -1; -// } else if (!item1.isDir && item2.isDir) { -// return 1; -// } -// return 0; -// } -// ); - -// const itemList = sortedItems.map((item: MetadataResp) => { -// const isSelected = this.state.selectedItems.has(item.name); -// const dirPath = this.props.browser.dirPath.join("/"); -// const itemPath = dirPath.endsWith("/") -// ? `${dirPath}${item.name}` -// : `${dirPath}/${item.name}`; - -// return item.isDir ? ( -// -// , - -// -// this.gotoChild(item.name)} -// > -// {item.name} -// -//
-// -// {item.modTime.slice(0, item.modTime.indexOf("T"))} -// -//
-//
, -// ])} -// childrenStyles={List([ -// { flex: "0 0 auto" }, -// { flex: "0 0 auto" }, -// ])} -// /> -// , - -// -// -// , -// ])} -// childrenStyles={List([ -// { flex: "0 0 auto", width: "60%" }, -// { flex: "0 0 auto", justifyContent: "flex-end", width: "40%" }, -// ])} -// /> -// ) : ( -//
-// , - -// -// -// {item.name} -// -//
-// -// {item.modTime.slice(0, item.modTime.indexOf("T"))} -// -//  /  -// {FileSize(item.size, { round: 0 })} -//
-//
, -// ])} -// childrenStyles={List([ -// { flex: "0 0 auto" }, -// { flex: "0 0 auto" }, -// ])} -// />, - -// -// - -// -// , -// ])} -// childrenStyles={List([ -// { flex: "0 0 auto", width: "60%" }, -// { flex: "0 0 auto", justifyContent: "flex-end", width: "40%" }, -// ])} -// /> - -//
-// -// SHA1: -// {` ${item.sha1}`} -// , -// , -// ])} -// className={`grey2-bg grey3-font detail margin-r-m`} -// childrenStyles={List([{}, { justifyContent: "flex-end" }])} -// /> -//
-//
-// ); -// }); - -// const usedSpace = FileSize(parseInt(this.props.login.usedSpace, 10), { -// round: 0, -// }); -// const spaceLimit = FileSize( -// parseInt(this.props.login.quota.spaceLimit, 10), -// { -// round: 0, -// } -// ); - -// const itemListPane = -// this.props.browser.tab === "" || this.props.browser.tab === "item" ? ( -//
-//
{ops}
- -//
-//
-// -// {this.props.browser.isSharing ? ( -// -// ) : ( -// -// )} -// , - -// -// {this.state.selectedItems.size > 0 ? ( -// -// - -// -// -// ) : null} -// , - -// -// {`${this.props.msg.pkg.get( -// "browser.used" -// )} ${usedSpace} / ${spaceLimit}`} -// , -// ])} -// childrenStyles={List([ -// { flex: "0 0 auto" }, -// { flex: "0 0 auto" }, -// { justifyContent: "flex-end" }, -// ])} -// /> -//
- -// -// , -// , -// ])} -// childrenStyles={List([ -// { flex: "0 0 auto" }, -// { flex: "0 0 auto" }, -// ])} -// /> -// , - -// -// -// , -// ])} -// childrenStyles={List([{}, { justifyContent: "flex-end" }])} -// /> - -// {itemList} -//
-//
-// ) : null; - -// const uploadingList = this.props.browser.uploadings.map( -// (uploading: UploadEntry) => { -// const pathParts = uploading.filePath.split("/"); -// const fileName = pathParts[pathParts.length - 1]; - -// return ( -//
-// -// , - -//
-// {fileName} -//
-// {FileSize(uploading.uploaded, { round: 0 })} -//  / {FileSize(uploading.size, { round: 0 })} -//
-//
, -// ])} -// /> -// , - -//
-// -// -//
, -// ])} -// childrenStyles={List([{}, { justifyContent: "flex-end" }])} -// /> -// {uploading.err.trim() === "" ? null : ( -//
{uploading.err.trim()}
-// )} -//
-// ); -// } -// ); - -// const uploadingListPane = -// this.props.browser.tab === "uploading" ? ( -// this.props.browser.uploadings.size === 0 ? ( -//
-// , -// -//

-// {this.props.msg.pkg.get("upload.404.title")} -//

-// -// {this.props.msg.pkg.get("upload.404.desc")} -// -//
, -// ])} -// childrenStyles={List([ -// { flex: "auto", justifyContent: "flex-end" }, -// { flex: "auto" }, -// ])} -// className="padding-l" -// /> -//
-// ) : ( -//
-// -// , - -// -// -// {this.props.msg.pkg.get("browser.upload.title")} -// -// -// {this.props.msg.pkg.get("browser.upload.desc")} -// -// , -// ])} -// /> -// , - -// , -// ])} -// /> - -// {uploadingList} -//
-// ) -// ) : null; - -// const sharingList = this.props.browser.sharings.map((dirPath: string) => { -// return ( -//
-// , -// {dirPath}, -// ])} -// />, - -// -// -// -// , -// ])} -// childrenStyles={List([{}, { justifyContent: "flex-end" }])} -// /> -//
-// ); -// }); - -// const sharingListPane = -// this.props.browser.tab === "sharing" ? ( -// this.props.browser.sharings.size === 0 ? ( -//
-// , -// -//

-// {this.props.msg.pkg.get("share.404.title")} -//

-// -// {this.props.msg.pkg.get("share.404.desc")} -// -//
, -// ])} -// childrenStyles={List([ -// { flex: "auto", justifyContent: "flex-end" }, -// { flex: "auto" }, -// ])} -// className="padding-l" -// /> -//
-// ) : ( -//
-// -// , - -// -// -// {this.props.msg.pkg.get("browser.share.title")} -// -// -// {this.props.msg.pkg.get("browser.share.desc")} -// -// , -// ])} -// /> -// , - -// , -// ])} -// /> - -// {sharingList} -//
-// ) -// ) : null; - -// const showTabs = this.props.login.userRole === roleVisitor ? "hidden" : ""; -// return ( -//
-//
-//
-//
-// -// -// -//
-//
- -//
{sharingListPane}
-//
{uploadingListPane}
-// {itemListPane} -//
-//
-// ); -// } -// } diff --git a/src/client/web/src/components/layout/table.tsx b/src/client/web/src/components/layout/table.tsx index 4480a87..1127df3 100644 --- a/src/client/web/src/components/layout/table.tsx +++ b/src/client/web/src/components/layout/table.tsx @@ -1,16 +1,13 @@ import * as React from "react"; import { List } from "immutable"; -import { updater } from "../state_updater"; - export interface Cell { - elem: React.ReactNode; + elem: React.ReactNode; // element to display val: string; // original cell value - // sortVal: string; // this value is for sorting } export interface Row { - cells: List; + cells: List; // columns of arow val: Object; // original object value } @@ -27,8 +24,8 @@ export interface Props { id?: string; style?: React.CSSProperties; className?: string; - originalVals?: List; - updateRows?: (rows: Object) => void; + originalVals?: List; // these are original values which will be updated to the state after re-sorting + updateRows?: (rows: Object) => void; // this is a callback which update state with re-sorted rows } export interface State { @@ -151,51 +148,3 @@ export class Table extends React.Component { ); } } - -// export const Table = (props: Props) => { -// const headCols = props.head.map((head: Head, i: number): React.ReactNode => { -// const style = props.colStyles != null ? props.colStyles.get(i) : {}; -// return ( -// -// {head.elem} -// -// ); -// }); - -// const bodyRows = props.rows.map( -// (row: List, i: number): React.ReactNode => { -// const tds = row.map((cell: Cell, j: number) => { -// const style = props.colStyles != null ? props.colStyles.get(j) : {}; -// return ( -// -// {cell.elem} -// -// ); -// }); -// return {tds}; -// } -// ); - -// const footCols = props.foot.map( -// (elem: React.ReactNode, i: number): React.ReactNode => { -// const style = props.colStyles != null ? props.colStyles.get(i) : {}; -// return ( -// -// {elem} -// -// ); -// } -// ); - -// return ( -// -// -// {headCols} -// -// {bodyRows} -// -// {footCols} -// -//
-// ); -// }; diff --git a/src/client/web/src/components/panel_files.tsx b/src/client/web/src/components/panel_files.tsx index c425881..42610a2 100644 --- a/src/client/web/src/components/panel_files.tsx +++ b/src/client/web/src/components/panel_files.tsx @@ -87,7 +87,7 @@ export class FilesPanel extends React.Component { infos: Map, refresh: boolean ) => { - updater().setUploadings(infos); + updater().setUploads(infos); let errCount = 0; infos.valueSeq().forEach((entry: UploadEntry) => { errCount += entry.state === UploadState.Error ? 1 : 0; @@ -126,7 +126,11 @@ export class FilesPanel extends React.Component { for (let i = 0; i < event.target.files.length; i++) { fileList = fileList.push(event.target.files[i]); } - updater().addUploads(fileList); + + const status = updater().addUploads(fileList); + if (status !== "") { + alertMsg(this.props.msg.pkg.get("upload.add.fail")); + } this.props.update(updater().updateUploadingsInfo); }; diff --git a/src/client/web/src/components/panel_uploadings.tsx b/src/client/web/src/components/panel_uploadings.tsx index d838c20..5475498 100644 --- a/src/client/web/src/components/panel_uploadings.tsx +++ b/src/client/web/src/components/panel_uploadings.tsx @@ -35,26 +35,16 @@ export class UploadingsPanel extends React.Component { this.state = {}; } - addUploads = (event: React.ChangeEvent) => { - if (event.target.files.length > 1000) { - alertMsg(this.props.msg.pkg.get("err.tooManyUploads")); - return; - } - - let fileList = List(); - for (let i = 0; i < event.target.files.length; i++) { - fileList = fileList.push(event.target.files[i]); - } - updater().addUploads(fileList); - this.props.update(updater().updateUploadingsInfo); - }; - deleteUpload = (filePath: string): Promise => { return updater() .deleteUpload(filePath) - .then((ok: boolean) => { - if (!ok) { - alertMsg(this.props.msg.pkg.get("browser.upload.del.fail")); + .then((status: string) => { + if (status !== "") { + alertMsg( + `${this.props.msg.pkg.get( + "browser.upload.del.fail" + )}: ${this.props.msg.pkg.get(status)}` + ); } return updater().refreshUploadings(); }) diff --git a/src/client/web/src/components/state_updater.ts b/src/client/web/src/components/state_updater.ts index 2adef0a..7adea2e 100644 --- a/src/client/web/src/components/state_updater.ts +++ b/src/client/web/src/components/state_updater.ts @@ -7,7 +7,7 @@ import { ctrlOff, ctrlHidden, } from "./core_state"; -import { getItemPath } from "./browser"; +import { getItemPath } from "../common/utils"; import { User, ListUsersResp, @@ -38,10 +38,6 @@ import { settingsDialogCtrl } from "./layers"; import { MsgPackage, isValidLanPack } from "../i18n/msger"; -function getCookieLanKey(user: string) { - return `qs_${user}_lan`; -} - export class Updater { props: ICoreState; private usersClient: IUsersClient = new UsersClient(""); @@ -59,31 +55,41 @@ export class Updater { this.settingsClient = settingsClient; } - initUploads = () => { + initUploads = (): string => { this.props.uploadingsInfo.uploadings.forEach((entry) => { Up().addStopped(entry.filePath, entry.uploaded, entry.size); }); + + return ""; }; - addUploads = (fileList: List) => { + addUploads = (fileList: List): string => { fileList.forEach((file) => { const filePath = getItemPath( this.props.filesInfo.dirPath.join("/"), file.name ); - // do not wait for the promise - Up().add(file, filePath); + const status = Up().add(file, filePath); + if (status !== ""){ + return status; + } }); - this.setUploadings(Up().list()); + + this.setUploads(Up().list()); + return ""; }; - deleteUpload = async (filePath: string): Promise => { - Up().delete(filePath); + deleteUpload = async (filePath: string): Promise => { + const status = Up().delete(filePath); + if (status !== "") { + return status; + } + const resp = await this.filesClient.deleteUploading(filePath); - return resp.status === 200; + return resp.status === 200 ? "" : "server.fail"; }; - setUploadings = (infos: Map) => { + setUploads = (infos: Map) => { this.props.uploadingsInfo.uploadings = List( infos.valueSeq().map((entry: UploadEntry): UploadEntry => { return entry; @@ -369,11 +375,8 @@ export class Updater { initStateForAuthedUser = async (): Promise => { // TOOD: status is ignored, should return alert - return Promise.all([ - this.refreshUploadings(), - this.initUploads(), - this.listSharings(), - ]); + this.initUploads(); // ignore return because it always succeed + return Promise.all([this.refreshUploadings(), this.listSharings()]); }; initStateForAdmin = async (): Promise => { diff --git a/src/client/web/src/i18n/en_US.ts b/src/client/web/src/i18n/en_US.ts index c25c951..d63b4e6 100644 --- a/src/client/web/src/i18n/en_US.ts +++ b/src/client/web/src/i18n/en_US.ts @@ -111,4 +111,6 @@ export const msgs: Map = Map({ "control.panelTabs.sharingsPanel": "Sharings", "control.settingsTabs.managementPane": "Management", "control.settingsTabs.preferencePane": "Preference", + "upload.add.fail": "Some files conflict with uploading files, please check.", + "server.fail": "The operation failed in the server", }); diff --git a/src/client/web/src/i18n/zh_CN.ts b/src/client/web/src/i18n/zh_CN.ts index fd5cc3d..eefe158 100644 --- a/src/client/web/src/i18n/zh_CN.ts +++ b/src/client/web/src/i18n/zh_CN.ts @@ -110,4 +110,6 @@ export const msgs: Map = Map({ "control.panelTabs.sharingsPanel": "共享", "control.settingsTabs.managementPane": "管理", "control.settingsTabs.preferencePane": "设置", + "upload.add.fail": "有些文件与上传任务冲突,请检查", + "server.fail": "操作在服务器端失败", }); diff --git a/src/client/web/src/worker/upload_mgr.ts b/src/client/web/src/worker/upload_mgr.ts index 3f87e18..c4af6db 100644 --- a/src/client/web/src/worker/upload_mgr.ts +++ b/src/client/web/src/worker/upload_mgr.ts @@ -98,7 +98,7 @@ export class UploadMgr { this.statusCb = cb; }; - // addStopped is for initializing uploading list in the UploadMgr + // addStopped is for initializing uploading list in the UploadMgr, when page is loaded // notice even uploading list are shown in the UI, it may not inited in the UploadMgr addStopped = (filePath: string, uploaded: number, fileSize: number) => { this.infos = this.infos.set(filePath, { @@ -111,8 +111,10 @@ export class UploadMgr { }); }; - add = (file: File, filePath: string) => { + add = (file: File, filePath: string): string => { const entry = this.infos.get(filePath); + let status = ""; + if (entry == null) { // new uploading this.infos = this.infos.set(filePath, { @@ -124,46 +126,55 @@ export class UploadMgr { err: "", }); } else { - // restart the uploading + // the uploading task exists, restart the uploading if ( entry.state === UploadState.Stopped && filePath === entry.filePath && file.size === entry.size ) { - // try to upload a file with same name but actually with different content. - // it still can not resolve one case: names and sizes are same, but contents are different - // TODO: showing file SHA will avoid above case + // try to upload a file with same name + // the file may be a totally different file. + // TODO: checking file SHA will avoid above case this.infos = this.infos.set(filePath, { ...entry, file: file, state: UploadState.Ready, }); } else { - alert( - `(${filePath}) seems not same file with uploading item, please check.` - ); + status = "op.fail"; } } + this.statusCb(this.infos.toMap(), false); + return status; }; - stop = (filePath: string) => { + stop = (filePath: string): string => { const entry = this.infos.get(filePath); + let status = ""; + if (entry != null) { this.infos = this.infos.set(filePath, { ...entry, state: UploadState.Stopped, }); } else { - alert(`failed to stop uploading ${filePath}: not found`); + status = "op.fail"; } + this.statusCb(this.infos.toMap(), false); + return status; }; - delete = (filePath: string) => { - this.stop(filePath); + delete = (filePath: string): string => { + const status = this.stop(filePath); + if (status !== "") { + return status; + } + this.infos = this.infos.delete(filePath); this.statusCb(this.infos.toMap(), false); + return ""; }; list = (): OrderedMap => {