@@ -22,14 +22,8 @@ import { logger } from 'matrix-js-sdk/src/logger';
22
22
import { MatrixClient } from 'matrix-js-sdk/src/client' ;
23
23
24
24
import { _t } from "../../../languageHandler" ;
25
- import AccessibleButton from '../elements/AccessibleButton' ;
26
- import QRCode from '../elements/QRCode' ;
27
- import Spinner from '../elements/Spinner' ;
28
- import { Icon as BackButtonIcon } from "../../../../res/img/element-icons/back.svg" ;
29
- import { Icon as DevicesIcon } from "../../../../res/img/element-icons/devices.svg" ;
30
- import { Icon as WarningBadge } from "../../../../res/img/element-icons/warning-badge.svg" ;
31
- import { Icon as InfoIcon } from "../../../../res/img/element-icons/i.svg" ;
32
25
import { wrapRequestWithDialog } from '../../../utils/UserInteractiveAuth' ;
26
+ import LoginWithQRFlow from './LoginWithQRFlow' ;
33
27
34
28
/**
35
29
* The intention of this enum is to have a mode that scans a QR code instead of generating one.
@@ -41,7 +35,7 @@ export enum Mode {
41
35
Show = "show" ,
42
36
}
43
37
44
- enum Phase {
38
+ export enum Phase {
45
39
Loading ,
46
40
ShowingQR ,
47
41
Connecting ,
@@ -51,6 +45,14 @@ enum Phase {
51
45
Error ,
52
46
}
53
47
48
+ export enum Click {
49
+ Cancel ,
50
+ Decline ,
51
+ Approve ,
52
+ TryAgain ,
53
+ Back ,
54
+ }
55
+
54
56
interface IProps {
55
57
client : MatrixClient ;
56
58
mode : Mode ;
@@ -68,7 +70,7 @@ interface IState {
68
70
/**
69
71
* A component that allows sign in and E2EE set up with a QR code.
70
72
*
71
- * It implements both `login.start` and `login- reciprocate` capabilities as well as both scanning and showing QR codes.
73
+ * It implements `login.reciprocate` capabilities and showing QR codes.
72
74
*
73
75
* This uses the unstable feature of MSC3906: https://github.com/matrix-org/matrix-spec-proposals/pull/3906
74
76
*/
@@ -138,6 +140,7 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
138
140
this . props . onFinished ( true ) ;
139
141
return ;
140
142
}
143
+ this . setState ( { phase : Phase . Verifying } ) ;
141
144
await this . state . rendezvous . verifyNewDeviceOnExistingDevice ( ) ;
142
145
this . props . onFinished ( true ) ;
143
146
} catch ( e ) {
@@ -197,200 +200,41 @@ export default class LoginWithQR extends React.Component<IProps, IState> {
197
200
} ) ;
198
201
}
199
202
200
- private cancelClicked = async ( e : React . FormEvent ) => {
201
- e . preventDefault ( ) ;
202
- await this . state . rendezvous ?. cancel ( RendezvousFailureReason . UserCancelled ) ;
203
- this . reset ( ) ;
204
- this . props . onFinished ( false ) ;
205
- } ;
206
-
207
- private declineClicked = async ( e : React . FormEvent ) => {
208
- e . preventDefault ( ) ;
209
- await this . state . rendezvous ?. declineLoginOnExistingDevice ( ) ;
210
- this . reset ( ) ;
211
- this . props . onFinished ( false ) ;
212
- } ;
213
-
214
- private tryAgainClicked = async ( e : React . FormEvent ) => {
215
- e . preventDefault ( ) ;
216
- this . reset ( ) ;
217
- await this . updateMode ( this . props . mode ) ;
218
- } ;
219
-
220
- private onBackClick = async ( ) => {
221
- await this . state . rendezvous ?. cancel ( RendezvousFailureReason . UserCancelled ) ;
222
-
223
- this . props . onFinished ( false ) ;
224
- } ;
225
-
226
- private cancelButton = ( ) => < AccessibleButton
227
- kind = "primary_outline"
228
- onClick = { this . cancelClicked }
229
- >
230
- { _t ( "Cancel" ) }
231
- </ AccessibleButton > ;
232
-
233
- private simpleSpinner = ( description ?: string ) : JSX . Element => {
234
- return < div className = "mx_LoginWithQR_spinner" >
235
- < div >
236
- < Spinner />
237
- { description && < p > { description } </ p > }
238
- </ div >
239
- </ div > ;
240
- } ;
241
-
242
- public render ( ) {
243
- let title : string ;
244
- let titleIcon : JSX . Element | undefined ;
245
- let main : JSX . Element | undefined ;
246
- let buttons : JSX . Element | undefined ;
247
- let backButton = true ;
248
- let cancellationMessage : string | undefined ;
249
- let centreTitle = false ;
250
-
251
- switch ( this . state . phase ) {
252
- case Phase . Error :
253
- switch ( this . state . failureReason ) {
254
- case RendezvousFailureReason . Expired :
255
- cancellationMessage = _t ( "The linking wasn't completed in the required time." ) ;
256
- break ;
257
- case RendezvousFailureReason . InvalidCode :
258
- cancellationMessage = _t ( "The scanned code is invalid." ) ;
259
- break ;
260
- case RendezvousFailureReason . UnsupportedAlgorithm :
261
- cancellationMessage = _t ( "Linking with this device is not supported." ) ;
262
- break ;
263
- case RendezvousFailureReason . UserDeclined :
264
- cancellationMessage = _t ( "The request was declined on the other device." ) ;
265
- break ;
266
- case RendezvousFailureReason . OtherDeviceAlreadySignedIn :
267
- cancellationMessage = _t ( "The other device is already signed in." ) ;
268
- break ;
269
- case RendezvousFailureReason . OtherDeviceNotSignedIn :
270
- cancellationMessage = _t ( "The other device isn't signed in." ) ;
271
- break ;
272
- case RendezvousFailureReason . UserCancelled :
273
- cancellationMessage = _t ( "The request was cancelled." ) ;
274
- break ;
275
- case RendezvousFailureReason . Unknown :
276
- cancellationMessage = _t ( "An unexpected error occurred." ) ;
277
- break ;
278
- case RendezvousFailureReason . HomeserverLacksSupport :
279
- cancellationMessage = _t ( "The homeserver doesn't support signing in another device." ) ;
280
- break ;
281
- default :
282
- cancellationMessage = _t ( "The request was cancelled." ) ;
283
- break ;
284
- }
285
- title = _t ( "Connection failed" ) ;
286
- centreTitle = true ;
287
- titleIcon = < WarningBadge className = "error" /> ;
288
- backButton = false ;
289
- main = < p data-testid = "cancellation-message" > { cancellationMessage } </ p > ;
290
- buttons = < >
291
- < AccessibleButton
292
- kind = "primary"
293
- onClick = { this . tryAgainClicked }
294
- >
295
- { _t ( "Try again" ) }
296
- </ AccessibleButton >
297
- { this . cancelButton ( ) }
298
- </ > ;
203
+ private onClick = async ( type : Click ) => {
204
+ switch ( type ) {
205
+ case Click . Cancel :
206
+ await this . state . rendezvous ?. cancel ( RendezvousFailureReason . UserCancelled ) ;
207
+ this . reset ( ) ;
208
+ this . props . onFinished ( false ) ;
299
209
break ;
300
- case Phase . Connected :
301
- title = _t ( "Devices connected" ) ;
302
- titleIcon = < DevicesIcon className = "normal" /> ;
303
- backButton = false ;
304
- main = < >
305
- < p > { _t ( "Check that the code below matches with your other device:" ) } </ p >
306
- < div className = "mx_LoginWithQR_confirmationDigits" >
307
- { this . state . confirmationDigits }
308
- </ div >
309
- < div className = "mx_LoginWithQR_confirmationAlert" >
310
- < div >
311
- < InfoIcon />
312
- </ div >
313
- < div > { _t ( "By approving access for this device, it will have full access to your account." ) } </ div >
314
- </ div >
315
- </ > ;
316
-
317
- buttons = < >
318
- < AccessibleButton
319
- data-testid = "decline-login-button"
320
- kind = "primary_outline"
321
- onClick = { this . declineClicked }
322
- >
323
- { _t ( "Cancel" ) }
324
- </ AccessibleButton >
325
- < AccessibleButton
326
- data-testid = "approve-login-button"
327
- kind = "primary"
328
- onClick = { this . approveLogin }
329
- >
330
- { _t ( "Approve" ) }
331
- </ AccessibleButton >
332
- </ > ;
333
- break ;
334
- case Phase . ShowingQR :
335
- title = _t ( "Sign in with QR code" ) ;
336
- if ( this . state . rendezvous ) {
337
- const code = < div className = "mx_LoginWithQR_qrWrapper" >
338
- < QRCode data = { [ { data : Buffer . from ( this . state . rendezvous . code ) , mode : 'byte' } ] } className = "mx_QRCode" />
339
- </ div > ;
340
- main = < >
341
- < p > { _t ( "Scan the QR code below with your device that's signed out." ) } </ p >
342
- < ol >
343
- < li > { _t ( "Start at the sign in screen" ) } </ li >
344
- < li > { _t ( "Select 'Scan QR code'" ) } </ li >
345
- < li > { _t ( "Review and approve the sign in" ) } </ li >
346
- </ ol >
347
- { code }
348
- </ > ;
349
- } else {
350
- main = this . simpleSpinner ( ) ;
351
- buttons = this . cancelButton ( ) ;
352
- }
353
- break ;
354
- case Phase . Loading :
355
- main = this . simpleSpinner ( ) ;
210
+ case Click . Approve :
211
+ await this . approveLogin ( ) ;
356
212
break ;
357
- case Phase . Connecting :
358
- main = this . simpleSpinner ( _t ( "Connecting..." ) ) ;
359
- buttons = this . cancelButton ( ) ;
213
+ case Click . Decline :
214
+ await this . state . rendezvous ?. declineLoginOnExistingDevice ( ) ;
215
+ this . reset ( ) ;
216
+ this . props . onFinished ( false ) ;
360
217
break ;
361
- case Phase . WaitingForDevice :
362
- main = this . simpleSpinner ( _t ( "Waiting for device to sign in" ) ) ;
363
- buttons = this . cancelButton ( ) ;
218
+ case Click . TryAgain :
219
+ this . reset ( ) ;
220
+ await this . updateMode ( this . props . mode ) ;
364
221
break ;
365
- case Phase . Verifying :
366
- title = _t ( "Success" ) ;
367
- centreTitle = true ;
368
- main = this . simpleSpinner ( _t ( "Completing set up of your new device" ) ) ;
222
+ case Click . Back :
223
+ await this . state . rendezvous ?. cancel ( RendezvousFailureReason . UserCancelled ) ;
224
+ this . props . onFinished ( false ) ;
369
225
break ;
370
226
}
227
+ } ;
371
228
229
+ public render ( ) {
372
230
return (
373
- < div data-testid = "login-with-qr" className = "mx_LoginWithQR" >
374
- < div className = { centreTitle ? "mx_LoginWithQR_centreTitle" : "" } >
375
- { backButton ?
376
- < AccessibleButton
377
- data-testid = "back-button"
378
- className = "mx_LoginWithQR_BackButton"
379
- onClick = { this . onBackClick }
380
- title = "Back"
381
- >
382
- < BackButtonIcon />
383
- </ AccessibleButton >
384
- : null }
385
- < h1 > { titleIcon } { title } </ h1 >
386
- </ div >
387
- < div className = "mx_LoginWithQR_main" >
388
- { main }
389
- </ div >
390
- < div className = "mx_LoginWithQR_buttons" >
391
- { buttons }
392
- </ div >
393
- </ div >
231
+ < LoginWithQRFlow
232
+ onClick = { this . onClick }
233
+ phase = { this . state . phase }
234
+ code = { this . state . phase === Phase . ShowingQR ? this . state . rendezvous ?. code : undefined }
235
+ confirmationDigits = { this . state . phase === Phase . Connected ? this . state . confirmationDigits : undefined }
236
+ failureReason = { this . state . phase === Phase . Error ? this . state . failureReason : undefined }
237
+ />
394
238
) ;
395
239
}
396
240
}
0 commit comments