2020import { Button } from 'primereact/button'
2121import { Chart } from 'primereact/chart'
2222import { Dialog } from 'primereact/dialog'
23+ import { InputSwitch } from 'primereact/inputswitch'
2324import { ListBox } from 'primereact/listbox'
2425import { Steps } from 'primereact/steps'
2526import { Toast } from 'primereact/toast'
@@ -38,6 +39,8 @@ import Pica from 'pica'
3839import ImageBlobReduce from 'image-blob-reduce'
3940import { notebookEditPseudoAppid } from './blackboard/jupyterhublet'
4041import { maybeUseLatex , convertToLatex } from './misc/latex'
42+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
43+ import { faUser } from '@fortawesome/free-solid-svg-icons'
4144
4245export class FailsBoard extends FailsBasis {
4346 constructor ( props ) {
@@ -57,6 +60,7 @@ export class FailsBoard extends FailsBasis {
5760 this . state . pictIndex = 0
5861 this . state . availscreens = [ ]
5962 this . state . welcomeMessageSend = 0
63+ this . state . pollLimitedGroup = false
6064
6165 this . availscreensmenu = React . createRef ( )
6266
@@ -82,6 +86,7 @@ export class FailsBoard extends FailsBasis {
8286 this . blockChat = this . blockChat . bind ( this )
8387 this . allowVideoquestion = this . allowVideoquestion . bind ( this )
8488 this . onStartPoll = this . onStartPoll . bind ( this )
89+ this . onSelParticipantsPoll = this . onSelParticipantsPoll . bind ( this )
8590 this . onStartSelPoll = this . onStartSelPoll . bind ( this )
8691 this . onFinishSelPoll = this . onFinishSelPoll . bind ( this )
8792 this . ipynbTemplate = this . ipynbTemplate . bind ( this )
@@ -201,14 +206,19 @@ export class FailsBoard extends FailsBasis {
201206 pollsel : undefined ,
202207 pollshowres : false ,
203208 pollvotes : { } ,
204- pollballots : [ ]
209+ pollballots : [ ] ,
210+ pollparticipantsServer : undefined
205211 } )
206212 } )
207213
208214 notepadsocket . on ( 'finishPoll' , ( data ) => {
209215 console . log ( 'finishpoll incoming' , data )
210216
211- this . setState ( { polltask : 2 , pollsel : undefined } )
217+ this . setState ( {
218+ polltask : 2 ,
219+ pollsel : undefined ,
220+ pollparticipantsServer : data . participants
221+ } )
212222 } )
213223
214224 notepadsocket . on ( 'castvote' , ( data ) => {
@@ -530,13 +540,46 @@ export class FailsBoard extends FailsBasis {
530540 this . setState ( { pollcoll : ret } )
531541 }
532542
543+ onSelParticipantsPoll ( ) {
544+ // if there are no participants set, we set some
545+ if ( ! this . state . pollparticipants ) {
546+ let pollparticipants = [ ]
547+ if ( this . state . identobj ?. idents ) {
548+ pollparticipants = this . state . identobj ?. idents
549+ . filter ( ( el ) => el . purpose === 'notes' )
550+ . map ( ( { userhash } ) => userhash )
551+ }
552+ this . setState ( { polltask : 0.5 , pollparticipants } )
553+ return
554+ } else {
555+ // we should filter out unavailable userhashes
556+ let pollparticipants = [ ]
557+ if ( this . state . identobj ?. idents ) {
558+ const hashes = this . state . identobj ?. idents
559+ . filter ( ( el ) => el . purpose === 'notes' )
560+ . map ( ( { userhash } ) => userhash )
561+
562+ pollparticipants = this . state . pollparticipants . filter ( ( el ) =>
563+ hashes . includes ( el )
564+ )
565+ this . setState ( { polltask : 0.5 , pollparticipants } )
566+ return
567+ }
568+ }
569+ this . setState ( { polltask : 0.5 } )
570+ }
571+
533572 onStartSelPoll ( ) {
534573 if ( ! this . state . pollcoll ) return
535574 const polfind = this . state . pollcoll . find (
536575 ( el ) => el . id === this . state . pollsel
537576 )
538577 this . socket . simpleEmit ( 'startPoll' , {
539- poll : polfind
578+ poll : polfind ,
579+ limited : this . state . pollLimitedGroup ,
580+ participants : this . state . pollLimitedGroup
581+ ? this . state . pollparticipants
582+ : undefined
540583 } )
541584 }
542585
@@ -664,6 +707,25 @@ export class FailsBoard extends FailsBasis {
664707 const pollanswers = [ ]
665708 let numballots = 0
666709
710+ let pollpotparticipants = [ ]
711+ if ( this . state . polltask === 0.5 ) {
712+ if ( this . state . identobj ?. idents ) {
713+ pollpotparticipants = this . state . identobj ?. idents
714+ . filter ( ( el ) => el . purpose === 'notes' )
715+ . map ( ( { displayname, userhash, purpose } ) => ( {
716+ displayname,
717+ userhash,
718+ purpose
719+ } ) )
720+ pollpotparticipants = pollpotparticipants . filter (
721+ ( el , index ) =>
722+ pollpotparticipants . findIndex (
723+ ( el2 ) => el2 . userhash === el . userhash
724+ ) === index
725+ )
726+ }
727+ }
728+
667729 if ( this . state . polltask === 1 || this . state . polltask === 2 ) {
668730 const tpollres = this . calcPollresults ( )
669731 const tpolldata = tpollres . data
@@ -705,6 +767,14 @@ export class FailsBoard extends FailsBasis {
705767 { label : 'Poll' } ,
706768 { label : 'Results' }
707769 ]
770+ let pollActiveIndex = this . state . polltask
771+ if ( this . state . pollLimitedGroup ) {
772+ pollitems . splice ( 1 , 0 , { label : 'Select' } )
773+ if ( pollActiveIndex > 0.5 ) {
774+ pollActiveIndex ++
775+ }
776+ if ( pollActiveIndex === 0.5 ) pollActiveIndex = 1
777+ }
708778 const blackbackground =
709779 typeof this . state . blackbackground === 'undefined'
710780 ? true
@@ -886,12 +956,16 @@ export class FailsBoard extends FailsBasis {
886956 < Dialog
887957 header = 'Poll'
888958 visible = { typeof this . state . polltask !== 'undefined' }
889- closable = { this . state . polltask === 2 || this . state . polltask === 0 }
959+ closable = {
960+ this . state . polltask === 2 ||
961+ this . state . polltask === 0 ||
962+ this . state . polltask === 0.5
963+ }
890964 onHide = { ( ) => {
891965 this . setState ( { polltask : undefined , pollsel : undefined } )
892966 } }
893967 >
894- < Steps model = { pollitems } activeIndex = { this . state . polltask } />
968+ < Steps model = { pollitems } activeIndex = { pollActiveIndex } />
895969 { this . state . polltask === 0 && (
896970 < React . Fragment >
897971 < ListBox
@@ -903,12 +977,88 @@ export class FailsBoard extends FailsBasis {
903977 ( el ) => el . children && el . children . length > 1
904978 )
905979 }
980+ style = { {
981+ maxHeight : '60vh' ,
982+ minWidth : '30vw' ,
983+ overflow : 'auto'
984+ } }
906985 optionLabel = 'name'
907986 optionValue = 'id'
908987 itemTemplate = { this . pollTemplate }
909988 onChange = { ( e ) => this . setState ( { pollsel : e . value } ) }
910989 />
911- { this . state . pollsel && (
990+ < div className = 'p-d-flex p-ai-center' >
991+ < InputSwitch
992+ id = 'switchPollLG'
993+ checked = { this . state . pollLimitedGroup }
994+ onChange = { ( e ) => this . setState ( { pollLimitedGroup : e . value } ) }
995+ />
996+ < div className = 'p-ml-2' >
997+ < label
998+ htmlFor = 'limitedGroupSwitch'
999+ className = 'p-d-block p-text-bold'
1000+ >
1001+ Limited Group Voting
1002+ </ label >
1003+ { /* p-text-secondary uses the theme's standard muted color */ }
1004+ < small className = 'p-text-secondary' >
1005+ { this . state . pollLimitedGroup
1006+ ? 'Only selected participants.'
1007+ : 'All participants.' }
1008+ </ small >
1009+ </ div >
1010+ </ div >
1011+ { this . state . pollsel && ! this . state . pollLimitedGroup && (
1012+ < Button
1013+ label = 'Start poll'
1014+ icon = 'pi pi-chart-bar'
1015+ className = 'p-m-2'
1016+ onClick = { this . onStartSelPoll }
1017+ />
1018+ ) }
1019+ { this . state . pollsel && this . state . pollLimitedGroup && (
1020+ < Button
1021+ label = 'Select participants'
1022+ icon = 'pi pi-id-card'
1023+ className = 'p-m-2'
1024+ onClick = { this . onSelParticipantsPoll }
1025+ />
1026+ ) }
1027+ </ React . Fragment >
1028+ ) }
1029+ { this . state . polltask === 0.5 && (
1030+ < React . Fragment >
1031+ < div className = 'p-d-flex p-ai-center' >
1032+ < div className = 'p-mr-2' >
1033+ < h3 > Select voters</ h3 >
1034+ </ div >
1035+ </ div >
1036+ { pollpotparticipants . length === 0 && < b > No voters present!</ b > }
1037+ { pollpotparticipants . length > 0 && (
1038+ < ListBox
1039+ optionLabel = 'displayname'
1040+ optionValue = 'userhash'
1041+ options = { pollpotparticipants }
1042+ style = { {
1043+ maxHeight : '60vh' ,
1044+ minHeight : '10vh' ,
1045+ minWidth : '30vw' ,
1046+ overflowY : 'auto'
1047+ } }
1048+ itemTemplate = { ( element ) => (
1049+ < span >
1050+ < FontAwesomeIcon icon = { faUser } /> { element . displayname }
1051+ </ span >
1052+ ) }
1053+ multiple
1054+ value = { this . state . pollparticipants }
1055+ onChange = { ( ev ) =>
1056+ this . setState ( { pollparticipants : ev . value } )
1057+ }
1058+ />
1059+ ) }
1060+ { ( this . state . pollparticipants ?. length > 0 ||
1061+ true ) /* just for debugging */ && (
9121062 < Button
9131063 label = 'Start poll'
9141064 icon = 'pi pi-chart-bar'
@@ -987,7 +1137,10 @@ export class FailsBoard extends FailsBasis {
9871137 {
9881138 ballots : this . state . pollballots ,
9891139 poll : this . state . curpoll ,
990- votes : this . state . pollvotes
1140+ votes : this . state . pollvotes ,
1141+ eglibleVoters :
1142+ this . state . pollparticipantsServer ||
1143+ 'unrestricted'
9911144 } ,
9921145 null ,
9931146 2
0 commit comments