chore(client): clean up and ci (#35)
* fix(client/web): move browser updater to single file * fix(client/web): make UploadMgr singleton * test(client/web): add unit tests for browser * fix(client/web): updater init should be in StateMgr * feat(client/browser): add selectAll button * chore(ci): disable travis although it is awsome
This commit is contained in:
parent
46f03e2e84
commit
e87a342c93
12 changed files with 532 additions and 415 deletions
|
@ -1,7 +0,0 @@
|
|||
language: go
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- "1.15.x"
|
||||
|
||||
go_import_path: github.com/ihexxa/quickshare
|
|
@ -15,8 +15,8 @@
|
|||
"build:task:dev": "webpack --config webpack.task.dev.js --watch",
|
||||
"e2e": "jest -c jest.e2e.config.js",
|
||||
"e2e:watch": "jest --watch -c jest.e2e.config.js",
|
||||
"test": "jest test",
|
||||
"test:watch": "jest test --watch",
|
||||
"test": "jest test --maxWorkers=2",
|
||||
"test:watch": "jest test --watch --maxWorkers=2",
|
||||
"copy": "cp -r ../../static ../../../dockers/nginx/"
|
||||
},
|
||||
"author": "hexxa",
|
||||
|
|
7
src/client/web/src/common/env.ts
Normal file
7
src/client/web/src/common/env.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export function alertMsg(msg: string) {
|
||||
if (alert != null) {
|
||||
alert(msg);
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
|
@ -1,72 +1,74 @@
|
|||
import * as React from "react";
|
||||
import { List, Map } from "immutable";
|
||||
import { mock, instance, anyString, when } from "ts-mockito";
|
||||
import { mock, instance, anyString, anything, when, verify } from "ts-mockito";
|
||||
|
||||
import { ICoreState, initWithWorker, mockState } from "../core_state";
|
||||
import {
|
||||
makePromise,
|
||||
makeNumberResponse,
|
||||
mockUpdate,
|
||||
addMockUpdate,
|
||||
mockFileList,
|
||||
} from "../../test/helpers";
|
||||
import { Updater, Browser } from "../browser";
|
||||
import { Browser } from "../browser";
|
||||
import { Updater, setUpdater } from "../browser.updater";
|
||||
import { MockUsersClient } from "../../client/users_mock";
|
||||
import { UsersClient } from "../../client/users";
|
||||
import { FilesClient } from "../../client/files";
|
||||
import { FilesClient as MockFilesClient } from "../../client/files_mock";
|
||||
import { MetadataResp, UploadInfo } from "../../client";
|
||||
import { MockWorker, UploadEntry } from "../../worker/interface";
|
||||
import { UploadMgr } from "../../worker/upload_mgr";
|
||||
import { UploadMgr, setUploadMgr } from "../../worker/upload_mgr";
|
||||
|
||||
describe("Browser", () => {
|
||||
const mockWorkerClass = mock(MockWorker);
|
||||
const mockWorker = instance(mockWorkerClass);
|
||||
|
||||
test("Updater: setPwd", async () => {
|
||||
const tests = [
|
||||
{
|
||||
listResp: {
|
||||
status: 200,
|
||||
statusText: "",
|
||||
data: {
|
||||
metadatas: [
|
||||
{
|
||||
name: "file",
|
||||
size: 1,
|
||||
modTime: "1-1",
|
||||
isDir: false,
|
||||
},
|
||||
{
|
||||
name: "folder",
|
||||
size: 0,
|
||||
modTime: "1-1",
|
||||
isDir: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
filePath: "path/file",
|
||||
},
|
||||
];
|
||||
test("Updater: addUploads: add each files to UploadMgr", async () => {
|
||||
let coreState = mockState();
|
||||
const UploadMgrClass = mock(UploadMgr);
|
||||
const uploadMgr = instance(UploadMgrClass);
|
||||
setUploadMgr(uploadMgr);
|
||||
|
||||
const usersClient = new MockUsersClient("");
|
||||
const filesClient = new MockFilesClient("");
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
const tc = tests[i];
|
||||
const filePaths = ["./file1", "./file2"];
|
||||
const fileList = mockFileList(filePaths);
|
||||
const updater = new Updater();
|
||||
updater.setUploadings = (infos: Map<string, UploadEntry>) => {};
|
||||
updater.init(coreState.panel.browser);
|
||||
|
||||
filesClient.listMock(makePromise(tc.listResp));
|
||||
Updater.setClients(usersClient, filesClient);
|
||||
updater.addUploads(fileList);
|
||||
|
||||
const coreState = initWithWorker(mockWorker);
|
||||
Updater.init(coreState.panel.browser);
|
||||
await Updater.setItems(List<string>(tc.filePath.split("/")));
|
||||
const newState = Updater.setBrowser(coreState);
|
||||
// it seems that new File will do some file path escaping, so just check call time here
|
||||
verify(UploadMgrClass.add(anything(), anything())).times(filePaths.length);
|
||||
// filePaths.forEach((filePath, i) => {
|
||||
// verify(UploadMgrClass.add(anything(), filePath)).once();
|
||||
// });
|
||||
});
|
||||
|
||||
newState.panel.browser.items.forEach((item, i) => {
|
||||
expect(item.name).toEqual(tc.listResp.data.metadatas[i].name);
|
||||
expect(item.size).toEqual(tc.listResp.data.metadatas[i].size);
|
||||
expect(item.modTime).toEqual(tc.listResp.data.metadatas[i].modTime);
|
||||
expect(item.isDir).toEqual(tc.listResp.data.metadatas[i].isDir);
|
||||
});
|
||||
}
|
||||
test("Updater: deleteUploads: call UploadMgr and api to delete", async () => {
|
||||
let coreState = mockState();
|
||||
const UploadMgrClass = mock(UploadMgr);
|
||||
const uploadMgr = instance(UploadMgrClass);
|
||||
setUploadMgr(uploadMgr);
|
||||
|
||||
const updater = new Updater();
|
||||
const filesClientClass = mock(FilesClient);
|
||||
when(filesClientClass.deleteUploading(anyString())).thenResolve({
|
||||
status: 200,
|
||||
statusText: "",
|
||||
data: "",
|
||||
});
|
||||
const filesClient = instance(filesClientClass);
|
||||
const usersClientClass = mock(UsersClient);
|
||||
const usersClient = instance(usersClientClass);
|
||||
updater.init(coreState.panel.browser);
|
||||
updater.setClients(usersClient, filesClient);
|
||||
|
||||
const filePath = "./path/file";
|
||||
updater.deleteUpload(filePath);
|
||||
|
||||
verify(filesClientClass.deleteUploading(filePath)).once();
|
||||
verify(UploadMgrClass.delete(filePath)).once();
|
||||
});
|
||||
|
||||
test("Updater: delete", async () => {
|
||||
|
@ -112,21 +114,71 @@ describe("Browser", () => {
|
|||
const filesClient = new MockFilesClient("");
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
const tc = tests[i];
|
||||
|
||||
const updater = new Updater();
|
||||
updater.setClients(usersClient, filesClient);
|
||||
filesClient.listMock(makePromise(tc.listResp));
|
||||
filesClient.deleteMock(makeNumberResponse(200));
|
||||
Updater.setClients(usersClient, filesClient);
|
||||
|
||||
const coreState = initWithWorker(mockWorker);
|
||||
Updater.init(coreState.panel.browser);
|
||||
await Updater.delete(
|
||||
updater.init(coreState.panel.browser);
|
||||
|
||||
await updater.delete(
|
||||
List<string>(tc.dirPath.split("/")),
|
||||
List<MetadataResp>(tc.items),
|
||||
Map<boolean>(tc.selected)
|
||||
Map(tc.selected)
|
||||
);
|
||||
const newState = Updater.setBrowser(coreState);
|
||||
|
||||
const newState = updater.setBrowser(coreState);
|
||||
|
||||
// TODO: check inputs of delete
|
||||
newState.panel.browser.items.forEach((item, i) => {
|
||||
expect(item.name).toEqual(tc.listResp.data.metadatas[i].name);
|
||||
expect(item.size).toEqual(tc.listResp.data.metadatas[i].size);
|
||||
expect(item.modTime).toEqual(tc.listResp.data.metadatas[i].modTime);
|
||||
expect(item.isDir).toEqual(tc.listResp.data.metadatas[i].isDir);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test("Updater: setItems", async () => {
|
||||
const tests = [
|
||||
{
|
||||
listResp: {
|
||||
status: 200,
|
||||
statusText: "",
|
||||
data: {
|
||||
metadatas: [
|
||||
{
|
||||
name: "file",
|
||||
size: 1,
|
||||
modTime: "1-1",
|
||||
isDir: false,
|
||||
},
|
||||
{
|
||||
name: "folder",
|
||||
size: 0,
|
||||
modTime: "1-1",
|
||||
isDir: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
filePath: "path/file",
|
||||
},
|
||||
];
|
||||
|
||||
const usersClient = new MockUsersClient("");
|
||||
const filesClient = new MockFilesClient("");
|
||||
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
const tc = tests[i];
|
||||
const updater = new Updater();
|
||||
filesClient.listMock(makePromise(tc.listResp));
|
||||
updater.setClients(usersClient, filesClient);
|
||||
const coreState = initWithWorker(mockWorker);
|
||||
updater.init(coreState.panel.browser);
|
||||
|
||||
await updater.setItems(List<string>(tc.filePath.split("/")));
|
||||
const newState = updater.setBrowser(coreState);
|
||||
|
||||
newState.panel.browser.items.forEach((item, i) => {
|
||||
expect(item.name).toEqual(tc.listResp.data.metadatas[i].name);
|
||||
|
@ -173,22 +225,19 @@ describe("Browser", () => {
|
|||
const filesClient = new MockFilesClient("");
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
const tc = tests[i];
|
||||
const updater = new Updater();
|
||||
|
||||
filesClient.listMock(makePromise(tc.listResp));
|
||||
filesClient.moveMock(makeNumberResponse(200));
|
||||
Updater.setClients(usersClient, filesClient);
|
||||
updater.setClients(usersClient, filesClient);
|
||||
|
||||
const coreState = initWithWorker(mockWorker);
|
||||
Updater.init(coreState.panel.browser);
|
||||
await Updater.moveHere(
|
||||
tc.dirPath1,
|
||||
tc.dirPath2,
|
||||
Map<boolean>(tc.selected)
|
||||
);
|
||||
updater.init(coreState.panel.browser);
|
||||
await updater.moveHere(tc.dirPath1, tc.dirPath2, Map(tc.selected));
|
||||
|
||||
const newState = updater.setBrowser(coreState);
|
||||
|
||||
// TODO: check inputs of move
|
||||
|
||||
const newState = Updater.setBrowser(coreState);
|
||||
newState.panel.browser.items.forEach((item, i) => {
|
||||
expect(item.name).toEqual(tc.listResp.data.metadatas[i].name);
|
||||
expect(item.size).toEqual(tc.listResp.data.metadatas[i].size);
|
||||
|
@ -198,71 +247,36 @@ describe("Browser", () => {
|
|||
}
|
||||
});
|
||||
|
||||
xtest("Browser: deleteUploading", async () => {
|
||||
interface TestCase {
|
||||
deleteFile: string;
|
||||
preState: ICoreState;
|
||||
postState: ICoreState;
|
||||
}
|
||||
test("Browser: deleteUpload: tell uploader to deleteUpload and refreshUploadings", async () => {
|
||||
let coreState = mockState();
|
||||
addMockUpdate(coreState.panel.browser);
|
||||
const component = new Browser(coreState.panel.browser);
|
||||
const UpdaterClass = mock(Updater);
|
||||
const mockUpdater = instance(UpdaterClass);
|
||||
setUpdater(mockUpdater);
|
||||
when(UpdaterClass.setItems(anything())).thenResolve();
|
||||
when(UpdaterClass.deleteUpload(anyString())).thenResolve(true);
|
||||
when(UpdaterClass.refreshUploadings()).thenResolve(true);
|
||||
|
||||
const tcs: any = [
|
||||
{
|
||||
deleteFile: "./path/file",
|
||||
preState: {
|
||||
browser: {
|
||||
uploadings: List<UploadInfo>([
|
||||
{
|
||||
realFilePath: "./path/file",
|
||||
size: 1,
|
||||
uploaded: 0,
|
||||
},
|
||||
]),
|
||||
update: mockUpdate,
|
||||
},
|
||||
},
|
||||
postState: {
|
||||
browser: {
|
||||
uploadings: List<UploadInfo>(),
|
||||
update: mockUpdate,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
const filePath = "filePath";
|
||||
await component.deleteUpload(filePath);
|
||||
|
||||
const setState = (patch: any, state: ICoreState): ICoreState => {
|
||||
state.panel.browser = patch.browser;
|
||||
return state;
|
||||
};
|
||||
const mockFilesClientClass = mock(FilesClient);
|
||||
when(mockFilesClientClass.deleteUploading(anyString())).thenResolve({
|
||||
status: 200,
|
||||
statusText: "",
|
||||
data: "",
|
||||
});
|
||||
// TODO: the return should dpends on test case
|
||||
when(mockFilesClientClass.listUploadings()).thenResolve({
|
||||
status: 200,
|
||||
statusText: "",
|
||||
data: { uploadInfos: Array<UploadInfo>() },
|
||||
});
|
||||
verify(UpdaterClass.deleteUpload(filePath)).once();
|
||||
verify(UpdaterClass.refreshUploadings()).once();
|
||||
});
|
||||
|
||||
const mockUsersClientClass = mock(UsersClient);
|
||||
test("Browser: stopUploading: tell updater to stopUploading", async () => {
|
||||
let coreState = mockState();
|
||||
addMockUpdate(coreState.panel.browser);
|
||||
const component = new Browser(coreState.panel.browser);
|
||||
const UpdaterClass = mock(Updater);
|
||||
const mockUpdater = instance(UpdaterClass);
|
||||
setUpdater(mockUpdater);
|
||||
when(UpdaterClass.stopUploading(anyString())).thenReturn();
|
||||
|
||||
const mockFilesClient = instance(mockFilesClientClass);
|
||||
const mockUsersClient = instance(mockUsersClientClass);
|
||||
tcs.forEach((tc: TestCase) => {
|
||||
const preState = setState(tc.preState, mockState());
|
||||
const postState = setState(tc.postState, mockState());
|
||||
// const existingFileName = preState.panel.browser.uploadings.get(0).realFilePath;
|
||||
const infos:Map<string, UploadEntry> = Map();
|
||||
UploadMgr._setInfos(infos);
|
||||
const filePath = "filePath";
|
||||
component.stopUploading(filePath);
|
||||
|
||||
const component = new Browser(preState.panel.browser);
|
||||
Updater.init(preState.panel.browser);
|
||||
Updater.setClients(mockUsersClient, mockFilesClient);
|
||||
|
||||
component.deleteUploading(tc.deleteFile);
|
||||
expect(Updater.props).toEqual(postState.panel.browser);
|
||||
});
|
||||
verify(UpdaterClass.stopUploading(filePath)).once();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,8 @@ import { List, Map } from "immutable";
|
|||
import FileSize from "filesize";
|
||||
|
||||
import { Layouter } from "./layouter";
|
||||
import { alertMsg } from "../common/env";
|
||||
import { updater } from "./browser.updater";
|
||||
import { ICoreState } from "./core_state";
|
||||
import {
|
||||
IUsersClient,
|
||||
|
@ -11,9 +13,7 @@ import {
|
|||
MetadataResp,
|
||||
UploadInfo,
|
||||
} from "../client";
|
||||
import { FilesClient } from "../client/files";
|
||||
import { UsersClient } from "../client/users";
|
||||
import { UploadMgr } from "../worker/upload_mgr";
|
||||
import { Up } from "../worker/upload_mgr";
|
||||
import { UploadEntry } from "../worker/interface";
|
||||
|
||||
export const uploadCheckCycle = 1000;
|
||||
|
@ -39,143 +39,12 @@ export interface Props {
|
|||
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
|
||||
}
|
||||
|
||||
function getItemPath(dirPath: string, itemName: string): string {
|
||||
export function getItemPath(dirPath: string, itemName: string): string {
|
||||
return dirPath.endsWith("/")
|
||||
? `${dirPath}${itemName}`
|
||||
: `${dirPath}/${itemName}`;
|
||||
}
|
||||
|
||||
export class Updater {
|
||||
static props: Props;
|
||||
private static usersClient: IUsersClient;
|
||||
private static filesClient: IFilesClient;
|
||||
|
||||
static init = (props: Props) => (Updater.props = { ...props });
|
||||
static setClients(usersClient: IUsersClient, filesClient: IFilesClient) {
|
||||
Updater.usersClient = usersClient;
|
||||
Updater.filesClient = filesClient;
|
||||
}
|
||||
|
||||
static setUploadings = (infos: Map<string, UploadEntry>) => {
|
||||
Updater.props.uploadings = List<UploadInfo>(
|
||||
infos.valueSeq().map(
|
||||
(v: UploadEntry): UploadInfo => {
|
||||
return {
|
||||
realFilePath: v.filePath,
|
||||
size: v.size,
|
||||
uploaded: v.uploaded,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
static setItems = async (dirParts: List<string>): Promise<void> => {
|
||||
const dirPath = dirParts.join("/");
|
||||
const listResp = await Updater.filesClient.list(dirPath);
|
||||
|
||||
Updater.props.dirPath = dirParts;
|
||||
Updater.props.items =
|
||||
listResp.status === 200
|
||||
? List<MetadataResp>(listResp.data.metadatas)
|
||||
: Updater.props.items;
|
||||
};
|
||||
|
||||
static refreshUploadings = async (): Promise<boolean> => {
|
||||
const luResp = await Updater.filesClient.listUploadings();
|
||||
|
||||
Updater.props.uploadings =
|
||||
luResp.status === 200
|
||||
? List<UploadInfo>(luResp.data.uploadInfos)
|
||||
: Updater.props.uploadings;
|
||||
return luResp.status === 200;
|
||||
};
|
||||
|
||||
static deleteUploading = async (filePath: string): Promise<boolean> => {
|
||||
UploadMgr.delete(filePath);
|
||||
const resp = await Updater.filesClient.deleteUploading(filePath);
|
||||
return resp.status === 200;
|
||||
};
|
||||
|
||||
static stopUploading = (filePath: string) => {
|
||||
UploadMgr.stop(filePath);
|
||||
};
|
||||
|
||||
static mkDir = async (dirPath: string): Promise<void> => {
|
||||
let resp = await Updater.filesClient.mkdir(dirPath);
|
||||
if (resp.status !== 200) {
|
||||
alert(`failed to make dir ${dirPath}`);
|
||||
}
|
||||
};
|
||||
|
||||
static delete = async (
|
||||
dirParts: List<string>,
|
||||
items: List<MetadataResp>,
|
||||
selectedItems: Map<string, boolean>
|
||||
): Promise<void> => {
|
||||
const delRequests = items
|
||||
.filter((item) => {
|
||||
return selectedItems.has(item.name);
|
||||
})
|
||||
.map(
|
||||
async (selectedItem: MetadataResp): Promise<string> => {
|
||||
const itemPath = getItemPath(dirParts.join("/"), selectedItem.name);
|
||||
const resp = await Updater.filesClient.delete(itemPath);
|
||||
return resp.status === 200 ? "" : selectedItem.name;
|
||||
}
|
||||
);
|
||||
|
||||
const failedFiles = await Promise.all(delRequests);
|
||||
failedFiles.forEach((failedFile) => {
|
||||
if (failedFile !== "") {
|
||||
alert(`failed to delete ${failedFile}`);
|
||||
}
|
||||
});
|
||||
return Updater.setItems(dirParts);
|
||||
};
|
||||
|
||||
static moveHere = async (
|
||||
srcDir: string,
|
||||
dstDir: string,
|
||||
selectedItems: Map<string, boolean>
|
||||
): Promise<void> => {
|
||||
const moveRequests = List<string>(selectedItems.keys()).map(
|
||||
async (itemName: string): Promise<string> => {
|
||||
const oldPath = getItemPath(srcDir, itemName);
|
||||
const newPath = getItemPath(dstDir, itemName);
|
||||
const resp = await Updater.filesClient.move(oldPath, newPath);
|
||||
return resp.status === 200 ? "" : itemName;
|
||||
}
|
||||
);
|
||||
|
||||
const failedFiles = await Promise.all(moveRequests);
|
||||
failedFiles.forEach((failedItem) => {
|
||||
if (failedItem !== "") {
|
||||
alert(`failed to move ${failedItem}`);
|
||||
}
|
||||
});
|
||||
|
||||
return Updater.setItems(List<string>(dstDir.split("/")));
|
||||
};
|
||||
|
||||
static addUploadFiles = (fileList: FileList, len: number) => {
|
||||
for (let i = 0; i < len; i++) {
|
||||
const filePath = getItemPath(
|
||||
Updater.props.dirPath.join("/"),
|
||||
fileList[i].name
|
||||
);
|
||||
// do not wait for the promise
|
||||
UploadMgr.add(fileList[i], filePath);
|
||||
}
|
||||
Updater.setUploadings(UploadMgr.list());
|
||||
};
|
||||
|
||||
static setBrowser = (prevState: ICoreState): ICoreState => {
|
||||
prevState.panel.browser = { ...prevState.panel, ...Updater.props };
|
||||
return prevState;
|
||||
};
|
||||
}
|
||||
|
||||
export interface State {
|
||||
inputValue: string;
|
||||
selectedSrc: string;
|
||||
|
@ -190,8 +59,6 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
|
||||
constructor(p: Props) {
|
||||
super(p);
|
||||
Updater.init(p);
|
||||
Updater.setClients(new UsersClient(""), new FilesClient(""));
|
||||
this.update = p.update;
|
||||
this.state = {
|
||||
inputValue: "",
|
||||
|
@ -199,6 +66,7 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
selectedItems: Map<string, boolean>(),
|
||||
};
|
||||
|
||||
Up().setStatusCb(this.updateProgress);
|
||||
this.uploadInput = undefined;
|
||||
this.assignInput = (input) => {
|
||||
this.uploadInput = ReactDOM.findDOMNode(input);
|
||||
|
@ -208,83 +76,64 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
const uploadInput = this.uploadInput as HTMLButtonElement;
|
||||
uploadInput.click();
|
||||
};
|
||||
|
||||
UploadMgr.setStatusCb(this.updateProgress);
|
||||
Updater.setItems(p.dirPath)
|
||||
.then(() => {
|
||||
return Updater.refreshUploadings();
|
||||
})
|
||||
.then((_: boolean) => {
|
||||
this.update(Updater.setBrowser);
|
||||
});
|
||||
}
|
||||
|
||||
onInputChange = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ inputValue: ev.target.value });
|
||||
};
|
||||
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.dirPath.join("/"),
|
||||
selectedItems: selectedItems,
|
||||
});
|
||||
addUploads = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const fileList = List<File>();
|
||||
for (let i = 0; i < event.target.files.length; i++) {
|
||||
fileList.push(event.target.files[i]);
|
||||
}
|
||||
updater().addUploads(fileList);
|
||||
this.update(updater().setBrowser);
|
||||
};
|
||||
|
||||
addUploadFile = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
Updater.addUploadFiles(event.target.files, event.target.files.length);
|
||||
this.update(Updater.setBrowser);
|
||||
deleteUpload = (filePath: string): Promise<void> => {
|
||||
return updater()
|
||||
.deleteUpload(filePath)
|
||||
.then((ok: boolean) => {
|
||||
if (!ok) {
|
||||
alertMsg(`Failed to delete uploading ${filePath}`);
|
||||
}
|
||||
return updater().refreshUploadings();
|
||||
})
|
||||
.then(() => {
|
||||
this.update(updater().setBrowser);
|
||||
});
|
||||
};
|
||||
|
||||
updateProgress = (infos: Map<string, UploadEntry>) => {
|
||||
Updater.setUploadings(infos);
|
||||
Updater.setItems(this.props.dirPath).then(() => {
|
||||
this.update(Updater.setBrowser);
|
||||
});
|
||||
stopUploading = (filePath: string) => {
|
||||
updater().stopUploading(filePath);
|
||||
this.update(updater().setBrowser);
|
||||
};
|
||||
|
||||
onMkDir = () => {
|
||||
if (this.state.inputValue === "") {
|
||||
alert("folder name can not be empty");
|
||||
alertMsg("folder name can not be empty");
|
||||
return;
|
||||
}
|
||||
|
||||
const dirPath = getItemPath(
|
||||
this.props.dirPath.join("/"),
|
||||
this.state.inputValue
|
||||
);
|
||||
Updater.mkDir(dirPath)
|
||||
updater()
|
||||
.mkDir(dirPath)
|
||||
.then(() => {
|
||||
this.setState({ inputValue: "" });
|
||||
return Updater.setItems(this.props.dirPath);
|
||||
return updater().setItems(this.props.dirPath);
|
||||
})
|
||||
.then(() => {
|
||||
this.update(Updater.setBrowser);
|
||||
this.update(updater().setBrowser);
|
||||
});
|
||||
};
|
||||
|
||||
deleteUploading = (filePath: string) => {
|
||||
Updater.deleteUploading(filePath)
|
||||
.then((ok: boolean) => {
|
||||
if (!ok) {
|
||||
alert(`Failed to delete uploading ${filePath}`);
|
||||
}
|
||||
return Updater.refreshUploadings();
|
||||
})
|
||||
.then(() => {
|
||||
this.update(Updater.setBrowser);
|
||||
});
|
||||
};
|
||||
|
||||
stopUploading = (filePath: string) => {
|
||||
Updater.stopUploading(filePath);
|
||||
this.update(Updater.setBrowser);
|
||||
};
|
||||
|
||||
delete = () => {
|
||||
if (this.props.dirPath.join("/") !== this.state.selectedSrc) {
|
||||
alert("please select file or folder to delete at first");
|
||||
alertMsg("please select file or folder to delete at first");
|
||||
this.setState({
|
||||
selectedSrc: this.props.dirPath.join("/"),
|
||||
selectedItems: Map<string, boolean>(),
|
||||
|
@ -292,17 +141,38 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
return;
|
||||
}
|
||||
|
||||
Updater.delete(
|
||||
this.props.dirPath,
|
||||
this.props.items,
|
||||
this.state.selectedItems
|
||||
).then(() => {
|
||||
this.update(Updater.setBrowser);
|
||||
this.setState({
|
||||
selectedSrc: "",
|
||||
selectedItems: Map<string, boolean>(),
|
||||
updater()
|
||||
.delete(this.props.dirPath, this.props.items, this.state.selectedItems)
|
||||
.then(() => {
|
||||
this.update(updater().setBrowser);
|
||||
this.setState({
|
||||
selectedSrc: "",
|
||||
selectedItems: Map<string, boolean>(),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
moveHere = () => {
|
||||
const oldDir = this.state.selectedSrc;
|
||||
const newDir = this.props.dirPath.join("/");
|
||||
if (oldDir === newDir) {
|
||||
alertMsg("source directory is same as destination directory");
|
||||
return;
|
||||
}
|
||||
|
||||
updater()
|
||||
.moveHere(
|
||||
this.state.selectedSrc,
|
||||
this.props.dirPath.join("/"),
|
||||
this.state.selectedItems
|
||||
)
|
||||
.then(() => {
|
||||
this.update(updater().setBrowser);
|
||||
this.setState({
|
||||
selectedSrc: "",
|
||||
selectedItems: Map<string, boolean>(),
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
gotoChild = (childDirName: string) => {
|
||||
|
@ -314,29 +184,49 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
return;
|
||||
}
|
||||
|
||||
Updater.setItems(dirPath).then(() => {
|
||||
this.update(Updater.setBrowser);
|
||||
updater()
|
||||
.setItems(dirPath)
|
||||
.then(() => {
|
||||
this.update(updater().setBrowser);
|
||||
});
|
||||
};
|
||||
|
||||
updateProgress = (infos: Map<string, UploadEntry>) => {
|
||||
updater().setUploadings(infos);
|
||||
updater()
|
||||
.setItems(this.props.dirPath)
|
||||
.then(() => {
|
||||
this.update(updater().setBrowser);
|
||||
});
|
||||
};
|
||||
|
||||
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.dirPath.join("/"),
|
||||
selectedItems: selectedItems,
|
||||
});
|
||||
};
|
||||
|
||||
moveHere = () => {
|
||||
const oldDir = this.state.selectedSrc;
|
||||
const newDir = this.props.dirPath.join("/");
|
||||
if (oldDir === newDir) {
|
||||
alert("source directory is same as destination directory");
|
||||
return;
|
||||
selectAll = () => {
|
||||
let newSelected = Map<string, boolean>();
|
||||
const someSelected = this.state.selectedItems.size === 0 ? true : false;
|
||||
if (someSelected) {
|
||||
this.props.items.forEach((item) => {
|
||||
newSelected = newSelected.set(item.name, true);
|
||||
});
|
||||
} else {
|
||||
this.props.items.forEach((item) => {
|
||||
newSelected = newSelected.delete(item.name);
|
||||
});
|
||||
}
|
||||
|
||||
Updater.moveHere(
|
||||
this.state.selectedSrc,
|
||||
this.props.dirPath.join("/"),
|
||||
this.state.selectedItems
|
||||
).then(() => {
|
||||
this.update(Updater.setBrowser);
|
||||
this.setState({
|
||||
selectedSrc: "",
|
||||
selectedItems: Map<string, boolean>(),
|
||||
});
|
||||
this.setState({
|
||||
selectedSrc: this.props.dirPath.join("/"),
|
||||
selectedItems: newSelected,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -397,7 +287,7 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
</button>
|
||||
<input
|
||||
type="file"
|
||||
onChange={this.addUploadFile}
|
||||
onChange={this.addUploads}
|
||||
multiple={true}
|
||||
value={this.props.uploadValue}
|
||||
ref={this.assignInput}
|
||||
|
@ -505,7 +395,7 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
Stop
|
||||
</button>
|
||||
<button
|
||||
onClick={() => this.deleteUploading(uploading.realFilePath)}
|
||||
onClick={() => this.deleteUpload(uploading.realFilePath)}
|
||||
className="white-font"
|
||||
>
|
||||
Delete
|
||||
|
@ -563,7 +453,15 @@ export class Browser extends React.Component<Props, State, {}> {
|
|||
<td>Name</td>
|
||||
<td className={sizeCellClass}>File Size</td>
|
||||
<td className={modTimeCellClass}>Mod Time</td>
|
||||
<td>Edit</td>
|
||||
<td>
|
||||
<button
|
||||
onClick={() => this.selectAll()}
|
||||
className={`white-font`}
|
||||
style={{ width: "8rem", display: "inline-block" }}
|
||||
>
|
||||
Select All
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{itemList}</tbody>
|
||||
|
|
153
src/client/web/src/components/browser.updater.ts
Normal file
153
src/client/web/src/components/browser.updater.ts
Normal file
|
@ -0,0 +1,153 @@
|
|||
import { List, Map } from "immutable";
|
||||
|
||||
import { ICoreState } from "./core_state";
|
||||
import { Props, getItemPath } from "./browser";
|
||||
import {
|
||||
IUsersClient,
|
||||
IFilesClient,
|
||||
MetadataResp,
|
||||
UploadInfo,
|
||||
} from "../client";
|
||||
import { FilesClient } from "../client/files";
|
||||
import { UsersClient } from "../client/users";
|
||||
import { UploadEntry } from "../worker/interface";
|
||||
import { Up } from "../worker/upload_mgr";
|
||||
|
||||
export class Updater {
|
||||
props: Props;
|
||||
private usersClient: IUsersClient = new UsersClient("");
|
||||
private filesClient: IFilesClient = new FilesClient("");
|
||||
|
||||
init = (props: Props) => (this.props = { ...props });
|
||||
setClients(usersClient: IUsersClient, filesClient: IFilesClient) {
|
||||
this.usersClient = usersClient;
|
||||
this.filesClient = filesClient;
|
||||
}
|
||||
|
||||
addUploads = (fileList: List<File>) => {
|
||||
fileList.forEach(file => {
|
||||
const filePath = getItemPath(
|
||||
this.props.dirPath.join("/"),
|
||||
file.name
|
||||
);
|
||||
// do not wait for the promise
|
||||
Up().add(file, filePath);
|
||||
})
|
||||
this.setUploadings(Up().list());
|
||||
};
|
||||
|
||||
deleteUpload = async (filePath: string): Promise<boolean> => {
|
||||
Up().delete(filePath);
|
||||
const resp = await this.filesClient.deleteUploading(filePath);
|
||||
return resp.status === 200;
|
||||
};
|
||||
|
||||
setUploadings = (infos: Map<string, UploadEntry>) => {
|
||||
this.props.uploadings = List<UploadInfo>(
|
||||
infos.valueSeq().map(
|
||||
(v: UploadEntry): UploadInfo => {
|
||||
return {
|
||||
realFilePath: v.filePath,
|
||||
size: v.size,
|
||||
uploaded: v.uploaded,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
refreshUploadings = async (): Promise<boolean> => {
|
||||
const luResp = await this.filesClient.listUploadings();
|
||||
|
||||
this.props.uploadings =
|
||||
luResp.status === 200
|
||||
? List<UploadInfo>(luResp.data.uploadInfos)
|
||||
: this.props.uploadings;
|
||||
return luResp.status === 200;
|
||||
};
|
||||
|
||||
stopUploading = (filePath: string) => {
|
||||
Up().stop(filePath);
|
||||
};
|
||||
|
||||
mkDir = async (dirPath: string): Promise<void> => {
|
||||
const resp = await this.filesClient.mkdir(dirPath);
|
||||
if (resp.status !== 200) {
|
||||
alert(`failed to make dir ${dirPath}`);
|
||||
}
|
||||
};
|
||||
|
||||
delete = async (
|
||||
dirParts: List<string>,
|
||||
items: List<MetadataResp>,
|
||||
selectedItems: Map<string, boolean>
|
||||
): Promise<void> => {
|
||||
const delRequests = items
|
||||
.filter((item) => {
|
||||
return selectedItems.has(item.name);
|
||||
})
|
||||
.map(
|
||||
async (selectedItem: MetadataResp): Promise<string> => {
|
||||
const itemPath = getItemPath(dirParts.join("/"), selectedItem.name);
|
||||
const resp = await this.filesClient.delete(itemPath);
|
||||
return resp.status === 200 ? "" : selectedItem.name;
|
||||
}
|
||||
);
|
||||
|
||||
const failedFiles = await Promise.all(delRequests);
|
||||
failedFiles.forEach((failedFile) => {
|
||||
if (failedFile !== "") {
|
||||
alert(`failed to delete ${failedFile}`);
|
||||
}
|
||||
});
|
||||
return this.setItems(dirParts);
|
||||
};
|
||||
|
||||
setItems = async (dirParts: List<string>): Promise<void> => {
|
||||
const dirPath = dirParts.join("/");
|
||||
const listResp = await this.filesClient.list(dirPath);
|
||||
|
||||
this.props.dirPath = dirParts;
|
||||
this.props.items =
|
||||
listResp.status === 200
|
||||
? List<MetadataResp>(listResp.data.metadatas)
|
||||
: this.props.items;
|
||||
};
|
||||
|
||||
moveHere = async (
|
||||
srcDir: string,
|
||||
dstDir: string,
|
||||
selectedItems: Map<string, boolean>
|
||||
): Promise<void> => {
|
||||
const moveRequests = List<string>(selectedItems.keys()).map(
|
||||
async (itemName: string): Promise<string> => {
|
||||
const oldPath = getItemPath(srcDir, itemName);
|
||||
const newPath = getItemPath(dstDir, itemName);
|
||||
const resp = await this.filesClient.move(oldPath, newPath);
|
||||
return resp.status === 200 ? "" : itemName;
|
||||
}
|
||||
);
|
||||
|
||||
const failedFiles = await Promise.all(moveRequests);
|
||||
failedFiles.forEach((failedItem) => {
|
||||
if (failedItem !== "") {
|
||||
alert(`failed to move ${failedItem}`);
|
||||
}
|
||||
});
|
||||
|
||||
return this.setItems(List<string>(dstDir.split("/")));
|
||||
};
|
||||
|
||||
setBrowser = (prevState: ICoreState): ICoreState => {
|
||||
prevState.panel.browser = { ...prevState.panel, ...this.props };
|
||||
return prevState;
|
||||
};
|
||||
}
|
||||
|
||||
export let browserUpdater = new Updater();
|
||||
export const updater = (): Updater => {
|
||||
return browserUpdater;
|
||||
};
|
||||
export const setUpdater = (updater: Updater) => {
|
||||
browserUpdater = updater;
|
||||
};
|
|
@ -6,7 +6,7 @@ import { FgWorker } from "../worker/upload.fgworker";
|
|||
import { Props as PanelProps } from "./root_frame";
|
||||
import { Item } from "./browser";
|
||||
import { UploadInfo } from "../client";
|
||||
import { UploadMgr, IWorker } from "../worker/upload_mgr";
|
||||
import { Up, initUploadMgr, IWorker } from "../worker/upload_mgr";
|
||||
|
||||
export class BaseUpdater {
|
||||
public static props: any;
|
||||
|
@ -27,15 +27,15 @@ export interface ICoreState {
|
|||
}
|
||||
|
||||
export function initWithWorker(worker: IWorker): ICoreState {
|
||||
UploadMgr.init(worker);
|
||||
initUploadMgr(worker);
|
||||
return initState();
|
||||
}
|
||||
|
||||
export function init(): ICoreState {
|
||||
const scripts = Array.from(document.querySelectorAll("script"));
|
||||
const worker = Worker == null ? new FgWorker() : new BgWorker();
|
||||
initUploadMgr(worker);
|
||||
|
||||
UploadMgr.init(worker);
|
||||
return initState();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { ICoreState } from "./core_state";
|
|||
import { IUsersClient } from "../client";
|
||||
import { UsersClient } from "../client/users";
|
||||
import { Updater as PanesUpdater } from "./panes";
|
||||
import { Updater as BrowserUpdater } from "./browser";
|
||||
import { updater as BrowserUpdater } from "./browser.updater";
|
||||
import { Layouter } from "./layouter";
|
||||
|
||||
export interface Props {
|
||||
|
@ -104,7 +104,7 @@ export class AuthPane extends React.Component<Props, State, {}> {
|
|||
this.update(PanesUpdater.updateState);
|
||||
|
||||
// refresh
|
||||
return BrowserUpdater.setItems(
|
||||
return BrowserUpdater().setItems(
|
||||
List<string>(["."])
|
||||
);
|
||||
} else {
|
||||
|
@ -113,10 +113,10 @@ export class AuthPane extends React.Component<Props, State, {}> {
|
|||
}
|
||||
})
|
||||
.then(() => {
|
||||
return BrowserUpdater.refreshUploadings();
|
||||
return BrowserUpdater().refreshUploadings();
|
||||
})
|
||||
.then((_: boolean) => {
|
||||
this.update(BrowserUpdater.setBrowser);
|
||||
this.update(BrowserUpdater().setBrowser);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import * as React from "react";
|
||||
|
||||
import { updater as BrowserUpdater } from "./browser.updater";
|
||||
import { ICoreState, init } from "./core_state";
|
||||
import { RootFrame } from "./root_frame";
|
||||
import { FilesClient } from "../client/files";
|
||||
import { UsersClient } from "../client/users";
|
||||
|
||||
export interface Props {}
|
||||
export interface State extends ICoreState {}
|
||||
|
@ -10,8 +13,22 @@ export class StateMgr extends React.Component<Props, State, {}> {
|
|||
constructor(p: Props) {
|
||||
super(p);
|
||||
this.state = init();
|
||||
this.initUpdaters(this.state);
|
||||
}
|
||||
|
||||
initUpdaters = (state: ICoreState) => {
|
||||
BrowserUpdater().init(state.panel.browser);
|
||||
BrowserUpdater().setClients(new UsersClient(""), new FilesClient(""));
|
||||
BrowserUpdater()
|
||||
.setItems(state.panel.browser.dirPath)
|
||||
.then(() => {
|
||||
return BrowserUpdater().refreshUploadings();
|
||||
})
|
||||
.then((_: boolean) => {
|
||||
this.update(BrowserUpdater().setBrowser);
|
||||
});
|
||||
};
|
||||
|
||||
update = (apply: (prevState: ICoreState) => ICoreState): void => {
|
||||
this.setState(apply(this.state));
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Response } from "../client";
|
||||
import { ICoreState } from "../components/core_state";
|
||||
import { ICoreState, mockState } from "../components/core_state";
|
||||
import { List } from "immutable";
|
||||
|
||||
export const makePromise = (ret: any): Promise<any> => {
|
||||
return new Promise<any>((resolve) => {
|
||||
|
@ -15,4 +16,25 @@ export const makeNumberResponse = (status: number): Promise<Response> => {
|
|||
});
|
||||
};
|
||||
|
||||
export const mockUpdate = (apply: (prevState: ICoreState) => ICoreState): void => {};
|
||||
export const mockUpdate = (
|
||||
apply: (prevState: ICoreState) => ICoreState
|
||||
): void => {
|
||||
apply(mockState());
|
||||
};
|
||||
|
||||
export const addMockUpdate = (subState: any) => {
|
||||
subState.update = mockUpdate;
|
||||
};
|
||||
|
||||
export function mockRandFile(filePath: string): File {
|
||||
const values = new Array<string>(Math.floor(7 * Math.random()));
|
||||
const content = [values.join("")];
|
||||
return new File(content, filePath);
|
||||
}
|
||||
|
||||
export function mockFileList(filePaths: Array<string>): List<File> {
|
||||
const files = filePaths.map(filePath => {
|
||||
return mockRandFile(filePath);
|
||||
})
|
||||
return List<File>(files);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { mock, instance, when, anything } from "ts-mockito";
|
|||
import { FilesClient } from "../../client/files_mock";
|
||||
import { makePromise } from "../../test/helpers";
|
||||
|
||||
import { UploadMgr } from "../upload_mgr";
|
||||
import { Up, initUploadMgr } from "../upload_mgr";
|
||||
|
||||
import {
|
||||
FileWorkerReq,
|
||||
|
@ -115,17 +115,18 @@ describe("UploadMgr", () => {
|
|||
];
|
||||
|
||||
const worker = new MockWorker();
|
||||
UploadMgr.setCycle(100);
|
||||
|
||||
for (let i = 0; i < tcs.length; i++) {
|
||||
const infoMap = arraytoMap(tcs[i].inputInfos);
|
||||
UploadMgr._setInfos(infoMap);
|
||||
initUploadMgr(worker);
|
||||
const up = Up();
|
||||
up.setCycle(100);
|
||||
|
||||
const infoMap = arraytoMap(tcs[i].inputInfos);
|
||||
up._setInfos(infoMap);
|
||||
|
||||
UploadMgr.init(worker);
|
||||
// polling needs several rounds to finish all the tasks
|
||||
await delay(tcs.length * UploadMgr.getCycle() + 1000);
|
||||
await delay(tcs.length * up.getCycle() + 1000);
|
||||
// TODO: find a better way to wait
|
||||
const gotInfos = UploadMgr.list();
|
||||
const gotInfos = up.list();
|
||||
|
||||
const expectedInfoMap = arraytoMap(tcs[i].expectedInfos);
|
||||
gotInfos.keySeq().forEach((filePath) => {
|
||||
|
@ -135,7 +136,7 @@ describe("UploadMgr", () => {
|
|||
expect(expectedInfoMap.get(filePath)).toEqual(gotInfos.get(filePath));
|
||||
});
|
||||
|
||||
UploadMgr.destory();
|
||||
up.destory();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,8 +10,9 @@ import {
|
|||
errKind,
|
||||
uploadInfoKind,
|
||||
} from "./interface";
|
||||
import { FgWorker } from "./upload.fgworker";
|
||||
|
||||
const win = self as any;
|
||||
const win: Window = self as any;
|
||||
|
||||
export interface IWorker {
|
||||
onmessage: (event: MessageEvent) => void;
|
||||
|
@ -19,51 +20,51 @@ export interface IWorker {
|
|||
}
|
||||
|
||||
export class UploadMgr {
|
||||
private static infos = Map<string, UploadEntry>();
|
||||
private static worker: IWorker;
|
||||
private static intervalID: NodeJS.Timeout;
|
||||
private static cycle: number = 500;
|
||||
private static statusCb = (infos: Map<string, UploadEntry>):void => {};
|
||||
private infos = Map<string, UploadEntry>();
|
||||
private worker: IWorker;
|
||||
private intervalID: number;
|
||||
private cycle: number = 500;
|
||||
private statusCb = (infos: Map<string, UploadEntry>): void => {};
|
||||
|
||||
static _setInfos = (infos: Map<string, UploadEntry>) => {
|
||||
UploadMgr.infos = infos;
|
||||
};
|
||||
|
||||
static setCycle = (ms: number) => {
|
||||
UploadMgr.cycle = ms;
|
||||
};
|
||||
|
||||
static getCycle = (): number => {
|
||||
return UploadMgr.cycle;
|
||||
};
|
||||
|
||||
static setStatusCb = (cb: (infos: Map<string, UploadEntry>) => void) => {
|
||||
UploadMgr.statusCb = cb;
|
||||
}
|
||||
|
||||
static init = (worker: IWorker) => {
|
||||
UploadMgr.worker = worker;
|
||||
constructor(worker: IWorker) {
|
||||
this.worker = worker;
|
||||
// TODO: fallback to normal if Web Worker is not available
|
||||
UploadMgr.worker.onmessage = UploadMgr.respHandler;
|
||||
this.worker.onmessage = this.respHandler;
|
||||
|
||||
const syncing = () => {
|
||||
UploadMgr.worker.postMessage({
|
||||
this.worker.postMessage({
|
||||
kind: syncReqKind,
|
||||
infos: UploadMgr.infos.valueSeq().toArray(),
|
||||
infos: this.infos.valueSeq().toArray(),
|
||||
});
|
||||
};
|
||||
UploadMgr.intervalID = win.setInterval(syncing, UploadMgr.cycle);
|
||||
this.intervalID = win.setInterval(syncing, this.cycle);
|
||||
}
|
||||
|
||||
destory = () => {
|
||||
win.clearInterval(this.intervalID);
|
||||
};
|
||||
|
||||
static destory = () => {
|
||||
win.clearInterval(UploadMgr.intervalID);
|
||||
_setInfos = (infos: Map<string, UploadEntry>) => {
|
||||
this.infos = infos;
|
||||
};
|
||||
|
||||
static add = (file: File, filePath: string) => {
|
||||
const entry = UploadMgr.infos.get(filePath);
|
||||
setCycle = (ms: number) => {
|
||||
this.cycle = ms;
|
||||
};
|
||||
|
||||
getCycle = (): number => {
|
||||
return this.cycle;
|
||||
};
|
||||
|
||||
setStatusCb = (cb: (infos: Map<string, UploadEntry>) => void) => {
|
||||
this.statusCb = cb;
|
||||
};
|
||||
|
||||
add = (file: File, filePath: string) => {
|
||||
const entry = this.infos.get(filePath);
|
||||
if (entry == null) {
|
||||
// new uploading
|
||||
UploadMgr.infos = UploadMgr.infos.set(filePath, {
|
||||
this.infos = this.infos.set(filePath, {
|
||||
file: file,
|
||||
filePath: filePath,
|
||||
size: file.size,
|
||||
|
@ -73,36 +74,35 @@ export class UploadMgr {
|
|||
});
|
||||
} else {
|
||||
// restart the uploading
|
||||
UploadMgr.infos = UploadMgr.infos.set(filePath, {
|
||||
this.infos = this.infos.set(filePath, {
|
||||
...entry,
|
||||
runnable: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
static stop = (filePath: string) => {
|
||||
const entry = UploadMgr.infos.get(filePath);
|
||||
stop = (filePath: string) => {
|
||||
const entry = this.infos.get(filePath);
|
||||
if (entry != null) {
|
||||
UploadMgr.infos = UploadMgr.infos.set(filePath, {
|
||||
this.infos = this.infos.set(filePath, {
|
||||
...entry,
|
||||
runnable: false,
|
||||
});
|
||||
console.log("stopped", filePath);
|
||||
} else {
|
||||
alert(`failed to stop uploading ${filePath}: not found`);
|
||||
}
|
||||
};
|
||||
|
||||
static delete = (filePath: string) => {
|
||||
UploadMgr.stop(filePath);
|
||||
UploadMgr.infos = UploadMgr.infos.delete(filePath);
|
||||
delete = (filePath: string) => {
|
||||
this.stop(filePath);
|
||||
this.infos = this.infos.delete(filePath);
|
||||
};
|
||||
|
||||
static list = (): Map<string, UploadEntry> => {
|
||||
return UploadMgr.infos;
|
||||
list = (): Map<string, UploadEntry> => {
|
||||
return this.infos;
|
||||
};
|
||||
|
||||
static respHandler = (event: MessageEvent) => {
|
||||
respHandler = (event: MessageEvent) => {
|
||||
const resp = event.data as FileWorkerResp;
|
||||
|
||||
switch (resp.kind) {
|
||||
|
@ -113,13 +113,13 @@ export class UploadMgr {
|
|||
break;
|
||||
case uploadInfoKind:
|
||||
const infoResp = resp as UploadInfoResp;
|
||||
const entry = UploadMgr.infos.get(infoResp.filePath);
|
||||
const entry = this.infos.get(infoResp.filePath);
|
||||
|
||||
if (entry != null) {
|
||||
if (infoResp.uploaded === entry.size) {
|
||||
UploadMgr.infos = UploadMgr.infos.delete(infoResp.filePath);
|
||||
this.infos = this.infos.delete(infoResp.filePath);
|
||||
} else {
|
||||
UploadMgr.infos = UploadMgr.infos.set(infoResp.filePath, {
|
||||
this.infos = this.infos.set(infoResp.filePath, {
|
||||
...entry,
|
||||
uploaded: infoResp.uploaded,
|
||||
runnable: infoResp.runnable,
|
||||
|
@ -128,13 +128,13 @@ export class UploadMgr {
|
|||
}
|
||||
|
||||
// call back to update the info
|
||||
UploadMgr.statusCb(UploadMgr.infos);
|
||||
this.statusCb(this.infos);
|
||||
} else {
|
||||
// TODO: refine this
|
||||
console.error(
|
||||
`respHandler: fail to found: file(${
|
||||
infoResp.filePath
|
||||
}) infos(${UploadMgr.infos.toObject()})`
|
||||
}) infos(${this.infos.toObject()})`
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
@ -143,3 +143,15 @@ export class UploadMgr {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
export let uploadMgr = new UploadMgr(new FgWorker());
|
||||
export const initUploadMgr = (worker: IWorker): UploadMgr => {
|
||||
uploadMgr = new UploadMgr(worker);
|
||||
return uploadMgr;
|
||||
};
|
||||
export const Up = (): UploadMgr => {
|
||||
return uploadMgr;
|
||||
};
|
||||
export const setUploadMgr = (up: UploadMgr) => {
|
||||
uploadMgr = up;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue