fix(browser): split browser into 3 panels
This commit is contained in:
parent
f181c023a6
commit
f84a99de26
15 changed files with 2151 additions and 1076 deletions
|
@ -1,120 +1,120 @@
|
||||||
import { mock, instance, verify, when, anything } from "ts-mockito";
|
// import { mock, instance, verify, when, anything } from "ts-mockito";
|
||||||
import { List } from "immutable";
|
// import { List } from "immutable";
|
||||||
|
|
||||||
import { Browser } from "../browser";
|
// import { Browser } from "../browser";
|
||||||
import { initUploadMgr } from "../../worker/upload_mgr";
|
// import { initUploadMgr } from "../../worker/upload_mgr";
|
||||||
import { ICoreState, newState } from "../core_state";
|
// import { ICoreState, newState } from "../core_state";
|
||||||
import { updater } from "../state_updater";
|
// import { updater } from "../state_updater";
|
||||||
import { MockWorker } from "../../worker/interface";
|
// import { MockWorker } from "../../worker/interface";
|
||||||
import { MockUsersClient, resps as usersResps } from "../../client/users_mock";
|
// import { MockUsersClient, resps as usersResps } from "../../client/users_mock";
|
||||||
import { MockFilesClient, resps as filesResps } from "../../client/files_mock";
|
// import { MockFilesClient, resps as filesResps } from "../../client/files_mock";
|
||||||
import { MockSettingsClient } from "../../client/settings_mock";
|
// import { MockSettingsClient } from "../../client/settings_mock";
|
||||||
|
|
||||||
describe("Browser", () => {
|
// describe("Browser", () => {
|
||||||
const initBrowser = (): any => {
|
// const initBrowser = (): any => {
|
||||||
const mockWorkerClass = mock(MockWorker);
|
// const mockWorkerClass = mock(MockWorker);
|
||||||
const mockWorker = instance(mockWorkerClass);
|
// const mockWorker = instance(mockWorkerClass);
|
||||||
initUploadMgr(mockWorker);
|
// initUploadMgr(mockWorker);
|
||||||
|
|
||||||
const coreState = newState();
|
// const coreState = newState();
|
||||||
const usersCl = new MockUsersClient("");
|
// const usersCl = new MockUsersClient("");
|
||||||
const filesCl = new MockFilesClient("");
|
// const filesCl = new MockFilesClient("");
|
||||||
const settingsCl = new MockSettingsClient("");
|
// const settingsCl = new MockSettingsClient("");
|
||||||
|
|
||||||
updater().init(coreState);
|
// updater().init(coreState);
|
||||||
updater().setClients(usersCl, filesCl, settingsCl);
|
// updater().setClients(usersCl, filesCl, settingsCl);
|
||||||
|
|
||||||
const browser = new Browser({
|
// const browser = new Browser({
|
||||||
browser: coreState.browser,
|
// browser: coreState.browser,
|
||||||
msg: coreState.msg,
|
// msg: coreState.msg,
|
||||||
login: coreState.login,
|
// login: coreState.login,
|
||||||
ui: coreState.ui,
|
// ui: coreState.ui,
|
||||||
update: (updater: (prevState: ICoreState) => ICoreState) => {},
|
// update: (updater: (prevState: ICoreState) => ICoreState) => {},
|
||||||
});
|
// });
|
||||||
|
|
||||||
return {
|
// return {
|
||||||
browser,
|
// browser,
|
||||||
usersCl,
|
// usersCl,
|
||||||
filesCl,
|
// filesCl,
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
test("addUploads", async () => {
|
// test("addUploads", async () => {
|
||||||
const { browser, usersCl, filesCl } = initBrowser();
|
// const { browser, usersCl, filesCl } = initBrowser();
|
||||||
|
|
||||||
const newSharings = [
|
// const newSharings = [
|
||||||
"mock_sharingfolder1",
|
// "mock_sharingfolder1",
|
||||||
"mock_sharingfolder2",
|
// "mock_sharingfolder2",
|
||||||
"newSharing",
|
// "newSharing",
|
||||||
];
|
// ];
|
||||||
|
|
||||||
filesCl.setMock({
|
// filesCl.setMock({
|
||||||
...filesResps,
|
// ...filesResps,
|
||||||
listSharingsMockResp: {
|
// listSharingsMockResp: {
|
||||||
status: 200,
|
// status: 200,
|
||||||
statusText: "",
|
// statusText: "",
|
||||||
data: {
|
// data: {
|
||||||
sharingDirs: newSharings,
|
// sharingDirs: newSharings,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
await browser.addSharing();
|
// await browser.addSharing();
|
||||||
|
|
||||||
// TODO: check addSharing's input
|
// // TODO: check addSharing's input
|
||||||
expect(updater().props.browser.isSharing).toEqual(true);
|
// expect(updater().props.browser.isSharing).toEqual(true);
|
||||||
expect(updater().props.browser.sharings).toEqual(List(newSharings));
|
// expect(updater().props.browser.sharings).toEqual(List(newSharings));
|
||||||
});
|
// });
|
||||||
|
|
||||||
test("deleteUploads", async () => {
|
// test("deleteUploads", async () => {
|
||||||
const { browser, usersCl, filesCl } = initBrowser();
|
// const { browser, usersCl, filesCl } = initBrowser();
|
||||||
|
|
||||||
const newSharings = ["mock_sharingfolder1", "mock_sharingfolder2"];
|
// const newSharings = ["mock_sharingfolder1", "mock_sharingfolder2"];
|
||||||
|
|
||||||
filesCl.setMock({
|
// filesCl.setMock({
|
||||||
...filesResps,
|
// ...filesResps,
|
||||||
listSharingsMockResp: {
|
// listSharingsMockResp: {
|
||||||
status: 200,
|
// status: 200,
|
||||||
statusText: "",
|
// statusText: "",
|
||||||
data: {
|
// data: {
|
||||||
sharingDirs: newSharings,
|
// sharingDirs: newSharings,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
await browser.deleteSharing();
|
// await browser.deleteSharing();
|
||||||
|
|
||||||
// TODO: check delSharing's input
|
// // TODO: check delSharing's input
|
||||||
expect(updater().props.browser.isSharing).toEqual(false);
|
// expect(updater().props.browser.isSharing).toEqual(false);
|
||||||
expect(updater().props.browser.sharings).toEqual(List(newSharings));
|
// expect(updater().props.browser.sharings).toEqual(List(newSharings));
|
||||||
});
|
// });
|
||||||
|
|
||||||
test("chdir", async () => {
|
// test("chdir", async () => {
|
||||||
const { browser, usersCl, filesCl } = initBrowser();
|
// const { browser, usersCl, filesCl } = initBrowser();
|
||||||
|
|
||||||
const newSharings = ["mock_sharingfolder1", "mock_sharingfolder2"];
|
// const newSharings = ["mock_sharingfolder1", "mock_sharingfolder2"];
|
||||||
const newCwd = List(["newPos", "subFolder"]);
|
// const newCwd = List(["newPos", "subFolder"]);
|
||||||
|
|
||||||
filesCl.setMock({
|
// filesCl.setMock({
|
||||||
...filesResps,
|
// ...filesResps,
|
||||||
listSharingsMockResp: {
|
// listSharingsMockResp: {
|
||||||
status: 200,
|
// status: 200,
|
||||||
statusText: "",
|
// statusText: "",
|
||||||
data: {
|
// data: {
|
||||||
sharingDirs: newSharings,
|
// sharingDirs: newSharings,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
await browser.chdir(newCwd);
|
// await browser.chdir(newCwd);
|
||||||
|
|
||||||
expect(updater().props.browser.dirPath).toEqual(newCwd);
|
// expect(updater().props.browser.dirPath).toEqual(newCwd);
|
||||||
expect(updater().props.browser.isSharing).toEqual(true);
|
// expect(updater().props.browser.isSharing).toEqual(true);
|
||||||
expect(updater().props.browser.sharings).toEqual(
|
// expect(updater().props.browser.sharings).toEqual(
|
||||||
List(filesResps.listSharingsMockResp.data.sharingDirs)
|
// List(filesResps.listSharingsMockResp.data.sharingDirs)
|
||||||
);
|
// );
|
||||||
expect(updater().props.browser.items).toEqual(
|
// expect(updater().props.browser.items).toEqual(
|
||||||
List(filesResps.listHomeMockResp.data.metadatas)
|
// List(filesResps.listHomeMockResp.data.metadatas)
|
||||||
);
|
// );
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,13 +2,19 @@ import { List, Set, Map } from "immutable";
|
||||||
|
|
||||||
import { UploadEntry } from "../worker/interface";
|
import { UploadEntry } from "../worker/interface";
|
||||||
|
|
||||||
import { BrowserProps } from "./browser";
|
import { FilesProps } from "./panel_files";
|
||||||
|
import { UploadingsProps } from "./panel_uploadings";
|
||||||
|
import { SharingsProps } from "./panel_sharings";
|
||||||
import { PanesProps } from "./panes";
|
import { PanesProps } from "./panes";
|
||||||
import { LoginProps } from "./pane_login";
|
import { LoginProps } from "./pane_login";
|
||||||
import { AdminProps } from "./pane_admin";
|
import { AdminProps } from "./pane_admin";
|
||||||
import { MsgPackage } from "../i18n/msger";
|
import { MsgPackage } from "../i18n/msger";
|
||||||
import { User, MetadataResp } from "../client";
|
import { User, MetadataResp } from "../client";
|
||||||
|
|
||||||
|
export interface PanelsProps {
|
||||||
|
displaying: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MsgProps {
|
export interface MsgProps {
|
||||||
lan: string;
|
lan: string;
|
||||||
pkg: Map<string, string>;
|
pkg: Map<string, string>;
|
||||||
|
@ -26,7 +32,10 @@ export interface UIProps {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export interface ICoreState {
|
export interface ICoreState {
|
||||||
browser: BrowserProps;
|
panels: PanelsProps;
|
||||||
|
filesInfo: FilesProps;
|
||||||
|
uploadingsInfo: UploadingsProps;
|
||||||
|
sharingsInfo: SharingsProps;
|
||||||
panes: PanesProps;
|
panes: PanesProps;
|
||||||
login: LoginProps;
|
login: LoginProps;
|
||||||
admin: AdminProps;
|
admin: AdminProps;
|
||||||
|
@ -40,15 +49,20 @@ export function newState(): ICoreState {
|
||||||
|
|
||||||
export function initState(): ICoreState {
|
export function initState(): ICoreState {
|
||||||
return {
|
return {
|
||||||
browser: {
|
panels: {
|
||||||
|
displaying: "item",
|
||||||
|
},
|
||||||
|
filesInfo: {
|
||||||
dirPath: List<string>([]),
|
dirPath: List<string>([]),
|
||||||
items: List<MetadataResp>([]),
|
items: List<MetadataResp>([]),
|
||||||
sharings: List<string>([]),
|
|
||||||
isSharing: false,
|
isSharing: false,
|
||||||
|
},
|
||||||
|
uploadingsInfo: {
|
||||||
uploadings: List<UploadEntry>([]),
|
uploadings: List<UploadEntry>([]),
|
||||||
uploadValue: "",
|
|
||||||
uploadFiles: List<File>([]),
|
uploadFiles: List<File>([]),
|
||||||
tab: "",
|
},
|
||||||
|
sharingsInfo: {
|
||||||
|
sharings: List<string>([]),
|
||||||
},
|
},
|
||||||
panes: {
|
panes: {
|
||||||
// which pane is displaying
|
// which pane is displaying
|
||||||
|
@ -78,7 +92,7 @@ export function initState(): ICoreState {
|
||||||
cssURL: "",
|
cssURL: "",
|
||||||
lanPackURL: "",
|
lanPackURL: "",
|
||||||
lan: "en_US",
|
lan: "en_US",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
users: Map<string, User>(),
|
users: Map<string, User>(),
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { ICoreState, MsgProps, UIProps } from "./core_state";
|
||||||
import { User, Quota } from "../client";
|
import { User, Quota } from "../client";
|
||||||
import { updater } from "./state_updater";
|
import { updater } from "./state_updater";
|
||||||
import { Flexbox } from "./layout/flexbox";
|
import { Flexbox } from "./layout/flexbox";
|
||||||
import { Flowgrid } from "./layout/flowgrid";
|
|
||||||
|
|
||||||
export interface AdminProps {
|
export interface AdminProps {
|
||||||
users: Map<string, User>;
|
users: Map<string, User>;
|
||||||
|
|
|
@ -75,7 +75,9 @@ export class AuthPane extends React.Component<Props, State, {}> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.update(updater().updateBrowser);
|
this.update(updater().updateFilesInfo);
|
||||||
|
this.update(updater().updateUploadingsInfo);
|
||||||
|
this.update(updater().updateSharingsInfo);
|
||||||
this.update(updater().updateLogin);
|
this.update(updater().updateLogin);
|
||||||
this.update(updater().updatePanes);
|
this.update(updater().updatePanes);
|
||||||
this.update(updater().updateAdmin);
|
this.update(updater().updateAdmin);
|
||||||
|
|
641
src/client/web/src/components/panel_files.tsx
Normal file
641
src/client/web/src/components/panel_files.tsx
Normal file
|
@ -0,0 +1,641 @@
|
||||||
|
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 { 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 { Flexbox } from "./layout/flexbox";
|
||||||
|
import { Up } from "../worker/upload_mgr";
|
||||||
|
import { UploadEntry, UploadState } from "../worker/interface";
|
||||||
|
|
||||||
|
export interface Item {
|
||||||
|
name: string;
|
||||||
|
size: number;
|
||||||
|
modTime: string;
|
||||||
|
isDir: boolean;
|
||||||
|
selected: boolean;
|
||||||
|
sha1: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilesProps {
|
||||||
|
dirPath: List<string>;
|
||||||
|
isSharing: boolean;
|
||||||
|
items: List<MetadataResp>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
filesInfo: FilesProps;
|
||||||
|
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<string, boolean>;
|
||||||
|
showDetail: Set<string>;
|
||||||
|
uploadFiles: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FilesPanel extends React.Component<Props, State, {}> {
|
||||||
|
private uploadInput: Element | Text;
|
||||||
|
private assignInput: (input: Element) => void;
|
||||||
|
private onClickUpload: () => void;
|
||||||
|
|
||||||
|
constructor(p: Props) {
|
||||||
|
super(p);
|
||||||
|
this.state = {
|
||||||
|
newFolderName: "",
|
||||||
|
selectedSrc: "",
|
||||||
|
selectedItems: Map<string, boolean>(),
|
||||||
|
showDetail: Set<string>(),
|
||||||
|
uploadFiles: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress = async (
|
||||||
|
infos: Map<string, UploadEntry>,
|
||||||
|
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.filesInfo.dirPath)
|
||||||
|
.then(() => {
|
||||||
|
this.props.update(updater().updateFilesInfo);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.props.update(updater().updateFilesInfo);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
addUploads = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (event.target.files.length > 1000) {
|
||||||
|
alertMsg(this.props.msg.pkg.get("err.tooManyUploads"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileList = List<File>();
|
||||||
|
for (let i = 0; i < event.target.files.length; i++) {
|
||||||
|
fileList = fileList.push(event.target.files[i]);
|
||||||
|
}
|
||||||
|
updater().addUploads(fileList);
|
||||||
|
this.props.update(updater().updateFilesInfo);
|
||||||
|
this.props.update(updater().updateSharingsInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
onNewFolderNameChange = (ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
this.setState({ newFolderName: ev.target.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
onMkDir = () => {
|
||||||
|
if (this.state.newFolderName === "") {
|
||||||
|
alertMsg(this.props.msg.pkg.get("browser.folder.add.fail"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dirPath = getItemPath(
|
||||||
|
this.props.filesInfo.dirPath.join("/"),
|
||||||
|
this.state.newFolderName
|
||||||
|
);
|
||||||
|
updater()
|
||||||
|
.mkDir(dirPath)
|
||||||
|
.then(() => {
|
||||||
|
this.setState({ newFolderName: "" });
|
||||||
|
return updater().setItems(this.props.filesInfo.dirPath);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.props.update(updater().updateFilesInfo);
|
||||||
|
this.props.update(updater().updateSharingsInfo);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
delete = () => {
|
||||||
|
// TODO: selected should be cleaned after change the cwd
|
||||||
|
if (this.props.filesInfo.dirPath.join("/") !== this.state.selectedSrc) {
|
||||||
|
alertMsg(this.props.msg.pkg.get("browser.del.fail"));
|
||||||
|
this.setState({
|
||||||
|
selectedSrc: this.props.filesInfo.dirPath.join("/"),
|
||||||
|
selectedItems: Map<string, boolean>(),
|
||||||
|
});
|
||||||
|
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.filesInfo.dirPath,
|
||||||
|
this.props.filesInfo.items,
|
||||||
|
this.state.selectedItems
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
return updater().self();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.props.update(updater().updateFilesInfo);
|
||||||
|
this.props.update(updater().updateSharingsInfo);
|
||||||
|
this.props.update(updater().updateLogin);
|
||||||
|
this.setState({
|
||||||
|
selectedSrc: "",
|
||||||
|
selectedItems: Map<string, boolean>(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
moveHere = () => {
|
||||||
|
const oldDir = this.state.selectedSrc;
|
||||||
|
const newDir = this.props.filesInfo.dirPath.join("/");
|
||||||
|
if (oldDir === newDir) {
|
||||||
|
alertMsg(this.props.msg.pkg.get("browser.move.fail"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updater()
|
||||||
|
.moveHere(
|
||||||
|
this.state.selectedSrc,
|
||||||
|
this.props.filesInfo.dirPath.join("/"),
|
||||||
|
this.state.selectedItems
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
this.props.update(updater().updateFilesInfo);
|
||||||
|
this.props.update(updater().updateSharingsInfo);
|
||||||
|
this.setState({
|
||||||
|
selectedSrc: "",
|
||||||
|
selectedItems: Map<string, boolean>(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
gotoChild = async (childDirName: string) => {
|
||||||
|
return this.chdir(this.props.filesInfo.dirPath.push(childDirName));
|
||||||
|
};
|
||||||
|
|
||||||
|
chdir = async (dirPath: List<string>) => {
|
||||||
|
if (dirPath === this.props.filesInfo.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().updateFilesInfo);
|
||||||
|
this.props.update(updater().updateSharingsInfo);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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.filesInfo.dirPath.join("/"),
|
||||||
|
selectedItems: selectedItems,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
selectAll = () => {
|
||||||
|
let newSelected = Map<string, boolean>();
|
||||||
|
const someSelected = this.state.selectedItems.size === 0 ? true : false;
|
||||||
|
if (someSelected) {
|
||||||
|
this.props.filesInfo.items.forEach((item) => {
|
||||||
|
newSelected = newSelected.set(item.name, true);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.props.filesInfo.items.forEach((item) => {
|
||||||
|
newSelected = newSelected.delete(item.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
selectedSrc: this.props.filesInfo.dirPath.join("/"),
|
||||||
|
selectedItems: newSelected,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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<boolean> => {
|
||||||
|
alertMsg(this.props.msg.pkg.get("refresh-hint"));
|
||||||
|
return updater().generateHash(filePath);
|
||||||
|
};
|
||||||
|
|
||||||
|
addSharing = async () => {
|
||||||
|
return updater()
|
||||||
|
.addSharing()
|
||||||
|
.then((ok) => {
|
||||||
|
if (!ok) {
|
||||||
|
alertMsg(this.props.msg.pkg.get("browser.share.add.fail"));
|
||||||
|
} else {
|
||||||
|
updater().setSharing(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.props.update(updater().updateFilesInfo);
|
||||||
|
this.props.update(updater().updateSharingsInfo);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.props.update(updater().updateFilesInfo);
|
||||||
|
this.props.update(updater().updateSharingsInfo);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const showOp = this.props.login.userRole === roleVisitor ? "hidden" : "";
|
||||||
|
const breadcrumb = this.props.filesInfo.dirPath.map(
|
||||||
|
(pathPart: string, key: number) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={pathPart}
|
||||||
|
onClick={() =>
|
||||||
|
this.chdir(this.props.filesInfo.dirPath.slice(0, key + 1))
|
||||||
|
}
|
||||||
|
className="item"
|
||||||
|
>
|
||||||
|
{pathPart}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const nameWidthClass = `item-name item-name-${
|
||||||
|
this.props.ui.isVertical ? "vertical" : "horizontal"
|
||||||
|
} pointer`;
|
||||||
|
|
||||||
|
const ops = (
|
||||||
|
<div id="upload-op">
|
||||||
|
<div className="float">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
onChange={this.onNewFolderNameChange}
|
||||||
|
value={this.state.newFolderName}
|
||||||
|
placeholder={this.props.msg.pkg.get("browser.folder.name")}
|
||||||
|
className="float"
|
||||||
|
/>
|
||||||
|
<button onClick={this.onMkDir} className="float">
|
||||||
|
{this.props.msg.pkg.get("browser.folder.add")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="float">
|
||||||
|
<button onClick={this.onClickUpload}>
|
||||||
|
{this.props.msg.pkg.get("browser.upload")}
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
onChange={this.addUploads}
|
||||||
|
multiple={true}
|
||||||
|
value={this.state.uploadFiles}
|
||||||
|
ref={this.assignInput}
|
||||||
|
className="hidden"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const sortedItems = this.props.filesInfo.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.filesInfo.dirPath.join("/");
|
||||||
|
const itemPath = dirPath.endsWith("/")
|
||||||
|
? `${dirPath}${item.name}`
|
||||||
|
: `${dirPath}/${item.name}`;
|
||||||
|
|
||||||
|
return item.isDir ? (
|
||||||
|
<Flexbox
|
||||||
|
key={item.name}
|
||||||
|
children={List([
|
||||||
|
<span className="padding-m">
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<RiFolder2Fill
|
||||||
|
size="3rem"
|
||||||
|
className="yellow0-font margin-r-m"
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<span className={`${nameWidthClass}`}>
|
||||||
|
<span
|
||||||
|
className="title-m"
|
||||||
|
onClick={() => this.gotoChild(item.name)}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</span>
|
||||||
|
<div className="desc-m grey0-font">
|
||||||
|
<span>
|
||||||
|
{item.modTime.slice(0, item.modTime.indexOf("T"))}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([
|
||||||
|
{ flex: "0 0 auto" },
|
||||||
|
{ flex: "0 0 auto" },
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
</span>,
|
||||||
|
|
||||||
|
<span className={`item-op padding-m ${showOp}`}>
|
||||||
|
<button
|
||||||
|
onClick={() => this.select(item.name)}
|
||||||
|
className={`${
|
||||||
|
isSelected ? "cyan0-bg white-font" : "grey2-bg grey3-font"
|
||||||
|
}`}
|
||||||
|
style={{ width: "8rem", display: "inline-block" }}
|
||||||
|
>
|
||||||
|
{isSelected
|
||||||
|
? this.props.msg.pkg.get("browser.deselect")
|
||||||
|
: this.props.msg.pkg.get("browser.select")}
|
||||||
|
</button>
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([
|
||||||
|
{ flex: "0 0 auto", width: "60%" },
|
||||||
|
{ flex: "0 0 auto", justifyContent: "flex-end", width: "40%" },
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div key={item.name}>
|
||||||
|
<Flexbox
|
||||||
|
key={item.name}
|
||||||
|
children={List([
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<RiFile2Fill
|
||||||
|
size="3rem"
|
||||||
|
className="cyan0-font margin-l-m margin-r-m"
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<span className={`${nameWidthClass}`}>
|
||||||
|
<a
|
||||||
|
className="title-m"
|
||||||
|
href={`/v1/fs/files?fp=${itemPath}`}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</a>
|
||||||
|
<div className="desc-m grey0-font">
|
||||||
|
<span>
|
||||||
|
{item.modTime.slice(0, item.modTime.indexOf("T"))}
|
||||||
|
</span>
|
||||||
|
/
|
||||||
|
<span>{FileSize(item.size, { round: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([
|
||||||
|
{ flex: "0 0 auto" },
|
||||||
|
{ flex: "0 0 auto" },
|
||||||
|
])}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<span className={`item-op padding-m ${showOp}`}>
|
||||||
|
<button
|
||||||
|
onClick={() => this.toggleDetail(item.name)}
|
||||||
|
style={{ width: "8rem" }}
|
||||||
|
className="float-input"
|
||||||
|
>
|
||||||
|
{this.props.msg.pkg.get("detail")}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => this.select(item.name)}
|
||||||
|
className={`float-input ${
|
||||||
|
isSelected ? "cyan0-bg white-font " : "grey2-bg grey3-font "
|
||||||
|
}`}
|
||||||
|
style={{ width: "8rem" }}
|
||||||
|
>
|
||||||
|
{isSelected
|
||||||
|
? this.props.msg.pkg.get("browser.deselect")
|
||||||
|
: this.props.msg.pkg.get("browser.select")}
|
||||||
|
</button>
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([
|
||||||
|
{ flex: "0 0 auto", width: "60%" },
|
||||||
|
{ flex: "0 0 auto", justifyContent: "flex-end", width: "40%" },
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`${
|
||||||
|
this.state.showDetail.has(item.name) ? "" : "hidden"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<span>
|
||||||
|
<b>SHA1:</b>
|
||||||
|
{` ${item.sha1}`}
|
||||||
|
</span>,
|
||||||
|
<button
|
||||||
|
onClick={() => this.generateHash(itemPath)}
|
||||||
|
className="black-bg white-font margin-l-m"
|
||||||
|
style={{ display: "inline-block" }}
|
||||||
|
>
|
||||||
|
{this.props.msg.pkg.get("refresh")}
|
||||||
|
</button>,
|
||||||
|
])}
|
||||||
|
className={`grey2-bg grey3-font detail margin-r-m`}
|
||||||
|
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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 = (
|
||||||
|
<div id="item-list">
|
||||||
|
<div className={`container ${showOp}`}>{ops}</div>
|
||||||
|
|
||||||
|
<div className="container">
|
||||||
|
<div id="browser-op" className={`${showOp}`}>
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<span>
|
||||||
|
{this.props.filesInfo.isSharing ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
this.deleteSharing(
|
||||||
|
this.props.filesInfo.dirPath.join("/")
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className="red-btn"
|
||||||
|
>
|
||||||
|
{this.props.msg.pkg.get("browser.share.del")}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={this.addSharing}
|
||||||
|
className="cyan-btn"
|
||||||
|
>
|
||||||
|
{this.props.msg.pkg.get("browser.share.add")}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</span>,
|
||||||
|
|
||||||
|
<span>
|
||||||
|
{this.state.selectedItems.size > 0 ? (
|
||||||
|
<span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => this.delete()}
|
||||||
|
className="red-btn"
|
||||||
|
>
|
||||||
|
{this.props.msg.pkg.get("browser.delete")}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" onClick={() => this.moveHere()}>
|
||||||
|
{this.props.msg.pkg.get("browser.paste")}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</span>,
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<span
|
||||||
|
id="space-used"
|
||||||
|
className="desc-m grey0-font"
|
||||||
|
>{`${this.props.msg.pkg.get(
|
||||||
|
"browser.used"
|
||||||
|
)} ${usedSpace} / ${spaceLimit}`}</span>
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([
|
||||||
|
{ flex: "0 0 auto" },
|
||||||
|
{ flex: "0 0 auto" },
|
||||||
|
{ justifyContent: "flex-end" },
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<span id="breadcrumb">
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<RiHomeSmileFill size="3rem" id="icon-home" />,
|
||||||
|
<Flexbox children={breadcrumb} />,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([
|
||||||
|
{ flex: "0 0 auto" },
|
||||||
|
{ flex: "0 0 auto" },
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
</span>,
|
||||||
|
|
||||||
|
<span className={`${showOp}`}>
|
||||||
|
<button onClick={() => this.selectAll()} className="select-btn">
|
||||||
|
{this.props.msg.pkg.get("browser.selectAll")}
|
||||||
|
</button>
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{itemList}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return <div id="browser">{itemListPane}</div>;
|
||||||
|
}
|
||||||
|
}
|
179
src/client/web/src/components/panel_sharings.tsx
Normal file
179
src/client/web/src/components/panel_sharings.tsx
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { List } from "immutable";
|
||||||
|
|
||||||
|
import { RiShareBoxLine } from "@react-icons/all-files/ri/RiShareBoxLine";
|
||||||
|
import { RiFolderSharedFill } from "@react-icons/all-files/ri/RiFolderSharedFill";
|
||||||
|
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 { Flexbox } from "./layout/flexbox";
|
||||||
|
|
||||||
|
export interface SharingsProps {
|
||||||
|
sharings: List<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
sharingsInfo: SharingsProps;
|
||||||
|
msg: MsgProps;
|
||||||
|
login: LoginProps;
|
||||||
|
ui: UIProps;
|
||||||
|
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface State {}
|
||||||
|
|
||||||
|
export class SharingsPanel extends React.Component<Props, State, {}> {
|
||||||
|
constructor(p: Props) {
|
||||||
|
super(p);
|
||||||
|
this.state = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
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().updateFilesInfo);
|
||||||
|
this.props.update(updater().updateSharingsInfo);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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().updateFilesInfo);
|
||||||
|
this.props.update(updater().updateFilesInfo);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
listSharings = async () => {
|
||||||
|
return updater()
|
||||||
|
.listSharings()
|
||||||
|
.then((ok) => {
|
||||||
|
if (ok) {
|
||||||
|
this.props.update(updater().updateFilesInfo);
|
||||||
|
this.props.update(updater().updateSharingsInfo);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const nameWidthClass = `item-name item-name-${
|
||||||
|
this.props.ui.isVertical ? "vertical" : "horizontal"
|
||||||
|
} pointer`;
|
||||||
|
|
||||||
|
const sharingList = this.props.sharingsInfo.sharings.map(
|
||||||
|
(dirPath: string) => {
|
||||||
|
return (
|
||||||
|
<div id="share-list" key={dirPath}>
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<RiFolderSharedFill
|
||||||
|
size="3rem"
|
||||||
|
className="purple0-font margin-r-m"
|
||||||
|
/>,
|
||||||
|
<span>{dirPath}</span>,
|
||||||
|
])}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
readOnly
|
||||||
|
className="float-input"
|
||||||
|
value={`${
|
||||||
|
document.location.href.split("?")[0]
|
||||||
|
}?dir=${encodeURIComponent(dirPath)}`}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
this.deleteSharing(dirPath);
|
||||||
|
}}
|
||||||
|
className="float-input"
|
||||||
|
>
|
||||||
|
{this.props.msg.pkg.get("browser.share.del")}
|
||||||
|
</button>
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.props.sharingsInfo.sharings.size === 0 ? (
|
||||||
|
<div id="sharing-list" className="container">
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<RiEmotionSadLine size="4rem" className="margin-r-m red0-font" />,
|
||||||
|
<span>
|
||||||
|
<h3 className="title-l">
|
||||||
|
{this.props.msg.pkg.get("share.404.title")}
|
||||||
|
</h3>
|
||||||
|
<span className="desc-l grey0-font">
|
||||||
|
{this.props.msg.pkg.get("share.404.desc")}
|
||||||
|
</span>
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([
|
||||||
|
{ flex: "auto", justifyContent: "flex-end" },
|
||||||
|
{ flex: "auto" },
|
||||||
|
])}
|
||||||
|
className="padding-l"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="container">
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<span className="padding-m">
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<RiShareBoxLine
|
||||||
|
size="3rem"
|
||||||
|
className="margin-r-m black-font"
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<span className="title-m bold">
|
||||||
|
{this.props.msg.pkg.get("browser.share.title")}
|
||||||
|
</span>
|
||||||
|
<span className="desc-m grey0-font">
|
||||||
|
{this.props.msg.pkg.get("browser.share.desc")}
|
||||||
|
</span>
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
</span>,
|
||||||
|
|
||||||
|
<span></span>,
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{sharingList}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
186
src/client/web/src/components/panel_uploadings.tsx
Normal file
186
src/client/web/src/components/panel_uploadings.tsx
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { List } from "immutable";
|
||||||
|
import FileSize from "filesize";
|
||||||
|
|
||||||
|
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 } from "../common/env";
|
||||||
|
import { updater } from "./state_updater";
|
||||||
|
import { ICoreState, MsgProps, UIProps } from "./core_state";
|
||||||
|
import { LoginProps } from "./pane_login";
|
||||||
|
import { UploadEntry, UploadState } from "../worker/interface";
|
||||||
|
import { Flexbox } from "./layout/flexbox";
|
||||||
|
|
||||||
|
export interface UploadingsProps {
|
||||||
|
uploadings: List<UploadEntry>;
|
||||||
|
uploadFiles: List<File>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
uploadingsInfo: UploadingsProps;
|
||||||
|
msg: MsgProps;
|
||||||
|
login: LoginProps;
|
||||||
|
ui: UIProps;
|
||||||
|
update?: (updater: (prevState: ICoreState) => ICoreState) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface State {}
|
||||||
|
|
||||||
|
export class UploadingsPanel extends React.Component<Props, State, {}> {
|
||||||
|
constructor(p: Props) {
|
||||||
|
super(p);
|
||||||
|
this.state = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
addUploads = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (event.target.files.length > 1000) {
|
||||||
|
alertMsg(this.props.msg.pkg.get("err.tooManyUploads"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileList = List<File>();
|
||||||
|
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<void> => {
|
||||||
|
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().updateUploadingsInfo);
|
||||||
|
this.props.update(updater().updateLogin);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
stopUploading = (filePath: string) => {
|
||||||
|
updater().stopUploading(filePath);
|
||||||
|
this.props.update(updater().updateUploadingsInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const nameWidthClass = `item-name item-name-${
|
||||||
|
this.props.ui.isVertical ? "vertical" : "horizontal"
|
||||||
|
} pointer`;
|
||||||
|
|
||||||
|
const uploadingList = this.props.uploadingsInfo.uploadings.map(
|
||||||
|
(uploading: UploadEntry) => {
|
||||||
|
const pathParts = uploading.filePath.split("/");
|
||||||
|
const fileName = pathParts[pathParts.length - 1];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={uploading.filePath}>
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<span className="padding-m">
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<RiUploadCloudLine
|
||||||
|
size="3rem"
|
||||||
|
id="icon-upload"
|
||||||
|
className="margin-r-m blue0-font"
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<div className={`${nameWidthClass}`}>
|
||||||
|
<span className="title-m">{fileName}</span>
|
||||||
|
<div className="desc-m grey0-font">
|
||||||
|
{FileSize(uploading.uploaded, { round: 0 })}
|
||||||
|
/ {FileSize(uploading.size, { round: 0 })}
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
</span>,
|
||||||
|
|
||||||
|
<div className="item-op">
|
||||||
|
<button
|
||||||
|
onClick={() => this.stopUploading(uploading.filePath)}
|
||||||
|
className="float-input"
|
||||||
|
>
|
||||||
|
{this.props.msg.pkg.get("browser.stop")}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => this.deleteUpload(uploading.filePath)}
|
||||||
|
className="float-input"
|
||||||
|
>
|
||||||
|
{this.props.msg.pkg.get("browser.delete")}
|
||||||
|
</button>
|
||||||
|
</div>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
|
||||||
|
/>
|
||||||
|
{uploading.err.trim() === "" ? null : (
|
||||||
|
<div className="error">{uploading.err.trim()}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.props.uploadingsInfo.uploadings.size === 0 ? (
|
||||||
|
<div id="upload-list" className="container">
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<RiEmotionSadLine size="4rem" className="margin-r-m red0-font" />,
|
||||||
|
<span>
|
||||||
|
<h3 className="title-l">
|
||||||
|
{this.props.msg.pkg.get("upload.404.title")}
|
||||||
|
</h3>
|
||||||
|
<span className="desc-l grey0-font">
|
||||||
|
{this.props.msg.pkg.get("upload.404.desc")}
|
||||||
|
</span>
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([
|
||||||
|
{ flex: "auto", justifyContent: "flex-end" },
|
||||||
|
{ flex: "auto" },
|
||||||
|
])}
|
||||||
|
className="padding-l"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div id="upload-list" className="container">
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<span className="upload-item">
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<RiUploadCloudFill
|
||||||
|
size="3rem"
|
||||||
|
className="margin-r-m black-font"
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<span className="title-m bold">
|
||||||
|
{this.props.msg.pkg.get("browser.upload.title")}
|
||||||
|
</span>
|
||||||
|
<span className="desc-m grey0-font">
|
||||||
|
{this.props.msg.pkg.get("browser.upload.desc")}
|
||||||
|
</span>
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
</span>,
|
||||||
|
|
||||||
|
<span></span>,
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{uploadingList}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,10 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { ICoreState, MsgProps, UIProps } from "./core_state";
|
import { ICoreState, MsgProps, UIProps } from "./core_state";
|
||||||
import { Browser, BrowserProps } from "./browser";
|
import { FilesPanel, FilesProps } from "./panel_files";
|
||||||
|
import { UploadingsPanel, UploadingsProps } from "./panel_uploadings";
|
||||||
|
import { SharingsPanel, SharingsProps } from "./panel_sharings";
|
||||||
|
|
||||||
import { LoginProps } from "./pane_login";
|
import { LoginProps } from "./pane_login";
|
||||||
import { Panes, PanesProps } from "./panes";
|
import { Panes, PanesProps } from "./panes";
|
||||||
import { AdminProps } from "./pane_admin";
|
import { AdminProps } from "./pane_admin";
|
||||||
|
@ -9,7 +12,9 @@ import { TopBar } from "./topbar";
|
||||||
import { roleVisitor } from "../client";
|
import { roleVisitor } from "../client";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
browser: BrowserProps;
|
filesInfo: FilesProps;
|
||||||
|
uploadingsInfo: UploadingsProps;
|
||||||
|
sharingsInfo: SharingsProps;
|
||||||
panes: PanesProps;
|
panes: PanesProps;
|
||||||
admin: AdminProps;
|
admin: AdminProps;
|
||||||
login: LoginProps;
|
login: LoginProps;
|
||||||
|
@ -26,7 +31,10 @@ export class RootFrame extends React.Component<Props, State, {}> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let bgStyle = undefined;
|
let bgStyle = undefined;
|
||||||
if (this.props.login.preferences != null && this.props.login.preferences.bg.url !== "") {
|
if (
|
||||||
|
this.props.login.preferences != null &&
|
||||||
|
this.props.login.preferences.bg.url !== ""
|
||||||
|
) {
|
||||||
bgStyle = {
|
bgStyle = {
|
||||||
background: `url("${this.props.login.preferences.bg.url}") ${this.props.login.preferences.bg.repeat} ${this.props.login.preferences.bg.position} ${this.props.login.preferences.bg.align}`,
|
background: `url("${this.props.login.preferences.bg.url}") ${this.props.login.preferences.bg.repeat} ${this.props.login.preferences.bg.position} ${this.props.login.preferences.bg.align}`,
|
||||||
};
|
};
|
||||||
|
@ -41,7 +49,8 @@ export class RootFrame extends React.Component<Props, State, {}> {
|
||||||
const fontSizeClass = "font-m";
|
const fontSizeClass = "font-m";
|
||||||
const theme = "theme-default";
|
const theme = "theme-default";
|
||||||
const showBrowser =
|
const showBrowser =
|
||||||
this.props.login.userRole === roleVisitor && !this.props.browser.isSharing
|
this.props.login.userRole === roleVisitor &&
|
||||||
|
!this.props.filesInfo.isSharing
|
||||||
? "hidden"
|
? "hidden"
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
|
@ -65,12 +74,33 @@ export class RootFrame extends React.Component<Props, State, {}> {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className={`container-center ${showBrowser}`}>
|
<div className={`container-center ${showBrowser}`}>
|
||||||
<Browser
|
{/* <Browser
|
||||||
browser={this.props.browser}
|
browser={this.props.browser}
|
||||||
msg={this.props.msg}
|
msg={this.props.msg}
|
||||||
login={this.props.login}
|
login={this.props.login}
|
||||||
ui={this.props.ui}
|
ui={this.props.ui}
|
||||||
update={this.props.update}
|
update={this.props.update}
|
||||||
|
/> */}
|
||||||
|
<FilesPanel
|
||||||
|
filesInfo={this.props.filesInfo}
|
||||||
|
msg={this.props.msg}
|
||||||
|
login={this.props.login}
|
||||||
|
ui={this.props.ui}
|
||||||
|
update={this.props.update}
|
||||||
|
/>
|
||||||
|
<UploadingsPanel
|
||||||
|
uploadingsInfo={this.props.uploadingsInfo}
|
||||||
|
msg={this.props.msg}
|
||||||
|
login={this.props.login}
|
||||||
|
ui={this.props.ui}
|
||||||
|
update={this.props.update}
|
||||||
|
/>
|
||||||
|
<SharingsPanel
|
||||||
|
sharingsInfo={this.props.sharingsInfo}
|
||||||
|
msg={this.props.msg}
|
||||||
|
login={this.props.login}
|
||||||
|
ui={this.props.ui}
|
||||||
|
update={this.props.update}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,9 @@ export class StateMgr extends React.Component<Props, State, {}> {
|
||||||
return updater()
|
return updater()
|
||||||
.initAll(params)
|
.initAll(params)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.update(updater().updateBrowser);
|
this.update(updater().updateFilesInfo);
|
||||||
|
this.update(updater().updateUploadingsInfo);
|
||||||
|
this.update(updater().updateSharingsInfo);
|
||||||
this.update(updater().updateLogin);
|
this.update(updater().updateLogin);
|
||||||
this.update(updater().updatePanes);
|
this.update(updater().updatePanes);
|
||||||
this.update(updater().updateAdmin);
|
this.update(updater().updateAdmin);
|
||||||
|
@ -76,7 +78,9 @@ export class StateMgr extends React.Component<Props, State, {}> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<RootFrame
|
<RootFrame
|
||||||
browser={this.state.browser}
|
filesInfo={this.state.filesInfo}
|
||||||
|
uploadingsInfo={this.state.uploadingsInfo}
|
||||||
|
sharingsInfo={this.state.sharingsInfo}
|
||||||
msg={this.state.msg}
|
msg={this.state.msg}
|
||||||
panes={this.state.panes}
|
panes={this.state.panes}
|
||||||
login={this.state.login}
|
login={this.state.login}
|
||||||
|
|
|
@ -51,7 +51,7 @@ export class Updater {
|
||||||
}
|
}
|
||||||
|
|
||||||
initUploads = () => {
|
initUploads = () => {
|
||||||
this.props.browser.uploadings.forEach((entry) => {
|
this.props.uploadingsInfo.uploadings.forEach((entry) => {
|
||||||
Up().addStopped(entry.filePath, entry.uploaded, entry.size);
|
Up().addStopped(entry.filePath, entry.uploaded, entry.size);
|
||||||
});
|
});
|
||||||
// this.setUploadings(Up().list());
|
// this.setUploadings(Up().list());
|
||||||
|
@ -60,7 +60,7 @@ export class Updater {
|
||||||
addUploads = (fileList: List<File>) => {
|
addUploads = (fileList: List<File>) => {
|
||||||
fileList.forEach((file) => {
|
fileList.forEach((file) => {
|
||||||
const filePath = getItemPath(
|
const filePath = getItemPath(
|
||||||
this.props.browser.dirPath.join("/"),
|
this.props.filesInfo.dirPath.join("/"),
|
||||||
file.name
|
file.name
|
||||||
);
|
);
|
||||||
// do not wait for the promise
|
// do not wait for the promise
|
||||||
|
@ -76,7 +76,7 @@ export class Updater {
|
||||||
};
|
};
|
||||||
|
|
||||||
setUploadings = (infos: Map<string, UploadEntry>) => {
|
setUploadings = (infos: Map<string, UploadEntry>) => {
|
||||||
this.props.browser.uploadings = List<UploadEntry>(
|
this.props.uploadingsInfo.uploadings = List<UploadEntry>(
|
||||||
infos.valueSeq().map((entry: UploadEntry): UploadEntry => {
|
infos.valueSeq().map((entry: UploadEntry): UploadEntry => {
|
||||||
return entry;
|
return entry;
|
||||||
})
|
})
|
||||||
|
@ -84,7 +84,7 @@ export class Updater {
|
||||||
};
|
};
|
||||||
|
|
||||||
addSharing = async (): Promise<boolean> => {
|
addSharing = async (): Promise<boolean> => {
|
||||||
const dirPath = this.props.browser.dirPath.join("/");
|
const dirPath = this.props.filesInfo.dirPath.join("/");
|
||||||
const resp = await this.filesClient.addSharing(dirPath);
|
const resp = await this.filesClient.addSharing(dirPath);
|
||||||
return resp.status === 200;
|
return resp.status === 200;
|
||||||
};
|
};
|
||||||
|
@ -96,20 +96,20 @@ export class Updater {
|
||||||
|
|
||||||
isSharing = async (dirPath: string): Promise<boolean> => {
|
isSharing = async (dirPath: string): Promise<boolean> => {
|
||||||
const resp = await this.filesClient.isSharing(dirPath);
|
const resp = await this.filesClient.isSharing(dirPath);
|
||||||
this.props.browser.isSharing = resp.status === 200;
|
this.props.filesInfo.isSharing = resp.status === 200;
|
||||||
return resp.status === 200; // TODO: differentiate 404 and error
|
return resp.status === 200; // TODO: differentiate 404 and error
|
||||||
};
|
};
|
||||||
|
|
||||||
setSharing = (shared: boolean) => {
|
setSharing = (shared: boolean) => {
|
||||||
this.props.browser.isSharing = shared;
|
this.props.filesInfo.isSharing = shared;
|
||||||
};
|
};
|
||||||
|
|
||||||
listSharings = async (): Promise<boolean> => {
|
listSharings = async (): Promise<boolean> => {
|
||||||
const resp = await this.filesClient.listSharings();
|
const resp = await this.filesClient.listSharings();
|
||||||
this.props.browser.sharings =
|
this.props.sharingsInfo.sharings =
|
||||||
resp.status === 200
|
resp.status === 200
|
||||||
? List<string>(resp.data.sharingDirs)
|
? List<string>(resp.data.sharingDirs)
|
||||||
: this.props.browser.sharings;
|
: this.props.sharingsInfo.sharings;
|
||||||
return resp.status === 200;
|
return resp.status === 200;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ export class Updater {
|
||||||
}
|
}
|
||||||
|
|
||||||
let localUploads = Map<string, UploadEntry>([]);
|
let localUploads = Map<string, UploadEntry>([]);
|
||||||
this.props.browser.uploadings.forEach((entry: UploadEntry) => {
|
this.props.uploadingsInfo.uploadings.forEach((entry: UploadEntry) => {
|
||||||
localUploads = localUploads.set(entry.filePath, entry);
|
localUploads = localUploads.set(entry.filePath, entry);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ export class Updater {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.browser.uploadings = updatedUploads;
|
this.props.uploadingsInfo.uploadings = updatedUploads;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -217,12 +217,12 @@ export class Updater {
|
||||||
const listResp = await this.filesClient.list(dirPath);
|
const listResp = await this.filesClient.list(dirPath);
|
||||||
|
|
||||||
if (listResp.status === 200) {
|
if (listResp.status === 200) {
|
||||||
this.props.browser.dirPath = dirParts;
|
this.props.filesInfo.dirPath = dirParts;
|
||||||
this.props.browser.items = List<MetadataResp>(listResp.data.metadatas);
|
this.props.filesInfo.items = List<MetadataResp>(listResp.data.metadatas);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
this.props.browser.dirPath = List<string>([]);
|
this.props.filesInfo.dirPath = List<string>([]);
|
||||||
this.props.browser.items = List<MetadataResp>([]);
|
this.props.filesInfo.items = List<MetadataResp>([]);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -230,12 +230,12 @@ export class Updater {
|
||||||
const listResp = await this.filesClient.listHome();
|
const listResp = await this.filesClient.listHome();
|
||||||
|
|
||||||
if (listResp.status === 200) {
|
if (listResp.status === 200) {
|
||||||
this.props.browser.dirPath = List<string>(listResp.data.cwd.split("/"));
|
this.props.filesInfo.dirPath = List<string>(listResp.data.cwd.split("/"));
|
||||||
this.props.browser.items = List<MetadataResp>(listResp.data.metadatas);
|
this.props.filesInfo.items = List<MetadataResp>(listResp.data.metadatas);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
this.props.browser.dirPath = List<string>([]);
|
this.props.filesInfo.dirPath = List<string>([]);
|
||||||
this.props.browser.items = List<MetadataResp>([]);
|
this.props.filesInfo.items = List<MetadataResp>([]);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -307,21 +307,19 @@ export class Updater {
|
||||||
initPanes = async (): Promise<Array<any>> => {
|
initPanes = async (): Promise<Array<any>> => {
|
||||||
// init browser content
|
// init browser content
|
||||||
if (this.props.login.userRole === roleVisitor) {
|
if (this.props.login.userRole === roleVisitor) {
|
||||||
if (this.props.browser.isSharing) {
|
if (this.props.filesInfo.isSharing) {
|
||||||
// sharing with visitor
|
// sharing with visitor
|
||||||
this.setPanes(Set<string>(["login"]));
|
this.setPanes(Set<string>(["login"]));
|
||||||
this.displayPane("");
|
this.displayPane("");
|
||||||
return Promise.all([]);
|
return Promise.all([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// redirect to login
|
// redirect to login
|
||||||
this.setPanes(Set<string>(["login"]));
|
this.setPanes(Set<string>(["login"]));
|
||||||
this.displayPane("login");
|
this.displayPane("login");
|
||||||
return Promise.all([this.getCaptchaID()]);
|
return Promise.all([this.getCaptchaID()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (this.props.login.userRole === roleAdmin) {
|
if (this.props.login.userRole === roleAdmin) {
|
||||||
this.setPanes(Set<string>(["login", "settings", "admin"]));
|
this.setPanes(Set<string>(["login", "settings", "admin"]));
|
||||||
} else {
|
} else {
|
||||||
|
@ -351,7 +349,7 @@ export class Updater {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.isSharing(this.props.browser.dirPath.join("/"));
|
return this.isSharing(this.props.filesInfo.dirPath.join("/"));
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// init settings
|
// init settings
|
||||||
|
@ -558,16 +556,16 @@ export class Updater {
|
||||||
setTab = (tabName: string) => {
|
setTab = (tabName: string) => {
|
||||||
switch (tabName) {
|
switch (tabName) {
|
||||||
case "item":
|
case "item":
|
||||||
this.props.browser.tab = tabName;
|
this.props.panels.displaying = tabName;
|
||||||
break;
|
break;
|
||||||
case "uploading":
|
case "uploading":
|
||||||
this.props.browser.tab = tabName;
|
this.props.panels.displaying = tabName;
|
||||||
break;
|
break;
|
||||||
case "sharing":
|
case "sharing":
|
||||||
this.props.browser.tab = tabName;
|
this.props.panels.displaying = tabName;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.props.browser.tab = "item";
|
this.props.panels.displaying = "item";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -648,10 +646,27 @@ export class Updater {
|
||||||
return resp.status;
|
return resp.status;
|
||||||
};
|
};
|
||||||
|
|
||||||
updateBrowser = (prevState: ICoreState): ICoreState => {
|
updateFilesInfo = (prevState: ICoreState): ICoreState => {
|
||||||
return {
|
return {
|
||||||
...prevState,
|
...prevState,
|
||||||
browser: { ...prevState.browser, ...this.props.browser },
|
filesInfo: { ...prevState.filesInfo, ...this.props.filesInfo },
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
updateUploadingsInfo = (prevState: ICoreState): ICoreState => {
|
||||||
|
return {
|
||||||
|
...prevState,
|
||||||
|
uploadingsInfo: {
|
||||||
|
...prevState.uploadingsInfo,
|
||||||
|
...this.props.uploadingsInfo,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
updateSharingsInfo = (prevState: ICoreState): ICoreState => {
|
||||||
|
return {
|
||||||
|
...prevState,
|
||||||
|
sharingsInfo: { ...prevState.sharingsInfo, ...this.props.sharingsInfo },
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,9 @@ export class TopBar extends React.Component<Props, State, {}> {
|
||||||
return this.refreshCaptcha();
|
return this.refreshCaptcha();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.props.update(updater().updateBrowser);
|
this.props.update(updater().updateFilesInfo);
|
||||||
|
this.props.update(updater().updateUploadingsInfo);
|
||||||
|
this.props.update(updater().updateSharingsInfo);
|
||||||
this.props.update(updater().updateLogin);
|
this.props.update(updater().updateLogin);
|
||||||
this.props.update(updater().updatePanes);
|
this.props.update(updater().updatePanes);
|
||||||
this.props.update(updater().updateAdmin);
|
this.props.update(updater().updateAdmin);
|
||||||
|
|
|
@ -105,4 +105,5 @@ export const msgs: Map<string, string> = Map({
|
||||||
"settings.customLan": "Customized Language Pack",
|
"settings.customLan": "Customized Language Pack",
|
||||||
"settings.lanPackURL": "Language Pack URL",
|
"settings.lanPackURL": "Language Pack URL",
|
||||||
"op.fail": "Operation Failed",
|
"op.fail": "Operation Failed",
|
||||||
|
"op.confirm": "Do you confirm to apply the action?",
|
||||||
});
|
});
|
||||||
|
|
|
@ -104,4 +104,5 @@ export const msgs: Map<string, string> = Map({
|
||||||
"settings.customLan": "自定义语言包",
|
"settings.customLan": "自定义语言包",
|
||||||
"settings.lanPackURL": "语言包链接",
|
"settings.lanPackURL": "语言包链接",
|
||||||
"op.fail": "操作失败",
|
"op.fail": "操作失败",
|
||||||
|
"op.confirm": "你确定执行此操作吗?",
|
||||||
});
|
});
|
||||||
|
|
|
@ -90,6 +90,8 @@ export class UploadMgr {
|
||||||
return this.cycle;
|
return this.cycle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: change it to observer pattern
|
||||||
|
// so that it can be observed by multiple components
|
||||||
setStatusCb = (
|
setStatusCb = (
|
||||||
cb: (infos: Map<string, UploadEntry>, refresh: boolean) => void
|
cb: (infos: Map<string, UploadEntry>, refresh: boolean) => void
|
||||||
) => {
|
) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue