2019-08-05 20:46:59 +08:00
import React , { Component , Fragment } from 'react' ;
2019-08-14 15:29:31 +08:00
import { Dropdown , DropdownToggle , DropdownItem } from 'reactstrap' ;
2019-04-12 05:28:15 +00:00
import PropTypes from 'prop-types' ;
2019-08-05 20:46:59 +08:00
import moment from 'moment' ;
2019-12-05 15:45:16 +08:00
import { gettext } from '../../utils/constants' ;
2019-08-05 20:46:59 +08:00
import { Utils } from '../../utils/utils' ;
import { seafileAPI } from '../../utils/seafile-api' ;
2019-04-12 05:28:15 +00:00
import InvitationsToolbar from '../../components/toolbar/invitations-toolbar' ;
2019-05-13 11:55:49 +08:00
import InvitePeopleDialog from '../../components/dialog/invite-people-dialog' ;
2019-08-05 20:46:59 +08:00
import InvitationRevokeDialog from '../../components/dialog/invitation-revoke-dialog' ;
2019-05-13 11:55:49 +08:00
import Loading from '../../components/loading' ;
2019-04-23 13:36:21 +08:00
import toaster from '../../components/toast' ;
2019-06-10 17:30:10 +08:00
import EmptyTip from '../../components/empty-tip' ;
2019-04-23 13:36:21 +08:00
2019-05-13 11:55:49 +08:00
import '../../css/invitations.css' ;
2019-04-19 18:09:12 +08:00
2019-08-05 20:46:59 +08:00
class Item extends React . Component {
2019-04-19 18:09:12 +08:00
constructor ( props ) {
super ( props ) ;
this . state = {
2020-11-02 13:56:35 +08:00
isOpIconShown : false ,
2019-08-14 15:29:31 +08:00
isOpMenuOpen : false , // for mobile
2019-08-05 20:46:59 +08:00
isRevokeDialogOpen : false
2019-04-19 18:09:12 +08:00
} ;
}
2019-08-14 15:29:31 +08:00
toggleOpMenu = ( ) => {
this . setState ( {
isOpMenuOpen : ! this . state . isOpMenuOpen
} ) ;
}
2019-08-05 20:46:59 +08:00
onMouseEnter = ( ) => {
2019-04-19 18:09:12 +08:00
this . setState ( {
2019-08-05 20:46:59 +08:00
isOpIconShown : true
2019-04-19 18:09:12 +08:00
} ) ;
}
2019-08-05 20:46:59 +08:00
onMouseLeave = ( ) => {
2019-04-19 18:09:12 +08:00
this . setState ( {
2020-11-02 13:56:35 +08:00
isOpIconShown : false
2019-04-19 18:09:12 +08:00
} ) ;
}
2019-08-05 20:46:59 +08:00
deleteItem = ( ) => {
// make the icon avoid being clicked repeatedly
2019-04-19 18:09:12 +08:00
this . setState ( {
2020-11-02 13:56:35 +08:00
isOpIconShown : false
2019-08-05 20:46:59 +08:00
} ) ;
const token = this . props . invitation . token ;
seafileAPI . deleteInvitation ( token ) . then ( ( res ) => {
this . setState ( { deleted : true } ) ;
toaster . success ( gettext ( 'Successfully deleted 1 item.' ) ) ;
} ) . catch ( ( error ) => {
const errorMsg = Utils . getErrorMsg ( error ) ;
toaster . danger ( errorMsg ) ;
this . setState ( {
2020-11-02 13:56:35 +08:00
isOpIconShown : true
2019-08-05 20:46:59 +08:00
} ) ;
} ) ;
}
revokeItem = ( ) => {
this . setState ( { deleted : true } ) ;
}
toggleRevokeDialog = ( ) => {
this . setState ( {
isRevokeDialogOpen : ! this . state . isRevokeDialogOpen
2019-04-19 18:09:12 +08:00
} ) ;
}
render ( ) {
2019-08-05 20:46:59 +08:00
const { isOpIconShown , deleted , isRevokeDialogOpen } = this . state ;
if ( deleted ) {
return null ;
}
2019-08-14 15:29:31 +08:00
const item = this . props . invitation ;
const desktopItem = (
< tr onMouseEnter = { this . onMouseEnter } onMouseLeave = { this . onMouseLeave } >
< td > { item . accepter } < / t d >
< td > { moment ( item . invite _time ) . format ( 'YYYY-MM-DD' ) } < / t d >
< td > { moment ( item . expire _time ) . format ( 'YYYY-MM-DD' ) } < / t d >
< td > { item . accept _time && < i className = "sf2-icon-tick invite-accept-icon" > < / i > } < / t d >
< td >
{ isOpIconShown && (
item . accept _time ?
< i
className = "action-icon sf3-font sf3-font-cancel-invitation"
title = { gettext ( 'Revoke Access' ) }
onClick = { this . toggleRevokeDialog } >
< / i > :
< i
className = "action-icon sf2-icon-x3"
title = { gettext ( 'Delete' ) }
onClick = { this . deleteItem } >
< / i >
) }
< / t d >
< / t r >
) ;
const mobileItem = (
< tr >
< td >
{ item . accepter } < br / >
< span className = "item-meta-info" > { moment ( item . invite _time ) . format ( 'YYYY-MM-DD' ) } < span className = "small" > ( { gettext ( 'Invite Time' ) } ) < / s p a n > < / s p a n >
< span className = "item-meta-info" > { moment ( item . expire _time ) . format ( 'YYYY-MM-DD' ) } < span className = "small" > ( { gettext ( 'Expiration' ) } ) < / s p a n > < / s p a n >
< span className = "item-meta-info" > { item . accept _time && gettext ( 'Accepted' ) } < / s p a n >
< / t d >
< td >
< Dropdown isOpen = { this . state . isOpMenuOpen } toggle = { this . toggleOpMenu } >
< DropdownToggle
tag = "i"
className = "sf-dropdown-toggle fa fa-ellipsis-v ml-0"
title = { gettext ( 'More Operations' ) }
data - toggle = "dropdown"
aria - expanded = { this . state . isOpMenuOpen }
/ >
< div className = { this . state . isOpMenuOpen ? '' : 'd-none' } onClick = { this . toggleOpMenu } >
< div className = "mobile-operation-menu-bg-layer" > < / d i v >
< div className = "mobile-operation-menu" >
{ item . accept _time ?
< DropdownItem className = "mobile-menu-item" onClick = { this . toggleRevokeDialog } > { gettext ( 'Revoke Access' ) } < / D r o p d o w n I t e m > :
< DropdownItem className = "mobile-menu-item" onClick = { this . deleteItem } > { gettext ( 'Delete' ) } < / D r o p d o w n I t e m >
}
< / d i v >
< / d i v >
< / D r o p d o w n >
< / t d >
< / t r >
) ;
2019-08-05 20:46:59 +08:00
2019-04-19 18:09:12 +08:00
return (
2019-08-05 20:46:59 +08:00
< Fragment >
2019-08-14 15:29:31 +08:00
{ this . props . isDesktop ? desktopItem : mobileItem }
2019-08-05 20:46:59 +08:00
{ isRevokeDialogOpen &&
< InvitationRevokeDialog
2019-08-14 15:29:31 +08:00
accepter = { item . accepter }
token = { item . token }
2019-08-05 20:46:59 +08:00
revokeInvitation = { this . revokeItem }
toggleDialog = { this . toggleRevokeDialog }
/ >
}
< / F r a g m e n t >
2019-04-19 18:09:12 +08:00
) ;
}
}
2019-08-05 20:46:59 +08:00
const ItemPropTypes = {
2019-04-19 18:09:12 +08:00
invitation : PropTypes . object . isRequired ,
} ;
2019-08-05 20:46:59 +08:00
Item . propTypes = ItemPropTypes ;
2019-04-19 18:09:12 +08:00
2019-08-05 20:46:59 +08:00
class Content extends Component {
2019-04-19 18:09:12 +08:00
constructor ( props ) {
super ( props ) ;
}
render ( ) {
2019-08-05 20:46:59 +08:00
const {
2020-11-02 13:56:35 +08:00
loading , errorMsg , invitationsList
2019-08-05 20:46:59 +08:00
} = this . props . data ;
if ( loading ) {
return < Loading / > ;
}
if ( errorMsg ) {
return < p className = "error text-center mt-2" > { errorMsg } < / p > ;
}
if ( ! invitationsList . length ) {
2020-11-02 13:56:35 +08:00
return (
2019-08-05 20:46:59 +08:00
< EmptyTip >
2020-01-03 16:50:17 +08:00
< h2 > { gettext ( 'No guest invitations' ) } < / h 2 >
< p > { gettext ( 'You have not invited any guests yet. A guest can access shared libraries through the web interface allowing more efficient ways to collaborate than through links. You can invite a guest by clicking the "Invite Guest" button in the menu bar.' ) } < / p >
2019-08-05 20:46:59 +08:00
< / E m p t y T i p >
) ;
}
2019-04-19 18:09:12 +08:00
2019-08-14 15:29:31 +08:00
const isDesktop = Utils . isDesktop ( ) ;
2019-04-19 18:09:12 +08:00
return (
2019-08-14 15:29:31 +08:00
< table className = { ` table-hover ${ isDesktop ? '' : ' table-thead-hidden' } ` } >
2019-05-13 11:55:49 +08:00
< thead >
2019-08-14 15:29:31 +08:00
{ isDesktop ?
< tr >
< th width = "25%" > { gettext ( 'Email' ) } < / t h >
< th width = "20%" > { gettext ( 'Invite Time' ) } < / t h >
< th width = "20%" > { gettext ( 'Expiration' ) } < / t h >
< th width = "18%" > { gettext ( 'Accepted' ) } < / t h >
< th width = "7%" > < / t h >
< / t r >
:
< tr >
< th width = "92%" > < / t h >
< th width = "8%" > < / t h >
< / t r >
}
2019-05-13 11:55:49 +08:00
< / t h e a d >
< tbody >
2019-08-05 20:46:59 +08:00
{ invitationsList . map ( ( invitation , index ) => {
return (
< Item
2019-08-14 15:29:31 +08:00
key = { index }
isDesktop = { isDesktop }
invitation = { invitation }
2019-08-05 20:46:59 +08:00
/ >
) ;
} ) }
2019-05-13 11:55:49 +08:00
< / t b o d y >
2019-08-05 20:46:59 +08:00
< / t a b l e >
2019-04-19 18:09:12 +08:00
) ;
}
}
2019-04-12 05:28:15 +00:00
class InvitationsView extends React . Component {
2019-04-19 18:09:12 +08:00
constructor ( props ) {
super ( props ) ;
this . state = {
2019-08-05 20:46:59 +08:00
loading : true ,
errorMsg : '' ,
2019-04-19 18:09:12 +08:00
invitationsList : [ ] ,
2019-08-05 20:46:59 +08:00
isInvitePeopleDialogOpen : false
2019-04-19 18:09:12 +08:00
} ;
}
2019-08-05 20:46:59 +08:00
componentDidMount ( ) {
2019-04-19 18:09:12 +08:00
seafileAPI . listInvitations ( ) . then ( ( res ) => {
this . setState ( {
invitationsList : res . data ,
2019-08-05 20:46:59 +08:00
loading : false
2019-04-19 18:09:12 +08:00
} ) ;
} ) . catch ( ( error ) => {
2019-12-05 15:45:16 +08:00
this . setState ( {
loading : false ,
errorMsg : Utils . getErrorMsg ( error , true ) // true: show login tip if 403
} ) ;
2020-11-02 13:56:35 +08:00
} ) ;
2019-04-19 18:09:12 +08:00
}
2019-04-23 13:36:21 +08:00
onInvitePeople = ( invitationsArray ) => {
2019-08-05 20:46:59 +08:00
invitationsArray . push . apply ( invitationsArray , this . state . invitationsList ) ;
2019-04-19 18:09:12 +08:00
this . setState ( {
2019-04-23 13:36:21 +08:00
invitationsList : invitationsArray ,
} ) ;
}
toggleInvitePeopleDialog = ( ) => {
2019-04-19 18:09:12 +08:00
this . setState ( {
2019-04-23 13:36:21 +08:00
isInvitePeopleDialogOpen : ! this . state . isInvitePeopleDialogOpen
2019-04-19 18:09:12 +08:00
} ) ;
}
2019-04-12 05:28:15 +00:00
render ( ) {
return (
< Fragment >
< InvitationsToolbar
onShowSidePanel = { this . props . onShowSidePanel }
onSearchedClick = { this . props . onSearchedClick }
2019-04-23 13:36:21 +08:00
toggleInvitePeopleDialog = { this . toggleInvitePeopleDialog }
2019-04-12 05:28:15 +00:00
/ >
< div className = "main-panel-center flex-row" >
< div className = "cur-view-container" >
< div className = "cur-view-path" >
2020-01-03 16:50:17 +08:00
< h3 className = "sf-heading" > { gettext ( 'Invite Guest' ) } < / h 3 >
2019-04-12 05:28:15 +00:00
< / d i v >
2019-04-23 13:36:21 +08:00
< div className = "cur-view-content" >
2019-08-05 20:46:59 +08:00
< Content data = { this . state } / >
2019-04-23 13:36:21 +08:00
< / d i v >
2019-04-12 05:28:15 +00:00
< / d i v >
< / d i v >
2019-04-23 13:36:21 +08:00
{ this . state . isInvitePeopleDialogOpen &&
2019-04-19 18:09:12 +08:00
< InvitePeopleDialog
onInvitePeople = { this . onInvitePeople }
2019-08-05 20:46:59 +08:00
toggleDialog = { this . toggleInvitePeopleDialog }
2019-04-19 18:09:12 +08:00
/ >
}
2019-04-12 05:28:15 +00:00
< / F r a g m e n t >
) ;
}
}
2019-05-13 11:55:49 +08:00
const InvitationsViewPropTypes = {
onShowSidePanel : PropTypes . func . isRequired ,
onSearchedClick : PropTypes . func . isRequired ,
} ;
InvitationsView . propTypes = InvitationsViewPropTypes ;
2019-04-12 05:28:15 +00:00
export default InvitationsView ;