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;
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
}
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`,
});
};
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;
listSharingsMockResp?: Response<ListSharingsResp>;
isSharingMockResp?: Response;
generateHashMockResp?: Response;
}
export const resps = {
@ -130,7 +131,9 @@ export const resps = {
},
},
isSharingMockResp: { status: 200, statusText: "", data: {} },
generateHashMockResp: { status: 200, statusText: "", data: {} },
};
export class MockFilesClient {
private url: string;
private resps: FilesClientResps;
@ -213,4 +216,8 @@ export class MockFilesClient {
isSharing = (dirPath: string): Promise<Response> => {
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>;
isSharing: (dirPath: string) => Promise<Response>;
listSharings: () => Promise<Response<ListSharingsResp>>;
generateHash: (filePath: string) => Promise<Response>;
}
export interface Response<T = any> {

View file

@ -2,6 +2,7 @@ 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";
@ -318,10 +319,17 @@ export class Browser extends React.Component<Props, State, {}> {
};
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 });
};
generateHash = async (filePath: string): Promise<boolean> => {
alertMsg(this.props.msg.pkg.get("refresh-hint"));
return updater().generateHash(filePath);
};
render() {
const showOp = this.props.login.userRole === roleVisitor ? "hidden" : "";
@ -341,8 +349,9 @@ export class Browser extends React.Component<Props, State, {}> {
}
);
const nameCellClass = `item-name item-name-${this.props.browser.isVertical ? "vertical" : "horizontal"
} pointer`;
const nameCellClass = `item-name item-name-${
this.props.browser.isVertical ? "vertical" : "horizontal"
} pointer`;
const sizeCellClass = this.props.browser.isVertical
? `hidden margin-s`
: ``;
@ -434,34 +443,30 @@ export class Browser extends React.Component<Props, State, {}> {
<span className={`padding-m ${showOp}`}>
<button
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" }}
>
{isSelected
? this.props.msg.pkg.get("browser.deselect")
: this.props.msg.pkg.get("browser.select")}
</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>,
])}
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
/>
) : (
<div key={item.name}>
<Flexbox
children={List([
<span className="padding-m">
<Flexbox
children={List([
<RiFile2Fill size="3rem" className="cyan0-font margin-r-m" />,
<RiFile2Fill
size="3rem"
className="cyan0-font margin-r-m"
/>,
<span className={`${nameCellClass}`}>
<a
@ -486,7 +491,7 @@ export class Browser extends React.Component<Props, State, {}> {
<span className={`item-op padding-m ${showOp}`}>
<button
onClick={() => this.toggleDetail(item.name)}
className="grey2-bg grey3-font"
className="grey2-bg grey3-font margin-r-m"
style={{ width: "8rem", display: "inline-block" }}
>
{this.props.msg.pkg.get("detail")}
@ -495,8 +500,9 @@ export class Browser extends React.Component<Props, State, {}> {
<button
type="button"
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" }}
>
{isSelected
@ -508,9 +514,27 @@ export class Browser extends React.Component<Props, State, {}> {
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
/>
<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>
);
@ -534,7 +558,7 @@ export class Browser extends React.Component<Props, State, {}> {
</div>
<div className="container" style={{ paddingBottom: "1rem" }}>
<div className={`${showOp}`} >
<div className={`${showOp}`}>
<Flexbox
children={List([
<span>
@ -672,9 +696,7 @@ export class Browser extends React.Component<Props, State, {}> {
/>
{uploading.err.trim() === "" ? null : (
<div className="alert-red margin-s">
<span className="padding-m">
{uploading.err.trim()}
</span>
<span className="padding-m">{uploading.err.trim()}</span>
</div>
)}
</div>
@ -761,8 +783,9 @@ export class Browser extends React.Component<Props, State, {}> {
type="text"
readOnly
className="margin-r-m"
value={`${document.location.href.split("?")[0]
}?dir=${encodeURIComponent(dirPath)}`}
value={`${
document.location.href.split("?")[0]
}?dir=${encodeURIComponent(dirPath)}`}
/>
<button
onClick={() => {
@ -902,12 +925,8 @@ export class Browser extends React.Component<Props, State, {}> {
</button>
</div>
</div>
<div>
{sharingListPane}
</div>
<div>
{uploadingListPane}
</div>
<div>{sharingListPane}</div>
<div>{uploadingListPane}</div>
{itemListPane}
</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 => {
return {
...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.desc": "You can upload a file in the items tab",
"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.desc": "在列表面板可以上传文件",
"detail": "详细",
"refresh": "刷新",
"refresh-hint": "请稍后刷新查看结果",
});

View file

@ -903,12 +903,12 @@ func (h *FileHandlers) ListSharings(c *gin.Context) {
c.JSON(200, &SharingResp{SharingDirs: dirs})
}
type HashBody struct {
type GenerateHashReq struct {
FilePath string `json:"filePath"`
}
func (h *FileHandlers) GenerateHash(c *gin.Context) {
req := &HashBody{}
req := &GenerateHashReq{}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(q.ErrResp(c, 400, err))
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, "GET", "/v1/fs/sharings"): 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
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, "GET", "/v1/fs/sharings"): 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
apiRuleCname(userstore.VisitorRole, "GET", "/"): true,
apiRuleCname(userstore.VisitorRole, "GET", publicPath): true,