From 5a11463386d9d49a6b04a2d0075aeb080eb7fac5 Mon Sep 17 00:00:00 2001 From: hexxa Date: Sun, 23 Jan 2022 13:55:22 +0800 Subject: [PATCH] feat(fe): enable loading during waiting --- public/static/css/default.css | 50 ++- public/static/css/white.css | 5 +- src/client/web/src/components/layers.tsx | 13 +- src/client/web/src/components/panel_files.tsx | 347 ++++++++++-------- .../web/src/components/visual/loading.tsx | 29 ++ 5 files changed, 276 insertions(+), 168 deletions(-) create mode 100644 src/client/web/src/components/visual/loading.tsx diff --git a/public/static/css/default.css b/public/static/css/default.css index 6a42324..0fa2a53 100644 --- a/public/static/css/default.css +++ b/public/static/css/default.css @@ -503,11 +503,19 @@ clear: both; } -.anm-rotate { - animation: trm-rotate 1s infinite linear; +.anm-rotate-s { + animation: trm-rotate-s 1s infinite linear; } -@keyframes trm-rotate { +.anm-rotate-m { + animation: trm-rotate-m 1s infinite linear; +} + +.anm-rotate-f { + animation: trm-rotate-f 1s infinite linear; +} + +@keyframes trm-rotate-f { 20% { transform: rotate(72deg); } @@ -524,3 +532,39 @@ transform: rotate(360deg); } } + +@keyframes trm-rotate-m { + 20% { + transform: rotate(36deg); + } + 40% { + transform: rotate(72deg); + } + 60% { + transform: rotate(144deg); + } + 80% { + transform: rotate(288deg); + } + 100% { + transform: rotate(360deg); + } +} + +@keyframes trm-rotate-s { + 20% { + transform: rotate(18deg); + } + 40% { + transform: rotate(36deg); + } + 60% { + transform: rotate(72deg); + } + 80% { + transform: rotate(144deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/public/static/css/white.css b/public/static/css/white.css index e0a9191..9e4aad3 100644 --- a/public/static/css/white.css +++ b/public/static/css/white.css @@ -513,13 +513,14 @@ .theme-default #loading-container { background-color: rgba(255, 255, 255, 1); - border-radius: 0.6rem; + border-radius: 3rem; padding: 0.5rem; position: fixed; right: 2rem; bottom: 2rem; - height: 3rem; z-index: 201; + height: 5rem; + width: 5rem; } .theme-default #settings-layer { diff --git a/src/client/web/src/components/layers.tsx b/src/client/web/src/components/layers.tsx index 4329770..2fd4a60 100644 --- a/src/client/web/src/components/layers.tsx +++ b/src/client/web/src/components/layers.tsx @@ -1,8 +1,6 @@ import * as React from "react"; import { List } from "immutable"; -import { RiTimer2Line } from "@react-icons/all-files/ri/RiTimer2Line"; - import { updater } from "./state_updater"; import { ICoreState, MsgProps, UIProps } from "./core_state"; import { AdminProps } from "./pane_admin"; @@ -13,6 +11,7 @@ import { FilesProps } from "./panel_files"; import { Flexbox } from "./layout/flexbox"; import { Container } from "./layout/container"; import { sharingCtrl, loadingCtrl, ctrlOn } from "../common/controls"; +import { LoadingIcon } from "./visual/loading"; export interface Props { filesInfo: FilesProps; @@ -50,6 +49,10 @@ export class Layers extends React.Component { return (
+
+ +
+
{
-
-
- -
-
-
diff --git a/src/client/web/src/components/panel_files.tsx b/src/client/web/src/components/panel_files.tsx index d0a2760..843adf9 100644 --- a/src/client/web/src/components/panel_files.tsx +++ b/src/client/web/src/components/panel_files.tsx @@ -2,10 +2,8 @@ import * as React from "react"; import * as ReactDOM from "react-dom"; import { List, Map, Set } from "immutable"; import FileSize from "filesize"; -import QRCode from "react-qr-code"; import { RiFolder2Fill } from "@react-icons/all-files/ri/RiFolder2Fill"; -import { RiArchiveDrawerFill } from "@react-icons/all-files/ri/RiArchiveDrawerFill"; import { RiFile2Fill } from "@react-icons/all-files/ri/RiFile2Fill"; import { RiFileList2Fill } from "@react-icons/all-files/ri/RiFileList2Fill"; import { RiCheckboxFill } from "@react-icons/all-files/ri/RiCheckboxFill"; @@ -30,7 +28,13 @@ import { Rows, Row } from "./layout/rows"; import { Up } from "../worker/upload_mgr"; import { UploadEntry, UploadState } from "../worker/interface"; import { getIcon } from "./visual/icons"; -import { ctrlOn, sharingCtrl, filesViewCtrl } from "../common/controls"; +import { + ctrlOff, + ctrlOn, + sharingCtrl, + filesViewCtrl, + loadingCtrl, +} from "../common/controls"; export interface Item { name: string; @@ -95,6 +99,11 @@ export class FilesPanel extends React.Component { }; } + setLoading = (state: boolean) => { + updater().setControlOption(loadingCtrl, state ? ctrlOn : ctrlOff); + this.props.update(updater().updateUI); + }; + updateProgress = async ( infos: Map, refresh: boolean @@ -107,29 +116,24 @@ export class FilesPanel extends React.Component { if (infos.size === 0 || infos.size === errCount) { // refresh used space - updater() - .self() - .then(() => { - this.props.update(updater().updateLogin); - this.props.update(updater().updateUploadingsInfo); - }); + const status = await updater().self(); + if (status !== "") { + alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", status)); + return; + } + this.props.update(updater().updateLogin); + this.props.update(updater().updateUploadingsInfo); } if (refresh) { - updater() - .setItems(this.props.filesInfo.dirPath) - .then((status: string) => { - if (status !== "") { - alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", status)); - } else { - this.props.update(updater().updateFilesInfo); - this.props.update(updater().updateUploadingsInfo); - } - }); - } else { - this.props.update(updater().updateFilesInfo); - this.props.update(updater().updateUploadingsInfo); + const status = await updater().setItems(this.props.filesInfo.dirPath); + if (status !== "") { + alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", status)); + return; + } } + this.props.update(updater().updateFilesInfo); + this.props.update(updater().updateUploadingsInfo); }; addUploads = (event: React.ChangeEvent) => { @@ -154,7 +158,7 @@ export class FilesPanel extends React.Component { this.setState({ newFolderName: ev.target.value }); }; - onMkDir = () => { + onMkDir = async () => { if (this.state.newFolderName === "") { alertMsg(this.props.msg.pkg.get("browser.folder.add.fail")); return; @@ -164,28 +168,37 @@ export class FilesPanel extends React.Component { this.props.filesInfo.dirPath.join("/"), this.state.newFolderName ); - updater() - .mkDir(dirPath) - .then((status: string) => { - if (status !== "") { - throw status; - } - this.setState({ newFolderName: "" }); - return updater().setItems(this.props.filesInfo.dirPath); - }) - .then((status: string) => { - if (status !== "") { - throw status; - } - this.props.update(updater().updateFilesInfo); - this.props.update(updater().updateSharingsInfo); - }) - .catch((status: Error) => { - alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", status.toString())); - }); + + this.setLoading(true); + + try { + const mkDirStatus = await updater().mkDir(dirPath); + if (mkDirStatus !== "") { + alertMsg( + getErrMsg(this.props.msg.pkg, "op.fail", mkDirStatus.toString()) + ); + return; + } + + const setItemsStatus = await updater().setItems( + this.props.filesInfo.dirPath + ); + if (setItemsStatus !== "") { + alertMsg( + getErrMsg(this.props.msg.pkg, "op.fail", setItemsStatus.toString()) + ); + return; + } + + this.setState({ newFolderName: "" }); + this.props.update(updater().updateFilesInfo); + this.props.update(updater().updateSharingsInfo); + } finally { + this.setLoading(false); + } }; - delete = () => { + delete = async () => { // 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")); @@ -207,37 +220,42 @@ export class FilesPanel extends React.Component { } } - updater() - .delete( + this.setLoading(true); + + try { + const deleteStatus = await updater().delete( this.props.filesInfo.dirPath, this.props.filesInfo.items, this.state.selectedItems - ) - .then((status: string) => { - if (status !== "") { - throw status; - } - return updater().self(); - }) - .then((status: string) => { - if (status !== "") { - throw status; - } + ); + if (deleteStatus !== "") { + alertMsg( + getErrMsg(this.props.msg.pkg, "op.fail", deleteStatus.toString()) + ); + return deleteStatus; + } - this.props.update(updater().updateFilesInfo); - this.props.update(updater().updateSharingsInfo); - this.props.update(updater().updateLogin); - this.setState({ - selectedSrc: "", - selectedItems: Map(), - }); - }) - .catch((status: Error) => { - alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", status.toString())); + const selfStatus = await updater().self(); + if (selfStatus !== "") { + alertMsg( + getErrMsg(this.props.msg.pkg, "op.fail", selfStatus.toString()) + ); + return selfStatus; + } + + this.props.update(updater().updateFilesInfo); + this.props.update(updater().updateSharingsInfo); + this.props.update(updater().updateLogin); + this.setState({ + selectedSrc: "", + selectedItems: Map(), }); + } finally { + this.setLoading(false); + } }; - moveHere = () => { + moveHere = async () => { const oldDir = this.state.selectedSrc; const newDir = this.props.filesInfo.dirPath.join("/"); if (oldDir === newDir) { @@ -245,26 +263,30 @@ export class FilesPanel extends React.Component { return; } - updater() - .moveHere( + this.setLoading(true); + + try { + const moveStatus = await updater().moveHere( this.state.selectedSrc, this.props.filesInfo.dirPath.join("/"), this.state.selectedItems - ) - .then((status: string) => { - if (status !== "") { - throw status; - } - this.props.update(updater().updateFilesInfo); - this.props.update(updater().updateSharingsInfo); - this.setState({ - selectedSrc: "", - selectedItems: Map(), - }); - }) - .catch((status: Error) => { - alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", status.toString())); + ); + if (moveStatus !== "") { + alertMsg( + getErrMsg(this.props.msg.pkg, "op.fail", moveStatus.toString()) + ); + return; + } + + this.props.update(updater().updateFilesInfo); + this.props.update(updater().updateSharingsInfo); + this.setState({ + selectedSrc: "", + selectedItems: Map(), }); + } finally { + this.setLoading(false); + } }; gotoChild = async (childDirName: string) => { @@ -272,17 +294,18 @@ export class FilesPanel extends React.Component { }; goHome = async () => { - return updater() - .setHomeItems() - .then((status: string) => { - if (status !== "") { - throw status; - } - this.props.update(updater().updateFilesInfo); - }) - .catch((status: Error) => { - alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", status.toString())); - }); + this.setLoading(true); + + try { + const status = await updater().setHomeItems(); + if (status !== "") { + alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", status)); + return; + } + this.props.update(updater().updateFilesInfo); + } finally { + this.setLoading(false); + } }; chdir = async (dirPath: List) => { @@ -293,24 +316,25 @@ export class FilesPanel extends React.Component { return; } - return updater() - .setItems(dirPath) - .then((status: string) => { - if (status !== "") { - throw status; - } - return updater().syncIsSharing(dirPath.join("/")); - }) - .then((status: string) => { - if (status !== "") { - throw status; - } - this.props.update(updater().updateFilesInfo); - this.props.update(updater().updateSharingsInfo); - }) - .catch((status: Error) => { - alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", status.toString())); - }); + this.setLoading(true); + try { + const status = await updater().setItems(dirPath); + if (status !== "") { + alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", status)); + return; + } + + const isSharingStatus = await updater().syncIsSharing(dirPath.join("/")); + if (isSharingStatus !== "") { + alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", isSharingStatus)); + return; + } + + this.props.update(updater().updateFilesInfo); + this.props.update(updater().updateSharingsInfo); + } finally { + this.setLoading(false); + } }; select = (itemName: string) => { @@ -356,49 +380,59 @@ export class FilesPanel extends React.Component { }; addSharing = async () => { - return updater() - .addSharing() - .then((status: string) => { - if (status !== "") { - throw status; - } else { - updater().setSharing(true); - return updater().listSharings(); - } - }) - .then((status: string) => { - if (status !== "") { - throw status; - } - this.props.update(updater().updateSharingsInfo); - this.props.update(updater().updateFilesInfo); - }) - .catch((status: Error) => { - alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", status.toString())); - }); + this.setLoading(true); + + try { + const addStatus = await updater().addSharing(); + if (addStatus !== "") { + alertMsg( + getErrMsg(this.props.msg.pkg, "op.fail", addStatus.toString()) + ); + return; + } + + updater().setSharing(true); + const listStatus = await updater().listSharings(); + if (listStatus !== "") { + alertMsg( + getErrMsg(this.props.msg.pkg, "op.fail", listStatus.toString()) + ); + return; + } + + this.props.update(updater().updateSharingsInfo); + this.props.update(updater().updateFilesInfo); + } finally { + this.setLoading(false); + } }; deleteSharing = async (dirPath: string) => { - return updater() - .deleteSharing(dirPath) - .then((status) => { - if (status !== "") { - throw status; - } else { - updater().setSharing(false); - return updater().listSharings(); - } - }) - .then((status: string) => { - if (status !== "") { - throw status; - } - this.props.update(updater().updateSharingsInfo); - this.props.update(updater().updateFilesInfo); - }) - .catch((status: Error) => { - alertMsg(getErrMsg(this.props.msg.pkg, "op.fail", status.toString())); - }); + this.setLoading(true); + + try { + const delStatus = await updater().deleteSharing(dirPath); + if (delStatus !== "") { + alertMsg( + getErrMsg(this.props.msg.pkg, "op.fail", delStatus.toString()) + ); + return; + } + + updater().setSharing(false); + const listStatus = await updater().listSharings(); + if (listStatus !== "") { + alertMsg( + getErrMsg(this.props.msg.pkg, "op.fail", listStatus.toString()) + ); + return; + } + + this.props.update(updater().updateSharingsInfo); + this.props.update(updater().updateFilesInfo); + } finally { + this.setLoading(false); + } }; updateItems = (items: Object) => { @@ -796,9 +830,12 @@ export class FilesPanel extends React.Component { this.prepareTable(this.props.filesInfo.items, showOp) ); - const usedSpace = FileSize(parseInt(this.props.login.extInfo.usedSpace, 10), { - round: 0, - }); + const usedSpace = FileSize( + parseInt(this.props.login.extInfo.usedSpace, 10), + { + round: 0, + } + ); const spaceLimit = FileSize( parseInt(this.props.login.quota.spaceLimit, 10), { diff --git a/src/client/web/src/components/visual/loading.tsx b/src/client/web/src/components/visual/loading.tsx new file mode 100644 index 0000000..f19aa90 --- /dev/null +++ b/src/client/web/src/components/visual/loading.tsx @@ -0,0 +1,29 @@ +import * as React from "react"; + +import { RiLoader5Fill } from "@react-icons/all-files/ri/RiLoader5Fill"; + +export interface Props {} + +export interface State {} + +export const LoadingIcon = (props: Props) => { + return ( +
+ + + +
+ ); +};