feat(ui/browser): support used space

This commit is contained in:
hexxa 2021-08-29 22:34:28 +08:00 committed by Hexxa
parent 93d946e535
commit c76e61a686
11 changed files with 162 additions and 61 deletions

View file

@ -1,7 +1,6 @@
// TODO: replace this with jest mocks // TODO: replace this with jest mocks
import { Response, Quota } from "./"; import { Response, Quota } from "./";
export interface UsersClientResps { export interface UsersClientResps {
loginMockResp: Response; loginMockResp: Response;
logoutMockResp: Response; logoutMockResp: Response;
@ -77,6 +76,11 @@ export const resps = {
name: "mockUser", name: "mockUser",
role: "admin", role: "admin",
usedSpace: "256", usedSpace: "256",
quota: {
spaceLimit: "7",
uploadSpeedLimit: 3,
downloadSpeedLimit: 3,
},
}, },
}, },
getCaptchaIDMockResp: { getCaptchaIDMockResp: {
@ -126,7 +130,6 @@ export class MockUsersClient {
return this.wrapPromise(this.resps.setUserMockResp); return this.wrapPromise(this.resps.setUserMockResp);
}; };
forceSetPwd = (userID: string, newPwd: string): Promise<Response> => { forceSetPwd = (userID: string, newPwd: string): Promise<Response> => {
return this.wrapPromise(this.resps.forceSetPwdMockResp); return this.wrapPromise(this.resps.forceSetPwdMockResp);
}; };

View file

@ -36,6 +36,12 @@ describe("Login", () => {
userName: "mockUser", userName: "mockUser",
userRole: "admin", userRole: "admin",
authed: true, authed: true,
usedSpace: "256",
quota: {
spaceLimit: "7",
uploadSpeedLimit: 3,
downloadSpeedLimit: 3,
},
captchaID: "", captchaID: "",
}); });

View file

@ -22,7 +22,7 @@ describe("State Manager", () => {
// TODO: depress warning // TODO: depress warning
mgr.update = (apply: (prevState: ICoreState) => ICoreState): void => { mgr.update = (apply: (prevState: ICoreState) => ICoreState): void => {
// no op // no op
}; };
const coreState = newWithWorker(mockWorker); const coreState = newWithWorker(mockWorker);
@ -52,6 +52,12 @@ describe("State Manager", () => {
userID: "0", userID: "0",
userName: "mockUser", userName: "mockUser",
userRole: "admin", userRole: "admin",
usedSpace: "256",
quota: {
spaceLimit: "7",
uploadSpeedLimit: 3,
downloadSpeedLimit: 3,
},
authed: true, authed: true,
captchaID: "mockCaptchaID", captchaID: "mockCaptchaID",
}); });

View file

@ -6,7 +6,7 @@ import { RiFolder2Fill } from "@react-icons/all-files/ri/RiFolder2Fill";
import { RiFileList2Fill } from "@react-icons/all-files/ri/RiFileList2Fill"; import { RiFileList2Fill } from "@react-icons/all-files/ri/RiFileList2Fill";
import { RiFile2Fill } from "@react-icons/all-files/ri/RiFile2Fill"; import { RiFile2Fill } from "@react-icons/all-files/ri/RiFile2Fill";
import { RiShareBoxLine } from "@react-icons/all-files/ri/RiShareBoxLine"; import { RiShareBoxLine } from "@react-icons/all-files/ri/RiShareBoxLine";
import { RiShareForwardBoxFill } from "@react-icons/all-files/ri/RiShareForwardBoxFill"; import { RiFolderSharedFill } from "@react-icons/all-files/ri/RiFolderSharedFill";
import { RiUploadCloudFill } from "@react-icons/all-files/ri/RiUploadCloudFill"; import { RiUploadCloudFill } from "@react-icons/all-files/ri/RiUploadCloudFill";
import { RiUploadCloudLine } from "@react-icons/all-files/ri/RiUploadCloudLine"; import { RiUploadCloudLine } from "@react-icons/all-files/ri/RiUploadCloudLine";
import { RiEmotionSadLine } from "@react-icons/all-files/ri/RiEmotionSadLine"; import { RiEmotionSadLine } from "@react-icons/all-files/ri/RiEmotionSadLine";
@ -14,6 +14,7 @@ import { RiEmotionSadLine } from "@react-icons/all-files/ri/RiEmotionSadLine";
import { alertMsg, comfirmMsg } from "../common/env"; import { alertMsg, comfirmMsg } from "../common/env";
import { updater } from "./state_updater"; import { updater } from "./state_updater";
import { ICoreState, MsgProps } from "./core_state"; import { ICoreState, MsgProps } from "./core_state";
import { LoginProps } from "./pane_login";
import { MetadataResp, UploadInfo } from "../client"; import { MetadataResp, UploadInfo } from "../client";
import { Up } from "../worker/upload_mgr"; import { Up } from "../worker/upload_mgr";
import { UploadEntry } from "../worker/interface"; import { UploadEntry } from "../worker/interface";
@ -44,6 +45,7 @@ export interface BrowserProps {
export interface Props { export interface Props {
browser: BrowserProps; browser: BrowserProps;
msg: MsgProps; msg: MsgProps;
login: LoginProps;
update?: (updater: (prevState: ICoreState) => ICoreState) => void; update?: (updater: (prevState: ICoreState) => ICoreState) => void;
} }
@ -477,6 +479,15 @@ export class Browser extends React.Component<Props, State, {}> {
); );
}); });
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 = const itemListPane =
this.props.browser.tab === "" || this.props.browser.tab === "item" ? ( this.props.browser.tab === "" || this.props.browser.tab === "item" ? (
<div> <div>
@ -485,49 +496,61 @@ export class Browser extends React.Component<Props, State, {}> {
</div> </div>
<div className="container"> <div className="container">
<div className="padding-m"> <Flexbox
{this.props.browser.isSharing ? ( children={List([
<button
type="button"
onClick={() => {
this.deleteSharing(this.props.browser.dirPath.join("/"));
}}
className="red0-bg white-font margin-r-m"
>
{this.props.msg.pkg.get("browser.share.del")}
</button>
) : (
<button
type="button"
onClick={this.addSharing}
className="green0-bg white-font margin-r-m"
>
{this.props.msg.pkg.get("browser.share.add")}
</button>
)}
{this.state.selectedItems.size > 0 ? (
<span> <span>
<button {this.props.browser.isSharing ? (
type="button" <button
onClick={() => this.delete()} type="button"
className="red0-bg white-font margin-r-m" onClick={() => {
> this.deleteSharing(
{this.props.msg.pkg.get("browser.delete")} this.props.browser.dirPath.join("/")
</button> );
}}
className="red0-bg white-font margin-r-m"
>
{this.props.msg.pkg.get("browser.share.del")}
</button>
) : (
<button
type="button"
onClick={this.addSharing}
className="green0-bg white-font margin-r-m"
>
{this.props.msg.pkg.get("browser.share.add")}
</button>
)}
<button {this.state.selectedItems.size > 0 ? (
type="button" <span>
onClick={() => this.moveHere()} <button
className="margin-r-m" type="button"
> onClick={() => this.delete()}
{this.props.msg.pkg.get("browser.paste")} className="red0-bg white-font margin-r-m"
</button> >
</span> {this.props.msg.pkg.get("browser.delete")}
) : null} </button>
<div className="hr white0-bg margin-t-m"></div> <button
</div> type="button"
onClick={() => this.moveHere()}
className="margin-r-m"
>
{this.props.msg.pkg.get("browser.paste")}
</button>
</span>
) : null}
</span>,
<span>
<span className="desc-m grey0-font">{`${this.props.msg.pkg.get(
"browser.used"
)} ${usedSpace} / ${spaceLimit}`}</span>
</span>,
])}
className="padding-m"
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
/>
<Flexbox <Flexbox
children={List([ children={List([
@ -539,18 +562,8 @@ export class Browser extends React.Component<Props, State, {}> {
className="margin-r-m black-font" className="margin-r-m black-font"
/>, />,
// <span>
// <span className="title-l">
// {this.props.msg.pkg.get("browser.item.title")}
// </span>
// <span className="desc-l grey0-font">
// Files and folders in current path
// </span>
// </span>,
<Flexbox children={breadcrumb} />, <Flexbox children={breadcrumb} />,
])} ])}
// style={{ flex: "column nowrap" }}
/> />
</span>, </span>,
@ -687,7 +700,7 @@ export class Browser extends React.Component<Props, State, {}> {
children={List([ children={List([
<Flexbox <Flexbox
children={List([ children={List([
<RiShareForwardBoxFill <RiFolderSharedFill
size="3rem" size="3rem"
className="purple0-font margin-r-m" className="purple0-font margin-r-m"
/>, />,

View file

@ -69,6 +69,12 @@ export function initState(): ICoreState {
userID: "", userID: "",
userName: "", userName: "",
userRole: "", userRole: "",
usedSpace: "0",
quota: {
spaceLimit: "0",
uploadSpeedLimit: 0,
downloadSpeedLimit: 0,
},
authed: false, authed: false,
captchaID: "", captchaID: "",
}, },

View file

@ -3,11 +3,14 @@ import * as React from "react";
import { ICoreState, MsgProps } from "./core_state"; import { ICoreState, MsgProps } from "./core_state";
import { updater } from "./state_updater"; import { updater } from "./state_updater";
import { alertMsg } from "../common/env"; import { alertMsg } from "../common/env";
import { Quota } from "../client";
export interface LoginProps { export interface LoginProps {
userID: string; userID: string;
userName: string; userName: string;
userRole: string; userRole: string;
usedSpace: string;
quota: Quota;
authed: boolean; authed: boolean;
captchaID: string; captchaID: string;
} }
@ -85,11 +88,17 @@ export class AuthPane extends React.Component<Props, State, {}> {
updater().initLan(); updater().initLan();
this.update(updater().updateMsg); this.update(updater().updateMsg);
})
.then(() => {
return updater().isSharing(updater().props.browser.dirPath.join("/"));
})
.then(() => {
this.update(updater().updateBrowser);
}); });
}; };
logout = () => { logout = async () => {
updater() return updater()
.logout() .logout()
.then((ok: boolean) => { .then((ok: boolean) => {
if (ok) { if (ok) {
@ -97,6 +106,9 @@ export class AuthPane extends React.Component<Props, State, {}> {
} else { } else {
alertMsg(this.props.msg.pkg.get("login.logout.fail")); alertMsg(this.props.msg.pkg.get("login.logout.fail"));
} }
})
.then(() => {
return this.refreshCaptcha();
}); });
}; };

View file

@ -52,13 +52,14 @@ export class RootFrame extends React.Component<Props, State, {}> {
<Browser <Browser
browser={this.props.browser} browser={this.props.browser}
msg={this.props.msg} msg={this.props.msg}
login={this.props.login}
update={this.props.update} update={this.props.update}
/> />
</div> </div>
<div id="tail" className="container-center black0-font"> <div id="tail" className="container-center black0-font">
<a href="https://github.com/ihexxa/quickshare">Quickshare</a> - <a href="https://github.com/ihexxa/quickshare">Quickshare</a> -
sharing in simple way. quick and simple file sharing.
</div> </div>
</div> </div>
</div> </div>

View file

@ -258,6 +258,8 @@ export class Updater {
this.props.login.userID = resp.data.id; this.props.login.userID = resp.data.id;
this.props.login.userName = resp.data.name; this.props.login.userName = resp.data.name;
this.props.login.userRole = resp.data.role; this.props.login.userRole = resp.data.role;
this.props.login.usedSpace = resp.data.usedSpace;
this.props.login.quota = resp.data.quota;
return true; return true;
} }
return false; return false;

View file

@ -1,8 +1,11 @@
import * as React from "react"; import * as React from "react";
import { List } from "immutable";
import { RiGithubFill } from "@react-icons/all-files/ri/RiGithubFill";
import { ICoreState, MsgProps } from "./core_state"; import { ICoreState, MsgProps } from "./core_state";
import { LoginProps } from "./pane_login"; import { LoginProps } from "./pane_login";
import { updater } from "./state_updater"; import { updater } from "./state_updater";
import { Flexbox } from "./layout/flexbox";
export interface State {} export interface State {}
export interface Props { export interface Props {
@ -43,6 +46,7 @@ export class TopBar extends React.Component<Props, State, {}> {
<button <button
onClick={this.showAdmin} onClick={this.showAdmin}
className="grey3-bg grey4-font margin-r-m" className="grey3-bg grey4-font margin-r-m"
style={{ minWidth: "7rem" }}
> >
{this.props.msg.pkg.get("admin")} {this.props.msg.pkg.get("admin")}
</button> </button>
@ -53,7 +57,48 @@ export class TopBar extends React.Component<Props, State, {}> {
id="top-bar" id="top-bar"
className="top-bar cyan1-font padding-t-m padding-b-m padding-l-l padding-r-l" className="top-bar cyan1-font padding-t-m padding-b-m padding-l-l padding-r-l"
> >
<div className="flex-2col-parent"> <Flexbox
children={List([
<a
href="https://github.com/ihexxa/quickshare"
target="_blank"
className="h5"
>
Quickshare
{/* <RiGithubFill size="2rem" className="grey4-font margin-r-m" /> */}
</a>,
<Flexbox
children={List([
<span>
<span className="grey3-font font-s">
{this.props.login.userName}
</span>
&nbsp;-&nbsp;
<span className="grey0-font font-s margin-r-m">
{this.props.login.userRole}
</span>
</span>,
<button
onClick={this.showSettings}
className="grey3-bg grey4-font margin-r-m"
style={{ minWidth: "7rem" }}
>
{this.props.msg.pkg.get("settings")}
</button>,
adminBtn,
])}
childrenStyles={List([{}, {}, {}, {}])}
/>,
])}
childrenStyles={List([
{},
{ justifyContent: "flex-end", alignItems: "center" },
])}
/>
{/* <div className="flex-2col-parent">
<a <a
href="https://github.com/ihexxa/quickshare" href="https://github.com/ihexxa/quickshare"
className="flex-13col h5" className="flex-13col h5"
@ -75,8 +120,13 @@ export class TopBar extends React.Component<Props, State, {}> {
{this.props.msg.pkg.get("settings")} {this.props.msg.pkg.get("settings")}
</button> </button>
{adminBtn} {adminBtn}
<RiGithubFill
size="2rem"
className="grey4-font margin-r-m"
/>
</span> </span>
</div> </div> */}
</div> </div>
); );
} }

View file

@ -26,6 +26,7 @@ export const msgs: Map<string, string> = Map({
"browser.disable": "Disable", "browser.disable": "Disable",
"browser.location": "Location", "browser.location": "Location",
"browser.item.title": "Items", "browser.item.title": "Items",
"browser.used": "Used Space",
"panes.close": "Close", "panes.close": "Close",
"login.logout.fail": "Failed to log out", "login.logout.fail": "Failed to log out",
"login.username": "User Name", "login.username": "User Name",

View file

@ -25,6 +25,7 @@ export const msgs: Map<string, string> = Map({
"browser.stop": "停止", "browser.stop": "停止",
"browser.location": "位置", "browser.location": "位置",
"browser.item.title": "列表", "browser.item.title": "列表",
"browser.used": "已用空间",
"panes.close": "关闭", "panes.close": "关闭",
"login.logout.fail": "登出失败", "login.logout.fail": "登出失败",
"login.username": "用户名", "login.username": "用户名",