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.icon_title} + {data.repo_name} + + { isPro && data.is_admin ? + + : ''} + + + {Utils.formatSize({bytes: data.size})} + {moment(data.last_modified).fromNow()} + {data.owner_name} + + ); + + const mobileItem = ( + + {data.icon_title} + + {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"),