feat: support refreshing hash

This commit is contained in:
hexxa 2021-09-26 10:41:25 +08:00 committed by Hexxa
parent f8bb187c89
commit ec1bb36c21
11 changed files with 99 additions and 36 deletions

View file

@ -731,3 +731,11 @@ div.hr {
color: #e74c3c; color: #e74c3c;
border-radius: 0.5rem; border-radius: 0.5rem;
} }
.detail {
font-size: 1.2rem;
line-height: 2rem;
margin: 0 1rem 1rem 1rem;
padding: 1rem;
border-top: dashed 1px #7f8c8d;
}

View file

@ -205,3 +205,12 @@ func (cl *FilesClient) ListSharings() (*http.Response, *fileshdr.SharingResp, []
} }
return resp, shResp, nil return resp, shResp, nil
} }
func (cl *FilesClient) GenerateHash(filepath string) (*http.Response, string, []error) {
return cl.r.Post(cl.url("/v1/fs/hashes/sha1")).
AddCookie(cl.token).
Send(fileshdr.GenerateHashReq{
FilePath: filepath,
}).
End()
}

View file

@ -198,4 +198,14 @@ export class FilesClient extends BaseClient {
url: `${this.url}/v1/fs/sharings`, url: `${this.url}/v1/fs/sharings`,
}); });
}; };
generateHash = (filePath: string): Promise<Response> => {
return this.do({
method: "post",
url: `${this.url}/v1/fs/hashes/sha1`,
data: {
filePath: filePath,
},
});
};
} }

View file

@ -25,6 +25,7 @@ export interface FilesClientResps {
deleteSharingMockResp?: Response; deleteSharingMockResp?: Response;
listSharingsMockResp?: Response<ListSharingsResp>; listSharingsMockResp?: Response<ListSharingsResp>;
isSharingMockResp?: Response; isSharingMockResp?: Response;
generateHashMockResp?: Response;
} }
export const resps = { export const resps = {
@ -130,7 +131,9 @@ export const resps = {
}, },
}, },
isSharingMockResp: { status: 200, statusText: "", data: {} }, isSharingMockResp: { status: 200, statusText: "", data: {} },
generateHashMockResp: { status: 200, statusText: "", data: {} },
}; };
export class MockFilesClient { export class MockFilesClient {
private url: string; private url: string;
private resps: FilesClientResps; private resps: FilesClientResps;
@ -213,4 +216,8 @@ export class MockFilesClient {
isSharing = (dirPath: string): Promise<Response> => { isSharing = (dirPath: string): Promise<Response> => {
return this.wrapPromise(this.resps.isSharingMockResp); return this.wrapPromise(this.resps.isSharingMockResp);
}; };
generateHash = (filePath: string): Promise<Response> => {
return this.wrapPromise(this.resps.generateHashMockResp);
};
} }

View file

@ -107,6 +107,7 @@ export interface IFilesClient {
deleteSharing: (dirPath: string) => Promise<Response>; deleteSharing: (dirPath: string) => Promise<Response>;
isSharing: (dirPath: string) => Promise<Response>; isSharing: (dirPath: string) => Promise<Response>;
listSharings: () => Promise<Response<ListSharingsResp>>; listSharings: () => Promise<Response<ListSharingsResp>>;
generateHash: (filePath: string) => Promise<Response>;
} }
export interface Response<T = any> { export interface Response<T = any> {

View file

@ -2,6 +2,7 @@ import * as React from "react";
import * as ReactDOM from "react-dom"; import * as ReactDOM from "react-dom";
import { List, Map, Set } from "immutable"; import { List, Map, Set } from "immutable";
import FileSize from "filesize"; import FileSize from "filesize";
import { RiFolder2Fill } from "@react-icons/all-files/ri/RiFolder2Fill"; import { RiFolder2Fill } from "@react-icons/all-files/ri/RiFolder2Fill";
import { RiHomeSmileFill } from "@react-icons/all-files/ri/RiHomeSmileFill"; import { RiHomeSmileFill } from "@react-icons/all-files/ri/RiHomeSmileFill";
import { RiFile2Fill } from "@react-icons/all-files/ri/RiFile2Fill"; import { RiFile2Fill } from "@react-icons/all-files/ri/RiFile2Fill";
@ -318,10 +319,17 @@ export class Browser extends React.Component<Props, State, {}> {
}; };
toggleDetail = (name: string) => { toggleDetail = (name: string) => {
const showDetail = this.state.showDetail.has(name) ? this.state.showDetail.delete(name) : this.state.showDetail.add(name); const showDetail = this.state.showDetail.has(name)
? this.state.showDetail.delete(name)
: this.state.showDetail.add(name);
this.setState({ showDetail }); this.setState({ showDetail });
}; };
generateHash = async (filePath: string): Promise<boolean> => {
alertMsg(this.props.msg.pkg.get("refresh-hint"));
return updater().generateHash(filePath);
};
render() { render() {
const showOp = this.props.login.userRole === roleVisitor ? "hidden" : ""; const showOp = this.props.login.userRole === roleVisitor ? "hidden" : "";
@ -341,7 +349,8 @@ export class Browser extends React.Component<Props, State, {}> {
} }
); );
const nameCellClass = `item-name item-name-${this.props.browser.isVertical ? "vertical" : "horizontal" const nameCellClass = `item-name item-name-${
this.props.browser.isVertical ? "vertical" : "horizontal"
} pointer`; } pointer`;
const sizeCellClass = this.props.browser.isVertical const sizeCellClass = this.props.browser.isVertical
? `hidden margin-s` ? `hidden margin-s`
@ -434,7 +443,8 @@ export class Browser extends React.Component<Props, State, {}> {
<span className={`padding-m ${showOp}`}> <span className={`padding-m ${showOp}`}>
<button <button
onClick={() => this.select(item.name)} onClick={() => this.select(item.name)}
className={`${isSelected ? "blue0-bg white-font" : "grey2-bg grey3-font" className={`${
isSelected ? "blue0-bg white-font" : "grey2-bg grey3-font"
}`} }`}
style={{ width: "8rem", display: "inline-block" }} style={{ width: "8rem", display: "inline-block" }}
> >
@ -442,26 +452,21 @@ export class Browser extends React.Component<Props, State, {}> {
? this.props.msg.pkg.get("browser.deselect") ? this.props.msg.pkg.get("browser.deselect")
: this.props.msg.pkg.get("browser.select")} : this.props.msg.pkg.get("browser.select")}
</button> </button>
{/* <button
onClick={() => this.toggleDetail(item.name)}
className="grey2-bg grey3-font"
style={{ width: "8rem", display: "inline-block" }}
>
{this.props.msg.pkg.get("detail")}
</button> */}
</span>, </span>,
])} ])}
childrenStyles={List([{}, { justifyContent: "flex-end" }])} childrenStyles={List([{}, { justifyContent: "flex-end" }])}
/> />
) : ( ) : (
<div key={item.name}> <div key={item.name}>
<Flexbox <Flexbox
children={List([ children={List([
<span className="padding-m"> <span className="padding-m">
<Flexbox <Flexbox
children={List([ children={List([
<RiFile2Fill size="3rem" className="cyan0-font margin-r-m" />, <RiFile2Fill
size="3rem"
className="cyan0-font margin-r-m"
/>,
<span className={`${nameCellClass}`}> <span className={`${nameCellClass}`}>
<a <a
@ -486,7 +491,7 @@ export class Browser extends React.Component<Props, State, {}> {
<span className={`item-op padding-m ${showOp}`}> <span className={`item-op padding-m ${showOp}`}>
<button <button
onClick={() => this.toggleDetail(item.name)} onClick={() => this.toggleDetail(item.name)}
className="grey2-bg grey3-font" className="grey2-bg grey3-font margin-r-m"
style={{ width: "8rem", display: "inline-block" }} style={{ width: "8rem", display: "inline-block" }}
> >
{this.props.msg.pkg.get("detail")} {this.props.msg.pkg.get("detail")}
@ -495,7 +500,8 @@ export class Browser extends React.Component<Props, State, {}> {
<button <button
type="button" type="button"
onClick={() => this.select(item.name)} onClick={() => this.select(item.name)}
className={`${isSelected ? "blue0-bg white-font" : "grey2-bg grey3-font" className={`${
isSelected ? "blue0-bg white-font" : "grey2-bg grey3-font"
}`} }`}
style={{ width: "8rem", display: "inline-block" }} style={{ width: "8rem", display: "inline-block" }}
> >
@ -508,9 +514,27 @@ export class Browser extends React.Component<Props, State, {}> {
childrenStyles={List([{}, { justifyContent: "flex-end" }])} childrenStyles={List([{}, { justifyContent: "flex-end" }])}
/> />
<div <div
className={`grey2-bg grey3-font margin-l ${this.state.showDetail.has(item.name) ? "" : "hidden"}`} className={`${
this.state.showDetail.has(item.name) ? "" : "hidden"
}`}
> >
<span className="margin-l">{`SHA1: ${item.sha1}`}</span> <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>
</div> </div>
); );
@ -534,7 +558,7 @@ export class Browser extends React.Component<Props, State, {}> {
</div> </div>
<div className="container" style={{ paddingBottom: "1rem" }}> <div className="container" style={{ paddingBottom: "1rem" }}>
<div className={`${showOp}`} > <div className={`${showOp}`}>
<Flexbox <Flexbox
children={List([ children={List([
<span> <span>
@ -672,9 +696,7 @@ export class Browser extends React.Component<Props, State, {}> {
/> />
{uploading.err.trim() === "" ? null : ( {uploading.err.trim() === "" ? null : (
<div className="alert-red margin-s"> <div className="alert-red margin-s">
<span className="padding-m"> <span className="padding-m">{uploading.err.trim()}</span>
{uploading.err.trim()}
</span>
</div> </div>
)} )}
</div> </div>
@ -761,7 +783,8 @@ export class Browser extends React.Component<Props, State, {}> {
type="text" type="text"
readOnly readOnly
className="margin-r-m" className="margin-r-m"
value={`${document.location.href.split("?")[0] value={`${
document.location.href.split("?")[0]
}?dir=${encodeURIComponent(dirPath)}`} }?dir=${encodeURIComponent(dirPath)}`}
/> />
<button <button
@ -902,12 +925,8 @@ export class Browser extends React.Component<Props, State, {}> {
</button> </button>
</div> </div>
</div> </div>
<div> <div>{sharingListPane}</div>
{sharingListPane} <div>{uploadingListPane}</div>
</div>
<div>
{uploadingListPane}
</div>
{itemListPane} {itemListPane}
</div> </div>
</div> </div>

View file

@ -548,6 +548,11 @@ export class Updater {
} }
}; };
generateHash = async (filePath: string): Promise<boolean> => {
const resp = await this.filesClient.generateHash(filePath);
return resp.status === 200;
};
updateBrowser = (prevState: ICoreState): ICoreState => { updateBrowser = (prevState: ICoreState): ICoreState => {
return { return {
...prevState, ...prevState,

View file

@ -75,4 +75,6 @@ export const msgs: Map<string, string> = Map({
"upload.404.title": "No uploading is in the progress", "upload.404.title": "No uploading is in the progress",
"upload.404.desc": "You can upload a file in the items tab", "upload.404.desc": "You can upload a file in the items tab",
"detail": "Detail", "detail": "Detail",
"refresh": "Refresh",
"refresh-hint": "Please refresh later to see the result",
}); });

View file

@ -73,4 +73,6 @@ export const msgs: Map<string, string> = Map({
"upload.404.title": "没有正在上传的文件", "upload.404.title": "没有正在上传的文件",
"upload.404.desc": "在列表面板可以上传文件", "upload.404.desc": "在列表面板可以上传文件",
"detail": "详细", "detail": "详细",
"refresh": "刷新",
"refresh-hint": "请稍后刷新查看结果",
}); });

View file

@ -903,12 +903,12 @@ func (h *FileHandlers) ListSharings(c *gin.Context) {
c.JSON(200, &SharingResp{SharingDirs: dirs}) c.JSON(200, &SharingResp{SharingDirs: dirs})
} }
type HashBody struct { type GenerateHashReq struct {
FilePath string `json:"filePath"` FilePath string `json:"filePath"`
} }
func (h *FileHandlers) GenerateHash(c *gin.Context) { func (h *FileHandlers) GenerateHash(c *gin.Context) {
req := &HashBody{} req := &GenerateHashReq{}
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(q.ErrResp(c, 400, err)) c.JSON(q.ErrResp(c, 400, err))
return return

View file

@ -69,7 +69,7 @@ func NewMultiUsersSvc(cfg gocfg.ICfg, deps *depidx.Deps) (*MultiUsersSvc, error)
apiRuleCname(userstore.AdminRole, "DELETE", "/v1/fs/sharings"): true, apiRuleCname(userstore.AdminRole, "DELETE", "/v1/fs/sharings"): true,
apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings"): true, apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings"): true,
apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings/exist"): true, apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings/exist"): true,
apiRuleCname(userstore.AdminRole, "GET", "/hashes/sha1"): true, apiRuleCname(userstore.AdminRole, "POST", "/v1/fs/hashes/sha1"): true,
// user rules // user rules
apiRuleCname(userstore.UserRole, "GET", "/"): true, apiRuleCname(userstore.UserRole, "GET", "/"): true,
@ -99,7 +99,7 @@ func NewMultiUsersSvc(cfg gocfg.ICfg, deps *depidx.Deps) (*MultiUsersSvc, error)
apiRuleCname(userstore.UserRole, "DELETE", "/v1/fs/sharings"): true, apiRuleCname(userstore.UserRole, "DELETE", "/v1/fs/sharings"): true,
apiRuleCname(userstore.UserRole, "GET", "/v1/fs/sharings"): true, apiRuleCname(userstore.UserRole, "GET", "/v1/fs/sharings"): true,
apiRuleCname(userstore.UserRole, "GET", "/v1/fs/sharings/exist"): true, apiRuleCname(userstore.UserRole, "GET", "/v1/fs/sharings/exist"): true,
apiRuleCname(userstore.AdminRole, "GET", "/hashes/sha1"): true, apiRuleCname(userstore.AdminRole, "POST", "/v1/fs/hashes/sha1"): true,
// visitor rules // visitor rules
apiRuleCname(userstore.VisitorRole, "GET", "/"): true, apiRuleCname(userstore.VisitorRole, "GET", "/"): true,
apiRuleCname(userstore.VisitorRole, "GET", publicPath): true, apiRuleCname(userstore.VisitorRole, "GET", publicPath): true,