1
1
/* global window */
2
2
3
- import React , { Component } from 'react' ;
3
+ import React , { useState , useRef } from 'react' ;
4
4
import PropTypes from 'prop-types' ;
5
5
import Printd from 'printd' ;
6
6
import { CopyToClipboard } from 'react-copy-to-clipboard' ;
@@ -10,148 +10,105 @@ import { formatCode } from 'lib/formatCode';
10
10
* This component provides the user interface for registering backup codes with a user. This process
11
11
* only involves showing the user the backup codes. User input is not required to set up the codes.
12
12
*/
13
- class Register extends Component {
14
- constructor ( props ) {
15
- super ( props ) ;
16
-
17
- this . state = {
18
- recentlyCopied : false
19
- } ;
20
-
21
- // Prepare a ref (in a React 15 compatible way) to use for the DOM node that will be printed
22
- this . printRef = null ;
23
- this . setPrintRef = element => {
24
- this . printRef = element ;
25
- } ;
26
- // Prepare a class member to store a timeout ref that provides feedback on copy to clipboard
27
- this . copyMessageTimeout = null ;
28
-
29
- this . handlePrint = this . handlePrint . bind ( this ) ;
30
- this . handleCopy = this . handleCopy . bind ( this ) ;
31
- }
13
+ function Register ( props ) {
14
+ const {
15
+ codes,
16
+ method,
17
+ onCompleteRegistration,
18
+ copyFeedbackDuration,
19
+ } = props ;
20
+ const [ recentlyCopied , setRecentlyCopied ] = useState ( false ) ;
21
+ const copyMessageTimeout = useRef ( null ) ;
22
+ const printRef = useRef ( null ) ;
23
+ const i18n = window . ss . i18n ;
32
24
33
25
/**
34
26
* Get codes from component properties and format them with spaces every 3 (or 4) characters.
35
27
* The number of characters in each group will never be less than 3 - the groups towards the end
36
28
* will have four characters instead.
37
- *
38
- * @return {string[] }
39
29
*/
40
- getFormattedCodes ( ) {
41
- const { codes } = this . props ;
42
-
30
+ function getFormattedCodes ( ) {
43
31
return codes . map ( code => formatCode ( code ) ) ;
44
32
}
45
33
46
34
/**
47
35
* Handle an event triggered requesting the backup codes to be printed
48
- *
49
- * @param {Event } event
50
36
*/
51
- handlePrint ( event ) {
37
+ function handlePrint ( event ) {
52
38
event . preventDefault ( ) ;
53
-
54
39
( new Printd ( ) ) . print (
55
- this . printRef ,
40
+ printRef ,
56
41
[ 'body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif }' ]
57
42
) ;
58
43
}
59
44
60
45
/**
61
46
* Handle an event triggered requesting the backup codes to be copied to clipboard
62
- *
63
- * @param {Event } event
64
47
*/
65
- handleCopy ( event ) {
48
+ function handleCopy ( event ) {
66
49
event . preventDefault ( ) ;
67
- const { copyFeedbackDuration } = this . props ;
68
-
69
- this . setState ( {
70
- recentlyCopied : true ,
71
- } ) ;
72
-
50
+ setRecentlyCopied ( true ) ;
73
51
// Clear an existing timeout to reset the text on the copy link
74
- if ( this . copyMessageTimeout ) {
75
- clearTimeout ( this . copyMessageTimeout ) ;
52
+ if ( copyMessageTimeout . current ) {
53
+ clearTimeout ( copyMessageTimeout . current ) ;
76
54
}
77
-
78
55
// And set that timeout too
79
- this . copyMessageTimeout = setTimeout ( ( ) => {
80
- this . setState ( {
81
- recentlyCopied : false ,
82
- } ) ;
56
+ copyMessageTimeout . current = setTimeout ( ( ) => {
57
+ setRecentlyCopied ( false ) ;
83
58
} , copyFeedbackDuration ) ;
84
59
}
85
60
86
61
/**
87
62
* Render a grid of formatted backup codes
88
- *
89
- * @return {HTMLElement }
90
63
*/
91
- renderCodes ( ) {
64
+ function renderCodes ( ) {
92
65
return (
93
- < pre ref = { this . setPrintRef } className = "mfa-register-backup-codes__code-grid" >
94
- { this . getFormattedCodes ( ) . map ( code => < div key = { code } > { code } </ div > ) }
66
+ < pre ref = { printRef } className = "mfa-register-backup-codes__code-grid" >
67
+ { getFormattedCodes ( ) . map ( code => < div key = { code } > { code } </ div > ) }
95
68
</ pre >
96
69
) ;
97
70
}
98
71
99
72
/**
100
73
* Render the description for registering in with this method
101
- *
102
- * @return {HTMLElement }
103
74
*/
104
- renderDescription ( ) {
105
- const { ss : { i18n } } = window ;
106
- const { method : { supportLink, supportText } } = this . props ;
107
-
108
- return (
109
- < p >
110
- { i18n . _t (
111
- 'MFABackupCodesRegister.DESCRIPTION' ,
112
- 'Recovery codes enable you to log into your account in the event your primary ' +
113
- 'authentication is not available. Each code can only be used once. Store these codes ' +
114
- 'somewhere safe, as they will not be viewable after leaving this page.'
115
- ) }
116
-
117
- { supportLink &&
118
- < a
119
- href = { supportLink }
120
- target = "_blank"
121
- rel = "noopener noreferrer"
122
- >
123
- { supportText || i18n . _t ( 'MFARegister.RECOVERY_HELP' , 'Learn more about recovery codes.' ) }
124
- </ a >
125
- }
126
- </ p >
127
- ) ;
75
+ function renderDescription ( ) {
76
+ const { supportLink, supportText } = method ;
77
+ return < p >
78
+ { i18n . _t (
79
+ 'MFABackupCodesRegister.DESCRIPTION' ,
80
+ 'Recovery codes enable you to log into your account in the event your primary ' +
81
+ 'authentication is not available. Each code can only be used once. Store these codes ' +
82
+ 'somewhere safe, as they will not be viewable after leaving this page.'
83
+ ) }
84
+
85
+ { supportLink &&
86
+ < a
87
+ href = { supportLink }
88
+ target = "_blank"
89
+ rel = "noopener noreferrer"
90
+ >
91
+ { supportText || i18n . _t ( 'MFARegister.RECOVERY_HELP' , 'Learn more about recovery codes.' ) }
92
+ </ a >
93
+ }
94
+ </ p > ;
128
95
}
129
96
130
97
/**
131
98
* Render the "print" action. A link allowing the user to trigger a print dialog for the codes
132
- *
133
- * @return {HTMLElement }
134
99
*/
135
- renderPrintAction ( ) {
136
- const { ss : { i18n } } = window ;
137
-
138
- return (
139
- < button type = "button" onClick = { this . handlePrint } className = "btn btn-link" >
140
- { i18n . _t ( 'MFABackupCodesRegister.PRINT' , 'Print codes' ) }
141
- </ button >
142
- ) ;
100
+ function renderPrintAction ( ) {
101
+ return < button type = "button" onClick = { handlePrint } className = "btn btn-link" >
102
+ { i18n . _t ( 'MFABackupCodesRegister.PRINT' , 'Print codes' ) }
103
+ </ button > ;
143
104
}
144
105
145
106
/**
146
107
* Render the "download" action. A link allowing the user to trigger a download of a text file
147
108
* containing the codes
148
- *
149
- * @return {HTMLElement }
150
109
*/
151
- renderDownloadAction ( ) {
152
- const { codes, method } = this . props ;
153
- const { Blob, URL , ss : { i18n } , navigator } = window ;
154
-
110
+ function renderDownloadAction ( ) {
111
+ const { Blob, URL , navigator } = window ;
155
112
const filename = `${ method . name } .txt` ;
156
113
const codesText = codes . join ( '\r\n' ) ;
157
114
const codesBlob = new Blob ( [ codesText ] , { type : 'text/plain;charset=UTF-8' } ) ;
@@ -162,62 +119,42 @@ class Register extends Component {
162
119
navigator . msSaveBlob ( codesBlob , filename ) ;
163
120
}
164
121
} ;
165
-
166
- return (
167
- < a download = { filename } href = { codesURL } className = "btn btn-link" onClick = { supportInternetExplorer } >
168
- { i18n . _t ( 'MFABackupCodesRegister.DOWNLOAD' , 'Download' ) }
169
- </ a >
170
- ) ;
122
+ return < a download = { filename } href = { codesURL } className = "btn btn-link" onClick = { supportInternetExplorer } >
123
+ { i18n . _t ( 'MFABackupCodesRegister.DOWNLOAD' , 'Download' ) }
124
+ </ a > ;
171
125
}
172
126
173
127
/**
174
128
* Render the "copy" action. A link allowing the user to easily copy the backup codes to clipboard
175
- *
176
- * @return {CopyToClipboard }
177
129
*/
178
- renderCopyAction ( ) {
179
- const { codes } = this . props ;
180
- const { recentlyCopied } = this . state ;
181
- const { ss : { i18n } } = window ;
182
-
130
+ function renderCopyAction ( ) {
183
131
const label = recentlyCopied
184
132
? i18n . _t ( 'MFABackupCodesRegister.COPY_RECENT' , 'Copied!' )
185
133
: i18n . _t ( 'MFABackupCodesRegister.COPY' , 'Copy codes' ) ;
186
-
187
- return (
188
- < CopyToClipboard text = { codes . join ( '\n' ) } >
189
- < button
190
- type = "button"
191
- className = "mfa-register-backup-codes__copy-to-clipboard btn btn-link"
192
- onClick = { this . handleCopy }
193
- >
194
- { label }
195
- </ button >
196
- </ CopyToClipboard >
197
- ) ;
134
+ return < CopyToClipboard text = { codes . join ( '\n' ) } >
135
+ < button
136
+ type = "button"
137
+ className = "mfa-register-backup-codes__copy-to-clipboard btn btn-link"
138
+ onClick = { ( ) => handleCopy ( ) }
139
+ >
140
+ { label }
141
+ </ button >
142
+ </ CopyToClipboard > ;
198
143
}
199
144
200
- render ( ) {
201
- const { onCompleteRegistration } = this . props ;
202
- const { ss : { i18n } } = window ;
203
-
204
- return (
205
- < div className = "mfa-register-backup-codes__container" >
206
- { this . renderDescription ( ) }
207
- { this . renderCodes ( ) }
208
-
209
- < div className = "mfa-register-backup-codes__helper-links" >
210
- { this . renderPrintAction ( ) }
211
- { this . renderDownloadAction ( ) }
212
- { this . renderCopyAction ( ) }
213
- </ div >
214
-
215
- < button className = "btn btn-primary" onClick = { ( ) => onCompleteRegistration ( ) } >
216
- { i18n . _t ( 'MFABackupCodesRegister.FINISH' , 'Finish' ) }
217
- </ button >
218
- </ div >
219
- ) ;
220
- }
145
+ // Render component
146
+ return < div className = "mfa-register-backup-codes__container" >
147
+ { renderDescription ( ) }
148
+ { renderCodes ( ) }
149
+ < div className = "mfa-register-backup-codes__helper-links" >
150
+ { renderPrintAction ( ) }
151
+ { renderDownloadAction ( ) }
152
+ { renderCopyAction ( ) }
153
+ </ div >
154
+ < button className = "btn btn-primary" onClick = { ( ) => onCompleteRegistration ( ) } >
155
+ { i18n . _t ( 'MFABackupCodesRegister.FINISH' , 'Finish' ) }
156
+ </ button >
157
+ </ div > ;
221
158
}
222
159
223
160
Register . propTypes = {
0 commit comments