diff --git a/frontend/src/components/dialog/perm-select.js b/frontend/src/components/dialog/perm-select.js
new file mode 100644
index 0000000000..814410f873
--- /dev/null
+++ b/frontend/src/components/dialog/perm-select.js
@@ -0,0 +1,65 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Modal, ModalBody } from 'reactstrap';
+import { Utils } from '../../utils/utils';
+
+const propTypes = {
+ currentPerm: PropTypes.string.isRequired,
+ permissions: PropTypes.array.isRequired,
+ changePerm: PropTypes.func.isRequired,
+ toggleDialog: PropTypes.func.isRequired
+};
+
+class PermSelect extends React.Component {
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ currentOption: this.props.currentPerm
+ };
+ }
+
+ switchOption = (e) => {
+ if (!e.target.checked) {
+ return;
+ }
+
+ const currentOption = e.target.value;
+ this.setState({
+ currentOption: currentOption
+ });
+
+ this.props.changePerm(currentOption);
+ this.props.toggleDialog();
+ }
+
+ render() {
+ const options = this.props.permissions;
+ const { currentOption } = this.state;
+
+ return (
+
+
+ {options.map((item, index) => {
+ return (
+
+
+
+
+ );
+ })}
+
+
+ );
+ }
+}
+
+PermSelect.propTypes = propTypes;
+
+export default PermSelect;
diff --git a/frontend/src/components/toolbar/invitations-toolbar.js b/frontend/src/components/toolbar/invitations-toolbar.js
index 63999e0c53..af5880899e 100644
--- a/frontend/src/components/toolbar/invitations-toolbar.js
+++ b/frontend/src/components/toolbar/invitations-toolbar.js
@@ -24,16 +24,15 @@ class InvitationsToolbar extends React.Component {
-
+ {window.innerWidth >= 768 ? (
-
-
-
-
+ ) : (
+
+ )}
diff --git a/frontend/src/css/files-activities.css b/frontend/src/css/files-activities.css
index 36ce527858..31ef2e24d4 100644
--- a/frontend/src/css/files-activities.css
+++ b/frontend/src/css/files-activities.css
@@ -5,15 +5,6 @@
.activity-details:hover {
color: #333;
}
-.mobile-activity-op {
- display: inline-block;
- margin: 0 0 .2em .8em;
- padding: 0 .5em;
- background: #ffbd6f;
- border-radius: 2px;
- color: #fff;
- font-size: 0.75rem;
-}
.mobile-activity-time {
display: inline-block;
margin-bottom: .2em;
diff --git a/frontend/src/models/shared-repo-info.js b/frontend/src/models/shared-repo-info.js
index b4b08ddcc6..e83cb6a9f3 100644
--- a/frontend/src/models/shared-repo-info.js
+++ b/frontend/src/models/shared-repo-info.js
@@ -13,8 +13,8 @@ class SharedRepoInfo {
this.is_admin = object.is_admin;
this.user_name = object.user_name;
this.user_email = object.user_email;
- this.contact_email = this.contact_email;
- } else if(this.share_type === 'group') {
+ this.contact_email = object.contact_email;
+ } else if (this.share_type === 'group') {
this.is_admin = object.is_admin;
this.group_id = object.group_id;
this.group_name = object.group_name;
diff --git a/frontend/src/pages/dashboard/files-activities.js b/frontend/src/pages/dashboard/files-activities.js
index 7bd0cd6350..ac1f12837d 100644
--- a/frontend/src/pages/dashboard/files-activities.js
+++ b/frontend/src/pages/dashboard/files-activities.js
@@ -269,7 +269,7 @@ class ActivityItem extends Component {
{item.author_name}
- {op}
+ {op}
{details}
|
diff --git a/frontend/src/pages/invitations/invitations-view.js b/frontend/src/pages/invitations/invitations-view.js
index 8b2dad0dcf..1769fe2c69 100644
--- a/frontend/src/pages/invitations/invitations-view.js
+++ b/frontend/src/pages/invitations/invitations-view.js
@@ -1,7 +1,8 @@
import React, { Component, Fragment } from 'react';
+import { Dropdown, DropdownToggle, DropdownItem } from 'reactstrap';
import PropTypes from 'prop-types';
import moment from 'moment';
-import { gettext, siteRoot, loginUrl, canInvitePeople } from '../../utils/constants';
+import { gettext, loginUrl } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import { seafileAPI } from '../../utils/seafile-api';
import InvitationsToolbar from '../../components/toolbar/invitations-toolbar';
@@ -19,10 +20,17 @@ class Item extends React.Component {
super(props);
this.state = {
isOpIconShown: false,
+ isOpMenuOpen: false, // for mobile
isRevokeDialogOpen: false
};
}
+ toggleOpMenu = () => {
+ this.setState({
+ isOpMenuOpen: !this.state.isOpMenuOpen
+ });
+ }
+
onMouseEnter = () => {
this.setState({
isOpIconShown: true
@@ -70,32 +78,70 @@ class Item extends React.Component {
return null;
}
- const invitationItem = this.props.invitation;
- const operation = invitationItem.accept_time ?
-
- :
-
- ;
+ const item = this.props.invitation;
+
+ const desktopItem = (
+ |
+ {item.accepter} |
+ {moment(item.invite_time).format('YYYY-MM-DD')} |
+ {moment(item.expire_time).format('YYYY-MM-DD')} |
+ {item.accept_time && } |
+
+ {isOpIconShown && (
+ item.accept_time ?
+
+ :
+
+
+ )}
+ |
+
+ );
+
+ const mobileItem = (
+
+
+ {item.accepter}
+ {moment(item.invite_time).format('YYYY-MM-DD')}({gettext('Invite Time')})
+ {moment(item.expire_time).format('YYYY-MM-DD')}({gettext('Expiration')})
+ {item.accept_time && gettext('Accepted')}
+ |
+
+
+
+
+
+
+ {item.accept_time ?
+ {gettext('Revoke Access')} :
+ {gettext('Delete')}
+ }
+
+
+
+ |
+
+ );
return (
-
- {invitationItem.accepter} |
- {moment(invitationItem.invite_time).format('YYYY-MM-DD')} |
- {moment(invitationItem.expire_time).format('YYYY-MM-DD')} |
- {invitationItem.accept_time && } |
- {isOpIconShown && operation} |
-
+ {this.props.isDesktop ? desktopItem : mobileItem}
{isRevokeDialogOpen &&
@@ -138,23 +184,32 @@ class Content extends Component {
);
}
+ const isDesktop = Utils.isDesktop();
return (
-
+
-
- {gettext('Email')} |
- {gettext('Invite Time')} |
- {gettext('Expiration')} |
- {gettext('Accepted')} |
- |
-
+ {isDesktop ?
+
+ {gettext('Email')} |
+ {gettext('Invite Time')} |
+ {gettext('Expiration')} |
+ {gettext('Accepted')} |
+ |
+
+ :
+
+ |
+ |
+
+ }
{invitationsList.map((invitation, index) => {
return (
);
})}
diff --git a/frontend/src/pages/share-admin/libraries.js b/frontend/src/pages/share-admin/libraries.js
index 9b67c9df23..4819f319f3 100644
--- a/frontend/src/pages/share-admin/libraries.js
+++ b/frontend/src/pages/share-admin/libraries.js
@@ -1,5 +1,6 @@
-import React, { Component } from 'react';
+import React, { Fragment, Component } from 'react';
import { Link } from '@reach/router';
+import { Dropdown, DropdownToggle, DropdownItem } from 'reactstrap';
import { seafileAPI } from '../../utils/seafile-api';
import { gettext, siteRoot, loginUrl, isPro } from '../../utils/constants';
import { Utils } from '../../utils/utils';
@@ -7,6 +8,7 @@ import toaster from '../../components/toast';
import EmptyTip from '../../components/empty-tip';
import SharePermissionEditor from '../../components/select-editor/share-permission-editor';
import SharedRepoInfo from '../../models/shared-repo-info';
+import PermSelect from '../../components/dialog/perm-select';
class Content extends Component {
@@ -36,20 +38,34 @@ class Content extends Component {
const sortByName = sortBy == 'name';
const sortIcon = sortOrder == 'asc' ? : ;
+ const isDesktop = Utils.isDesktop();
const table = (
-
+
@@ -69,21 +85,40 @@ class Item extends Component {
this.state = {
share_permission: item.share_permission,
is_admin: item.is_admin,
- showOpIcon: false,
+ isOpIconShown: false,
+ isOpMenuOpen: false, // for mobile
+ isPermSelectDialogOpen: false, // for mobile
unshared: false
};
- this.permissions = ['rw', 'r'];
+ let permissions = ['rw', 'r'];
+ this.permissions = permissions;
+ this.showAdmin = isPro && (item.share_type !== 'public');
+ if (this.showAdmin) {
+ permissions.push('admin');
+ }
if (isPro) {
- this.permissions = ['rw', 'r', 'cloud-edit', 'preview'];
+ permissions.push('cloud-edit', 'preview');
}
}
+ toggleOpMenu = () => {
+ this.setState({
+ isOpMenuOpen: !this.state.isOpMenuOpen
+ });
+ }
+
+ togglePermSelectDialog = () => {
+ this.setState({
+ isPermSelectDialogOpen: !this.state.isPermSelectDialogOpen
+ });
+ }
+
onMouseEnter = () => {
- this.setState({showOpIcon: true});
+ this.setState({isOpIconShown: true});
}
onMouseLeave = () => {
- this.setState({showOpIcon: false});
+ this.setState({isOpIconShown: false});
}
changePerm = (permission) => {
@@ -97,8 +132,6 @@ class Item extends Component {
options.user = item.user_email;
} else if (share_type == 'group') {
options.group_id = item.group_id;
- } else if (share_type === 'public') {
- // nothing todo
}
seafileAPI.updateRepoSharePerm(item.repo_id, options).then(() => {
@@ -106,72 +139,102 @@ class Item extends Component {
share_permission: permission == 'admin' ? 'rw' : permission,
is_admin: permission == 'admin',
});
+ toaster.success(gettext('Successfully modified permission.'));
}).catch((error) => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
- unshare = () => {
- this.props.unshareFolder(this.props.item);
- }
-
- getRepoParams = () => {
- let item = this.props.item;
-
- let iconUrl = Utils.getLibIconUrl(item);
- let iconTitle = Utils.getLibIconTitle(item);
- let repoUrl = `${siteRoot}library/${item.repo_id}/${item.repo_name}/`;
-
- return { iconUrl, iconTitle, repoUrl };
+ unshare = (e) => {
+ e.preventDefault();
+ this.props.unshareItem(this.props.item);
}
render() {
- let { iconUrl, iconTitle, repoUrl } = this.getRepoParams();
let item = this.props.item;
- let { share_permission, is_admin } = this.state;
+ let iconUrl = Utils.getLibIconUrl(item);
+ let iconTitle = Utils.getLibIconTitle(item);
+ let repoUrl = `${siteRoot}library/${item.repo_id}/${encodeURIComponent(item.repo_name)}/`;
+
+ let { share_permission, is_admin, isOpIconShown, isPermSelectDialogOpen } = this.state;
let shareTo;
const shareType = item.share_type;
if (shareType == 'personal') {
- shareTo = {item.user_name} | ;
+ shareTo = item.user_name;
} else if (shareType == 'group') {
- shareTo = {item.group_name} | ;
+ shareTo = item.group_name;
} else if (shareType == 'public') {
- shareTo = {gettext('all members')} | ;
+ shareTo = gettext('all members');
}
- // show 'admin' perm or not
- let showAdmin = isPro && (item.share_type !== 'public');
- if (showAdmin && is_admin) {
+ if (this.showAdmin && is_admin) {
share_permission = 'admin';
}
- let iconVisibility = this.state.showOpIcon ? '' : ' invisible';
- let unshareIconClassName = 'unshare action-icon sf2-icon-x3' + iconVisibility;
-
- if (showAdmin && this.permissions.indexOf('admin') === -1) {
- this.permissions.splice(2, 0, 'admin'); // add a item after 'r' permission;
- }
-
- return (
+ const desktopItem = (
 |
{item.repo_name} |
- {shareTo}
+
+ {item.share_type == 'personal' ? {shareTo} : shareTo}
+ |
|
- |
+ |
);
+
+ const mobileItem = (
+
+
+  |
+
+ {item.repo_name}
+ {Utils.sharePerms(share_permission)}
+
+ {`${gettext('Share To:')} ${shareTo}`}
+ |
+
+
+
+
+
+
+ {gettext('Permission')}
+ {gettext('Unshare')}
+
+
+
+ |
+
+ {isPermSelectDialogOpen &&
+
+ }
+
+ );
+
+ return this.props.isDesktop ? desktopItem : mobileItem;
}
}
@@ -221,7 +284,7 @@ class ShareAdminLibraries extends Component {
});
}
- unshareFolder = (item) => {
+ unshareItem = (item) => {
const share_type = item.share_type;
let options = {
'share_type': share_type
@@ -282,7 +345,7 @@ class ShareAdminLibraries extends Component {
sortBy={this.state.sortBy}
sortOrder={this.state.sortOrder}
sortItems={this.sortItems}
- unshareFolder={this.unshareFolder}
+ unshareItem={this.unshareItem}
/>
diff --git a/frontend/src/utils/utils.js b/frontend/src/utils/utils.js
index c60602255d..7069f20c5c 100644
--- a/frontend/src/utils/utils.js
+++ b/frontend/src/utils/utils.js
@@ -33,6 +33,10 @@ export const Utils = {
}
},
+ isDesktop: function() {
+ return window.innerWidth >= 768;
+ },
+
FILEEXT_ICON_MAP: {
// text file
@@ -90,7 +94,7 @@ export const Utils = {
},
// check if a file is an image
- imageCheck: function (filename) {
+ imageCheck: function(filename) {
// no file ext
if (filename.lastIndexOf('.') == -1) {
return false;
@@ -119,7 +123,7 @@ export const Utils = {
},
// check if a file is a video
- videoCheck: function (filename) {
+ videoCheck: function(filename) {
// no file ext
if (filename.lastIndexOf('.') == -1) {
return false;
diff --git a/media/css/seahub_react.css b/media/css/seahub_react.css
index 28e94e8803..d88ec6cb01 100644
--- a/media/css/seahub_react.css
+++ b/media/css/seahub_react.css
@@ -1133,6 +1133,16 @@ a.table-sort-op:focus {
font-size: 12px;
color: #666;
}
+
+ .item-meta-info-highlighted {
+ display: inline-block;
+ margin: 0 0 .2em .8em;
+ padding: 0 .5em;
+ background: #ffbd6f;
+ border-radius: 2px;
+ color: #fff;
+ font-size: 0.75rem;
+ }
.mobile-operation-menu-bg-layer {
position: fixed;