1
- import { EuiFieldText , useEuiTheme } from '@elastic/eui' ;
2
- import { animated , useSpring } from '@react-spring/web' ;
1
+ import { EuiFieldText } from '@elastic/eui' ;
3
2
import type { KeyboardEvent , KeyboardEventHandler , ReactNode } from 'react' ;
4
- import { useCallback , useEffect , useRef , useState } from 'react' ;
5
- import type { GameEvent } from '../../../common/game/types.js' ;
6
- import { GameEventType } from '../../../common/game/types.js' ;
3
+ import { useCallback } from 'react' ;
7
4
import { isEmpty } from '../../../common/string/string.utils.js' ;
8
5
import { useCommandHistory } from '../../hooks/command-history.jsx' ;
9
- import { useSubscribe } from '../../hooks/pubsub.jsx' ;
10
6
import { runInBackground } from '../../lib/async/run-in-background.js' ;
11
-
12
- interface GameTimeDisplayProps {
13
- currentTime : number ;
14
- initialTime : number ;
15
- fillColor : string ;
16
- textColor : string ;
17
- }
18
-
19
- const GameTimeDisplay : React . FC < GameTimeDisplayProps > = (
20
- options : GameTimeDisplayProps
21
- ) => {
22
- const { currentTime, initialTime } = options ;
23
-
24
- const fillColor = currentTime > 0 ? options . fillColor : 'inherit' ;
25
- const textColor = currentTime > 0 ? options . textColor : 'inherit' ;
26
-
27
- const fillHeight = ( currentTime / initialTime ) * 100 || 0 ;
28
-
29
- const fillSpringProps = useSpring ( {
30
- height : `${ fillHeight } %` ,
31
- immediate : true ,
32
- } ) ;
33
-
34
- return (
35
- < div
36
- style = { {
37
- display : 'inline-block' ,
38
- width : '30px' ,
39
- height : '30px' ,
40
- position : 'relative' ,
41
- margin : 0 ,
42
- padding : 0 ,
43
- } }
44
- >
45
- < animated . div
46
- style = { {
47
- position : 'absolute' ,
48
- bottom : 0 ,
49
- width : '100%' ,
50
- backgroundColor : fillColor ,
51
- ...fillSpringProps ,
52
- } }
53
- />
54
- < div
55
- style = { {
56
- position : 'absolute' ,
57
- width : '100%' ,
58
- height : '100%' ,
59
- lineHeight : '30px' ,
60
- textAlign : 'center' ,
61
- zIndex : 1 ,
62
- color : textColor ,
63
- } }
64
- >
65
- { currentTime }
66
- </ div >
67
- </ div >
68
- ) ;
69
- } ;
70
-
71
- GameTimeDisplay . displayName = 'GameTimeDisplay' ;
7
+ import { GameRoundTime } from './game-roundtime.jsx' ;
72
8
73
9
export interface GameBottomBarProps {
74
- // TODO
75
10
todo ?: true ;
76
11
}
77
12
78
13
export const GameBottomBar : React . FC < GameBottomBarProps > = (
79
- props : GameBottomBarProps
14
+ _props : GameBottomBarProps
80
15
) : ReactNode => {
81
16
const { input, handleKeyDown, handleOnChange } = useCommandHistory ( ) ;
82
17
83
- const { euiTheme } = useEuiTheme ( ) ;
84
-
85
- const nowInSeconds = useCallback ( ( ) => {
86
- return Math . floor ( Date . now ( ) / 1000 ) ;
87
- } , [ ] ) ;
88
-
89
18
const onKeyDown = useCallback < KeyboardEventHandler < HTMLInputElement > > (
90
19
( event : KeyboardEvent < HTMLInputElement > ) => {
91
20
// Handle any history navigation.
@@ -101,97 +30,12 @@ export const GameBottomBar: React.FC<GameBottomBarProps> = (
101
30
[ handleKeyDown ]
102
31
) ;
103
32
104
- // Machine-friendly timestamps (in seconds).
105
- // Example: '1737941270'.
106
- const serverTimeRef = useRef < number > ( 0 ) ; // current time on game server
107
- const roundTimeRef = useRef < number > ( 0 ) ; // future time when can take action
108
- const castTimeRef = useRef < number > ( 0 ) ; // future time when spell prepared
109
-
110
- // User-friendly remaining durations (in seconds).
111
- // Example: '6' (for 6 seconds remaining).
112
- const [ currentRT , setCurrentRT ] = useState < number > ( 0 ) ;
113
- const [ initialRT , setInitialRT ] = useState < number > ( 0 ) ;
114
- const [ currentCT , setCurrentCT ] = useState < number > ( 0 ) ;
115
- const [ initialCT , setInitialCT ] = useState < number > ( 0 ) ;
116
-
117
- // Interval for updating remaining round time.
118
- // Used to refresh the UI every second.
119
- const intervalRef = useRef < NodeJS . Timeout > ( ) ;
120
-
121
- // Recalculates the remaining round time.
122
- const calculateRoundTimes = useCallback ( ( ) => {
123
- const elapsed = Math . floor ( Date . now ( ) / 1000 ) ; // current time in seconds
124
- const remainingRT = Math . max ( 0 , roundTimeRef . current - elapsed ) ;
125
- const remainingCT = Math . max ( 0 , castTimeRef . current - elapsed ) ;
126
- setCurrentRT ( remainingRT ) ;
127
- setCurrentCT ( remainingCT ) ;
128
- } , [ ] ) ;
129
-
130
- // Periodically recalculate the round time UI for the user.
131
- useEffect ( ( ) => {
132
- intervalRef . current = setInterval ( ( ) => {
133
- calculateRoundTimes ( ) ;
134
- } , 1000 ) ;
135
- return ( ) => {
136
- clearInterval ( intervalRef . current ) ;
137
- } ;
138
- } , [ calculateRoundTimes ] ) ;
139
-
140
- // Technically, we don't need to explicitly recalculate the round time
141
- // when these game events are received, but doing so will immediately
142
- // refresh the UI when new roundtimes are incurred rather than on a delay.
143
- useSubscribe ( [ 'game:event' ] , ( gameEvent : GameEvent ) => {
144
- switch ( gameEvent . type ) {
145
- case GameEventType . SERVER_TIME :
146
- serverTimeRef . current = gameEvent . time ;
147
- calculateRoundTimes ( ) ;
148
- break ;
149
- case GameEventType . ROUND_TIME :
150
- roundTimeRef . current = gameEvent . time ;
151
- setInitialRT ( roundTimeRef . current - nowInSeconds ( ) ) ;
152
- calculateRoundTimes ( ) ;
153
- break ;
154
- case GameEventType . CAST_TIME :
155
- castTimeRef . current = gameEvent . time ;
156
- setInitialCT ( castTimeRef . current - nowInSeconds ( ) ) ;
157
- calculateRoundTimes ( ) ;
158
- break ;
159
- }
160
- } ) ;
161
-
162
33
return (
163
34
< EuiFieldText
164
35
value = { input }
165
36
compressed = { true }
166
37
fullWidth = { true }
167
- prepend = {
168
- < >
169
- < div
170
- style = { {
171
- display : 'inline-block' ,
172
- textAlign : 'center' ,
173
- marginRight : '5px' ,
174
- } }
175
- >
176
- < div > R</ div >
177
- < div > T</ div >
178
- </ div >
179
- < GameTimeDisplay
180
- currentTime = { currentRT }
181
- initialTime = { initialRT }
182
- // TODO: use text color that contrats better with background
183
- textColor = { euiTheme . colors . dangerText }
184
- fillColor = { euiTheme . colors . warning }
185
- />
186
- < GameTimeDisplay
187
- currentTime = { currentCT }
188
- initialTime = { initialCT }
189
- // TODO: use text color that contrats better with background
190
- textColor = { euiTheme . colors . dangerText }
191
- fillColor = { euiTheme . colors . primary }
192
- />
193
- </ >
194
- }
38
+ prepend = { < GameRoundTime /> }
195
39
tabIndex = { 0 }
196
40
onKeyDown = { onKeyDown }
197
41
onChange = { handleOnChange }
0 commit comments