@@ -9,6 +9,8 @@ import PublicProfileContext from '../../../../Profile/PublicProfileContext';
9
9
import RouterContext from '../../../RouterContext' ;
10
10
import { retryIfFailed } from '../../../../Utils/RetryIfFailed' ;
11
11
import optionalRequire from '../../../../Utils/OptionalRequire' ;
12
+ import { isNativeMobileApp } from '../../../../Utils/Platform' ;
13
+ import Window from '../../../../Utils/Window' ;
12
14
const electron = optionalRequire ( 'electron' ) ;
13
15
14
16
// If the iframe is displaying a game, it will continue playing its audio as long as the iframe
@@ -46,8 +48,8 @@ let gdevelopGamesMonetization: {|
46
48
let gdevelopGamesMonetizationPromise : Promise < void > | null = null ;
47
49
48
50
const ensureGDevelopGamesMonetizationReady = async ( ) => {
49
- if ( ! ! electron ) {
50
- // Not supported on desktop.
51
+ if ( ! ! electron || isNativeMobileApp ( ) ) {
52
+ // Not supported on desktop or mobile .
51
53
return ;
52
54
}
53
55
if ( gdevelopGamesMonetization ) {
@@ -84,6 +86,80 @@ const ensureGDevelopGamesMonetizationReady = async () => {
84
86
return gdevelopGamesMonetizationPromise ;
85
87
} ;
86
88
89
+ /**
90
+ * Generate a custom token for the user (or `null` if the user is not connected),
91
+ * and keep it prepared to be sent to the Games Platform frame.
92
+ * This avoids doing it at the last moment, and create useless wait on the frame side.
93
+ */
94
+ const useUserCustomToken = ( ) : { |
95
+ userCustomToken : ?string ,
96
+ | } => {
97
+ const { profile, getAuthorizationHeader } = React . useContext (
98
+ AuthenticatedUserContext
99
+ ) ;
100
+ const userId = profile ? profile . id : null ;
101
+
102
+ const [ customTokenUserId , setCustomTokenUserId ] = React . useState ( null ) ;
103
+ const [ userCustomToken , setUserCustomToken ] = React . useState ( null ) ;
104
+ const [ lastTokenGenerationTime , setLastTokenGenerationTime ] = React . useState (
105
+ 0
106
+ ) ;
107
+
108
+ // Regenerate a token every 30 minutes (expiration is usually 60 minutes, but be safe)
109
+ // or if the user changed.
110
+ const hasUserChanged = customTokenUserId !== userId ;
111
+ const shouldRegenerateToken =
112
+ Date . now ( ) - lastTokenGenerationTime > 1000 * 60 * 30 || hasUserChanged ;
113
+
114
+ const clearStoredToken = React . useCallback ( ( ) => {
115
+ setUserCustomToken ( null ) ;
116
+ setLastTokenGenerationTime ( 0 ) ;
117
+ setCustomTokenUserId ( null ) ;
118
+ } , [ ] ) ;
119
+
120
+ React . useEffect (
121
+ ( ) => {
122
+ if ( hasUserChanged ) {
123
+ clearStoredToken ( ) ;
124
+ console . info ( 'User has changed, cleared stored user custom token.' ) ;
125
+ }
126
+ } ,
127
+ [ hasUserChanged , clearStoredToken ]
128
+ ) ;
129
+
130
+ React . useEffect (
131
+ ( ) => {
132
+ ( async ( ) => {
133
+ if ( ! shouldRegenerateToken ) return ;
134
+ if ( ! userId ) {
135
+ clearStoredToken ( ) ;
136
+ return ;
137
+ }
138
+
139
+ try {
140
+ console . info (
141
+ `Generating a custom token for user ${ userId } , for usage in the Games Platform frame...`
142
+ ) ;
143
+ const userCustomToken = await retryIfFailed ( { times : 2 } , ( ) =>
144
+ generateCustomAuthToken ( getAuthorizationHeader , userId )
145
+ ) ;
146
+ setUserCustomToken ( userCustomToken ) ;
147
+ setLastTokenGenerationTime ( Date . now ( ) ) ;
148
+ setCustomTokenUserId ( userId ) ;
149
+ } catch ( error ) {
150
+ console . error (
151
+ 'Error while generating custom token. User will not be logged in the Games Platform frame.' ,
152
+ error
153
+ ) ;
154
+ }
155
+ } ) ( ) ;
156
+ } ,
157
+ [ shouldRegenerateToken , userId , getAuthorizationHeader , clearStoredToken ]
158
+ ) ;
159
+
160
+ return { userCustomToken } ;
161
+ } ;
162
+
87
163
type GamesPlatformFrameStateProviderProps = { |
88
164
children : React . Node ,
89
165
| } ;
@@ -102,7 +178,6 @@ const GamesPlatformFrameStateProvider = ({
102
178
onOpenCreateAccountDialog,
103
179
onOpenProfileDialog,
104
180
profile,
105
- getAuthorizationHeader,
106
181
} = React . useContext ( AuthenticatedUserContext ) ;
107
182
const [
108
183
newProjectActions ,
@@ -310,59 +385,44 @@ const GamesPlatformFrameStateProvider = ({
310
385
[ handleIframeMessage ]
311
386
) ;
312
387
313
- const sendTokenToIframeIfConnected = React . useCallback (
388
+ const { userCustomToken } = useUserCustomToken ( ) ;
389
+
390
+ const sendUserCustomTokenToFrame = React . useCallback (
314
391
async ( ) => {
315
- if ( iframeLoaded && userId ) {
316
- // The iframe is loaded and the user is authenticated, so we can
317
- // send that information to the iframe to automatically log the user in.
318
- // $FlowFixMe - we know it's an iframe.
319
- const iframe : ?HTMLIFrameElement = document . getElementById (
320
- GAMES_PLATFORM_IFRAME_ID
321
- ) ;
322
- if ( iframe && iframe . contentWindow ) {
323
- try {
324
- const userCustomToken = await generateCustomAuthToken (
325
- getAuthorizationHeader ,
326
- userId
327
- ) ;
328
- iframe . contentWindow . postMessage (
329
- {
330
- id : 'connectUserWithCustomToken' ,
331
- token : userCustomToken ,
332
- } ,
333
- // Specify the target origin to avoid leaking the customToken.
334
- // Replace with '*' to test locally.
335
- 'https://gd.games'
336
- // '*'
337
- ) ;
338
- } catch ( error ) {
339
- console . error (
340
- 'Error while generating custom token. User will not be logged in in the frame.' ,
341
- error
342
- ) ;
343
- return ;
344
- }
345
- }
392
+ if ( ! iframeLoaded ) {
393
+ return ;
346
394
}
347
- } ,
348
- [ iframeLoaded , userId , getAuthorizationHeader ]
349
- ) ;
350
395
351
- React . useEffect (
352
- ( ) => {
353
- sendTokenToIframeIfConnected ( ) ;
354
- } ,
355
- [ sendTokenToIframeIfConnected ]
356
- ) ;
396
+ // The iframe is loaded:
397
+ // we can now sent the information that the user is connected,
398
+ // to automatically log the user in the frame,
399
+ // or notify it the user is not connected (or just disconnected).
357
400
358
- const notifyIframeUserDisconnected = React . useCallback (
359
- ( ) => {
360
- if ( iframeLoaded && ! userId ) {
361
- // $FlowFixMe - we know it's an iframe.
362
- const iframe : ?HTMLIFrameElement = document . getElementById (
363
- GAMES_PLATFORM_IFRAME_ID
364
- ) ;
365
- if ( iframe && iframe . contentWindow ) {
401
+ // $FlowFixMe - we know it's an iframe.
402
+ const iframe : ?HTMLIFrameElement = document . getElementById (
403
+ GAMES_PLATFORM_IFRAME_ID
404
+ ) ;
405
+ if ( ! iframe || ! iframe . contentWindow ) {
406
+ console . error ( 'Iframe not found or not accessible.' ) ;
407
+ return ;
408
+ }
409
+
410
+ try {
411
+ if ( userCustomToken ) {
412
+ console . info ( 'Sending user custom token to Games Platform frame...' ) ;
413
+ iframe . contentWindow . postMessage (
414
+ {
415
+ id : 'connectUserWithCustomToken' ,
416
+ token : userCustomToken ,
417
+ } ,
418
+ // Specify the target origin to avoid leaking the customToken.
419
+ // Use '*' to test locally.
420
+ Window . isDev ( ) ? '*' : 'https://gd.games'
421
+ ) ;
422
+ } else {
423
+ console . info (
424
+ 'Notifying the Games Platform frame that the user is not connected (or just disconnected).'
425
+ ) ;
366
426
iframe . contentWindow . postMessage (
367
427
{
368
428
id : 'disconnectUser' ,
@@ -371,16 +431,22 @@ const GamesPlatformFrameStateProvider = ({
371
431
'*'
372
432
) ;
373
433
}
434
+ } catch ( error ) {
435
+ console . error (
436
+ 'Error while sending user custom token. User will not be logged in the Games Platform frame.' ,
437
+ error
438
+ ) ;
439
+ return ;
374
440
}
375
441
} ,
376
- [ iframeLoaded , userId ]
442
+ [ iframeLoaded , userCustomToken ]
377
443
) ;
378
444
379
445
React . useEffect (
380
446
( ) => {
381
- notifyIframeUserDisconnected ( ) ;
447
+ sendUserCustomTokenToFrame ( ) ;
382
448
} ,
383
- [ notifyIframeUserDisconnected ]
449
+ [ sendUserCustomTokenToFrame ]
384
450
) ;
385
451
386
452
const configureNewProjectActions = React . useCallback (
0 commit comments