11import classNames from 'classnames' ;
22import { Link } from 'react-router-dom' ;
3- import { Button } from '@/components/Button' ;
43import { Container } from '@/components/Container' ;
54import { useCompetitionRemoteControl } from '@/hooks/useCompetitionRemoteControl' ;
65
@@ -9,6 +8,10 @@ interface NotifyCompRemoteBarProps {
98}
109
1110const groupLabel = ( count : number ) => `${ count } active activit${ count === 1 ? 'y' : 'ies' } ` ;
11+ const iconButtonClassName =
12+ 'flex h-8 w-8 items-center justify-center rounded-full text-base leading-none text-gray-200 hover-transition hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-40 md:h-9 md:w-9 md:text-lg' ;
13+ const primaryButtonClassName =
14+ 'flex h-9 w-9 items-center justify-center rounded-full bg-white text-base leading-none text-gray-950 hover-transition hover:scale-105 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:scale-100 md:h-10 md:w-10 md:text-lg' ;
1215
1316export function NotifyCompRemoteBar ( { competitionId } : NotifyCompRemoteBarProps ) {
1417 const remote = useCompetitionRemoteControl ( { competitionId } ) ;
@@ -25,50 +28,105 @@ export function NotifyCompRemoteBar({ competitionId }: NotifyCompRemoteBarProps)
2528 : remote . nextGroup
2629 ? `Next: ${ remote . nextGroup . name } `
2730 : 'Remote overview' ;
31+ const completedGroups = remote . activityGroups . filter ( ( group ) => group . status === 'done' ) . length ;
32+ const progress =
33+ remote . activityGroups . length > 0 ? ( completedGroups / remote . activityGroups . length ) * 100 : 0 ;
2834
2935 const runSwitch = ( direction : 'previous' | 'next' ) => {
3036 const group = direction === 'previous' ? remote . previousGroup : remote . nextGroup ;
3137 void remote . switchToGroup ( group ) ;
3238 } ;
39+ const togglePlayback = ( ) => {
40+ if ( remote . activeGroups . length > 0 ) {
41+ void Promise . all ( remote . activeGroups . map ( ( group ) => remote . stopGroup ( group ) ) ) ;
42+ return ;
43+ }
44+
45+ void remote . switchToGroup ( remote . nextGroup ) ;
46+ } ;
3347
3448 return (
3549 < nav
3650 aria-label = "Remote control"
37- className = "z-20 w-full border-t border-tertiary-weak bg-panel shadow-md shadow-tertiary-dark print:hidden" >
38- < Container className = "flex-row items-center gap-2 px-2 py-2" >
39- < Button
40- type = "button"
41- variant = "light"
42- className = "min-w-[76px] justify-center"
43- disabled = { remote . isSaving || ! remote . previousGroup }
44- onClick = { ( ) => runSwitch ( 'previous' ) } >
45- Back
46- </ Button >
47-
51+ className = "z-20 w-full border-t border-gray-800 bg-black text-white shadow-md print:hidden" >
52+ < Container className = "grid grid-cols-[minmax(0,1fr)_minmax(176px,1.2fr)_auto] items-center gap-2 px-2 py-2 md:grid-cols-[minmax(0,1fr)_minmax(220px,1.4fr)_minmax(0,1fr)] md:gap-4" >
4853 < Link
4954 to = { `/competitions/${ competitionId } /remote` }
50- className = { classNames (
51- 'min-w-0 flex-1 rounded border border-tertiary-weak px-3 py-2 hover-transition hover:bg-gray-100 dark:hover:bg-gray-700' ,
52- {
53- 'opacity-60' : remote . isLoading ,
54- } ,
55- ) } >
56- < div className = "flex min-w-0 flex-col" >
57- < span className = "truncate type-label" > { title } </ span >
58- < span className = "truncate type-meta" >
59- { remote . error ? `Remote error: ${ remote . error } ` : detail }
60- </ span >
55+ className = "flex min-w-0 items-center gap-2 rounded px-1 py-1 hover-transition hover:bg-gray-900" >
56+ < div className = "flex h-12 w-12 shrink-0 items-center justify-center rounded bg-green-600 text-sm font-bold text-white" >
57+ RC
58+ </ div >
59+ < div className = "min-w-0 space-y-1" >
60+ < div className = "truncate text-sm font-medium text-white" > { title } </ div >
61+ < div className = "truncate text-xs text-gray-400" > { detail } </ div >
6162 </ div >
6263 </ Link >
6364
64- < Button
65- type = "button"
66- variant = "green"
67- className = "min-w-[76px] justify-center"
68- disabled = { remote . isSaving || ! remote . nextGroup }
69- onClick = { ( ) => runSwitch ( 'next' ) } >
70- Next
71- </ Button >
65+ < div className = "min-w-0 space-y-1" >
66+ < div className = "flex items-center justify-center gap-2" >
67+ < button
68+ type = "button"
69+ className = { iconButtonClassName }
70+ disabled = { remote . isSaving || ! remote . previousGroup }
71+ aria-label = "Go back to previous remote activity"
72+ onClick = { ( ) => runSwitch ( 'previous' ) } >
73+ ⏮
74+ </ button >
75+
76+ < button
77+ type = "button"
78+ className = { primaryButtonClassName }
79+ disabled = { remote . isSaving || ( remote . activeGroups . length === 0 && ! remote . nextGroup ) }
80+ aria-label = {
81+ remote . activeGroups . length > 0
82+ ? 'Stop current remote activities'
83+ : 'Start next remote activity'
84+ }
85+ onClick = { togglePlayback } >
86+ { remote . activeGroups . length > 0 ? < > ■</ > : < > ▶</ > }
87+ </ button >
88+
89+ < button
90+ type = "button"
91+ className = { iconButtonClassName }
92+ disabled = { remote . isSaving || ! remote . nextGroup }
93+ aria-label = "Go to next remote activity"
94+ onClick = { ( ) => runSwitch ( 'next' ) } >
95+ ⏭
96+ </ button >
97+ </ div >
98+
99+ < Link
100+ to = { `/competitions/${ competitionId } /remote` }
101+ className = { classNames ( 'grid grid-cols-[44px_minmax(0,1fr)_44px] items-center gap-2' , {
102+ 'opacity-60' : remote . isLoading ,
103+ } ) } >
104+ < span className = "text-right text-xs tabular-nums text-gray-400" > { completedGroups } </ span >
105+ < span className = "h-1 overflow-hidden rounded-full bg-gray-700" >
106+ < span
107+ className = "block h-full rounded-full bg-white"
108+ style = { { width : `${ progress } %` } }
109+ />
110+ </ span >
111+ < span className = "text-xs tabular-nums text-gray-400" >
112+ { remote . activityGroups . length }
113+ </ span >
114+ </ Link >
115+ </ div >
116+
117+ < div className = "flex min-w-0 items-center justify-end gap-3" >
118+ < div className = "hidden min-w-0 text-right sm:block" >
119+ < div className = "truncate text-sm font-medium text-white" > Remote</ div >
120+ < div className = "truncate text-xs text-gray-400" >
121+ { remote . error ? remote . error : remote . isSaving ? 'Syncing' : 'Ready' }
122+ </ div >
123+ </ div >
124+ < Link
125+ to = { `/competitions/${ competitionId } /remote` }
126+ className = "rounded border border-green-500 px-3 py-1 text-sm font-medium text-green-400 hover-transition hover:bg-green-950" >
127+ Open
128+ </ Link >
129+ </ div >
72130 </ Container >
73131 </ nav >
74132 ) ;
0 commit comments