@@ -24,7 +24,8 @@ interface PhoneVerificationProps extends HTMLAttributes<HTMLElement> {
24
24
interface PhoneVerificationState {
25
25
phone : string ;
26
26
code : string ;
27
- isCodeSent : boolean ;
27
+ isCodeSent ?: boolean ;
28
+ isPhoneCallInitiated ?: boolean ;
28
29
isPhoneNumberHidden : boolean ;
29
30
isLoading : boolean ;
30
31
showForgetScreen : boolean ;
@@ -41,7 +42,10 @@ const PhoneVerification = observer((props: PhoneVerificationProps) => {
41
42
const user = userStore . items [ userPk ] ;
42
43
const isCurrentUser = userStore . currentUserPk === user . pk ;
43
44
44
- const [ { showForgetScreen, phone, code, isCodeSent, isPhoneNumberHidden, isLoading } , setState ] = useReducer (
45
+ const [
46
+ { showForgetScreen, phone, code, isCodeSent, isPhoneCallInitiated, isPhoneNumberHidden, isLoading } ,
47
+ setState ,
48
+ ] = useReducer (
45
49
( state : PhoneVerificationState , newState : Partial < PhoneVerificationState > ) => ( {
46
50
...state ,
47
51
...newState ,
@@ -51,6 +55,7 @@ const PhoneVerification = observer((props: PhoneVerificationProps) => {
51
55
phone : user . verified_phone_number || '+' ,
52
56
isLoading : false ,
53
57
isCodeSent : false ,
58
+ isPhoneCallInitiated : false ,
54
59
showForgetScreen : false ,
55
60
isPhoneNumberHidden : user . hide_phone_number ,
56
61
}
@@ -70,7 +75,7 @@ const PhoneVerification = observer((props: PhoneVerificationProps) => {
70
75
) ;
71
76
72
77
const onChangePhoneCallback = useCallback ( ( event : React . ChangeEvent < HTMLInputElement > ) => {
73
- setState ( { isCodeSent : false , phone : event . target . value } ) ;
78
+ setState ( { isCodeSent : false , isPhoneCallInitiated : false , phone : event . target . value } ) ;
74
79
} , [ ] ) ;
75
80
76
81
const onChangeCodeCallback = useCallback ( ( event : React . ChangeEvent < HTMLInputElement > ) => {
@@ -81,59 +86,91 @@ const PhoneVerification = observer((props: PhoneVerificationProps) => {
81
86
userStore . makeTestCall ( userPk ) ;
82
87
} , [ userPk , userStore . makeTestCall ] ) ;
83
88
89
+ const handleSendTestSmsClick = useCallback ( ( ) => {
90
+ userStore . sendTestSms ( userPk ) ;
91
+ } , [ userPk , userStore . sendTestSms ] ) ;
92
+
84
93
const handleForgetNumberClick = useCallback ( ( ) => {
85
94
userStore . forgetPhone ( userPk ) . then ( async ( ) => {
86
95
await userStore . loadUser ( userPk ) ;
87
- setState ( { phone : '' , showForgetScreen : false , isCodeSent : false } ) ;
96
+ setState ( { phone : '' , showForgetScreen : false , isCodeSent : false , isPhoneCallInitiated : false } ) ;
88
97
} ) ;
89
98
} , [ userPk , userStore . forgetPhone , userStore . loadUser ] ) ;
90
99
91
- const onSubmitCallback = useCallback ( async ( ) => {
92
- if ( isCodeSent ) {
93
- userStore . verifyPhone ( userPk , code ) . then ( ( ) => {
94
- userStore . loadUser ( userPk ) ;
95
- } ) ;
96
- } else {
97
- window . grecaptcha . ready ( function ( ) {
98
- window . grecaptcha
99
- . execute ( rootStore . recaptchaSiteKey , { action : 'mobile_verification_code' } )
100
- . then ( async function ( token ) {
101
- await userStore . updateUser ( {
102
- pk : userPk ,
103
- email : user . email ,
104
- unverified_phone_number : phone ,
100
+ const onSubmitCallback = useCallback (
101
+ async ( type ) => {
102
+ let codeVerification = isCodeSent ;
103
+ if ( type === 'verification_call' ) {
104
+ codeVerification = isPhoneCallInitiated ;
105
+ }
106
+ if ( codeVerification ) {
107
+ userStore . verifyPhone ( userPk , code ) . then ( ( ) => {
108
+ userStore . loadUser ( userPk ) ;
109
+ } ) ;
110
+ } else {
111
+ window . grecaptcha . ready ( function ( ) {
112
+ window . grecaptcha
113
+ . execute ( rootStore . recaptchaSiteKey , { action : 'mobile_verification_code' } )
114
+ . then ( async function ( token ) {
115
+ await userStore . updateUser ( {
116
+ pk : userPk ,
117
+ email : user . email ,
118
+ unverified_phone_number : phone ,
119
+ } ) ;
120
+
121
+ switch ( type ) {
122
+ case 'verification_call' :
123
+ userStore . fetchVerificationCall ( userPk , token ) . then ( ( ) => {
124
+ setState ( { isPhoneCallInitiated : true } ) ;
125
+ if ( codeInputRef . current ) {
126
+ codeInputRef . current . focus ( ) ;
127
+ }
128
+ } ) ;
129
+ break ;
130
+ case 'verification_sms' :
131
+ userStore . fetchVerificationCode ( userPk , token ) . then ( ( ) => {
132
+ setState ( { isCodeSent : true } ) ;
133
+ if ( codeInputRef . current ) {
134
+ codeInputRef . current . focus ( ) ;
135
+ }
136
+ } ) ;
137
+ break ;
138
+ }
105
139
} ) ;
140
+ } ) ;
141
+ }
142
+ } ,
143
+ [
144
+ code ,
145
+ isCodeSent ,
146
+ phone ,
147
+ user . email ,
148
+ userPk ,
149
+ userStore . verifyPhone ,
150
+ userStore . updateUser ,
151
+ userStore . fetchVerificationCode ,
152
+ ]
153
+ ) ;
106
154
107
- userStore . fetchVerificationCode ( userPk , token ) . then ( ( ) => {
108
- setState ( { isCodeSent : true } ) ;
155
+ const onVerifyCallback = useCallback ( async ( ) => {
156
+ userStore . verifyPhone ( userPk , code ) . then ( ( ) => {
157
+ userStore . loadUser ( userPk ) ;
158
+ } ) ;
159
+ } , [ code , userPk , userStore . verifyPhone , userStore . loadUser ] ) ;
160
+
161
+ const isPhoneProviderConfigured = teamStore . currentTeam ?. env_status . phone_provider ?. configured ;
162
+ const providerConfiguration = teamStore . currentTeam ?. env_status . phone_provider ;
109
163
110
- if ( codeInputRef . current ) {
111
- codeInputRef . current . focus ( ) ;
112
- }
113
- } ) ;
114
- } ) ;
115
- } ) ;
116
- }
117
- } , [
118
- code ,
119
- isCodeSent ,
120
- phone ,
121
- user . email ,
122
- userPk ,
123
- userStore . verifyPhone ,
124
- userStore . updateUser ,
125
- userStore . fetchVerificationCode ,
126
- ] ) ;
127
-
128
- const isTwilioConfigured = teamStore . currentTeam ?. env_status . twilio_configured ;
129
164
const phoneHasMinimumLength = phone ?. length > 8 ;
130
165
131
166
const isPhoneValid = phoneHasMinimumLength && PHONE_REGEX . test ( phone ) ;
132
167
const showPhoneInputError = phoneHasMinimumLength && ! isPhoneValid && ! isPhoneNumberHidden && ! isLoading ;
133
168
134
169
const action = isCurrentUser ? UserActions . UserSettingsWrite : UserActions . UserSettingsAdmin ;
135
170
const isButtonDisabled =
136
- phone === user . verified_phone_number || ( ! isCodeSent && ! isPhoneValid ) || ! isTwilioConfigured ;
171
+ phone === user . verified_phone_number ||
172
+ ( ! isCodeSent && ! isPhoneValid && ! isPhoneCallInitiated ) ||
173
+ ! isPhoneProviderConfigured ;
137
174
138
175
const isPhoneDisabled = ! ! user . verified_phone_number ;
139
176
const isCodeFieldDisabled = ! isCodeSent || ! isUserActionAllowed ( action ) ;
@@ -158,15 +195,15 @@ const PhoneVerification = observer((props: PhoneVerificationProps) => {
158
195
</ >
159
196
) }
160
197
161
- { ! isTwilioConfigured && store . hasFeature ( AppFeature . LiveSettings ) && (
198
+ { ! isPhoneProviderConfigured && store . hasFeature ( AppFeature . LiveSettings ) && (
162
199
< >
163
200
< Alert
164
201
severity = "warning"
165
202
// @ts -ignore
166
203
title = {
167
204
< >
168
- Can't verify phone. < PluginLink query = { { page : 'live-settings' } } > Check ENV variables</ PluginLink > { ' ' }
169
- related to Twilio .
205
+ Can't verify phone. < PluginLink query = { { page : 'live-settings' } } > Check ENV variables</ PluginLink > to
206
+ configure your provider .
170
207
</ >
171
208
}
172
209
/>
@@ -185,7 +222,7 @@ const PhoneVerification = observer((props: PhoneVerificationProps) => {
185
222
autoFocus
186
223
id = "phone"
187
224
required
188
- disabled = { ! isTwilioConfigured || isPhoneDisabled }
225
+ disabled = { ! isPhoneProviderConfigured || isPhoneDisabled }
189
226
placeholder = "Please enter the phone number with country code, e.g. +12451111111"
190
227
// @ts -ignore
191
228
prefix = { < Icon name = "phone" /> }
@@ -233,11 +270,14 @@ const PhoneVerification = observer((props: PhoneVerificationProps) => {
233
270
< PhoneVerificationButtonsGroup
234
271
action = { action }
235
272
isCodeSent = { isCodeSent }
273
+ isPhoneCallInitiated = { isPhoneCallInitiated }
236
274
isButtonDisabled = { isButtonDisabled }
237
275
isTestCallInProgress = { userStore . isTestCallInProgress }
238
- isTwilioConfigured = { isTwilioConfigured }
276
+ providerConfiguration = { providerConfiguration }
239
277
onSubmitCallback = { onSubmitCallback }
278
+ onVerifyCallback = { onVerifyCallback }
240
279
handleMakeTestCallClick = { handleMakeTestCallClick }
280
+ handleSendTestSmsClick = { handleSendTestSmsClick }
241
281
onShowForgetScreen = { ( ) => setState ( { showForgetScreen : true } ) }
242
282
user = { user }
243
283
/>
@@ -273,12 +313,20 @@ interface PhoneVerificationButtonsGroupProps {
273
313
action : UserAction ;
274
314
275
315
isCodeSent : boolean ;
316
+ isPhoneCallInitiated : boolean ;
276
317
isButtonDisabled : boolean ;
277
318
isTestCallInProgress : boolean ;
278
- isTwilioConfigured : boolean ;
279
-
280
- onSubmitCallback ( ) : void ;
319
+ providerConfiguration : {
320
+ configured : boolean ;
321
+ test_call : boolean ;
322
+ test_sms : boolean ;
323
+ verification_call : boolean ;
324
+ verification_sms : boolean ;
325
+ } ;
326
+ onSubmitCallback ( type : string ) : void ;
327
+ onVerifyCallback ( ) : void ;
281
328
handleMakeTestCallClick ( ) : void ;
329
+ handleSendTestSmsClick ( ) : void ;
282
330
onShowForgetScreen ( ) : void ;
283
331
284
332
user : User ;
@@ -287,25 +335,60 @@ interface PhoneVerificationButtonsGroupProps {
287
335
function PhoneVerificationButtonsGroup ( {
288
336
action,
289
337
isCodeSent,
338
+ isPhoneCallInitiated,
290
339
isButtonDisabled,
291
340
isTestCallInProgress,
292
- isTwilioConfigured ,
341
+ providerConfiguration ,
293
342
onSubmitCallback,
343
+ onVerifyCallback,
294
344
handleMakeTestCallClick,
345
+ handleSendTestSmsClick,
295
346
onShowForgetScreen,
296
347
user,
297
348
} : PhoneVerificationButtonsGroupProps ) {
298
349
const showForgetNumber = ! ! user . verified_phone_number ;
299
350
const showVerifyOrSendCodeButton = ! user . verified_phone_number ;
300
-
351
+ const verificationStarted = isCodeSent || isPhoneCallInitiated ;
301
352
return (
302
353
< HorizontalGroup >
303
354
{ showVerifyOrSendCodeButton && (
304
- < WithPermissionControlTooltip userAction = { action } >
305
- < Button variant = "primary" onClick = { onSubmitCallback } disabled = { isButtonDisabled } >
306
- { isCodeSent ? 'Verify' : 'Send Code' }
307
- </ Button >
308
- </ WithPermissionControlTooltip >
355
+ < HorizontalGroup >
356
+ { verificationStarted ? (
357
+ < >
358
+ < WithPermissionControlTooltip userAction = { action } >
359
+ < Button variant = "primary" onClick = { onVerifyCallback } >
360
+ Verify
361
+ </ Button >
362
+ </ WithPermissionControlTooltip >
363
+ </ >
364
+ ) : (
365
+ < HorizontalGroup >
366
+ { ' ' }
367
+ { providerConfiguration . verification_sms && (
368
+ < WithPermissionControlTooltip userAction = { action } >
369
+ < Button
370
+ variant = "primary"
371
+ onClick = { ( ) => onSubmitCallback ( 'verification_sms' ) }
372
+ disabled = { isButtonDisabled }
373
+ >
374
+ Send Code
375
+ </ Button >
376
+ </ WithPermissionControlTooltip >
377
+ ) }
378
+ { providerConfiguration . verification_call && (
379
+ < WithPermissionControlTooltip userAction = { action } >
380
+ < Button
381
+ variant = "primary"
382
+ onClick = { ( ) => onSubmitCallback ( 'verification_call' ) }
383
+ disabled = { isButtonDisabled }
384
+ >
385
+ Call to get the code
386
+ </ Button >
387
+ </ WithPermissionControlTooltip >
388
+ ) }
389
+ </ HorizontalGroup >
390
+ ) }
391
+ </ HorizontalGroup >
309
392
) }
310
393
311
394
{ showForgetNumber && (
@@ -321,24 +404,33 @@ function PhoneVerificationButtonsGroup({
321
404
) }
322
405
323
406
{ user . verified_phone_number && (
324
- < >
325
- < WithPermissionControlTooltip userAction = { action } >
326
- < Button
327
- disabled = { ! user ?. verified_phone_number || ! isTwilioConfigured || isTestCallInProgress }
328
- onClick = { handleMakeTestCallClick }
329
- >
330
- { isTestCallInProgress ? 'Making Test Call...' : 'Make Test Call' }
331
- </ Button >
332
- </ WithPermissionControlTooltip >
333
- < Tooltip content = { 'Click "Make Test Call" to save a phone number and add it to DnD exceptions.' } >
334
- < Icon
335
- name = "info-circle"
336
- style = { {
337
- marginLeft : '10px' ,
338
- } }
339
- />
340
- </ Tooltip >
341
- </ >
407
+ < HorizontalGroup >
408
+ { providerConfiguration . test_sms && (
409
+ < WithPermissionControlTooltip userAction = { action } >
410
+ < Button
411
+ disabled = { ! user ?. verified_phone_number || ! providerConfiguration . configured || isTestCallInProgress }
412
+ onClick = { handleSendTestSmsClick }
413
+ >
414
+ Send test sms
415
+ </ Button >
416
+ </ WithPermissionControlTooltip >
417
+ ) }
418
+ { providerConfiguration . test_call && (
419
+ < HorizontalGroup spacing = "xs" >
420
+ < WithPermissionControlTooltip userAction = { action } >
421
+ < Button
422
+ disabled = { ! user ?. verified_phone_number || ! providerConfiguration . configured || isTestCallInProgress }
423
+ onClick = { handleMakeTestCallClick }
424
+ >
425
+ { isTestCallInProgress ? 'Making Test Call...' : 'Make Test Call' }
426
+ </ Button >
427
+ </ WithPermissionControlTooltip >
428
+ < Tooltip content = { 'Click "Make Test Call" to save a phone number and add it to DnD exceptions.' } >
429
+ < Icon name = "info-circle" />
430
+ </ Tooltip >
431
+ </ HorizontalGroup >
432
+ ) }
433
+ </ HorizontalGroup >
342
434
) }
343
435
</ HorizontalGroup >
344
436
) ;
0 commit comments