2018-11-26 17:53:18 +08:00
import React , { Component } from 'react' ;
2023-09-13 08:40:50 +08:00
import PropTypes from 'prop-types' ;
2019-07-23 14:44:47 +08:00
import { Dropdown , DropdownToggle , DropdownItem } from 'reactstrap' ;
2018-11-10 17:14:07 +08:00
import moment from 'moment' ;
import { seafileAPI } from '../../utils/seafile-api' ;
2019-12-05 15:45:16 +08:00
import { gettext } from '../../utils/constants' ;
2018-12-07 14:58:37 +08:00
import toaster from '../../components/toast' ;
2019-06-11 14:11:10 +08:00
import EmptyTip from '../../components/empty-tip' ;
2020-09-28 10:44:12 +08:00
import ConfirmUnlinkDeviceDialog from '../../components/dialog/confirm-unlink-device' ;
2019-07-16 10:01:09 +08:00
import { Utils } from '../../utils/utils' ;
2018-11-10 17:14:07 +08:00
class Content extends Component {
render ( ) {
const { loading , errorMsg , items } = this . props . data ;
if ( loading ) {
return < span className = "loading-icon loading-tip" > < / s p a n > ;
} else if ( errorMsg ) {
return < p className = "error text-center" > { errorMsg } < / p > ;
} else {
2019-06-11 14:11:10 +08:00
const emptyTip = (
< EmptyTip >
2020-01-03 16:50:17 +08:00
< h2 > { gettext ( 'No linked devices' ) } < / h 2 >
< p > { gettext ( 'You have not accessed your files with any client (desktop or mobile) yet. Configure clients on your devices to access your data more comfortably.' ) } < / p >
2019-06-11 14:11:10 +08:00
< / E m p t y T i p >
) ;
2018-11-10 17:14:07 +08:00
const desktopThead = (
< thead >
< tr >
2019-01-31 17:37:02 +08:00
< th width = "13%" > { gettext ( 'Platform' ) } < / t h >
< th width = "30%" > { gettext ( 'Device Name' ) } < / t h >
< th width = "30%" > { gettext ( 'IP' ) } < / t h >
< th width = "17%" > { gettext ( 'Last Access' ) } < / t h >
2018-11-10 17:14:07 +08:00
< th width = "10%" > < / t h >
< / t r >
< / t h e a d >
) ;
const mobileThead = (
< thead >
< tr >
2019-07-23 14:44:47 +08:00
< th width = "92%" > < / t h >
< th width = "8%" > < / t h >
2018-11-10 17:14:07 +08:00
< / t r >
< / t h e a d >
) ;
2019-08-22 20:23:08 +08:00
const isDesktop = Utils . isDesktop ( ) ;
2019-06-11 14:11:10 +08:00
return items . length ? (
2019-08-22 20:23:08 +08:00
< table className = { ` table-hover ${ isDesktop ? '' : 'table-thead-hidden' } ` } >
{ isDesktop ? desktopThead : mobileThead }
< tbody >
{ items . map ( ( item , index ) => {
return < Item key = { index } data = { item } isDesktop = { isDesktop } / > ;
} ) }
< / t b o d y >
2018-11-10 17:14:07 +08:00
< / t a b l e >
2019-06-11 14:11:10 +08:00
) : emptyTip ;
2018-11-10 17:14:07 +08:00
}
}
}
2023-09-13 08:40:50 +08:00
Content . propTypes = {
data : PropTypes . object . isRequired ,
} ;
2018-11-10 17:14:07 +08:00
class Item extends Component {
constructor ( props ) {
super ( props ) ;
this . state = {
2019-07-23 14:44:47 +08:00
isOpMenuOpen : false , // for mobile
2020-09-28 10:44:12 +08:00
isOpIconShown : false ,
unlinked : false ,
isConfirmUnlinkDialogOpen : false
2018-11-10 17:14:07 +08:00
} ;
2019-07-23 14:44:47 +08:00
}
2018-11-10 17:14:07 +08:00
2019-07-23 14:44:47 +08:00
toggleOpMenu = ( ) => {
this . setState ( {
isOpMenuOpen : ! this . state . isOpMenuOpen
} ) ;
2023-09-13 08:40:50 +08:00
} ;
2018-11-10 17:14:07 +08:00
2019-07-23 14:44:47 +08:00
handleMouseOver = ( ) => {
2018-11-10 17:14:07 +08:00
this . setState ( {
2020-09-28 10:44:12 +08:00
isOpIconShown : true
2018-11-10 17:14:07 +08:00
} ) ;
2023-09-13 08:40:50 +08:00
} ;
2018-11-10 17:14:07 +08:00
2019-07-23 14:44:47 +08:00
handleMouseOut = ( ) => {
2018-11-10 17:14:07 +08:00
this . setState ( {
2020-09-28 10:44:12 +08:00
isOpIconShown : false
} ) ;
2023-09-13 08:40:50 +08:00
} ;
2020-09-28 10:44:12 +08:00
toggleDialog = ( ) => {
this . setState ( {
2020-11-02 13:56:35 +08:00
isConfirmUnlinkDialogOpen : ! this . state . isConfirmUnlinkDialogOpen
2018-11-10 17:14:07 +08:00
} ) ;
2023-09-13 08:40:50 +08:00
} ;
2018-11-10 17:14:07 +08:00
2019-07-23 14:44:47 +08:00
handleClick = ( e ) => {
2018-11-10 17:14:07 +08:00
e . preventDefault ( ) ;
const data = this . props . data ;
2020-09-28 10:44:12 +08:00
if ( data . is _desktop _client ) {
this . toggleDialog ( ) ;
} else {
const wipeDevice = true ;
this . unlinkDevice ( wipeDevice ) ;
}
2023-09-13 08:40:50 +08:00
} ;
2018-11-10 17:14:07 +08:00
2020-09-28 10:44:12 +08:00
unlinkDevice = ( wipeDevice ) => {
const data = this . props . data ;
seafileAPI . unlinkDevice ( data . platform , data . device _id , wipeDevice ) . then ( ( res ) => {
2018-11-10 17:14:07 +08:00
this . setState ( {
unlinked : true
} ) ;
2020-09-28 10:44:12 +08:00
let msg = gettext ( 'Successfully unlinked %(name)s.' ) ;
msg = msg . replace ( '%(name)s' , data . device _name ) ;
toaster . success ( msg ) ;
2018-11-10 17:14:07 +08:00
} ) . catch ( ( error ) => {
2019-07-16 10:01:09 +08:00
let errMessage = Utils . getErrorMsg ( error ) ;
toaster . danger ( errMessage ) ;
2018-11-10 17:14:07 +08:00
} ) ;
2023-09-13 08:40:50 +08:00
} ;
2018-11-10 17:14:07 +08:00
render ( ) {
if ( this . state . unlinked ) {
return null ;
}
const data = this . props . data ;
2018-12-28 11:12:24 +08:00
let opClasses = 'sf2-icon-delete unlink-device action-icon' ;
2020-09-28 10:44:12 +08:00
opClasses += this . state . isOpIconShown ? '' : ' invisible' ;
2018-11-10 17:14:07 +08:00
const desktopItem = (
2021-09-24 13:35:25 +08:00
< tr onMouseOver = { this . handleMouseOver } onMouseOut = { this . handleMouseOut } onFocus = { this . handleMouseOver } >
2018-11-10 17:14:07 +08:00
< td > { data . platform } < / t d >
< td > { data . device _name } < / t d >
< td > { data . last _login _ip } < / t d >
< td > { moment ( data . last _accessed ) . fromNow ( ) } < / t d >
< td >
2021-09-24 13:35:25 +08:00
< a href = "#" className = { opClasses } title = { gettext ( 'Unlink' ) } role = "button" aria - label = { gettext ( 'Unlink' ) } onClick = { this . handleClick } > < / a >
2018-11-10 17:14:07 +08:00
< / t d >
< / t r >
) ;
const mobileItem = (
< tr >
< td >
2019-07-23 14:44:47 +08:00
{ data . device _name } < br / >
< span className = "item-meta-info" > { data . last _login _ip } < / s p a n >
< span className = "item-meta-info" > { moment ( data . last _accessed ) . fromNow ( ) } < / s p a n >
< span className = "item-meta-info" > { data . platform } < / 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"
2024-01-03 18:08:24 +08:00
title = { gettext ( 'More operations' ) }
aria - label = { gettext ( 'More operations' ) }
2019-07-23 14:44:47 +08:00
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" >
< DropdownItem className = "mobile-menu-item" onClick = { this . handleClick } > { gettext ( 'Unlink' ) } < / 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 >
2018-11-10 17:14:07 +08:00
< / t d >
< / t r >
) ;
2020-09-28 10:44:12 +08:00
return (
< React . Fragment >
{ this . props . isDesktop ? desktopItem : mobileItem }
{ this . state . isConfirmUnlinkDialogOpen &&
< ConfirmUnlinkDeviceDialog
executeOperation = { this . unlinkDevice }
2020-11-02 13:56:35 +08:00
toggleDialog = { this . toggleDialog }
2020-09-28 10:44:12 +08:00
/ >
}
< / R e a c t . F r a g m e n t >
) ;
2018-11-10 17:14:07 +08:00
}
}
2023-09-13 08:40:50 +08:00
Item . propTypes = {
isDesktop : PropTypes . bool . isRequired ,
data : PropTypes . object . isRequired ,
} ;
2018-11-10 17:14:07 +08:00
class LinkedDevices extends Component {
constructor ( props ) {
super ( props ) ;
this . state = {
loading : true ,
errorMsg : '' ,
items : [ ]
} ;
}
componentDidMount ( ) {
seafileAPI . listLinkedDevices ( ) . then ( ( res ) => {
this . setState ( {
loading : false ,
items : res . data
} ) ;
} ) . 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
} ) ;
2018-11-10 17:14:07 +08:00
} ) ;
}
render ( ) {
return (
2018-11-26 17:53:18 +08:00
< div className = "main-panel-center" >
< div className = "cur-view-container" id = "linked-devices" >
< div className = "cur-view-path" >
< h3 className = "sf-heading" > { gettext ( 'Linked Devices' ) } < / h 3 >
< / d i v >
< div className = "cur-view-content" >
< Content data = { this . state } / >
2018-11-26 14:00:32 +08:00
< / d i v >
2018-11-10 17:14:07 +08:00
< / d i v >
2018-11-26 17:53:18 +08:00
< / d i v >
2018-11-10 17:14:07 +08:00
) ;
}
}
export default LinkedDevices ;