diff --git a/frontend/src/app.js b/frontend/src/app.js
index 614351222f..ac29f8871d 100644
--- a/frontend/src/app.js
+++ b/frontend/src/app.js
@@ -15,6 +15,7 @@ import ShareAdminLibraries from './pages/share-admin/libraries';
import ShareAdminFolders from './pages/share-admin/folders';
import ShareAdminShareLinks from './pages/share-admin/share-links';
import ShareAdminUploadLinks from './pages/share-admin/upload-links';
+import SharedLibraries from './pages/shared-libs/shared-libs';
import 'seafile-ui';
import './assets/css/fa-solid.css';
@@ -96,6 +97,7 @@ class App extends Component {
+
diff --git a/frontend/src/components/main-side-nav.js b/frontend/src/components/main-side-nav.js
index 369f9bb539..7aacd6d1f4 100644
--- a/frontend/src/components/main-side-nav.js
+++ b/frontend/src/components/main-side-nav.js
@@ -131,10 +131,10 @@ class MainSideNav extends React.Component {
- this.tabItemClick('shared-libs')}>
+ this.tabItemClick('shared-libs')}>
{gettext('Shared with me')}
-
+
{ canViewOrg &&
this.tabItemClick('org')}>
diff --git a/frontend/src/pages/shared-libs/shared-libs.js b/frontend/src/pages/shared-libs/shared-libs.js
new file mode 100644
index 0000000000..8ced58e4a5
--- /dev/null
+++ b/frontend/src/pages/shared-libs/shared-libs.js
@@ -0,0 +1,266 @@
+import React, { Component } from 'react';
+import moment from 'moment';
+import { seafileAPI } from '../../utils/seafile-api';
+import { Utils } from '../../utils/utils';
+import { gettext, siteRoot, loginUrl, isPro } from '../../utils/constants';
+import Loading from '../../components/loading';
+
+class Content extends Component {
+
+ render() {
+ const {loading, errorMsg, items} = this.props.data;
+
+ if (loading) {
+ return ;
+ } else if (errorMsg) {
+ return {errorMsg}
;
+ } else {
+ const emptyTip = (
+
+
{gettext('No libraries have been shared with you')}
+
{gettext('No libraries have been shared directly with you. You can find more shared libraries at "Shared with groups".')}
+
+ );
+
+ const desktopThead = (
+
+
+ {gettext("Library Type")} |
+ {gettext("Name")}{/*TODO: sort*/} |
+ {gettext("Actions")} |
+ {gettext("Size")} |
+ {gettext("Last Update")}{/*TODO: sort*/} |
+ {gettext("Owner")} |
+
+
+ );
+
+ const mobileThead = (
+
+
+ {gettext("Library Type")} |
+
+ {gettext("Sort:")} {/* TODO: sort */}
+ {gettext("name")}
+ {gettext("last update")}
+ |
+ {gettext("Actions")} |
+
+
+ );
+
+ const table = (
+
+ {window.innerWidth >= 768 ? desktopThead : mobileThead}
+
+
+ );
+
+ return items.length ? table : emptyTip;
+ }
+ }
+}
+
+class TableBody extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ items: this.props.items
+ };
+ }
+
+ render() {
+
+ let listItems = this.state.items.map(function(item, index) {
+ return ;
+ }, this);
+
+ return (
+ {listItems}
+ );
+ }
+}
+
+class Item extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ showOpIcon: false,
+ unshared: false
+ };
+
+ this.handleMouseOver = this.handleMouseOver.bind(this);
+ this.handleMouseOut = this.handleMouseOut.bind(this);
+
+ this.share = this.share.bind(this);
+ this.leaveShare = this.leaveShare.bind(this);
+ }
+
+ handleMouseOver() {
+ this.setState({
+ showOpIcon: true
+ });
+ }
+
+ handleMouseOut() {
+ this.setState({
+ showOpIcon: false
+ });
+ }
+
+ share(e) {
+ e.preventDefault();
+ // TODO
+ }
+
+ leaveShare(e) {
+ e.preventDefault();
+
+ const data = this.props.data;
+
+ let request;
+ if (data.owner_email.indexOf('@seafile_group') == -1) {
+ let options = {
+ 'share_type': 'personal',
+ 'from': data.owner_email
+ };
+ request = seafileAPI.leaveShareRepo(data.repo_id, options);
+ } else {
+ request = seafileAPI.leaveShareGroupOwnedRepo(data.repo_id);
+ }
+
+ request.then((res) => {
+ this.setState({
+ unshared: true
+ });
+ // TODO: show feedback msg
+ }).catch((error) => {
+ // TODO: show feedback msg
+ });
+ }
+
+ render() {
+
+ if (this.state.unshared) {
+ return null;
+ }
+
+ const data = this.props.data;
+ const permission = data.permission;
+
+ let is_readonly = false;
+ if (permission == 'r' || permission == 'preview') {
+ is_readonly = true;
+ }
+ data.icon_url = Utils.getLibIconUrl({
+ is_encrypted: data.encrypted,
+ is_readonly: is_readonly,
+ size: Utils.isHiDPI() ? 48 : 24
+ });
+ data.icon_title = Utils.getLibIconTitle({
+ 'encrypted': data.encrypted,
+ 'is_admin': data.is_admin,
+ 'permission': permission
+ });
+ data.url = `${siteRoot}#shared-libs/lib/${data.repo_id}/`;
+
+ let iconVisibility = this.state.showOpIcon ? '' : ' invisible';
+ let shareIconClassName = 'sf2-icon-share sf2-x repo-share-btn op-icon' + iconVisibility;
+ let leaveShareIconClassName = 'sf2-icon-delete sf2-x op-icon' + iconVisibility;
+
+ const desktopItem = (
+
+  |
+ {data.repo_name} |
+
+ { isPro && data.is_admin ?
+
+ : ''}
+
+ |
+ {Utils.formatSize({bytes: data.size})} |
+ {moment(data.last_modified).fromNow()} |
+ {data.owner_name} |
+
+ );
+
+ const mobileItem = (
+
+  |
+
+ {data.repo_name}
+ {data.owner_name}
+ {Utils.formatSize({bytes: data.size})}
+ {moment(data.last_modified).fromNow()}
+ |
+
+ { isPro && data.is_admin ?
+
+ : ''}
+
+ |
+
+ );
+
+ return window.innerWidth >= 768 ? desktopItem : mobileItem;
+ }
+}
+
+class SharedLibraries extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ loading: true,
+ errorMsg: '',
+ items: []
+ };
+ }
+
+ componentDidMount() {
+ seafileAPI.listRepos({type:'shared'}).then((res) => {
+ // res: {data: {...}, status: 200, statusText: "OK", headers: {…}, config: {…}, …}
+ this.setState({
+ loading: false,
+ items: res.data.repos
+ });
+ }).catch((error) => {
+ if (error.response) {
+ if (error.response.status == 403) {
+ this.setState({
+ loading: false,
+ errorMsg: gettext("Permission denied")
+ });
+ location.href = `${loginUrl}?next=${encodeURIComponent(location.href)}`;
+ } else {
+ this.setState({
+ loading: false,
+ errorMsg: gettext("Error")
+ });
+ }
+
+ } else {
+ this.setState({
+ loading: false,
+ errorMsg: gettext("Please check the network.")
+ });
+ }
+ });
+ }
+
+ render() {
+ return (
+
+
+
{gettext("Shared with me")}
+
+
+
+
+
+ );
+ }
+}
+
+export default SharedLibraries;
diff --git a/frontend/src/utils/utils.js b/frontend/src/utils/utils.js
index f0d080141c..6750412d04 100644
--- a/frontend/src/utils/utils.js
+++ b/frontend/src/utils/utils.js
@@ -288,6 +288,38 @@ export const Utils = {
'admin': gettext("Admin"),
'cloud-edit': gettext("Preview-Edit-on-Cloud"),
'preview': gettext("Preview-on-Cloud")
+ },
+
+ formatSize: function(options) {
+ /*
+ * param: {bytes, precision}
+ */
+ var bytes = options.bytes;
+ var precision = options.precision || 0;
+
+ var kilobyte = 1024;
+ var megabyte = kilobyte * 1024;
+ var gigabyte = megabyte * 1024;
+ var terabyte = gigabyte * 1024;
+
+ if ((bytes >= 0) && (bytes < kilobyte)) {
+ return bytes + ' B';
+
+ } else if ((bytes >= kilobyte) && (bytes < megabyte)) {
+ return (bytes / kilobyte).toFixed(precision) + ' KB';
+
+ } else if ((bytes >= megabyte) && (bytes < gigabyte)) {
+ return (bytes / megabyte).toFixed(precision) + ' MB';
+
+ } else if ((bytes >= gigabyte) && (bytes < terabyte)) {
+ return (bytes / gigabyte).toFixed(precision) + ' GB';
+
+ } else if (bytes >= terabyte) {
+ return (bytes / terabyte).toFixed(precision) + ' TB';
+
+ } else {
+ return bytes + ' B';
+ }
}
};
diff --git a/media/css/seahub_react.css b/media/css/seahub_react.css
index 945fcb6b2d..8a9ca6cf2e 100644
--- a/media/css/seahub_react.css
+++ b/media/css/seahub_react.css
@@ -242,6 +242,14 @@ ul,ol,li {
margin-right:3px;
vertical-align:middle;
}
+.op-icon.sf2-x,
+.op-icon.sf2-x:hover {
+ color:#f89a68;
+}
+.op-icon.sf2-x:hover,
+.op-icon.sf2-x:focus {
+ text-decoration: underline;
+}
/** Account info **/
#account {
@@ -1028,3 +1036,9 @@ table .menu-toggle {
color: #eb8205;
border-bottom: 2px solid #eb8205;
}
+.item-meta-info {
+ display: inline-block;
+ margin-right: 8px;
+ font-size: 12px;
+ color: #666;
+}
diff --git a/seahub/templates/home_base.html b/seahub/templates/home_base.html
index 2b9772138b..9fa780a93a 100644
--- a/seahub/templates/home_base.html
+++ b/seahub/templates/home_base.html
@@ -13,7 +13,7 @@
{% if user.permissions.can_add_repo %}
{% trans "My Libraries" %}
{% endif %}
- {% trans "Shared with me" %}
+ {% trans "Shared with me" %}
{% if user.permissions.can_view_org %}
{% trans "Shared with all" %}
{% endif %}
diff --git a/seahub/templates/js/templates.html b/seahub/templates/js/templates.html
index 33024cdd3c..409c696d38 100644
--- a/seahub/templates/js/templates.html
+++ b/seahub/templates/js/templates.html
@@ -1499,7 +1499,7 @@
{% endif %}
- {% trans "Shared with me" %}
+ {% trans "Shared with me" %}
{% if user.permissions.can_view_org %}
diff --git a/seahub/urls.py b/seahub/urls.py
index b78ec01110..d3be3bfada 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -198,6 +198,7 @@ urlpatterns = [
url(r'^share-admin-folders/$', react_fake_view, name="share_admin_folders"),
url(r'^share-admin-share-links/$', react_fake_view, name="share_admin_share_links"),
url(r'^share-admin-upload-links/$', react_fake_view, name="share_admin_upload_links"),
+ url(r'^shared-libs/$', react_fake_view, name="shared_libs"),
### Ajax ###
url(r'^ajax/repo/(?P[-0-9a-f]{36})/dirents/$', get_dirents, name="get_dirents"),