1
1
import type { IpcRendererEvent } from 'electron' ;
2
+ import { EuiLoadingSpinner , EuiOverlayMask } from '@elastic/eui' ;
3
+ import { useRouter } from 'next/router.js' ;
2
4
import type { ReactNode } from 'react' ;
3
- import { createContext , useEffect } from 'react' ;
5
+ import { createContext , useEffect , useState } from 'react' ;
4
6
import type {
5
7
GameConnectMessage ,
6
8
GameDisconnectMessage ,
7
9
GameErrorMessage ,
8
10
} from '../../common/game/types.js' ;
9
11
import { useQuitCharacter } from '../hooks/characters.jsx' ;
10
12
import { useLogger } from '../hooks/logger.jsx' ;
13
+ import { useSubscribe } from '../hooks/pubsub.jsx' ;
11
14
import { runInBackground } from '../lib/async/run-in-background.js' ;
12
15
13
16
/**
@@ -34,9 +37,41 @@ export const GameProvider: React.FC<GameProviderProps> = (
34
37
const { children } = props ;
35
38
36
39
const logger = useLogger ( 'context:game' ) ;
40
+ const router = useRouter ( ) ;
37
41
38
42
const quitCharacter = useQuitCharacter ( ) ;
39
43
44
+ // To protect against a user pressing play/stop while the app
45
+ // is transitioning between characters, show a loading spinner.
46
+ const [ showPlayStartingOverlay , setShowPlayStartingOverlay ] =
47
+ useState < boolean > ( false ) ;
48
+
49
+ const [ showPlayStoppingOverlay , setShowPlayStoppingOverlay ] =
50
+ useState < boolean > ( false ) ;
51
+
52
+ // You may be lured into subscribing to multiple events
53
+ // to set a single overlay state as true/false, but don't do that.
54
+ // The start/stop events fire back-to-back when you play
55
+ // a second character and one is already playing. What you see
56
+ // is a quick flicker of the overlay then no overlay at all.
57
+ // Instead, use two variables to drive the overlay.
58
+ useSubscribe ( [ 'character:play:starting' ] , async ( ) => {
59
+ setShowPlayStartingOverlay ( true ) ;
60
+ } ) ;
61
+
62
+ useSubscribe ( [ 'character:play:started' ] , async ( ) => {
63
+ setShowPlayStartingOverlay ( false ) ;
64
+ await router . push ( '/grid' ) ;
65
+ } ) ;
66
+
67
+ useSubscribe ( [ 'character:play:stopping' ] , async ( ) => {
68
+ setShowPlayStoppingOverlay ( true ) ;
69
+ } ) ;
70
+
71
+ useSubscribe ( [ 'character:play:stopped' ] , async ( ) => {
72
+ setShowPlayStoppingOverlay ( false ) ;
73
+ } ) ;
74
+
40
75
useEffect ( ( ) => {
41
76
const unsubscribe = window . api . onMessage (
42
77
'game:connect' ,
@@ -90,5 +125,16 @@ export const GameProvider: React.FC<GameProviderProps> = (
90
125
} ;
91
126
} , [ logger ] ) ;
92
127
93
- return < GameContext . Provider value = { { } } > { children } </ GameContext . Provider > ;
128
+ return (
129
+ < GameContext . Provider value = { { } } >
130
+ < >
131
+ { ( showPlayStartingOverlay || showPlayStoppingOverlay ) && (
132
+ < EuiOverlayMask >
133
+ < EuiLoadingSpinner size = "l" />
134
+ </ EuiOverlayMask >
135
+ ) }
136
+ { children }
137
+ </ >
138
+ </ GameContext . Provider >
139
+ ) ;
94
140
} ;
0 commit comments