2020-06-04 14:01:31 +02:00
import React , { useEffect , useState } from 'react' ;
2020-05-17 11:09:16 +02:00
import { CopyToClipboard } from 'react-copy-to-clipboard' ;
2024-05-15 17:39:59 +02:00
import { api , Recording , Room , Restriction , Access , Permission } from '../Common/Api' ;
2020-06-16 16:54:50 +02:00
import EditRoom from './EditRoom' ;
2020-05-17 11:09:16 +02:00
import RecordingRow from './RecordingRow' ;
2020-06-04 14:01:31 +02:00
import EditableValue from './EditableValue' ;
2021-01-23 11:41:58 +01:00
import { AccessOptions } from '../Common/Translation' ;
2020-04-26 11:36:41 +02:00
type Props = {
2020-05-17 11:09:16 +02:00
room : Room ;
2020-08-27 17:21:34 +02:00
restriction? : Restriction ;
2020-06-04 14:01:31 +02:00
updateRoom : ( room : Room ) = > Promise < void > ;
2020-05-17 11:09:16 +02:00
deleteRoom : ( id : number ) = > void ;
2022-03-25 16:47:19 +01:00
cloneRoom : ( room : Room ) = > void ;
2020-04-26 11:36:41 +02:00
}
2020-05-17 11:09:16 +02:00
type RecordingsNumberProps = {
recordings : null | Recording [ ] ;
showRecordings : boolean ;
setShowRecordings : ( showRecordings : boolean ) = > void ;
}
const RecordingsNumber : React.FC < RecordingsNumberProps > = ( { recordings , showRecordings , setShowRecordings } ) = > {
if ( recordings === null ) {
return < span className = "icon icon-loading-small icon-visible" > < / span > ;
}
if ( recordings . length > 0 ) {
return (
< a onClick = { ( ) = > setShowRecordings ( ! showRecordings ) } >
2021-08-04 16:47:30 +02:00
< span className = 'sort_arrow' > { showRecordings ? '▾' : '▸' } < / span > { recordings . length }
2020-05-17 11:09:16 +02:00
< / a >
) ;
}
return < span > 0 < / span > ;
} ;
2020-04-26 11:36:41 +02:00
const RoomRow : React.FC < Props > = ( props ) = > {
2020-05-17 11:09:16 +02:00
const [ recordings , setRecordings ] = useState < Recording [ ] | null > ( null ) ;
const [ showRecordings , setShowRecordings ] = useState < boolean > ( false ) ;
2020-04-27 16:47:32 +02:00
const room = props . room ;
2020-05-17 11:09:16 +02:00
const areRecordingsLoaded = recordings !== null ;
useEffect ( ( ) = > {
if ( areRecordingsLoaded ) {
return ;
}
2020-04-26 11:36:41 +02:00
2020-05-17 11:09:16 +02:00
api . getRecordings ( room . uid ) . then ( recordings = > {
setRecordings ( recordings ) ;
} ) . catch ( err = > {
console . warn ( 'Could not request recordings: ' + room . uid , err ) ;
setRecordings ( [ ] ) ;
} ) ;
} , [ areRecordingsLoaded ] ) ;
function updateRoom ( key : string , value : string | boolean | number ) {
2020-06-04 14:01:31 +02:00
return props . updateRoom ( {
2020-04-27 16:47:32 +02:00
. . . props . room ,
[ key ] : value ,
} ) ;
}
2020-04-26 11:36:41 +02:00
2020-04-27 16:47:32 +02:00
function deleteRow ( ev : MouseEvent ) {
ev . preventDefault ( ) ;
2020-04-26 11:36:41 +02:00
2020-04-27 16:47:32 +02:00
OC . dialogs . confirm (
2021-04-12 11:33:27 +02:00
t ( 'bbb' , 'Are you sure you want to delete "{name}"? This operation cannot be undone.' , { name : room.name } ) ,
2020-04-27 16:47:32 +02:00
t ( 'bbb' , 'Delete "{name}"?' , { name : room.name } ) ,
confirmed = > {
if ( confirmed ) {
props . deleteRoom ( room . id ) ;
}
} ,
true
) ;
}
2020-04-26 11:36:41 +02:00
2020-05-17 16:51:26 +02:00
function storeRoom() {
OC . dialogs . filepicker ( t ( 'bbb' , 'Select target folder' ) , ( path : string ) = > {
api . storeRoom ( room , path ) . then ( ( filename ) = > {
OC . dialogs . info (
t ( 'bbb' , 'Room URL was stored in "{path}" as "{filename}".' , { path : path + '/' , filename } ) ,
t ( 'bbb' , 'Link stored' ) ,
( ) = > undefined ,
) ;
} ) . catch ( err = > {
console . warn ( 'Could not store room' , err ) ;
OC . dialogs . alert (
t ( 'bbb' , 'URL to room could not be stored.' ) ,
t ( 'bbb' , 'Error' ) ,
( ) = > undefined
) ;
} ) ;
} , undefined , 'httpd/unix-directory' ) ;
}
2020-05-17 11:09:16 +02:00
function storeRecording ( recording : Recording ) {
OC . dialogs . filepicker ( t ( 'bbb' , 'Select target folder' ) , ( path : string ) = > {
2020-05-17 16:51:26 +02:00
api . storeRecording ( recording , path ) . then ( ( filename ) = > {
2020-05-17 11:09:16 +02:00
OC . dialogs . info (
2020-05-17 16:51:26 +02:00
t ( 'bbb' , 'URL to presentation was stored in "{path}" as "{filename}".' , { path : path + '/' , filename } ) ,
t ( 'bbb' , 'Link stored' ) ,
2020-05-17 11:09:16 +02:00
( ) = > undefined ,
) ;
} ) . catch ( err = > {
console . warn ( 'Could not store recording' , err ) ;
OC . dialogs . alert (
t ( 'bbb' , 'URL to presentation could not be stored.' ) ,
t ( 'bbb' , 'Error' ) ,
( ) = > undefined
) ;
} ) ;
} , undefined , 'httpd/unix-directory' ) ;
}
function deleteRecording ( recording : Recording ) {
OC . dialogs . confirm (
2021-04-12 11:33:27 +02:00
t ( 'bbb' , 'Are you sure you want to delete the recording from "{startDate}"? This operation cannot be undone.' , { startDate : ( new Date ( recording . startTime ) ) . toLocaleString ( ) } ) ,
2020-05-17 11:09:16 +02:00
t ( 'bbb' , 'Delete?' ) ,
confirmed = > {
if ( confirmed ) {
api . deleteRecording ( recording . id ) . then ( success = > {
if ( ! success ) {
OC . dialogs . info (
t ( 'bbb' , 'Could not delete record' ) ,
t ( 'bbb' , 'Error' ) ,
( ) = > undefined ,
) ;
return ;
}
if ( recordings === null ) {
return ;
}
setRecordings ( recordings . filter ( r = > r . id !== recording . id ) ) ;
} ) . catch ( err = > {
console . warn ( 'Could not delete recording' , err ) ;
OC . dialogs . info (
t ( 'bbb' , 'Could not delete record' ) ,
t ( 'bbb' , 'Server error' ) ,
( ) = > undefined ,
) ;
} ) ;
}
} ,
true
) ;
}
2024-05-29 17:09:06 +02:00
function publishRecording ( recording : Recording , publish : boolean ) {
api . publishRecording ( recording . id , publish ) . then ( success = > {
if ( recordings === null ) {
return ;
}
setRecordings ( recordings . map ( recordItem = > {
if ( recordItem . id === recording . id ) {
recordItem . published = success ;
recordItem . state = success ? 'published' : 'unpublished' ;
}
return recordItem ;
} ) ) ;
} ) . catch ( err = > {
console . warn ( 'Could not modify publishing state' , err ) ;
OC . dialogs . info (
t ( 'bbb' , 'Could not modify publishing state' ) ,
t ( 'bbb' , 'Server error' ) ,
( ) = > undefined ,
) ;
} ) ;
}
2020-09-08 17:13:21 +02:00
function accessToIcon ( access : string ) {
switch ( access ) {
case Access . Public :
2021-01-23 11:41:58 +01:00
return < span className = "icon icon-visible icon-link" title = { AccessOptions [ access ] } / > ;
2020-09-08 17:13:21 +02:00
case Access . Password :
2021-01-23 11:41:58 +01:00
return < span className = "icon icon-visible icon-password" title = { AccessOptions [ access ] } / > ;
2020-09-08 17:13:21 +02:00
case Access . Internal :
2021-01-23 11:41:58 +01:00
return < span className = "icon icon-visible icon-group" title = { AccessOptions [ access ] } / > ;
2020-09-08 17:13:21 +02:00
case Access . InternalRestricted :
2021-01-23 11:41:58 +01:00
return < span className = "icon icon-visible icon-user" title = { AccessOptions [ access ] } / > ;
2020-09-08 17:13:21 +02:00
case Access . WaitingRoom :
2021-01-23 11:41:58 +01:00
return < span className = "icon icon-visible icon-timezone" title = { AccessOptions [ access ] } / > ;
2020-09-08 17:13:21 +02:00
}
return < span > < / span > ;
}
2024-05-29 17:09:06 +02:00
function edit ( field : string , type : 'text' | 'number' = 'text' , canEdit = true , options ? ) {
2024-05-15 17:39:59 +02:00
return canEdit ?
< EditableValue field = { field } value = { room [ field ] } setValue = { updateRoom } type = { type } options = { options } / >
:
< span > { room [ field ] } < / span > ;
2020-04-27 16:47:32 +02:00
}
2020-04-26 11:36:41 +02:00
2022-03-25 16:47:19 +01:00
function cloneRow() {
props . cloneRoom ( { . . . props . room } ) ;
}
2020-06-17 10:56:28 +02:00
const avatarUrl = OC . generateUrl ( '/avatar/' + encodeURIComponent ( room . userId ) + '/' + 24 , {
user : room.userId ,
size : 24 ,
requesttoken : OC.requestToken ,
} ) ;
2020-08-27 17:21:34 +02:00
const maxParticipantsLimit = props . restriction ? . maxParticipants || - 1 ;
const minParticipantsLimit = ( props . restriction ? . maxParticipants || - 1 ) < 1 ? 0 : 1 ;
2024-05-15 17:39:59 +02:00
const adminRoom = room . permission === null || room . permission === Permission . Admin ;
2020-04-27 16:47:32 +02:00
return (
2020-05-17 11:09:16 +02:00
< >
< tr className = { showRecordings ? 'selected-row' : '' } >
2022-03-16 16:12:33 +01:00
< td className = "start" >
2024-07-30 14:16:29 +02:00
< a href = { api . getRoomUrl ( room ) }
2024-08-15 15:42:55 +02:00
className = { 'button ' + ( room . running ? 'success' : 'primary' ) }
2024-07-30 14:16:29 +02:00
target = "_blank"
rel = "noopener noreferrer"
title = { t ( 'bbb' , 'Open room' ) } >
2022-03-16 16:12:33 +01:00
{ room . running ? t ( 'bbb' , 'Join' ) : t ( 'bbb' , 'Start' ) }
2021-01-23 11:36:59 +01:00
< / a >
2020-05-22 13:20:47 +02:00
< / td >
2020-05-17 11:09:16 +02:00
< td className = "share icon-col" >
2020-09-23 09:13:26 +02:00
< CopyToClipboard text = { api . getRoomUrl ( room ) } >
2021-01-23 11:41:58 +01:00
< button className = "action-item copy-to-clipboard" title = { t ( 'bbb' , 'Copy to clipboard' ) } >
2021-01-23 11:36:59 +01:00
< span className = "icon icon-clippy icon-visible" > < / span >
< / button >
2020-05-17 11:09:16 +02:00
< / CopyToClipboard >
< / td >
2020-05-17 16:51:26 +02:00
< td className = "store icon-col" >
2021-01-23 11:41:58 +01:00
< button className = "action-item" onClick = { ( ) = > storeRoom ( ) } title = { t ( 'bbb' , 'Save as file' ) } >
2021-01-23 11:36:59 +01:00
< span className = "icon icon-add-shortcut icon-visible" > < / span >
< / button >
2020-05-17 16:51:26 +02:00
< / td >
2020-05-17 11:09:16 +02:00
< td className = "name" >
2024-05-15 17:39:59 +02:00
{ edit ( 'name' , 'text' , adminRoom ) }
2020-05-17 11:09:16 +02:00
< / td >
2020-06-17 11:08:31 +02:00
< td className = "bbb-shrink" >
2020-06-17 10:56:28 +02:00
{ room . userId !== OC . currentUser && < img src = { avatarUrl } alt = "Avatar" className = "bbb-avatar" / > }
2020-09-08 17:01:50 +02:00
{ ( room . userId === OC . currentUser && room . shared ) && < span className = "icon icon-shared icon-visible" / > }
2020-06-17 10:56:28 +02:00
< / td >
2020-09-08 17:13:21 +02:00
< td >
{ accessToIcon ( room . access ) }
< / td >
2020-06-17 11:08:31 +02:00
< td className = "max-participants bbb-shrink" >
2024-05-15 17:39:59 +02:00
{ edit ( 'maxParticipants' , 'number' , adminRoom , { min : minParticipantsLimit , max : maxParticipantsLimit < 0 ? undefined : maxParticipantsLimit } ) }
2020-05-17 11:09:16 +02:00
< / td >
2024-05-15 17:39:59 +02:00
{ adminRoom &&
2020-06-17 11:08:31 +02:00
< td className = "record bbb-shrink" >
2024-08-15 15:42:55 +02:00
< input id = { 'bbb-record-' + room . id } type = "checkbox" className = "checkbox" disabled = { ! props . restriction ? . allowRecording } checked = { room . record } onChange = { ( event ) = > updateRoom ( 'record' , event . target . checked ) } / >
< label htmlFor = { 'bbb-record-' + room . id } > < / label >
2020-05-17 11:09:16 +02:00
< / td >
2024-05-15 17:39:59 +02:00
}
{ ! adminRoom &&
< td className = "record bbb-shrink" >
2024-05-29 17:09:06 +02:00
< span className = { 'icon ' + ( room . record ? 'icon-checkmark' : 'icon-close' ) + ' icon-visible' } > < / span >
2024-05-15 17:39:59 +02:00
< / td >
}
< td className = "bbb-shrink" >
2024-05-29 17:28:49 +02:00
{ < RecordingsNumber recordings = { recordings } showRecordings = { showRecordings } setShowRecordings = { setShowRecordings } / > }
2020-06-04 14:01:31 +02:00
< / td >
2022-03-25 16:47:19 +01:00
< td className = "clone icon-col" >
2024-07-30 14:16:29 +02:00
< button
className = "action-item"
onClick = { cloneRow }
title = { t ( 'bbb' , 'Clone room' ) } >
2022-03-25 16:47:19 +01:00
< span className = "icon icon-template-add icon-visible" > < / span >
< / button >
< / td >
2024-05-15 17:39:59 +02:00
< td className = "edit icon-col" >
{ adminRoom &&
< EditRoom room = { props . room } restriction = { props . restriction } updateProperty = { updateRoom } / >
}
< / td >
2020-05-17 11:09:16 +02:00
< td className = "remove icon-col" >
2024-05-15 17:39:59 +02:00
{ adminRoom &&
2021-01-23 11:36:59 +01:00
< button className = "action-item" onClick = { deleteRow as any } title = { t ( 'bbb' , 'Delete' ) } >
< span className = "icon icon-delete icon-visible" > < / span >
< / button >
2024-05-15 17:39:59 +02:00
}
2020-05-17 11:09:16 +02:00
< / td >
< / tr >
{ showRecordings && < tr className = "recordings-row" >
2020-11-24 11:45:06 +01:00
< td colSpan = { 11 } >
2020-05-17 11:09:16 +02:00
< table >
< tbody >
2024-05-29 17:09:06 +02:00
{ recordings ? . sort ( ( r1 , r2 ) = > r1 . startTime - r2 . startTime ) . map ( recording = > < RecordingRow key = { recording . id } isAdmin = { adminRoom } recording = { recording } deleteRecording = { deleteRecording } storeRecording = { storeRecording } publishRecording = { publishRecording } / > ) }
2020-05-17 11:09:16 +02:00
< / tbody >
< / table >
< / td >
< / tr > }
< / >
2020-04-27 16:50:45 +02:00
) ;
} ;
2020-04-26 11:36:41 +02:00
2020-05-19 12:26:53 +02:00
export default RoomRow ;