diff --git a/public/static/css/style.css b/public/static/css/style.css index 788604c..91faa32 100644 --- a/public/static/css/style.css +++ b/public/static/css/style.css @@ -730,4 +730,12 @@ div.hr { border: dashed 1px #e74c3c; color: #e74c3c; border-radius: 0.5rem; -} \ No newline at end of file +} + +.detail { + font-size: 1.2rem; + line-height: 2rem; + margin: 0 1rem 1rem 1rem; + padding: 1rem; + border-top: dashed 1px #7f8c8d; +} diff --git a/src/client/files.go b/src/client/files.go index 3d3639f..080f5aa 100644 --- a/src/client/files.go +++ b/src/client/files.go @@ -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() +} diff --git a/src/client/web/src/client/files.ts b/src/client/web/src/client/files.ts index 9332581..9f069da 100644 --- a/src/client/web/src/client/files.ts +++ b/src/client/web/src/client/files.ts @@ -198,4 +198,14 @@ export class FilesClient extends BaseClient { url: `${this.url}/v1/fs/sharings`, }); }; + + generateHash = (filePath: string): Promise => { + return this.do({ + method: "post", + url: `${this.url}/v1/fs/hashes/sha1`, + data: { + filePath: filePath, + }, + }); + }; } diff --git a/src/client/web/src/client/files_mock.ts b/src/client/web/src/client/files_mock.ts index 2a37269..20f9009 100644 --- a/src/client/web/src/client/files_mock.ts +++ b/src/client/web/src/client/files_mock.ts @@ -25,6 +25,7 @@ export interface FilesClientResps { deleteSharingMockResp?: Response; listSharingsMockResp?: Response; 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 => { return this.wrapPromise(this.resps.isSharingMockResp); }; + + generateHash = (filePath: string): Promise => { + return this.wrapPromise(this.resps.generateHashMockResp); + }; } diff --git a/src/client/web/src/client/index.ts b/src/client/web/src/client/index.ts index d1b994c..9e42080 100644 --- a/src/client/web/src/client/index.ts +++ b/src/client/web/src/client/index.ts @@ -107,6 +107,7 @@ export interface IFilesClient { deleteSharing: (dirPath: string) => Promise; isSharing: (dirPath: string) => Promise; listSharings: () => Promise>; + generateHash: (filePath: string) => Promise; } export interface Response { diff --git a/src/client/web/src/components/browser.tsx b/src/client/web/src/components/browser.tsx index 798435f..17354cf 100644 --- a/src/client/web/src/components/browser.tsx +++ b/src/client/web/src/components/browser.tsx @@ -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 { }; 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 => { + 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 { } ); - 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 { - {/* */} , ])} childrenStyles={List([{}, { justifyContent: "flex-end" }])} /> ) : (
- , + , { , + ])} + className={`grey2-bg grey3-font detail margin-r-m`} + childrenStyles={List([{}, { justifyContent: "flex-end" }])} + />
); @@ -534,7 +558,7 @@ export class Browser extends React.Component {
-
+
@@ -672,9 +696,7 @@ export class Browser extends React.Component { /> {uploading.err.trim() === "" ? null : (
- - {uploading.err.trim()} - + {uploading.err.trim()}
)}
@@ -761,8 +783,9 @@ export class Browser extends React.Component { 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)}`} />
-
- {sharingListPane} -
-
- {uploadingListPane} -
+
{sharingListPane}
+
{uploadingListPane}
{itemListPane} diff --git a/src/client/web/src/components/state_updater.ts b/src/client/web/src/components/state_updater.ts index ec9ae83..4487c64 100644 --- a/src/client/web/src/components/state_updater.ts +++ b/src/client/web/src/components/state_updater.ts @@ -548,6 +548,11 @@ export class Updater { } }; + generateHash = async (filePath: string): Promise => { + const resp = await this.filesClient.generateHash(filePath); + return resp.status === 200; + }; + updateBrowser = (prevState: ICoreState): ICoreState => { return { ...prevState, diff --git a/src/client/web/src/i18n/en_US.ts b/src/client/web/src/i18n/en_US.ts index 7485bff..1b96267 100644 --- a/src/client/web/src/i18n/en_US.ts +++ b/src/client/web/src/i18n/en_US.ts @@ -75,4 +75,6 @@ export const msgs: Map = 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", }); diff --git a/src/client/web/src/i18n/zh_CN.ts b/src/client/web/src/i18n/zh_CN.ts index ebcc933..22b0a35 100644 --- a/src/client/web/src/i18n/zh_CN.ts +++ b/src/client/web/src/i18n/zh_CN.ts @@ -73,4 +73,6 @@ export const msgs: Map = Map({ "upload.404.title": "没有正在上传的文件", "upload.404.desc": "在列表面板可以上传文件", "detail": "详细", + "refresh": "刷新", + "refresh-hint": "请稍后刷新查看结果", }); diff --git a/src/handlers/fileshdr/handlers.go b/src/handlers/fileshdr/handlers.go index a0e02a7..bf2aa1f 100644 --- a/src/handlers/fileshdr/handlers.go +++ b/src/handlers/fileshdr/handlers.go @@ -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 diff --git a/src/handlers/multiusers/handlers.go b/src/handlers/multiusers/handlers.go index 22d90ee..6f8ec18 100644 --- a/src/handlers/multiusers/handlers.go +++ b/src/handlers/multiusers/handlers.go @@ -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,