diff --git a/frontend/src/app.js b/frontend/src/app.js
index 45d03eae4c..e7c2c410de 100644
--- a/frontend/src/app.js
+++ b/frontend/src/app.js
@@ -18,8 +18,8 @@ import ShareAdminUploadLinks from './pages/share-admin/upload-links';
import SharedLibraries from './pages/shared-libs/shared-libs';
import MyLibraries from './pages/my-libs/my-libs';
import DirView from './components/dir-view/dir-view';
-import Groups from './pages/group/groups';
-import Group from './pages/group/group';
+import Group from './pages/groups/group-view';
+import Groups from './pages/groups/groups-view';
import MainContentWrapper from './components/main-content-wrapper';
import './assets/css/fa-solid.css';
diff --git a/frontend/src/pages/group/group-repo-item.js b/frontend/src/pages/group/group-repo-item.js
deleted file mode 100644
index 8f81b4288f..0000000000
--- a/frontend/src/pages/group/group-repo-item.js
+++ /dev/null
@@ -1,270 +0,0 @@
-import React, { Component } from 'react';
-import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
-import moment from 'moment';
-import { seafileAPI } from '../../utils/seafile-api';
-import { Utils } from '../../utils/utils';
-import { gettext, siteRoot, loginUrl, isPro, folderPermEnabled, username } from '../../utils/constants';
-
-class GroupRepoItem extends Component {
-
- constructor(props) {
- super(props);
- this.state = {
- showOpIcon: false,
- operationMenuOpen: false,
- unshared: false
- };
-
- this.handleMouseOver = this.handleMouseOver.bind(this);
- this.handleMouseOut = this.handleMouseOut.bind(this);
- this.toggleOperationMenu = this.toggleOperationMenu.bind(this);
- this.clickOperationMenuToggle = this.clickOperationMenuToggle.bind(this);
-
- this.share = this.share.bind(this);
- this.unshare = this.unshare.bind(this);
-
- this.deleteItem = this.deleteItem.bind(this);
-
- this.rename = this.rename.bind(this);
- this.folderPerm = this.folderPerm.bind(this);
- this.showDetails = this.showDetails.bind(this);
- }
-
- handleMouseOver() {
- this.setState({
- showOpIcon: true
- });
- }
-
- handleMouseOut() {
- this.setState({
- showOpIcon: false
- });
- }
-
- toggleOperationMenu() {
- this.setState({
- operationMenuOpen: !this.state.operationMenuOpen
- });
- }
-
- clickOperationMenuToggle(e) {
- e.preventDefault();
- this.toggleOperationMenu();
- }
-
- share(e) {
- e.preventDefault();
- // TODO
- }
-
- unshare(e) {
- e.preventDefault();
- // TODO
-
- 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
- });
- }
-
- deleteItem() {
- // TODO
- const data = this.props.data;
- seafileAPI.deleteRepo(data.repo_id).then((res) => {
- this.setState({
- deleted: true
- });
- // TODO: show feedback msg
- }).catch((error) => {
- // TODO: show feedback msg
- });
- }
-
- rename() {
- }
-
- folderPerm() {
- }
-
- showDetails() {
- }
-
- render() {
-
- if (this.state.unshared) {
- return null;
- }
-
- const data = this.props.data;
- const permission = data.permission;
-
- const {groupId, isStaff, showRepoOwner} = this.props.extra;
- const isRepoOwner = username == data.owner_email;
- const isAdmin = data.is_admin;
-
- 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}#group/${groupId}/lib/${data.repo_id}/`;
-
- let iconVisibility = this.state.showOpIcon ? '' : ' invisible';
- let shareIconClassName = 'sf2-icon-share sf2-x repo-share-btn op-icon' + iconVisibility;
- let unshareIconClassName = 'sf2-icon-x3 sf2-x op-icon' + iconVisibility;
- let deleteIconClassName = 'sf2-icon-delete sf2-x op-icon' + iconVisibility;
- let operationMenuToggleIconClassName = 'sf2-icon-caret-down item-operation-menu-toggle-icon op-icon';
- if (window.innerWidth >= 768) {
- operationMenuToggleIconClassName += iconVisibility;
- }
-
- const commonToggle = (
-
-
- );
-
- const commonOperationsInMenu = (
-
- {gettext('Rename')}
- {folderPermEnabled ? {gettext('Folder Permission')} : null}
- {gettext('Details')}
-
- );
-
- let desktopOperations;
- let mobileOperationMenu;
-
- const share = ;
- const unshare =
-
- const shareDropdownItem = {gettext('Share')};
- const unshareDropdownItem = {gettext('Unshare')};
-
- if (isPro) {
- if (data.owner_email.indexOf('@seafile_group') != -1) { // group owned repo
- if (isStaff) {
- if (data.owner_email == groupId + '@seafile_group') { // this repo belongs to the current group
- desktopOperations = (
-
- {share}
-
-
- {commonToggle}
-
- {commonOperationsInMenu}
-
-
-
- );
- mobileOperationMenu = (
-
- {shareDropdownItem}
- {gettext('Delete')}
- {commonOperationsInMenu}
-
- );
- } else {
- desktopOperations = unshare;
- mobileOperationMenu = unshareDropdownItem;
- }
- }
- } else {
- desktopOperations = (
-
- {isRepoOwner || isAdmin ? share : null}
- {isStaff || isRepoOwner || isAdmin ? unshare : null}
-
- );
- mobileOperationMenu = (
-
- {isRepoOwner || isAdmin ? shareDropdownItem : null}
- {isStaff || isRepoOwner || isAdmin ? unshareDropdownItem : null}
-
- );
- }
- } else {
- desktopOperations = (
-
- {isRepoOwner ? share : null}
- {isStaff || isRepoOwner ? unshare : null}
-
- );
- mobileOperationMenu = (
-
- {isRepoOwner ? shareDropdownItem : null}
- {isStaff || isRepoOwner ? unshareDropdownItem : null}
-
- );
- }
-
- const mobileOperations = (
-
- {commonToggle}
-
-
-
- {mobileOperationMenu}
-
-
-
- );
-
- const desktopItem = (
-
-  |
- {data.repo_name} |
- {desktopOperations} |
- {Utils.formatSize({bytes: data.size})} |
- {moment(data.last_modified).fromNow()} |
- {showRepoOwner ? {data.owner_name} | : null}
-
- );
-
- const mobileItem = (
-
-  |
-
- {data.repo_name}
- {showRepoOwner ? {data.owner_name} : null}
- {Utils.formatSize({bytes: data.size})}
- {moment(data.last_modified).fromNow()}
- |
- {mobileOperations} |
-
- );
-
- return window.innerWidth >= 768 ? desktopItem : mobileItem;
- }
-}
-
-export default GroupRepoItem;
diff --git a/frontend/src/pages/group/group.js b/frontend/src/pages/group/group.js
deleted file mode 100644
index 74a78e15a1..0000000000
--- a/frontend/src/pages/group/group.js
+++ /dev/null
@@ -1,305 +0,0 @@
-import React, { Component, Fragment } from 'react';
-import { seafileAPI } from '../../utils/seafile-api';
-import { Utils } from '../../utils/utils';
-import { gettext, siteRoot, loginUrl, username } from '../../utils/constants';
-import Loading from '../../components/loading';
-import GroupRepoItem from './group-repo-item';
-import CommonToolbar from '../../components/toolbar/common-toolbar';
-import RepoViewToolbar from '../../components/toolbar/repo-view-toobar';
-
-import '../../css/groups.css';
-
-class Header extends Component {
-
- render() {
- const {loading, errorMsg, data} = this.props.data;
-
- if (loading) {
- return ;
- } else if (errorMsg) {
- return {errorMsg}
;
- } else {
- /*
- admins: ["lj@1.com"]
- avatar_url: "http://127.0.0.1:8000/media/avatars/groups/default.png"
- created_at: "2018-10-25T08:18:11+00:00"
- id: 2
- name: "g1"
- owner: "lj@1.com"
- parent_group_id: 0
- wiki_enabled: false
- */
- const path = (
-
- );
-
- let showSettingsIcon = true;
- if (data.parent_group_id != 0 && data.admins.indexOf(username) == -1) {
- showSettingsIcon = false;
- }
-
- // TODO: click icon
- const toolbar = (
-
- {showSettingsIcon ?
: null}
-
-
- );
-
- return (
-
- {path}
- {toolbar}
-
- );
- }
- }
-}
-
-class Content extends Component {
-
- render() {
- const {loading, errorMsg, items} = this.props.data.repos;
-
- if (loading) {
- return ;
- } else if (errorMsg) {
- return {errorMsg}
;
- } else {
- if (!items) {
- return null;
- }
-
- const groupInfo = this.props.data.groupMetaInfo.data;
-
- let emptyTip;
- if (groupInfo.parent_group_id == 0) {
- emptyTip = (
-
-
{gettext('No library is shared to this group')}
-
{gettext('You can share libraries by clicking the "New Library" button above or the "Share" icon on your libraries list.')}
-
{gettext('Libraries shared as writable can be downloaded and synced by other group members. Read only libraries can only be downloaded, updates by others will not be uploaded.')}
-
- );
- } else {
- if (groupInfo.admins.indexOf(username) == -1) {
- emptyTip = (
-
-
{gettext('No libraries')}
-
- );
- } else {
- emptyTip = (
-
-
{gettext('No libraries')}
-
{gettext('You can create libraries by clicking the "New Library" button above.')}
-
- );
- }
- }
-
- 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 extraData = {
- groupId: groupInfo.id,
- isStaff: groupInfo.admins.indexOf(username) != -1,
- showRepoOwner: true
- };
-
- 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 Group extends Component {
- constructor(props) {
- super(props);
- this.state = {
- groupMetaInfo: {
- loading: true,
- errorMsg: ''
- },
- repos: {
- loading: false,
- errorMsg: ''
- }
- };
- }
-
- componentDidMount() {
- this.updateGroupRepoList(this.props.groupID);
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.groupID !== this.props.groupID) {
- this.updateGroupRepoList(nextProps.groupID);
- }
- }
-
- updateGroupRepoList = (groupID) => {
- seafileAPI.getGroup(groupID).then((res) => {
- // res: {data: {...}, status: 200, statusText: "OK", headers: {…}, config: {…}, …}
- this.setState({
- groupMetaInfo: {
- loading: false,
- data: res.data
- }
- });
- this.listGroupRepos();
- }).catch((error) => {
- if (error.response) {
- if (error.response.status == 403) {
- this.setState({
- groupMetaInfo: {
- loading: false,
- errorMsg: gettext("Permission denied")
- }
- });
- location.href = `${loginUrl}?next=${encodeURIComponent(location.href)}`;
- } else {
- this.setState({
- groupMetaInfo: {
- loading: false,
- errorMsg: gettext("Error")
- }
- });
- }
- } else {
- this.setState({
- groupMetaInfo: {
- loading: false,
- errorMsg: gettext("Please check the network.")
- }
- });
- }
- });
- }
-
- listGroupRepos() {
- seafileAPI.listGroupRepos(this.props.groupID).then((res) => {
- // res: {data: [...], status: 200, statusText: "OK", headers: {…}, config: {…}, …}
- this.setState({
- repos: {
- loading: false,
- items: res.data
- }
- });
- }).catch((error) => {
- if (error.response) {
- if (error.response.status == 403) {
- this.setState({
- repos: {
- loading: false,
- errorMsg: gettext("Permission denied")
- }
- });
- location.href = `${loginUrl}?next=${encodeURIComponent(location.href)}`;
- } else {
- this.setState({
- repos: {
- loading: false,
- errorMsg: gettext("Error")
- }
- });
- }
- } else {
- this.setState({
- repos: {
- loading: false,
- errorMsg: gettext("Please check the network.")
- }
- });
- }
- });
- }
-
- onCreateRepo = (repo) => {
- let groupId = this.props.groupID;
- seafileAPI.createGroupRepo(groupId, repo).then(() => {
- //todo update group list
- });
- }
-
- render() {
- return (
-
-
-
-
-
-
-
- );
- }
-}
-
-export default Group;
diff --git a/frontend/src/pages/group/groups.js b/frontend/src/pages/group/groups.js
deleted file mode 100644
index e12873c1b2..0000000000
--- a/frontend/src/pages/group/groups.js
+++ /dev/null
@@ -1,178 +0,0 @@
-import React, { Component, Fragment } from 'react';
-import { seafileAPI } from '../../utils/seafile-api';
-import { gettext, siteRoot, loginUrl, username } from '../../utils/constants';
-import Loading from '../../components/loading';
-import GroupRepoItem from './group-repo-item';
-import GeneralToolbar from '../../components/toolbar/general-toolbar';
-
-import '../../css/groups.css';
-
-class Content extends Component {
-
- render() {
- const {loading, errorMsg, items} = this.props.data;
-
- if (loading) {
- return ;
- } else if (errorMsg) {
- return {errorMsg}
;
- } else {
- /* TODO:
- {% if user.permissions.can_add_group %}
- {% blocktrans %}Groups allow multiple people to collaborate on libraries. You can create a group by clicking the "New Group" button.{% endblocktrans %}
- {% else %}
- {% trans "Groups allow multiple people to collaborate on libraries. Groups you join will be listed here." %}
- {% endif %}
- */
- const emptyTip = (
-
-
{gettext('You are not in any groups')}
-
{gettext('Groups allow multiple people to collaborate on libraries.')}
-
- );
-
- let listItems = items.map(function(item, index) {
- return ;
- }, this);
-
- const groupItems = (
-
- {listItems}
-
- );
-
- return items.length ? groupItems : emptyTip;
- }
- }
-}
-
-class GroupItem extends Component {
-
- render() {
-
- const data = this.props.data;
-
- const desktopThead = (
-
-
- {gettext("Library Type")} |
- {gettext("Name")} |
- {gettext("Actions")} |
- {gettext("Size")} |
- {gettext("Last Update")} |
-
-
- );
-
- const mobileThead = (
-
-
- {gettext("Library Type")} |
- {gettext("name")} |
- {gettext("Actions")} |
-
-
- );
-
- const extraData = {
- groupId: data.id,
- isStaff: data.admins.indexOf(username) != -1,
- showRepoOwner: false
- };
-
- const table = (
-
- {window.innerWidth >= 768 ? desktopThead : mobileThead}
-
-
- );
-
- const emptyTip = {gettext('No libraries')}
;
-
- const item = (
-
-
- {data.repos.length ? table : emptyTip}
-
- );
-
- return item;
- }
-}
-
-class TableBody extends Component {
-
- render() {
-
- let listItems = this.props.items.map(function(item, index) {
- return ;
- }, this);
-
- return (
- {listItems}
- );
- }
-}
-
-class Groups extends Component {
- constructor(props) {
- super(props);
- this.state = {
- loading: true,
- errorMsg: '',
- items: []
- };
- }
-
- componentDidMount() {
- seafileAPI.listGroupsV2({'with_repos': 1}).then((res) => { // TODO: api name
- // `{'with_repos': 1}`: list repos of every group
- // res: {data: [...], status: 200, statusText: "OK", headers: {…}, config: {…}, …}
- this.setState({
- loading: false,
- items: res.data
- });
- }).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("My Groups")}
-
-
-
-
-
-
-
- );
- }
-}
-
-export default Groups;