From cf262f09dbfc27d789513260e69a758136c3c6eb Mon Sep 17 00:00:00 2001 From: llj <lingjun.li1@gmail.com> Date: Tue, 8 Apr 2025 13:47:28 +0800 Subject: [PATCH] =?UTF-8?q?[group]=20added=20full=20operation=20menus=20fo?= =?UTF-8?q?r=20all=20the=20department/group=20items=E2=80=A6=20(#7706)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [group] added full operation menus for all the department/group items in 'Files' page; fixed & improved all the operations * remove useless state * fix test lib --------- Co-authored-by: Michael An <1822852997@qq.com> --- .github/workflows/test.yml | 1 + frontend/src/app.js | 15 +- .../components/dialog/dismiss-group-dialog.js | 25 +- .../components/dialog/group-members-dialog.js | 2 +- .../dialog/import-members-dialog.js | 7 +- .../components/dialog/leave-group-dialog.js | 21 +- .../dialog/manage-members-dialog.js | 2 +- .../components/dialog/rename-group-dialog.js | 54 ++-- .../dialog/transfer-group-dialog.js | 35 +- frontend/src/components/group-members.js | 4 +- .../components/list-and-add-group-members.js | 2 +- frontend/src/pages/groups/group-item.js | 84 ++--- frontend/src/pages/groups/group-op-menu.js | 273 ++++++++++++++++ frontend/src/pages/groups/group-view.js | 300 +++--------------- frontend/src/pages/libraries/index.js | 38 ++- 15 files changed, 443 insertions(+), 420 deletions(-) create mode 100644 frontend/src/pages/groups/group-op-menu.js diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 35b2111be6..a1966b762d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,6 +25,7 @@ jobs: sudo apt-get install -y libfuse-dev cmake re2c flex sqlite3 sudo apt-get install -y libssl-dev libsasl2-dev libldap2-dev libonig-dev sudo apt-get install -y libxml2 libxml2-dev libjwt-dev + sudo apt-get install -y libhiredis-dev - name: clone and build run: | diff --git a/frontend/src/app.js b/frontend/src/app.js index 3ae3ee9f2b..dcd28cb392 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -157,19 +157,6 @@ class App extends Component { } }; - onGroupChanged = (groupID) => { - setTimeout(function () { - let url; - if (groupID) { - url = siteRoot + 'group/' + groupID + '/'; - } - else { - url = siteRoot + 'libraries/'; - } - window.location = url.toString(); - }, 1); - }; - tabItemClick = (tabName, groupID) => { let pathPrefix = []; if (groupID || this.dirViewPanels.indexOf(tabName) > -1) { @@ -343,7 +330,7 @@ class App extends Component { <InvitationsView path={siteRoot + 'invitations/'} /> <FilesActivities path={siteRoot + 'dashboard'} /> <MyFileActivities path={siteRoot + 'my-activities'} /> - <GroupView path={siteRoot + 'group/:groupID'} onGroupChanged={this.onGroupChanged} /> + <GroupView path={siteRoot + 'group/:groupID'} /> <LinkedDevices path={siteRoot + 'linked-devices'} /> <ShareAdminLibraries path={siteRoot + 'share-admin-libs'} /> <ShareAdminFolders path={siteRoot + 'share-admin-folders'} /> diff --git a/frontend/src/components/dialog/dismiss-group-dialog.js b/frontend/src/components/dialog/dismiss-group-dialog.js index cb8825172f..fe26b696fa 100644 --- a/frontend/src/components/dialog/dismiss-group-dialog.js +++ b/frontend/src/components/dialog/dismiss-group-dialog.js @@ -9,14 +9,11 @@ import SeahubModalHeader from '@/components/common/seahub-modal-header'; class DismissGroupDialog extends React.Component { - constructor(props) { - super(props); - } - dismissGroup = () => { - let that = this; - seafileAPI.deleteGroup(this.props.groupID).then((res) => { - that.props.onGroupChanged(); + const { groupID } = this.props; + seafileAPI.deleteGroup(groupID).then((res) => { + this.props.onGroupDeleted(); + toaster.success(gettext('Group deleted')); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); @@ -25,13 +22,13 @@ class DismissGroupDialog extends React.Component { render() { return ( - <Modal isOpen={this.props.showDismissGroupDialog} toggle={this.props.toggleDismissGroupDialog}> - <SeahubModalHeader>{gettext('Delete Group')}</SeahubModalHeader> + <Modal isOpen={true} toggle={this.props.toggleDialog}> + <SeahubModalHeader toggle={this.props.toggleDialog}>{gettext('Delete Group')}</SeahubModalHeader> <ModalBody> <span>{gettext('Really want to delete this group?')}</span> </ModalBody> <ModalFooter> - <Button color="secondary" onClick={this.props.toggleDismissGroupDialog}>{gettext('Cancel')}</Button> + <Button color="secondary" onClick={this.props.toggleDialog}>{gettext('Cancel')}</Button> <Button color="primary" onClick={this.dismissGroup}>{gettext('Delete')}</Button> </ModalFooter> </Modal> @@ -40,11 +37,9 @@ class DismissGroupDialog extends React.Component { } const DismissGroupDialogPropTypes = { - showDismissGroupDialog: PropTypes.bool.isRequired, - toggleDismissGroupDialog: PropTypes.func.isRequired, - loadGroup: PropTypes.func.isRequired, - groupID: PropTypes.string, - onGroupChanged: PropTypes.func.isRequired, + groupID: PropTypes.number.isRequired, + toggleDialog: PropTypes.func.isRequired, + onGroupDeleted: PropTypes.func.isRequired }; DismissGroupDialog.propTypes = DismissGroupDialogPropTypes; diff --git a/frontend/src/components/dialog/group-members-dialog.js b/frontend/src/components/dialog/group-members-dialog.js index 0d845e8d6d..535177c915 100644 --- a/frontend/src/components/dialog/group-members-dialog.js +++ b/frontend/src/components/dialog/group-members-dialog.js @@ -9,7 +9,7 @@ import SeahubModalHeader from '@/components/common/seahub-modal-header'; import Loading from '../loading'; const propTypes = { - groupID: PropTypes.string.isRequired, + groupID: PropTypes.number.isRequired, toggleDialog: PropTypes.func.isRequired }; diff --git a/frontend/src/components/dialog/import-members-dialog.js b/frontend/src/components/dialog/import-members-dialog.js index 7547d3f02e..abeb08cacb 100644 --- a/frontend/src/components/dialog/import-members-dialog.js +++ b/frontend/src/components/dialog/import-members-dialog.js @@ -5,7 +5,7 @@ import { gettext, siteRoot, groupImportMembersExtraMsg } from '../../utils/const import SeahubModalHeader from '@/components/common/seahub-modal-header'; const propTypes = { - toggleImportMembersDialog: PropTypes.func.isRequired, + toggleDialog: PropTypes.func.isRequired, importMembersInBatch: PropTypes.func.isRequired, }; @@ -19,7 +19,7 @@ class ImportMembersDialog extends React.Component { } toggle = () => { - this.props.toggleImportMembersDialog(); + this.props.toggleDialog(); }; openFileInput = () => { @@ -49,9 +49,8 @@ class ImportMembersDialog extends React.Component { return ( <Modal isOpen={true} toggle={this.toggle}> <SeahubModalHeader toggle={this.toggle}>{gettext('Import members from a .xlsx file')}</SeahubModalHeader> - <ModalBody> - <p>{groupImportMembersExtraMsg}</p> + {groupImportMembersExtraMsg && <p>{groupImportMembersExtraMsg}</p>} <p><a className="text-secondary small" href={`${siteRoot}api/v2.1/group-members-import-example/`}>{gettext('Download an example file')}</a></p> <button className="btn btn-outline-primary" onClick={this.openFileInput}>{gettext('Upload file')}</button> <input className="d-none" type="file" onChange={this.uploadFile} ref={this.fileInputRef} /> diff --git a/frontend/src/components/dialog/leave-group-dialog.js b/frontend/src/components/dialog/leave-group-dialog.js index 1f30156cec..a80d4e939a 100644 --- a/frontend/src/components/dialog/leave-group-dialog.js +++ b/frontend/src/components/dialog/leave-group-dialog.js @@ -9,13 +9,10 @@ import SeahubModalHeader from '@/components/common/seahub-modal-header'; class LeaveGroupDialog extends React.Component { - constructor(props) { - super(props); - } - leaveGroup = () => { - seafileAPI.quitGroup(this.props.groupID, username).then((res) => { - this.props.onGroupChanged(); + const { groupID } = this.props; + seafileAPI.quitGroup(groupID, username).then((res) => { + this.props.onLeavingGroup(); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); @@ -24,13 +21,13 @@ class LeaveGroupDialog extends React.Component { render() { return ( - <Modal isOpen={true} toggle={this.props.toggleLeaveGroupDialog}> - <SeahubModalHeader toggle={this.props.toggleLeaveGroupDialog}>{gettext('Leave Group')}</SeahubModalHeader> + <Modal isOpen={true} toggle={this.props.toggleDialog}> + <SeahubModalHeader toggle={this.props.toggleDialog}>{gettext('Leave Group')}</SeahubModalHeader> <ModalBody> <p>{gettext('Really want to leave this group?')}</p> </ModalBody> <ModalFooter> - <Button color="secondary" onClick={this.props.toggleLeaveGroupDialog}>{gettext('Cancel')}</Button> + <Button color="secondary" onClick={this.props.toggleDialog}>{gettext('Cancel')}</Button> <Button color="primary" onClick={this.leaveGroup}>{gettext('Leave')}</Button> </ModalFooter> </Modal> @@ -39,9 +36,9 @@ class LeaveGroupDialog extends React.Component { } const LeaveGroupDialogPropTypes = { - toggleLeaveGroupDialog: PropTypes.func.isRequired, - groupID: PropTypes.string, - onGroupChanged: PropTypes.func.isRequired, + groupID: PropTypes.number.isRequired, + onLeavingGroup: PropTypes.func.isRequired, + toggleDialog: PropTypes.func.isRequired }; LeaveGroupDialog.propTypes = LeaveGroupDialogPropTypes; diff --git a/frontend/src/components/dialog/manage-members-dialog.js b/frontend/src/components/dialog/manage-members-dialog.js index cea7a01631..8b3b4c7dbc 100644 --- a/frontend/src/components/dialog/manage-members-dialog.js +++ b/frontend/src/components/dialog/manage-members-dialog.js @@ -8,7 +8,7 @@ import SeahubModalHeader from '@/components/common/seahub-modal-header'; import '../../css/manage-members-dialog.css'; const propTypes = { - groupID: PropTypes.string, + groupID: PropTypes.number.isRequired, isOwner: PropTypes.bool.isRequired, toggleManageMembersDialog: PropTypes.func, toggleDepartmentDetailDialog: PropTypes.func, diff --git a/frontend/src/components/dialog/rename-group-dialog.js b/frontend/src/components/dialog/rename-group-dialog.js index 7c3af16a38..1c564f89d1 100644 --- a/frontend/src/components/dialog/rename-group-dialog.js +++ b/frontend/src/components/dialog/rename-group-dialog.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { gettext } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; -import { Modal, ModalBody, ModalFooter, Input, Button } from 'reactstrap'; +import { Modal, ModalBody, ModalFooter, Input, Label, Button } from 'reactstrap'; import SeahubModalHeader from '@/components/common/seahub-modal-header'; import toaster from '../toast'; @@ -12,7 +12,7 @@ class RenameGroupDialog extends React.Component { constructor(props) { super(props); this.state = { - newGroupName: this.props.currentGroupName, + newGroupName: this.props.groupName, isSubmitBtnActive: false, }; } @@ -30,48 +30,42 @@ class RenameGroupDialog extends React.Component { }); }; - renameGroup = () => { - let name = this.state.newGroupName.trim(); - if (name) { - let that = this; - seafileAPI.renameGroup(this.props.groupID, name).then((res) => { - that.props.loadGroup(this.props.groupID); - that.props.onGroupChanged(res.data.id); - }).catch(error => { - let errMessage = Utils.getErrorMsg(error); - toaster.danger(errMessage); - }); - } - this.setState({ - newGroupName: '', + handleSubmit = () => { + const { groupID } = this.props; + const { newGroupName } = this.state; + seafileAPI.renameGroup(groupID, newGroupName.trim()).then((res) => { + const { name } = res.data; + this.props.onGroupNameChanged(name); + }).catch(error => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); }); - this.props.toggleRenameGroupDialog(); + this.props.toggleDialog(); }; handleKeyDown = (event) => { if (event.keyCode === 13) { - this.renameGroup(); + this.handleSubmit(); } }; render() { return ( - <Modal isOpen={this.props.showRenameGroupDialog} toggle={this.props.toggleRenameGroupDialog}> - <SeahubModalHeader>{gettext('Rename Group')}</SeahubModalHeader> + <Modal isOpen={true} toggle={this.props.toggleDialog}> + <SeahubModalHeader toggle={this.props.toggleDialog}>{gettext('Rename Group')}</SeahubModalHeader> <ModalBody> - <label htmlFor="newGroupName">{gettext('Rename group to')}</label> + <Label for="group-name">{gettext('Rename group to')}</Label> <Input type="text" - id="newGroupName" - name="new-group-name" + id="group-name" value={this.state.newGroupName} onChange={this.handleGroupNameChange} onKeyDown={this.handleKeyDown} /> </ModalBody> <ModalFooter> - <Button color="secondary" onClick={this.props.toggleRenameGroupDialog}>{gettext('Cancel')}</Button> - <Button color="primary" onClick={this.renameGroup} disabled={!this.state.isSubmitBtnActive}>{gettext('Submit')}</Button> + <Button color="secondary" onClick={this.props.toggleDialog}>{gettext('Cancel')}</Button> + <Button color="primary" onClick={this.handleSubmit} disabled={!this.state.isSubmitBtnActive}>{gettext('Submit')}</Button> </ModalFooter> </Modal> ); @@ -79,12 +73,10 @@ class RenameGroupDialog extends React.Component { } const RenameGroupDialogPropTypes = { - showRenameGroupDialog: PropTypes.bool.isRequired, - toggleRenameGroupDialog: PropTypes.func.isRequired, - loadGroup: PropTypes.func.isRequired, - groupID: PropTypes.string, - onGroupChanged: PropTypes.func.isRequired, - currentGroupName: PropTypes.string.isRequired, + toggleDialog: PropTypes.func.isRequired, + groupID: PropTypes.number, + onGroupNameChanged: PropTypes.func.isRequired, + groupName: PropTypes.string.isRequired, }; RenameGroupDialog.propTypes = RenameGroupDialogPropTypes; diff --git a/frontend/src/components/dialog/transfer-group-dialog.js b/frontend/src/components/dialog/transfer-group-dialog.js index c36540cd3f..7a6bae824b 100644 --- a/frontend/src/components/dialog/transfer-group-dialog.js +++ b/frontend/src/components/dialog/transfer-group-dialog.js @@ -11,9 +11,9 @@ import toaster from '../toast'; import '../../css/transfer-group-dialog.css'; const propTypes = { - groupID: PropTypes.string, - toggleTransferGroupDialog: PropTypes.func.isRequired, - onGroupChanged: PropTypes.func.isRequired + groupID: PropTypes.number.isRequired, + onGroupTransfered: PropTypes.func.isRequired, + toggleDialog: PropTypes.func.isRequired }; class TransferGroupDialog extends React.Component { @@ -21,18 +21,14 @@ class TransferGroupDialog extends React.Component { constructor(props) { super(props); this.state = { - selectedOption: null, - errMessage: '', + selectedOption: null }; - this.options = []; } handleSelectChange = (option) => { this.setState({ - selectedOption: option, - errMessage: '', + selectedOption: option }); - this.options = []; }; transferGroup = () => { @@ -41,19 +37,21 @@ class TransferGroupDialog extends React.Component { if (selectedOption && selectedOption[0]) { email = selectedOption[0].email; } - if (email) { - seafileAPI.transferGroup(this.props.groupID, email).then((res) => { - this.props.toggleTransferGroupDialog(); - toaster.success(gettext('Group has been transfered')); - }).catch((error) => { - let errMessage = Utils.getErrorMsg(error); - this.setState({ errMessage: errMessage }); - }); + if (!email) { + return false; } + seafileAPI.transferGroup(this.props.groupID, email).then((res) => { + toaster.success(gettext('Group has been transfered')); + this.props.onGroupTransfered(res.data); + this.props.toggleDialog(); + }).catch((error) => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + }); }; toggle = () => { - this.props.toggleTransferGroupDialog(); + this.props.toggleDialog(); }; render() { @@ -68,7 +66,6 @@ class TransferGroupDialog extends React.Component { placeholder={gettext('Please enter 1 or more character')} onSelectChange={this.handleSelectChange} /> - <div className="error">{this.state.errMessage}</div> </ModalBody> <ModalFooter> <Button color="secondary" onClick={this.toggle}>{gettext('Close')}</Button> diff --git a/frontend/src/components/group-members.js b/frontend/src/components/group-members.js index 3f15bb6e1f..2bda3f81ac 100644 --- a/frontend/src/components/group-members.js +++ b/frontend/src/components/group-members.js @@ -10,7 +10,7 @@ import OpIcon from './op-icon'; const propTypes = { groupMembers: PropTypes.array.isRequired, - groupID: PropTypes.string, + groupID: PropTypes.number.isRequired, isOwner: PropTypes.bool.isRequired, isItemFreezed: PropTypes.bool.isRequired, toggleItemFreezed: PropTypes.func.isRequired, @@ -61,7 +61,7 @@ const MemberPropTypes = { changeMember: PropTypes.func.isRequired, deleteMember: PropTypes.func.isRequired, toggleItemFreezed: PropTypes.func.isRequired, - groupID: PropTypes.string, + groupID: PropTypes.number.isRequired, isOwner: PropTypes.bool.isRequired, isItemFreezed: PropTypes.bool.isRequired }; diff --git a/frontend/src/components/list-and-add-group-members.js b/frontend/src/components/list-and-add-group-members.js index af0f2e1bcf..d12900890b 100644 --- a/frontend/src/components/list-and-add-group-members.js +++ b/frontend/src/components/list-and-add-group-members.js @@ -12,7 +12,7 @@ import GroupMembers from './group-members'; const propTypes = { toggleManageMembersDialog: PropTypes.func, toggleDepartmentDetailDialog: PropTypes.func, - groupID: PropTypes.string, + groupID: PropTypes.number.isRequired, isOwner: PropTypes.bool.isRequired }; diff --git a/frontend/src/pages/groups/group-item.js b/frontend/src/pages/groups/group-item.js index 09e2079968..f35e3571a1 100644 --- a/frontend/src/pages/groups/group-item.js +++ b/frontend/src/pages/groups/group-item.js @@ -1,14 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { gettext, siteRoot, username } from '../../utils/constants'; +import { gettext, siteRoot } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import toaster from '../../components/toast'; import SharedRepoListView from '../../components/shared-repo-list-view/shared-repo-list-view'; -import SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar'; -import CreateRepoDialog from '../../components/dialog/create-repo-dialog'; -import Repo from '../../models/repo'; import { LIST_MODE } from '../../components/dir-view-mode/constants'; +import GroupOperationMenu from './group-op-menu'; const propTypes = { inAllLibs: PropTypes.bool, @@ -17,21 +15,18 @@ const propTypes = { onMonitorRepo: PropTypes.func, renameRelatedGroupsRepos: PropTypes.func, deleteRelatedGroupsRepos: PropTypes.func, - insertRepoIntoGroup: PropTypes.func, + addRepoToGroup: PropTypes.func, unshareRepoToGroup: PropTypes.func, onTransferRepo: PropTypes.func.isRequired, + onGroupNameChanged: PropTypes.func.isRequired, + onGroupTransfered: PropTypes.func.isRequired, + onGroupDeleted: PropTypes.func.isRequired, + onLeavingGroup: PropTypes.func.isRequired }; class GroupItem extends React.Component { - constructor(props) { - super(props); - this.state = { - isCreateRepoDialogOpen: false - }; - } - onItemUnshare = (repo) => { const { group } = this.props; const { id: group_id } = group; @@ -61,53 +56,45 @@ class GroupItem extends React.Component { this.props.onMonitorRepo(repo, monitored); }; - toggleCreateRepoDialog = () => { - this.setState({ - isCreateRepoDialogOpen: !this.state.isCreateRepoDialogOpen - }); - }; - - onCreateRepo = (repo) => { + addNewRepo = (newRepo) => { const { group } = this.props; const { id: group_id } = group; - seafileAPI.createGroupOwnedLibrary(group_id, repo).then(res => { - let object = { - repo_id: res.data.id, - repo_name: res.data.name, - owner_name: res.data.group_name, - owner_email: res.data.owner, - permission: res.data.permission, - mtime: res.data.mtime, - size: res.data.size, - encrypted: res.data.encrypted, - }; - const newRepo = new Repo(object); - this.props.insertRepoIntoGroup({ repo: newRepo, group_id }); - }).catch(error => { - let errMessage = Utils.getErrorMsg(error); - toaster.danger(errMessage); - }); - this.toggleCreateRepoDialog(); + this.props.addRepoToGroup({ repo: newRepo, group_id }); + }; + + onGroupNameChanged = (newName) => { + const { group } = this.props; + this.props.onGroupNameChanged(newName, group.id); + }; + + onGroupDeleted = () => { + const { group } = this.props; + this.props.onGroupDeleted(group.id); + }; + + onLeavingGroup = () => { + const { group } = this.props; + this.props.onLeavingGroup(group.id); }; render() { const { inAllLibs = false, group, currentViewMode = LIST_MODE } = this.props; - const { parent_group_id, admins } = group; const emptyTip = <p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No libraries')}</p>; - const isDeptAdmin = parent_group_id != 0 && admins.indexOf(username) > -1; return ( <div className="pb-3"> <div className={`d-flex justify-content-between mt-3 py-1 ${currentViewMode == LIST_MODE ? 'sf-border-bottom' : ''}`}> <h4 className="sf-heading m-0 d-flex align-items-center"> <span className={`${group.parent_group_id == 0 ? 'sf3-font-group' : 'sf3-font-department'} sf3-font nav-icon`} aria-hidden="true"></span> <a href={`${siteRoot}group/${group.id}/`} title={group.name} className="ellipsis">{group.name}</a> - {isDeptAdmin && - <SingleDropdownToolbar - withPlusIcon={true} - opList={[{ 'text': gettext('New Library'), 'onClick': this.toggleCreateRepoDialog }]} - /> - } + <GroupOperationMenu + group={group} + addNewRepo={this.addNewRepo} + onGroupNameChanged={this.onGroupNameChanged} + onGroupTransfered={this.props.onGroupTransfered} + onGroupDeleted={this.onGroupDeleted} + onLeavingGroup={this.onLeavingGroup} + /> </h4> </div> {group.repos.length === 0 ? @@ -127,13 +114,6 @@ class GroupItem extends React.Component { currentViewMode={currentViewMode} /> } - {this.state.isCreateRepoDialogOpen && - <CreateRepoDialog - onCreateToggle={this.toggleCreateRepoDialog} - onCreateRepo={this.onCreateRepo} - libraryType='department' - /> - } </div> ); } diff --git a/frontend/src/pages/groups/group-op-menu.js b/frontend/src/pages/groups/group-op-menu.js new file mode 100644 index 0000000000..b387d39b91 --- /dev/null +++ b/frontend/src/pages/groups/group-op-menu.js @@ -0,0 +1,273 @@ +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { gettext, username, canAddRepo } from '../../utils/constants'; +import { seafileAPI } from '../../utils/seafile-api'; +import { Utils } from '../../utils/utils'; +import toaster from '../../components/toast'; +import { Repo } from '../../models'; +import CreateRepoDialog from '../../components/dialog/create-repo-dialog'; +import GroupMembersDialog from '../../components/dialog/group-members-dialog'; +import DismissGroupDialog from '../../components/dialog/dismiss-group-dialog'; +import RenameGroupDialog from '../../components/dialog/rename-group-dialog'; +import TransferGroupDialog from '../../components/dialog/transfer-group-dialog'; +import ImportMembersDialog from '../../components/dialog/import-members-dialog'; +import ManageMembersDialog from '../../components/dialog/manage-members-dialog'; +import DepartmentDetailDialog from '../../components/dialog/department-detail-dialog'; +import LeaveGroupDialog from '../../components/dialog/leave-group-dialog'; +import SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar'; + +import '../../css/group-view.css'; + +const propTypes = { + group: PropTypes.object.isRequired, + addNewRepo: PropTypes.func.isRequired, + onGroupNameChanged: PropTypes.func.isRequired, + onGroupTransfered: PropTypes.func.isRequired, + onGroupDeleted: PropTypes.func.isRequired, + onLeavingGroup: PropTypes.func.isRequired +}; + +class GroupOperationMenu extends React.Component { + + constructor(props) { + super(props); + this.state = { + isCreateRepoDialogOpen: false, + isShowDepartmentDetailDialog: false, + isRenameGroupDialogOpen: false, + isDeleteGroupDialogOpen: false, + isTransferGroupDialogOpen: false, + isImportMembersDialogOpen: false, + isManageMembersDialogOpen: false, + isLeaveGroupDialogOpen: false, + isMembersDialogOpen: false + }; + } + + onCreateRepoToggle = () => { + this.setState({ isCreateRepoDialogOpen: !this.state.isCreateRepoDialogOpen }); + }; + + onCreateRepo = (repo, groupType) => { + const { group } = this.props; + const groupId = group.id; + if (groupType && groupType === 'department') { + seafileAPI.createGroupOwnedLibrary(groupId, repo).then(res => { + let object = { + repo_id: res.data.id, + repo_name: res.data.name, + owner_name: res.data.group_name, + owner_email: res.data.owner, + permission: res.data.permission, + mtime: res.data.mtime, + size: res.data.size, + encrypted: res.data.encrypted, + }; + const repo = new Repo(object); + this.props.addNewRepo(repo); + }).catch(error => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + }); + + } else { + seafileAPI.createGroupRepo(groupId, repo).then(res => { + const repo = new Repo(res.data); + this.props.addNewRepo(repo); + }).catch(error => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + }); + } + this.onCreateRepoToggle(); + }; + + toggleDeleteGroupDialog = () => { + this.setState({ + isDeleteGroupDialogOpen: !this.state.isDeleteGroupDialogOpen, + }); + }; + + toggleRenameGroupDialog = () => { + this.setState({ + isRenameGroupDialogOpen: !this.state.isRenameGroupDialogOpen, + }); + }; + + toggleTransferGroupDialog = () => { + this.setState({ + isTransferGroupDialogOpen: !this.state.isTransferGroupDialogOpen, + }); + }; + + toggleImportMembersDialog = () => { + this.setState({ + isImportMembersDialogOpen: !this.state.isImportMembersDialogOpen + }); + }; + + importMembersInBatch = (file) => { + toaster.notify(gettext('It may take some time, please wait.'), { 'id': 'importing-members' }); + const { group } = this.props; + seafileAPI.importGroupMembersViaFile(group.id, file).then((res) => { + res.data.success.forEach(item => { + toaster.success(gettext('Successfully imported {user_placeholder}').replace('{user_placeholder}', `${item.contact_email}`), { 'id': 'importing-members' }); + }); + res.data.failed.forEach(item => { + toaster.danger(`${item.email}: ${item.error_msg}`, { 'id': 'importing-members' }); + }); + }).catch((error) => { + let errMsg = Utils.getErrorMsg(error); + toaster.danger(errMsg); + }); + }; + + toggleManageMembersDialog = () => { + this.setState({ + isManageMembersDialogOpen: !this.state.isManageMembersDialogOpen, + }); + }; + + toggleLeaveGroupDialog = () => { + this.setState({ + isLeaveGroupDialogOpen: !this.state.isLeaveGroupDialogOpen, + }); + }; + + toggleMembersDialog = () => { + this.setState({ + isMembersDialogOpen: !this.state.isMembersDialogOpen + }); + }; + + toggleDepartmentDetailDialog = () => { + this.setState({ + isShowDepartmentDetailDialog: !this.state.isShowDepartmentDetailDialog + }); + }; + + getOpList = () => { + const { group } = this.props; + const isDepartment = group.parent_group_id !== 0; + const isStaff = group.admins.indexOf(username) > -1; + const isOwner = group.owner === username; + const opList = []; + if ((!isDepartment && canAddRepo) || + (isDepartment && isStaff)) { + this.newLibraryEnabled = true; + opList.push({ 'text': gettext('New Library'), 'onClick': this.onCreateRepoToggle }, 'Divider'); + } + opList.push({ 'text': gettext('Members'), 'onClick': this.toggleMembersDialog }); + if (isStaff || isOwner) { + opList.push({ 'text': gettext('Import members'), 'onClick': this.toggleImportMembersDialog }); + opList.push({ 'text': gettext('Manage members'), 'onClick': this.toggleManageMembersDialog }); + opList.push('Divider'); + opList.push({ 'text': gettext('Rename'), 'onClick': this.toggleRenameGroupDialog }); + if (isOwner) { + opList.push({ 'text': gettext('Transfer'), 'onClick': this.toggleTransferGroupDialog }); + } + if (isOwner) { + opList.push({ 'text': gettext('Delete group'), 'onClick': this.toggleDeleteGroupDialog }); + } + } + + if (!isOwner && !isDepartment) { + opList.push({ 'text': gettext('Leave group'), 'onClick': this.toggleLeaveGroupDialog }); + } + + return opList; + }; + + + render() { + const { + isCreateRepoDialogOpen, + isMembersDialogOpen + } = this.state; + const { group } = this.props; + const { id: groupID, parent_group_id, owner } = group; + const isDepartment = parent_group_id !== 0; + const isOwner = owner === username; + + const opList = this.getOpList(); + return ( + <Fragment> + {group && ( + <SingleDropdownToolbar + withPlusIcon={this.newLibraryEnabled} + opList={opList} + /> + )} + {isCreateRepoDialogOpen && + <CreateRepoDialog + onCreateToggle={this.onCreateRepoToggle} + onCreateRepo={this.onCreateRepo} + libraryType={isDepartment ? 'department' : 'group'} + /> + } + {isMembersDialogOpen && + <GroupMembersDialog + groupID={groupID} + toggleDialog={this.toggleMembersDialog} + /> + } + {this.state.isRenameGroupDialogOpen && + <RenameGroupDialog + groupID={groupID} + groupName={group.name} + onGroupNameChanged={this.props.onGroupNameChanged} + toggleDialog={this.toggleRenameGroupDialog} + /> + } + {this.state.isDeleteGroupDialogOpen && + <DismissGroupDialog + groupID={groupID} + onGroupDeleted={this.props.onGroupDeleted} + toggleDialog={this.toggleDeleteGroupDialog} + /> + } + {this.state.isTransferGroupDialogOpen && + <TransferGroupDialog + groupID={groupID} + onGroupTransfered={this.props.onGroupTransfered} + toggleDialog={this.toggleTransferGroupDialog} + /> + } + {this.state.isImportMembersDialogOpen && + <ImportMembersDialog + importMembersInBatch={this.importMembersInBatch} + toggleDialog={this.toggleImportMembersDialog} + /> + } + {this.state.isManageMembersDialogOpen && + <ManageMembersDialog + groupID={groupID} + isOwner={isOwner} + toggleManageMembersDialog={this.toggleManageMembersDialog} + toggleDepartmentDetailDialog={this.toggleDepartmentDetailDialog} + /> + } + {this.state.isShowDepartmentDetailDialog && + <DepartmentDetailDialog + usedFor='add_group_member' + toggleDepartmentDetailDialog={this.toggleDepartmentDetailDialog} + toggleManageMembersDialog={this.toggleManageMembersDialog} + groupID={groupID} + isOwner={isOwner} + /> + } + {this.state.isLeaveGroupDialogOpen && + <LeaveGroupDialog + groupID={groupID} + toggleDialog={this.toggleLeaveGroupDialog} + onLeavingGroup={this.props.onLeavingGroup} + /> + } + </Fragment> + ); + } +} + +GroupOperationMenu.propTypes = propTypes; + +export default GroupOperationMenu; diff --git a/frontend/src/pages/groups/group-view.js b/frontend/src/pages/groups/group-view.js index fc523fc855..2e023ec14d 100644 --- a/frontend/src/pages/groups/group-view.js +++ b/frontend/src/pages/groups/group-view.js @@ -2,35 +2,25 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import cookie from 'react-cookies'; import classnames from 'classnames'; -import { gettext, username, canAddRepo } from '../../utils/constants'; +import { navigate } from '@gatsbyjs/reach-router'; +import { gettext, siteRoot, username } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import Loading from '../../components/loading'; import EmptyTip from '../../components/empty-tip'; -import ModalPortal from '../../components/modal-portal'; import toaster from '../../components/toast'; import { Group, Repo } from '../../models'; -import CreateRepoDialog from '../../components/dialog/create-repo-dialog'; -import GroupMembersDialog from '../../components/dialog/group-members-dialog'; -import DismissGroupDialog from '../../components/dialog/dismiss-group-dialog'; -import RenameGroupDialog from '../../components/dialog/rename-group-dialog'; -import TransferGroupDialog from '../../components/dialog/transfer-group-dialog'; -import ImportMembersDialog from '../../components/dialog/import-members-dialog'; -import ManageMembersDialog from '../../components/dialog/manage-members-dialog'; -import DepartmentDetailDialog from '../../components/dialog/department-detail-dialog'; -import LeaveGroupDialog from '../../components/dialog/leave-group-dialog'; import SharedRepoListView from '../../components/shared-repo-list-view/shared-repo-list-view'; import SortOptionsDialog from '../../components/dialog/sort-options'; -import SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar'; import ViewModes from '../../components/view-modes'; import ReposSortMenu from '../../components/sort-menu'; import { LIST_MODE } from '../../components/dir-view-mode/constants'; +import GroupOperationMenu from './group-op-menu'; import '../../css/group-view.css'; const propTypes = { - onGroupChanged: PropTypes.func.isRequired, - groupID: PropTypes.string, + groupID: PropTypes.string }; class GroupView extends React.Component { @@ -43,9 +33,6 @@ class GroupView extends React.Component { errMessage: '', emptyTip: null, currentGroup: null, - currentRepo: null, - isStaff: false, - isOwner: false, currentViewMode: localStorage.getItem('sf_repo_list_view_mode') || LIST_MODE, sortBy: cookie.load('seafile-repo-dir-sort-by') || 'name', // 'name' or 'time' or 'size' sortOrder: cookie.load('seafile-repo-dir-sort-order') || 'asc', // 'asc' or 'desc' @@ -54,19 +41,7 @@ class GroupView extends React.Component { currentPage: 1, perPage: 300, hasNextPage: false, - libraryType: 'group', - isCreateRepoDialogShow: false, isDepartmentGroup: false, - isShowDepartmentDetailDialog: false, - showGroupDropdown: false, - showGroupMembersPopover: false, - showRenameGroupDialog: false, - showDismissGroupDialog: false, - showTransferGroupDialog: false, - showImportMembersDialog: false, - showManageMembersDialog: false, - isLeaveGroupDialogOpen: false, - isMembersDialogOpen: false }; } @@ -86,9 +61,7 @@ class GroupView extends React.Component { this.setState({ emptyTip: this.getEmptyTip(currentGroup), currentGroup, - isStaff: currentGroup.admins.indexOf(username) > -1, // for item operations isDepartmentGroup: currentGroup.parent_group_id !== 0, - isOwner: currentGroup.owner === username, currentPage: 1, repoList: [] // empty it for the current group }, () => { @@ -160,45 +133,6 @@ class GroupView extends React.Component { return null; }; - onCreateRepoToggle = () => { - this.setState({ isCreateRepoDialogShow: !this.state.isCreateRepoDialogShow }); - }; - - onCreateRepo = (repo, groupOwnerType) => { - let groupId = this.props.groupID; - if (groupOwnerType && groupOwnerType === 'department') { - seafileAPI.createGroupOwnedLibrary(groupId, repo).then(res => { // need modify endpoint api - let object = { - repo_id: res.data.id, - repo_name: res.data.name, - owner_name: res.data.group_name, - owner_email: res.data.owner, - permission: res.data.permission, - mtime: res.data.mtime, - size: res.data.size, - encrypted: res.data.encrypted, - }; - let repo = new Repo(object); - let repoList = this.addRepoItem(repo); - this.setState({ repoList: repoList }); - }).catch(error => { - let errMessage = Utils.getErrorMsg(error); - toaster.danger(errMessage); - }); - - } else { - seafileAPI.createGroupRepo(groupId, repo).then(res => { - let repo = new Repo(res.data); - let repoList = this.addRepoItem(repo); - this.setState({ repoList: repoList }); - }).catch(error => { - let errMessage = Utils.getErrorMsg(error); - toaster.danger(errMessage); - }); - } - this.onCreateRepoToggle(); - }; - onItemDelete = (repo) => { let repoList = this.state.repoList.filter(item => { return item.repo_id !== repo.repo_id; @@ -215,12 +149,6 @@ class GroupView extends React.Component { this.loadGroup(this.props.groupID); }; - addRepoItem = (repo) => { - let newRepoList = this.state.repoList.map(item => {return item;}); - newRepoList.unshift(repo); - return newRepoList; - }; - onItemUnshare = (repo) => { let group = this.state.currentGroup; seafileAPI.unshareRepoToGroup(repo.repo_id, group.id).then(() => { @@ -260,59 +188,6 @@ class GroupView extends React.Component { this.setState({ repoList: repoList }); }; - toggleDismissGroupDialog = () => { - this.setState({ - showDismissGroupDialog: !this.state.showDismissGroupDialog, - showGroupDropdown: false, - }); - }; - - toggleRenameGroupDialog = () => { - this.setState({ - showRenameGroupDialog: !this.state.showRenameGroupDialog, - showGroupDropdown: false, - }); - }; - - toggleTransferGroupDialog = () => { - this.setState({ - showTransferGroupDialog: !this.state.showTransferGroupDialog, - showGroupDropdown: false, - }); - }; - - toggleImportMembersDialog = () => { - this.setState({ - showImportMembersDialog: !this.state.showImportMembersDialog - }); - }; - - importMembersInBatch = (file) => { - toaster.notify(gettext('It may take some time, please wait.')); - seafileAPI.importGroupMembersViaFile(this.state.currentGroup.id, file).then((res) => { - res.data.failed.forEach(item => { - toaster.danger(`${item.email}: ${item.error_msg}`); - }); - }).catch((error) => { - let errMsg = Utils.getErrorMsg(error); - toaster.danger(errMsg); - }); - }; - - toggleManageMembersDialog = () => { - this.setState({ - showManageMembersDialog: !this.state.showManageMembersDialog, - showGroupDropdown: false, - }); - }; - - toggleLeaveGroupDialog = () => { - this.setState({ - isLeaveGroupDialogOpen: !this.state.isLeaveGroupDialogOpen, - showGroupDropdown: false, - }); - }; - sortItems = (sortBy, sortOrder) => { cookie.save('seafile-repo-dir-sort-by', sortBy); cookie.save('seafile-repo-dir-sort-order', sortOrder); @@ -323,18 +198,6 @@ class GroupView extends React.Component { }); }; - translateRole = (role) => { - if (role === 'Admin') { - return gettext('Admin'); - } - else if (role === 'Member') { - return gettext('Member'); - } - else if (role === 'Owner') { - return gettext('Owner'); - } - }; - toggleSortOptionsDialog = () => { this.setState({ isSortOptionsDialogOpen: !this.state.isSortOptionsDialogOpen @@ -357,43 +220,6 @@ class GroupView extends React.Component { } }; - toggleMembersDialog = () => { - this.setState({ - isMembersDialogOpen: !this.state.isMembersDialogOpen - }); - }; - - getOpList = () => { - const { currentGroup, isDepartmentGroup, isStaff, isOwner } = this.state; - const opList = []; - if ((!isDepartmentGroup && canAddRepo) || - (isDepartmentGroup && isStaff)) { - this.newLibraryEnalbed = true; - opList.push({ 'text': gettext('New Library'), 'onClick': this.onCreateRepoToggle }, 'Divider'); - } - opList.push({ 'text': gettext('Members'), 'onClick': this.toggleMembersDialog }); - if (currentGroup) { - if (isStaff || isOwner) { - opList.push({ 'text': gettext('Import members'), 'onClick': this.toggleImportMembersDialog }); - opList.push({ 'text': gettext('Manage members'), 'onClick': this.toggleManageMembersDialog }); - opList.push('Divider'); - opList.push({ 'text': gettext('Rename'), 'onClick': this.toggleRenameGroupDialog }); - if (isOwner) { - opList.push({ 'text': gettext('Transfer'), 'onClick': this.toggleTransferGroupDialog }); - } - if (isOwner) { - opList.push({ 'text': gettext('Delete group'), 'onClick': this.toggleDismissGroupDialog }); - } - } - - if (!isOwner && !isDepartmentGroup) { - opList.push({ 'text': gettext('Leave group'), 'onClick': this.toggleLeaveGroupDialog }); - } - } - - return opList; - }; - switchViewMode = (newMode) => { this.setState({ currentViewMode: newMode @@ -409,16 +235,38 @@ class GroupView extends React.Component { }); }; - toggleDepartmentDetailDialog = () => { + addNewRepo = (newRepo) => { + let { repoList } = this.state; + repoList.unshift(newRepo); + this.setState({ repoList: repoList }); + }; + + onGroupNameChanged = (newName) => { + const { currentGroup } = this.state; + currentGroup.name = newName; this.setState({ - isShowDepartmentDetailDialog: !this.state.isShowDepartmentDetailDialog + currentGroup: currentGroup }); }; + onGroupTransfered = (group) => { + this.setState({ + currentGroup: group + }); + }; + + onGroupDeleted = () => { + navigate(siteRoot); + }; + + onLeavingGroup = () => { + navigate(siteRoot); + }; + render() { const { isLoading, repoList, errMessage, emptyTip, - currentGroup, isDepartmentGroup, isMembersDialogOpen, + currentGroup, isDepartmentGroup, currentViewMode, sortBy, sortOrder } = this.state; @@ -427,7 +275,6 @@ class GroupView extends React.Component { useRate = currentGroup.group_quota_usage / currentGroup.group_quota * 100 + '%'; } - const opList = this.getOpList(); return ( <Fragment> <div className="main-panel-center flex-row"> @@ -440,9 +287,13 @@ class GroupView extends React.Component { <span className="sf3-font-department sf3-font nav-icon" title={gettext('This is a special group representing a department.')}></span> } <span>{currentGroup.name}</span> - <SingleDropdownToolbar - withPlusIcon={this.newLibraryEnalbed} - opList={opList} + <GroupOperationMenu + group={currentGroup} + addNewRepo={this.addNewRepo} + onGroupNameChanged={this.onGroupNameChanged} + onGroupTransfered={this.onGroupTransfered} + onGroupDeleted={this.onGroupDeleted} + onLeavingGroup={this.onLeavingGroup} /> </div> <div className="path-tool d-flex align-items-center"> @@ -510,85 +361,6 @@ class GroupView extends React.Component { </div> </div> </div> - {this.state.isCreateRepoDialogShow && !this.state.isDepartmentGroup && ( - <ModalPortal> - <CreateRepoDialog - libraryType={this.state.libraryType} - onCreateToggle={this.onCreateRepoToggle} - onCreateRepo={this.onCreateRepo} - /> - </ModalPortal> - )} - {this.state.isCreateRepoDialogShow && this.state.isDepartmentGroup && - <CreateRepoDialog - onCreateToggle={this.onCreateRepoToggle} - onCreateRepo={this.onCreateRepo} - libraryType='department' - /> - } - {isMembersDialogOpen && - <GroupMembersDialog - groupID={this.props.groupID} - toggleDialog={this.toggleMembersDialog} - /> - } - {this.state.showRenameGroupDialog && - <RenameGroupDialog - showRenameGroupDialog={this.state.showRenameGroupDialog} - toggleRenameGroupDialog={this.toggleRenameGroupDialog} - loadGroup={this.loadGroup} - groupID={this.props.groupID} - onGroupChanged={this.props.onGroupChanged} - currentGroupName={currentGroup.name} - /> - } - {this.state.showDismissGroupDialog && - <DismissGroupDialog - showDismissGroupDialog={this.state.showDismissGroupDialog} - toggleDismissGroupDialog={this.toggleDismissGroupDialog} - loadGroup={this.loadGroup} - groupID={this.props.groupID} - onGroupChanged={this.props.onGroupChanged} - /> - } - {this.state.showTransferGroupDialog && - <TransferGroupDialog - toggleTransferGroupDialog={this.toggleTransferGroupDialog} - groupID={this.props.groupID} - onGroupChanged={this.props.onGroupChanged} - /> - } - { this.state.showImportMembersDialog && - <ImportMembersDialog - toggleImportMembersDialog={this.toggleImportMembersDialog} - importMembersInBatch={this.importMembersInBatch} - /> - } - {this.state.showManageMembersDialog && - <ManageMembersDialog - toggleManageMembersDialog={this.toggleManageMembersDialog} - groupID={this.props.groupID} - onGroupChanged={this.props.onGroupChanged} - isOwner={this.state.isOwner} - toggleDepartmentDetailDialog={this.toggleDepartmentDetailDialog} - /> - } - {this.state.isShowDepartmentDetailDialog && - <DepartmentDetailDialog - usedFor='add_group_member' - toggleDepartmentDetailDialog={this.toggleDepartmentDetailDialog} - toggleManageMembersDialog={this.toggleManageMembersDialog} - groupID={this.props.groupID} - isOwner={this.state.isOwner} - /> - } - {this.state.isLeaveGroupDialogOpen && - <LeaveGroupDialog - toggleLeaveGroupDialog={this.toggleLeaveGroupDialog} - groupID={this.props.groupID} - onGroupChanged={this.props.onGroupChanged} - /> - } </Fragment> ); } diff --git a/frontend/src/pages/libraries/index.js b/frontend/src/pages/libraries/index.js index 5d39a8b752..99b297631a 100644 --- a/frontend/src/pages/libraries/index.js +++ b/frontend/src/pages/libraries/index.js @@ -52,7 +52,7 @@ class Libraries extends Component { const eventBus = EventBus.getInstance(); this.unsubscribeAddNewGroup = eventBus.subscribe(EVENT_BUS_TYPE.ADD_NEW_GROUP, this.addNewGroup); - this.unsubscribeAddSharedRepoIntoGroup = eventBus.subscribe(EVENT_BUS_TYPE.ADD_SHARED_REPO_INTO_GROUP, this.insertRepoIntoGroup); + this.unsubscribeAddSharedRepoIntoGroup = eventBus.subscribe(EVENT_BUS_TYPE.ADD_SHARED_REPO_INTO_GROUP, this.addRepoToGroup); this.unsubscribeUnsharedRepoToGroup = eventBus.subscribe(EVENT_BUS_TYPE.UN_SHARE_REPO_TO_GROUP, this.unshareRepoToGroup); } @@ -282,7 +282,7 @@ class Libraries extends Component { }; */ - insertRepoIntoGroup = ({ repo, group_id }) => { + addRepoToGroup = ({ repo, group_id }) => { if (!repo) { return; } @@ -299,11 +299,37 @@ class Libraries extends Component { } targetGroup.repos.unshift(repo); - targetGroup.repos = Utils.sortRepos(targetGroup.repos, this.state.sortBy, this.state.sortOrder); this.groupsReposManager.add(repo.repo_id, group_id); this.setState({ groupList: newGroupList }); }; + onGroupNameChanged = (newName, groupID) => { + const { groupList } = this.state; + let newGroupList = [...groupList]; + let targetGroup = newGroupList.find((group) => group.id === groupID); + targetGroup.name = newName; + this.setState({ groupList: newGroupList }); + }; + + onGroupTransfered = (group) => { + const { groupList } = this.state; + let newGroupList = [...groupList]; + let targetGroup = newGroupList.find((item) => item.id === group.id); + targetGroup.owner = group.owner; + targetGroup.admins = group.admins; + this.setState({ groupList: newGroupList }); + }; + + onGroupDeleted = (groupID) => { + const { groupList } = this.state; + this.setState({ groupList: groupList.filter(item => item.id != groupID) }); + }; + + onLeavingGroup = (groupID) => { + const { groupList } = this.state; + this.setState({ groupList: groupList.filter(item => item.id != groupID) }); + }; + unshareRepoToGroup = ({ repo_id, group_id }) => { const { groupList } = this.state; let newGroupList = [...groupList]; @@ -498,7 +524,11 @@ class Libraries extends Component { onMonitorRepo={this.onMonitorRepo} renameRelatedGroupsRepos={this.renameRelatedGroupsRepos} deleteRelatedGroupsRepos={this.deleteRelatedGroupsRepos} - insertRepoIntoGroup={this.insertRepoIntoGroup} + addRepoToGroup={this.addRepoToGroup} + onGroupNameChanged={this.onGroupNameChanged} + onGroupTransfered={this.onGroupTransfered} + onGroupDeleted={this.onGroupDeleted} + onLeavingGroup={this.onLeavingGroup} unshareRepoToGroup={this.unshareRepoToGroup} onTransferRepo={this.onGroupTransferRepo} currentViewMode={currentViewMode}