diff --git a/frontend/src/components/dialog/generate-upload-link.js b/frontend/src/components/dialog/generate-upload-link.js index 9dc019e75e..f0b09a57fd 100644 --- a/frontend/src/components/dialog/generate-upload-link.js +++ b/frontend/src/components/dialog/generate-upload-link.js @@ -6,7 +6,7 @@ import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, Ale import { gettext, shareLinkPasswordMinLength, canSendShareLinkEmail } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; -import SharedUploadInfo from '../../models/shared-upload-info'; +import UploadLink from '../../models/upload-link'; import toaster from '../toast'; import SendLink from '../send-link'; import SessionExpiredTip from '../session-expired-tip'; @@ -41,7 +41,7 @@ class GenerateUploadLink extends React.Component { let repoID = this.props.repoID; seafileAPI.getUploadLinks(repoID, path).then((res) => { if (res.data.length !== 0) { - let sharedUploadInfo = new SharedUploadInfo(res.data[0]); + let sharedUploadInfo = new UploadLink(res.data[0]); this.setState({sharedUploadInfo: sharedUploadInfo}); } }).catch((err) => { @@ -98,7 +98,7 @@ class GenerateUploadLink extends React.Component { let isValid = this.validateParamsInput(); if (isValid) { seafileAPI.createUploadLink(repoID, path, password, expireDays).then((res) => { - let sharedUploadInfo = new SharedUploadInfo(res.data); + let sharedUploadInfo = new UploadLink(res.data); this.setState({sharedUploadInfo: sharedUploadInfo}); }).catch(error => { let errMessage = Utils.getErrorMsg(error); diff --git a/frontend/src/components/dialog/share-admin-link.js b/frontend/src/components/dialog/share-admin-link.js new file mode 100644 index 0000000000..eea392965f --- /dev/null +++ b/frontend/src/components/dialog/share-admin-link.js @@ -0,0 +1,44 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap'; +import copy from '@seafile/seafile-editor/dist//utils/copy-to-clipboard'; +import { gettext } from '../../utils/constants'; +import toaster from '../../components/toast'; + +const propTypes = { + link: PropTypes.string.isRequired, + toggleDialog: PropTypes.func.isRequired +}; + +class ShareAdminLink extends React.Component { + + constructor(props) { + super(props); + } + + copyToClipboard = () => { + copy(this.props.link); + this.props.toggleDialog(); + toaster.success(gettext('The link is copied to the clipboard.')), {duration: 2}; + } + + render() { + const { link, toggleDialog } = this.props; + return ( + + {gettext('Link')} + + {link} + + + + + + + ); + } +} + +ShareAdminLink.propTypes = propTypes; + +export default ShareAdminLink; diff --git a/frontend/src/models/shared-upload-info.js b/frontend/src/models/upload-link.js similarity index 88% rename from frontend/src/models/shared-upload-info.js rename to frontend/src/models/upload-link.js index cd7f0f9b8c..adead88785 100644 --- a/frontend/src/models/shared-upload-info.js +++ b/frontend/src/models/upload-link.js @@ -1,4 +1,4 @@ -class SharedUploadInfo { +class UploadLink { constructor(object) { this.repo_id = object.repo_id; @@ -17,4 +17,4 @@ class SharedUploadInfo { } -export default SharedUploadInfo; +export default UploadLink; diff --git a/frontend/src/pages/share-admin/share-links.js b/frontend/src/pages/share-admin/share-links.js index 1e22395917..05faf00698 100644 --- a/frontend/src/pages/share-admin/share-links.js +++ b/frontend/src/pages/share-admin/share-links.js @@ -2,8 +2,6 @@ import React, { Component, Fragment } from 'react'; import { Link } from '@reach/router'; import moment from 'moment'; import { Dropdown, DropdownToggle, DropdownItem } from 'reactstrap'; -import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap'; -import copy from '@seafile/seafile-editor/dist//utils/copy-to-clipboard'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import { isPro, gettext, siteRoot, loginUrl, canGenerateUploadLink } from '../../utils/constants'; @@ -13,6 +11,7 @@ import Loading from '../../components/loading'; import toaster from '../../components/toast'; import EmptyTip from '../../components/empty-tip'; import ShareLinkPermissionSelect from '../../components/dialog/share-link-permission-select'; +import ShareAdminLink from '../../components/dialog/share-admin-link'; class Content extends Component { @@ -30,37 +29,6 @@ class Content extends Component { this.props.sortItems(sortBy, sortOrder); } - constructor(props) { - super(props); - this.state = { - modalOpen: false, - modalContent: '' - }; - } - - // required by `Modal`, and can only set the 'open' state - toggleModal = () => { - this.setState({ - modalOpen: !this.state.modalOpen - }); - } - - showModal = (options) => { - this.toggleModal(); - this.setState({modalContent: options.content}); - } - - copyToClipboard = () => { - copy(this.state.modalContent); - this.setState({ - modalOpen: false - }); - let message = gettext('Share link is copied to the clipboard.'); - toaster.success(message), { - duration: 2 - }; - } - render() { const { loading, errorMsg, items, sortBy, sortOrder } = this.props; @@ -85,44 +53,32 @@ class Content extends Component { // only for some columns const columnWidths = isPro ? ['14%', '7%', '14%'] : ['21%', '14%', '20%']; const table = ( - - - - {isDesktop ? ( - - - - - {isPro && } - - - - - ) : ( - - - - - - )} - - - {items.map((item, index) => { - return (); - })} - -
{/*icon*/}{gettext('Name')} {sortByName && sortIcon}{gettext('Library')}{gettext('Permission')}{gettext('Visits')}{gettext('Expiration')} {sortByTime && sortIcon}{/*Operations*/}
- - {gettext('Link')} - - {this.state.modalContent} - - - {' '} - - - -
+ + + {isDesktop ? ( + + + + + {isPro && } + + + + + ) : ( + + + + + + )} + + + {items.map((item, index) => { + return (); + })} + +
{/*icon*/}{gettext('Name')} {sortByName && sortIcon}{gettext('Library')}{gettext('Permission')}{gettext('Visits')}{gettext('Expiration')} {sortByTime && sortIcon}{/*Operations*/}
); return items.length ? table : emptyTip; @@ -147,7 +103,8 @@ class Item extends Component { currentPermission: isPro ? this.getCurrentPermission() : '', isOpIconShown: false, isOpMenuOpen: false, // for mobile - isPermSelectDialogOpen: false // for mobile + isPermSelectDialogOpen: false, // for mobile + isLinkDialogOpen: false }; } @@ -199,6 +156,12 @@ class Item extends Component { }); } + toggleLinkDialog = () => { + this.setState({ + isLinkDialogOpen: !this.state.isLinkDialogOpen + }); + } + handleMouseOver = () => { this.setState({isOpIconShown: true}); } @@ -209,7 +172,7 @@ class Item extends Component { viewLink = (e) => { e.preventDefault(); - this.props.showModal({content: this.props.item.link}); + this.toggleLinkDialog(); } removeLink = (e) => { @@ -217,7 +180,7 @@ class Item extends Component { this.props.onRemoveLink(this.props.item); } - renderExpriedData = () => { + renderExpiration = () => { let item = this.props.item; if (!item.expire_date) { return ( @@ -228,8 +191,7 @@ class Item extends Component { return ( {item.is_expired ? - {expire_date} : - expire_date + {expire_date} : expire_date } ); @@ -252,16 +214,16 @@ class Item extends Component { render() { const item = this.props.item; - const { currentPermission, isOpIconShown, isPermSelectDialogOpen } = this.state; + const { currentPermission, isOpIconShown, isPermSelectDialogOpen, isLinkDialogOpen } = this.state; - let iconUrl, linkUrl; + let iconUrl, objUrl; if (item.is_dir) { let path = item.path === '/' ? '/' : item.path.slice(0, item.path.length - 1); iconUrl = Utils.getFolderIconUrl(false); - linkUrl = `${siteRoot}library/${item.repo_id}/${encodeURIComponent(item.repo_name)}${Utils.encodePath(path)}`; + objUrl = `${siteRoot}library/${item.repo_id}/${encodeURIComponent(item.repo_name)}${Utils.encodePath(path)}`; } else { iconUrl = Utils.getFileIconUrl(item.obj_name); - linkUrl = `${siteRoot}lib/${item.repo_id}/file${Utils.encodePath(item.path)}`; + objUrl = `${siteRoot}lib/${item.repo_id}/file${Utils.encodePath(item.path)}`; } const desktopItem = ( @@ -269,8 +231,8 @@ class Item extends Component { {item.is_dir ? - {item.obj_name} : - {item.obj_name} + {item.obj_name} : + {item.obj_name} } {item.repo_name} @@ -286,9 +248,9 @@ class Item extends Component { } {item.view_cnt} - {this.renderExpriedData()} + {this.renderExpiration()} - + {!item.is_expired && } @@ -300,14 +262,14 @@ class Item extends Component { {item.is_dir ? - {item.obj_name} : - {item.obj_name} + {item.obj_name} : + {item.obj_name} } {isPro && {Utils.getShareLinkPermissionObject(currentPermission).text}}
{item.repo_name}
{item.view_cnt}({gettext('Visits')}) - {this.renderExpriedData()}({gettext('Expiration')}) + {this.renderExpiration()}({gettext('Expiration')}) @@ -322,7 +284,7 @@ class Item extends Component {
{(isPro && !item.is_expired) && {gettext('Permission')}} - {gettext('View')} + {!item.is_expired && {gettext('View')}} {gettext('Remove')}
@@ -340,7 +302,17 @@ class Item extends Component { ); - return this.props.isDesktop ? desktopItem : mobileItem; + return ( + + {this.props.isDesktop ? desktopItem : mobileItem} + {isLinkDialogOpen && + + } + + ); } } diff --git a/frontend/src/pages/share-admin/upload-links.js b/frontend/src/pages/share-admin/upload-links.js index 1924de7d83..92ea770082 100644 --- a/frontend/src/pages/share-admin/upload-links.js +++ b/frontend/src/pages/share-admin/upload-links.js @@ -1,84 +1,65 @@ import React, { Component, Fragment } from 'react'; import { Link } from '@reach/router'; import moment from 'moment'; -import { Modal, ModalHeader, ModalBody } from 'reactstrap'; +import { Dropdown, DropdownToggle, DropdownItem } from 'reactstrap'; import { gettext, siteRoot, loginUrl, canGenerateShareLink } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import toaster from '../../components/toast'; -import SharedUploadInfo from '../../models/shared-upload-info'; +import Loading from '../../components/loading'; import EmptyTip from '../../components/empty-tip'; +import UploadLink from '../../models/upload-link'; +import ShareAdminLink from '../../components/dialog/share-admin-link'; class Content extends Component { - constructor(props) { - super(props); - this.state = { - modalOpen: false, - modalContent: '' - }; - } - - // required by `Modal`, and can only set the 'open' state - toggleModal = () => { - this.setState({ - modalOpen: !this.state.modalOpen - }); - } - - showModal = (options) => { - this.toggleModal(); - this.setState({ - modalContent: options.content - }); - } - render() { const { loading, errorMsg, items } = this.props; if (loading) { - return ; - } else if (errorMsg) { - return

{errorMsg}

; - } else { - const emptyTip = ( - -

{gettext('You don\'t have any upload links')}

-

{gettext('You can generate an upload link from any folder. Anyone who receives this link can upload files to this folder.')}

-
- ); - - const table = ( - - - - - - - - - - - - - - {items.map((item, index) => { - return (); - })} - -
{/*icon*/}{gettext('Name')}{gettext('Library')}{gettext('Visits')}{gettext('Expiration')}{/*Operations*/}
- - - {gettext('Link')} - - {this.state.modalContent} - - -
- ); - - return items.length ? table : emptyTip; + return ; } + if (errorMsg) { + return

{errorMsg}

; + } + + const emptyTip = ( + +

{gettext('You don\'t have any upload links')}

+

{gettext('You can generate an upload link from any folder. Anyone who receives this link can upload files to this folder.')}

+
+ ); + + const isDesktop = Utils.isDesktop(); + const table = ( + + + {isDesktop ? ( + + + + + + + + + ) : ( + + + + + + )} + + + {items.map((item, index) => { + return (); + })} + +
{/*icon*/}{gettext('Name')}{gettext('Library')}{gettext('Visits')}{gettext('Expiration')}{/*Operations*/}
+ ); + + return items.length ? table : emptyTip; } } @@ -87,21 +68,35 @@ class Item extends Component { constructor(props) { super(props); this.state = { - showOpIcon: false, + isOpIconShown: false, + isOpMenuOpen: false, // for mobile + isLinkDialogOpen: false }; } + toggleOpMenu = () => { + this.setState({ + isOpMenuOpen: !this.state.isOpMenuOpen + }); + } + + toggleLinkDialog = () => { + this.setState({ + isLinkDialogOpen: !this.state.isLinkDialogOpen + }); + } + handleMouseOver = () => { - this.setState({showOpIcon: true}); + this.setState({isOpIconShown: true}); } handleMouseOut = () => { - this.setState({showOpIcon: false}); + this.setState({isOpIconShown: false}); } viewLink = (e) => { e.preventDefault(); - this.props.showModal({content: this.props.item.link}); + this.toggleLinkDialog(); } removeLink = (e) => { @@ -109,15 +104,7 @@ class Item extends Component { this.props.onRemoveLink(this.props.item); } - getUploadParams = () => { - let item = this.props.item; - let iconUrl = Utils.getFolderIconUrl(false); - let uploadUrl = `${siteRoot}library/${item.repo_id}/${item.repo_name}${Utils.encodePath(item.path)}`; - - return { iconUrl, uploadUrl }; - } - - renderExpriedData = () => { + renderExpiration = () => { let item = this.props.item; if (!item.expire_date) { return ( @@ -128,8 +115,7 @@ class Item extends Component { return ( {item.is_expired ? - {expire_date} : - expire_date + {expire_date} : expire_date } ); @@ -137,25 +123,67 @@ class Item extends Component { render() { let item = this.props.item; - let { iconUrl, uploadUrl } = this.getUploadParams(); + const { isOpIconShown, isLinkDialogOpen } = this.state; - let iconVisibility = this.state.showOpIcon ? '' : ' invisible'; - let linkIconClassName = 'sf2-icon-link action-icon' + iconVisibility; - let deleteIconClassName = 'sf2-icon-delete action-icon' + iconVisibility; + const iconUrl = Utils.getFolderIconUrl(false); + const repoUrl = `${siteRoot}library/${item.repo_id}/${encodeURIComponent(item.repo_name)}`; + const objUrl = `${repoUrl}${Utils.encodePath(item.path)}`; - return ( + const desktopItem = ( - - {item.obj_name} - {item.repo_name} + + {item.obj_name} + {item.repo_name} {item.view_cnt} - {this.renderExpriedData()} + {this.renderExpiration()} - - + {!item.is_expired && } + ); + + const mobileItem = ( + + + + {item.obj_name} +
+ {item.repo_name}
+ {item.view_cnt}({gettext('Visits')}) + {this.renderExpiration()}({gettext('Expiration')}) + + + + +
+
+
+ {!item.is_expired && {gettext('View')}} + {gettext('Remove')} +
+
+
+ + + ); + return ( + + {this.props.isDesktop ? desktopItem : mobileItem} + {isLinkDialogOpen && + + } + + ); } } @@ -172,9 +200,8 @@ class ShareAdminUploadLinks extends Component { componentDidMount() { seafileAPI.listUploadLinks().then((res) => { - // res: {data: Array(2), status: 200, statusText: "OK", headers: {…}, config: {…}, …} let items = res.data.map(item => { - return new SharedUploadInfo(item); + return new UploadLink(item); }); this.setState({ loading: false, @@ -209,13 +236,10 @@ class ShareAdminUploadLinks extends Component { return uploadItem.token !== item.token; }); this.setState({items: items}); - let message = gettext("Successfully deleted upload link."); + const message = gettext('Successfully deleted 1 item.'); toaster.success(message); }).catch((error) => { - let errMessage = Utils.getErrorMsg(error); - if (errMessage === gettext('Error')) { - errMessage = gettext("Failed to delete upload link."); - } + const errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); } @@ -226,7 +250,7 @@ class ShareAdminUploadLinks extends Component {
    - { canGenerateShareLink && ( + {canGenerateShareLink && (
  • {gettext('Share Links')}
  • )}
  • {gettext('Upload Links')}
  • @@ -234,9 +258,9 @@ class ShareAdminUploadLinks extends Component {