Compare commits
1 Commits
open-relea
...
alangsto/c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
139689ccf7 |
14113
package-lock.json
generated
14113
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@@ -31,16 +31,17 @@
|
||||
"dependencies": {
|
||||
"@edx/frontend-component-footer": "10.0.11",
|
||||
"@edx/frontend-component-header": "2.0.5",
|
||||
"@edx/frontend-platform": "1.6.1",
|
||||
"@edx/frontend-platform": "1.1.14",
|
||||
"@edx/paragon": "9.1.1",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.32",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.30",
|
||||
"@fortawesome/free-brands-svg-icons": "5.8.2",
|
||||
"@fortawesome/free-regular-svg-icons": "5.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "5.8.2",
|
||||
"@fortawesome/react-fontawesome": "0.1.12",
|
||||
"@tensorflow-models/blazeface": "git+https://github.com/alangsto/blazeface.git",
|
||||
"@tensorflow/tfjs-converter": "1.6.1",
|
||||
"@tensorflow/tfjs-core": "1.6.1",
|
||||
"@fortawesome/react-fontawesome": "0.1.11",
|
||||
"@tensorflow-models/coco-ssd": "1.0.0",
|
||||
"@tensorflow/tfjs": "1.6.0",
|
||||
"@tensorflow/tfjs-converter": "1.6.0",
|
||||
"@tensorflow/tfjs-core": "1.6.0",
|
||||
"babel-polyfill": "6.26.0",
|
||||
"bowser": "^2.10.0",
|
||||
"classnames": "2.2.6",
|
||||
@@ -77,15 +78,15 @@
|
||||
"redux-saga": "1.1.3",
|
||||
"redux-thunk": "2.3.0",
|
||||
"reselect": "4.0.0",
|
||||
"universal-cookie": "4.0.4"
|
||||
"universal-cookie": "4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edx/frontend-build": "5.3.2",
|
||||
"@edx/frontend-build": "2.0.6",
|
||||
"@testing-library/jest-dom": "^5.11.2",
|
||||
"@testing-library/react": "^10.4.7",
|
||||
"codecov": "3.7.2",
|
||||
"enzyme": "3.10.0",
|
||||
"enzyme-adapter-react-16": "1.15.5",
|
||||
"enzyme-adapter-react-16": "1.15.4",
|
||||
"es-check": "5.0.0",
|
||||
"husky": "3.0.9",
|
||||
"jest": "^26.1.0",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<title>Account | edX</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="<%=webpackConfig.output.publicPath%>favicon.ico" type="image/x-icon" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -106,7 +106,6 @@ function EditableField(props) {
|
||||
>
|
||||
<label className="h6 d-block" htmlFor={id}>{label}</label>
|
||||
<Input
|
||||
data-hj-suppress
|
||||
name={name}
|
||||
id={id}
|
||||
type={type}
|
||||
@@ -156,7 +155,7 @@ function EditableField(props) {
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
<p data-hj-suppress>{renderValue(value)}</p>
|
||||
<p>{renderValue(value)}</p>
|
||||
<p className="small text-muted mt-n2">{renderConfirmationMessage() || helpText}</p>
|
||||
</div>
|
||||
),
|
||||
|
||||
@@ -109,7 +109,6 @@ function EmailField(props) {
|
||||
>
|
||||
<label className="h6 d-block" htmlFor={id}>{label}</label>
|
||||
<Input
|
||||
data-hj-suppress
|
||||
name={name}
|
||||
id={id}
|
||||
type="email"
|
||||
@@ -157,7 +156,7 @@ function EmailField(props) {
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
<p data-hj-suppress>{renderValue()}</p>
|
||||
<p>{renderValue()}</p>
|
||||
{renderConfirmationMessage() || <p className="small text-muted mt-n2">{helpText}</p>}
|
||||
</div>
|
||||
),
|
||||
|
||||
@@ -74,9 +74,7 @@ exports[`DemographicsSection should render 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -138,9 +136,7 @@ exports[`DemographicsSection should render 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
<button
|
||||
className="btn btn-link p-0"
|
||||
onBlur={[Function]}
|
||||
@@ -210,9 +206,7 @@ exports[`DemographicsSection should render 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -274,9 +268,7 @@ exports[`DemographicsSection should render 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -338,9 +330,7 @@ exports[`DemographicsSection should render 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -402,9 +392,7 @@ exports[`DemographicsSection should render 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -466,9 +454,7 @@ exports[`DemographicsSection should render 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -530,9 +516,7 @@ exports[`DemographicsSection should render 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -594,9 +578,7 @@ exports[`DemographicsSection should render 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -697,9 +679,7 @@ exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -761,9 +741,7 @@ exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
<button
|
||||
className="btn btn-link p-0"
|
||||
onBlur={[Function]}
|
||||
@@ -833,9 +811,7 @@ exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -897,9 +873,7 @@ exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -961,9 +935,7 @@ exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1025,9 +997,7 @@ exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1089,9 +1059,7 @@ exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1153,9 +1121,7 @@ exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1217,9 +1183,7 @@ exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1341,9 +1305,7 @@ exports[`DemographicsSection should render ethnicity correctly when multiple opt
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1405,9 +1367,7 @@ exports[`DemographicsSection should render ethnicity correctly when multiple opt
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Hispanic, Latin, or Spanish origin, White
|
||||
</p>
|
||||
<p
|
||||
@@ -1469,9 +1429,7 @@ exports[`DemographicsSection should render ethnicity correctly when multiple opt
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1533,9 +1491,7 @@ exports[`DemographicsSection should render ethnicity correctly when multiple opt
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1597,9 +1553,7 @@ exports[`DemographicsSection should render ethnicity correctly when multiple opt
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1661,9 +1615,7 @@ exports[`DemographicsSection should render ethnicity correctly when multiple opt
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1725,9 +1677,7 @@ exports[`DemographicsSection should render ethnicity correctly when multiple opt
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1789,9 +1739,7 @@ exports[`DemographicsSection should render ethnicity correctly when multiple opt
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1853,9 +1801,7 @@ exports[`DemographicsSection should render ethnicity correctly when multiple opt
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -1942,9 +1888,7 @@ exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2006,9 +1950,7 @@ exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Asian
|
||||
</p>
|
||||
<p
|
||||
@@ -2070,9 +2012,7 @@ exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2134,9 +2074,7 @@ exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2198,9 +2136,7 @@ exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2262,9 +2198,7 @@ exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2326,9 +2260,7 @@ exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2390,9 +2322,7 @@ exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2454,9 +2384,7 @@ exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2543,9 +2471,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2607,9 +2533,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
<button
|
||||
className="btn btn-link p-0"
|
||||
onBlur={[Function]}
|
||||
@@ -2679,9 +2603,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2743,9 +2665,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2807,9 +2727,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2871,9 +2789,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -2935,9 +2851,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Other: test
|
||||
</p>
|
||||
<p
|
||||
@@ -2999,9 +2913,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -3063,9 +2975,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -3152,9 +3062,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer to self describe: test
|
||||
</p>
|
||||
<p
|
||||
@@ -3216,9 +3124,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
<button
|
||||
className="btn btn-link p-0"
|
||||
onBlur={[Function]}
|
||||
@@ -3288,9 +3194,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -3352,9 +3256,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -3416,9 +3318,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -3480,9 +3380,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -3544,9 +3442,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -3608,9 +3504,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
@@ -3672,9 +3566,7 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
data-hj-suppress={true}
|
||||
>
|
||||
<p>
|
||||
Prefer not to respond
|
||||
</p>
|
||||
<p
|
||||
|
||||
@@ -182,21 +182,6 @@
|
||||
"id.verification.existing.request.pending.text": "You have already submitted your verification information. You will see a message on your dashboard when the verification process is complete (usually within 5 days).",
|
||||
"id.verification.photo.take": "Take Photo",
|
||||
"id.verification.photo.retake": "Retake Photo",
|
||||
"id.verification.photo.enable.detection": "Enable Face Detection",
|
||||
"id.verification.photo.enable.detection.portrait.help.text": "If checked, a box will appear around your face. Your face can be seen clearly if the box around it is blue. If your face is not in a good position or undetectable, the box will be red.",
|
||||
"id.verification.photo.enable.detection.id.help.text": "If checked, a box will appear around the face on your ID card. The face can be seen clearly if the box around it is blue. If the face is not in a good position or undetectable, the box will be red.",
|
||||
"id.verification.photo.feedback.correct": "Face is in a good position.",
|
||||
"id.verification.photo.feedback.two.faces": "More than one face detected.",
|
||||
"id.verification.photo.feedback.no.faces": "No face detected.",
|
||||
"id.verification.photo.feedback.top.left": "Incorrect position. Top left.",
|
||||
"id.verification.photo.feedback.top.center": "Incorrect position. Top center.",
|
||||
"id.verification.photo.feedback.top.right": "Incorrect position. Top right.",
|
||||
"id.verification.photo.feedback.center.left": "Incorrect position. Center left.",
|
||||
"id.verification.photo.feedback.center.center": "Incorrect position. Too close to camera.",
|
||||
"id.verification.photo.feedback.center.right": "Incorrect position. Center right.",
|
||||
"id.verification.photo.feedback.bottom.left": "Incorrect position. Bottom left.",
|
||||
"id.verification.photo.feedback.bottom.center": "Incorrect position. Bottom center.",
|
||||
"id.verification.photo.feedback.bottom.right": "Incorrect position. Bottom right.",
|
||||
"id.verification.camera.access.title": "Camera Permissions",
|
||||
"id.verification.camera.access.title.success": "Camera Access Enabled",
|
||||
"id.verification.camera.access.title.failed": "Camera Access Failed",
|
||||
@@ -258,8 +243,7 @@
|
||||
"id.verification.id.photo.title.upload": "Upload a Photo of Your ID",
|
||||
"id.verification.id.photo.preview.alt": "Preview of photo ID.",
|
||||
"id.verification.id.photo.instructions.camera": "When your ID is in position, use the Take Photo button below to take your photo.",
|
||||
"id.verification.id.photo.instructions.upload": "Please upload an ID photo. Ensure the entire ID fits inside the frame and is well-lit. The file size must be under 10 MB. (Supported formats: .jpg, .jpeg, .png)",
|
||||
"id.verification.id.photo.instructions.upload.error": "The file you have selected is too large. Please try again with a file less than 10MB.",
|
||||
"id.verification.id.photo.instructions.upload": "Please upload an ID photo. Ensure the entire ID fits inside the frame and is well-lit. (Supported formats: .jpg, .jpeg, .png)",
|
||||
"id.verification.account.name.title": "Account Name Check",
|
||||
"id.verification.account.name.instructions": "The name on your account and the name on your ID must be an exact match. If not, please click \"No\" to update your account name.",
|
||||
"id.verification.account.name.radio.label": "Does the name on your ID match the Account Name below?",
|
||||
@@ -280,13 +264,11 @@
|
||||
"id.verification.review.id.alt": "Photo of your ID to be submitted.",
|
||||
"id.verification.review.id.retake": "Retake ID Photo",
|
||||
"id.verification.review.confirm": "Submit",
|
||||
"id.verification.review.error": "edX Support Page",
|
||||
"id.verification.submitted.title": "Identity Verification in Progress",
|
||||
"id.verification.submitted.text": "We have received your information and are verifying your identity. You will see a message on your dashboard when the verification process is complete (usually within 5 days). In the meantime, you can still access all available course content.",
|
||||
"id.verification.return.dashboard": "Return to Your Dashboard",
|
||||
"id.verification.return.course": "Return to Course",
|
||||
"id.verification.request.camera.access.instructions": "In order to take a photo using your webcam, you may receive a browser prompt for access to your camera. {clickAllow}",
|
||||
"id.verification.requirements.card.device.text": "You need a device that has a camera. If you receive a browser prompt for access to your camera, please make sure to click {allow}.",
|
||||
"idv.submission.alert.error": "\n We encountered a technical error while trying to submit ID verification.\n This might be a temporary issue, so please try again in a few minutes.\n If the problem persists,\n please go to {support_link} for help.\n ",
|
||||
"id.verification.account.name.edit": "Edit{sr}"
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
"account.settings.section.account.information": "Información de la cuenta",
|
||||
"account.settings.section.account.information.description": "Estas configuraciones incluyen información básica sobre tu cuenta.",
|
||||
"account.settings.section.profile.information": "Información del perfil",
|
||||
"account.settings.section.demographics.information": "Información opcional",
|
||||
"account.settings.section.demographics.information": "Optional Information",
|
||||
"account.settings.section.site.preferences": "Preferencias del sitio",
|
||||
"account.settings.section.linked.accounts": "Cuentas vinculadas",
|
||||
"account.settings.section.linked.accounts.description": "Puedes vincular tus cuentas de redes sociales para simplificar el proceso de iniciar sesión en edX.",
|
||||
@@ -117,43 +117,43 @@
|
||||
"account.settings.delete.account.modal.confirm.cancel": "Cancelar",
|
||||
"account.settings.delete.account.error.unable.to.delete": "No es posible eliminar esta cuenta",
|
||||
"account.settings.delete.account.error.no.password": "Se requiere una contraseña",
|
||||
"account.settings.delete.account.error.invalid.password": "Contraseña incorrecta",
|
||||
"account.settings.delete.account.error.invalid.password": "Password is incorrect",
|
||||
"account.settings.delete.account.error.unable.to.delete.details": "Ocurrió un error al procesar tu solicitud. Por favor, intente nuevamente más tarde.",
|
||||
"account.settings.delete.account.modal.after.header": "¡Sentimos que te vayas! Tu cuenta será eliminada en breve.",
|
||||
"account.settings.delete.account.modal.after.text": "La eliminación de cuenta, incluyendo la eliminación de las listas de correo electrónico, puede tardar unas semanas en procesarse totalmente en nuestro sistema. Si quieres renunciar a recibir correos antes de que la eliminación se haya completado, por favor date de baja mediante el enlace que aparece al final de los correos.",
|
||||
"account.settings.delete.account.modal.after.button": "Cerrar",
|
||||
"account.settings.delete.account.text.3": "Puede que también pierdas el acceso a los certificados verificados y otros certificados de programas como los de los MicroMasters. Si quieres hacer una copia de dichos certificados para tus archivos antes de proceder a la eliminación, {actionLink}.",
|
||||
"account.settings.message.demographics.service.issue": "Ocurrió un error al intentar recuperar o guardar la información de tu cuenta. Por favor inténtalo más tarde.",
|
||||
"account.settings.field.demographics.gender": "Identidad de género",
|
||||
"account.settings.field.demographics.gender.empty": "Añade identidad de género",
|
||||
"account.settings.field.demographics.gender.options.empty": "Selecciona una identidad de género",
|
||||
"account.settings.field.demographics.gender_description": "Descripción de identidad de género",
|
||||
"account.settings.field.demographics.gender_description.empty": "Ingresa descripción",
|
||||
"account.settings.field.demographics.ethnicity": "Identidad étnica/raza",
|
||||
"account.settings.field.demographics.ethnicity.empty": "Añade identidad étnica/raza",
|
||||
"account.settings.field.demographics.ethnicity.options.empty": "Selecciona todas las que apliquen",
|
||||
"account.settings.field.demographics.income": "Ingreso familiar",
|
||||
"account.settings.field.demographics.income.empty": "Añade ingreso familiar",
|
||||
"account.settings.field.demographics.income.options.empty": "Selecciona un rango de ingreso familiar",
|
||||
"account.settings.field.demographics.military_history": "Estatus militar en EE.UU.",
|
||||
"account.settings.message.demographics.service.issue": "An error occurred attempting to retrieve or save your account information. Please try again later.",
|
||||
"account.settings.field.demographics.gender": "Gender identity",
|
||||
"account.settings.field.demographics.gender.empty": "Add gender identity",
|
||||
"account.settings.field.demographics.gender.options.empty": "Select a gender identity",
|
||||
"account.settings.field.demographics.gender_description": "Gender identity description",
|
||||
"account.settings.field.demographics.gender_description.empty": "Enter description",
|
||||
"account.settings.field.demographics.ethnicity": "Race/Ethnicity identity",
|
||||
"account.settings.field.demographics.ethnicity.empty": "Add race/ethnicity identity",
|
||||
"account.settings.field.demographics.ethnicity.options.empty": "Select all that apply",
|
||||
"account.settings.field.demographics.income": "Family income",
|
||||
"account.settings.field.demographics.income.empty": "Add family income",
|
||||
"account.settings.field.demographics.income.options.empty": "Select a family income range",
|
||||
"account.settings.field.demographics.military_history": "U.S. Military status",
|
||||
"account.settings.field.demographics.military_history.empty": "Add military status",
|
||||
"account.settings.field.demographics.military_history.options.empty": "Selecciona estatus militar",
|
||||
"account.settings.field.demographics.learner_education_level": "Tu nivel educacional",
|
||||
"account.settings.field.demographics.learner_education_level.empty": "Añade nivel educacional",
|
||||
"account.settings.field.demographics.parent_education_level": "Nivel educacional de padres/tutores",
|
||||
"account.settings.field.demographics.parent_education_level.empty": "Añade nivel educacional",
|
||||
"account.settings.field.demographics.education_level.options.empty": "Selecciona nivel educacional",
|
||||
"account.settings.field.demographics.work_status": "Estatus laboral",
|
||||
"account.settings.field.demographics.work_status.empty": "Añade estatus laboral",
|
||||
"account.settings.field.demographics.work_status.options.empty": "Selecciona estatus laboral",
|
||||
"account.settings.field.demographics.work_status_description": "Descripción estatus laboral",
|
||||
"account.settings.field.demographics.work_status_description.empty": "Ingresa descripción",
|
||||
"account.settings.field.demographics.current_work_sector": "Área profesional actual",
|
||||
"account.settings.field.demographics.current_work_sector.empty": "Añade área profesional",
|
||||
"account.settings.field.demographics.future_work_sector": "Área profesional futura",
|
||||
"account.settings.field.demographics.future_work_sector.empty": "Añade área profesional",
|
||||
"account.settings.field.demographics.work_sector.options.empty": "Selecciona área profesional",
|
||||
"account.settings.section.demographics.why": "¿Por qué edX obtiene esta información?",
|
||||
"account.settings.field.demographics.military_history.options.empty": "Select military status",
|
||||
"account.settings.field.demographics.learner_education_level": "Your education level",
|
||||
"account.settings.field.demographics.learner_education_level.empty": "Add education level",
|
||||
"account.settings.field.demographics.parent_education_level": "Parents/Guardians education level",
|
||||
"account.settings.field.demographics.parent_education_level.empty": "Add education level",
|
||||
"account.settings.field.demographics.education_level.options.empty": "Select education level",
|
||||
"account.settings.field.demographics.work_status": "Employment status",
|
||||
"account.settings.field.demographics.work_status.empty": "Add employment status",
|
||||
"account.settings.field.demographics.work_status.options.empty": "Select employment status",
|
||||
"account.settings.field.demographics.work_status_description": "Employment status description",
|
||||
"account.settings.field.demographics.work_status_description.empty": "Enter description",
|
||||
"account.settings.field.demographics.current_work_sector": "Current work industry",
|
||||
"account.settings.field.demographics.current_work_sector.empty": "Add work industry",
|
||||
"account.settings.field.demographics.future_work_sector": "Future work industry",
|
||||
"account.settings.field.demographics.future_work_sector.empty": "Add work industry",
|
||||
"account.settings.field.demographics.work_sector.options.empty": "Select work industry",
|
||||
"account.settings.section.demographics.why": "Why does edX collect this information?",
|
||||
"error.notfound.message": "La página que estas buscando no está disponible o hay un error en la URL. Por favor, comprueba la URL y vuelve a intentarlo.",
|
||||
"account.settings.editable.field.password.reset.button.confirmation.support.link": "soporte técnico",
|
||||
"account.settings.editable.field.password.reset.button.confirmation": "Hemos mandado un mensaje a {email}. Haz clic en el enlace en el mensaje para restablecer tu contraseña. ¿No recibiste el mensaje? Contáctate con {technicalSupportLink}.",
|
||||
@@ -166,45 +166,30 @@
|
||||
"account.settings.sso.unlink.account": "Desvincular la cuenta de {accountName} ",
|
||||
"account.settings.sso.no.providers": "No se pueden vincular cuentas en este momento.",
|
||||
"id.verification.existing.request.denied.text": "You cannot verify your identity at this time. If you have yet to activate your account, please check your spam folder for the activation email from {email}.",
|
||||
"id.verification.next": "Siguiente",
|
||||
"id.verification.requirements.title": "Requerimientos de verificación por foto",
|
||||
"id.verification.requirements.description": "Para completar la verificación por foto en línea, necesitarás lo siguiente:",
|
||||
"id.verification.requirements.card.device.title": "Dispositivo con cámara",
|
||||
"id.verification.requirements.card.device.allow": "Permitir",
|
||||
"id.verification.requirements.card.id.title": "Identificación por foto",
|
||||
"id.verification.requirements.card.id.text": "Necesitas un ID válido que contenga tu nombre completo y tu foto.",
|
||||
"id.verification.next": "Next",
|
||||
"id.verification.requirements.title": "Photo Verification Requirements",
|
||||
"id.verification.requirements.description": "In order to complete Photo Verification online, you will need the following:",
|
||||
"id.verification.requirements.card.device.title": "Device with Camera",
|
||||
"id.verification.requirements.card.device.allow": "Allow",
|
||||
"id.verification.requirements.card.id.title": "Photo Identification",
|
||||
"id.verification.requirements.card.id.text": "You need a valid ID that contains your full name and photo.",
|
||||
"id.verification.privacy.title": "Privacy Information",
|
||||
"id.verification.privacy.need.photo.question": "¿Por qué edX necesita mi foto?",
|
||||
"id.verification.privacy.need.photo.answer": "Utilizamos tus fotos de verificación para confirmar tu identidad y garantizar la validez de tu certificado.",
|
||||
"id.verification.privacy.do.with.photo.question": "¿Qué hace edX con esta foto?",
|
||||
"id.verification.privacy.need.photo.question": "Why does edX need my photo?",
|
||||
"id.verification.privacy.need.photo.answer": "We use your verification photos to confirm your identity and ensure the validity of your certificate.",
|
||||
"id.verification.privacy.do.with.photo.question": "What does edX do with this photo?",
|
||||
"id.verification.privacy.do.with.photo.answer": "We securely encrypt your photo and send it our authorization service for review. Your photo and information are not saved or visible anywhere on edX after the verification process is complete.",
|
||||
"id.verification.existing.request.title": "Verificación de identidad",
|
||||
"id.verification.existing.request.title": "Identity Verification",
|
||||
"id.verification.existing.request.pending.text": "You have already submitted your verification information. You will see a message on your dashboard when the verification process is complete (usually within 5 days).",
|
||||
"id.verification.photo.take": "Tomar la foto",
|
||||
"id.verification.photo.retake": "Tomar nuevamente la foto",
|
||||
"id.verification.photo.enable.detection": "Enable Face Detection",
|
||||
"id.verification.photo.enable.detection.portrait.help.text": "If checked, a box will appear around your face. Your face can be seen clearly if the box around it is blue. If your face is not in a good position or undetectable, the box will be red.",
|
||||
"id.verification.photo.enable.detection.id.help.text": "If checked, a box will appear around the face on your ID card. The face can be seen clearly if the box around it is blue. If the face is not in a good position or undetectable, the box will be red.",
|
||||
"id.verification.photo.feedback.correct": "Face is in a good position.",
|
||||
"id.verification.photo.feedback.two.faces": "More than one face detected.",
|
||||
"id.verification.photo.feedback.no.faces": "No face detected.",
|
||||
"id.verification.photo.feedback.top.left": "Incorrect position. Top left.",
|
||||
"id.verification.photo.feedback.top.center": "Incorrect position. Top center.",
|
||||
"id.verification.photo.feedback.top.right": "Incorrect position. Top right.",
|
||||
"id.verification.photo.feedback.center.left": "Incorrect position. Center left.",
|
||||
"id.verification.photo.feedback.center.center": "Incorrect position. Too close to camera.",
|
||||
"id.verification.photo.feedback.center.right": "Incorrect position. Center right.",
|
||||
"id.verification.photo.feedback.bottom.left": "Incorrect position. Bottom left.",
|
||||
"id.verification.photo.feedback.bottom.center": "Incorrect position. Bottom center.",
|
||||
"id.verification.photo.feedback.bottom.right": "Incorrect position. Bottom right.",
|
||||
"id.verification.camera.access.title": "Permisos de la cámara",
|
||||
"id.verification.photo.take": "Take Photo",
|
||||
"id.verification.photo.retake": "Retake Photo",
|
||||
"id.verification.camera.access.title": "Camera Permissions",
|
||||
"id.verification.camera.access.title.success": "Camera Access Enabled",
|
||||
"id.verification.camera.access.title.failed": "Camera Access Failed",
|
||||
"id.verification.camera.access.click.allow": "Por favor asegúrate de hacer clic en \"Permitir\"",
|
||||
"id.verification.camera.access.enable": "Habilitar cámara",
|
||||
"id.verification.camera.access.problems": "¿Tienes problemas?",
|
||||
"id.verification.camera.access.skip": "Omitir y cargar un archivo de imagen",
|
||||
"id.verification.camera.access.success": "Parece que tu cámara está funcionando y está lista.",
|
||||
"id.verification.camera.access.click.allow": "Please make sure to click \"Allow\"",
|
||||
"id.verification.camera.access.enable": "Enable Camera",
|
||||
"id.verification.camera.access.problems": "Having problems?",
|
||||
"id.verification.camera.access.skip": "Skip and upload image files instead",
|
||||
"id.verification.camera.access.success": "Looks like your camera is working and ready.",
|
||||
"id.verification.camera.access.failure": "It looks like we're unable to access your camera. You will need to upload image files of you and your photo id.",
|
||||
"id.verification.camera.access.failure.temporary": "It looks like we're unable to access your camera. Please verify that your webcam is connected and that you have allowed your browser to access it.",
|
||||
"id.verification.camera.access.failure.temporary.chrome": "To enable camera access in Chrome:",
|
||||
@@ -232,7 +217,7 @@
|
||||
"id.verification.camera.access.failure.temporary.safari.step2": "Click on the Safari app menu, then select \"Preferences.\" You can also use Command+, as a keyboard shortcut.",
|
||||
"id.verification.camera.access.failure.temporary.safari.step3": "Select the \"Websites\" tab and then select \"Camera.\"",
|
||||
"id.verification.camera.access.failure.temporary.safari.step4": "Select \"edx.org\" and change the camera permissions to \"Allow.\"",
|
||||
"id.verification.photo.tips.title": "Consejos útiles de fotos",
|
||||
"id.verification.photo.tips.title": "Helpful Photo Tips",
|
||||
"id.verification.photo.tips.description": "Next, we'll need you to take a photo of your face. Please review the helpful tips below.",
|
||||
"id.verification.photo.tips.list.title": "Photo Tips",
|
||||
"id.verification.photo.tips.list.description": "To take a successful photo, make sure that:",
|
||||
@@ -258,8 +243,7 @@
|
||||
"id.verification.id.photo.title.upload": "Upload a Photo of Your ID",
|
||||
"id.verification.id.photo.preview.alt": "Preview of photo ID.",
|
||||
"id.verification.id.photo.instructions.camera": "When your ID is in position, use the Take Photo button below to take your photo.",
|
||||
"id.verification.id.photo.instructions.upload": "Please upload an ID photo. Ensure the entire ID fits inside the frame and is well-lit. The file size must be under 10 MB. (Supported formats: .jpg, .jpeg, .png)",
|
||||
"id.verification.id.photo.instructions.upload.error": "The file you have selected is too large. Please try again with a file less than 10MB.",
|
||||
"id.verification.id.photo.instructions.upload": "Please upload an ID photo. Ensure the entire ID fits inside the frame and is well-lit. (Supported formats: .jpg, .jpeg, .png)",
|
||||
"id.verification.account.name.title": "Account Name Check",
|
||||
"id.verification.account.name.instructions": "The name on your account and the name on your ID must be an exact match. If not, please click \"No\" to update your account name.",
|
||||
"id.verification.account.name.radio.label": "Does the name on your ID match the Account Name below?",
|
||||
@@ -280,13 +264,11 @@
|
||||
"id.verification.review.id.alt": "Photo of your ID to be submitted.",
|
||||
"id.verification.review.id.retake": "Retake ID Photo",
|
||||
"id.verification.review.confirm": "Submit",
|
||||
"id.verification.review.error": "edX Support Page",
|
||||
"id.verification.submitted.title": "Identity Verification in Progress",
|
||||
"id.verification.submitted.text": "We have received your information and are verifying your identity. You will see a message on your dashboard when the verification process is complete (usually within 5 days). In the meantime, you can still access all available course content.",
|
||||
"id.verification.return.dashboard": "Return to Your Dashboard",
|
||||
"id.verification.return.course": "Return to Course",
|
||||
"id.verification.request.camera.access.instructions": "In order to take a photo using your webcam, you may receive a browser prompt for access to your camera. {clickAllow}",
|
||||
"id.verification.requirements.card.device.text": "You need a device that has a camera. If you receive a browser prompt for access to your camera, please make sure to click {allow}.",
|
||||
"idv.submission.alert.error": "\n We encountered a technical error while trying to submit ID verification.\n This might be a temporary issue, so please try again in a few minutes.\n If the problem persists,\n please go to {support_link} for help.\n ",
|
||||
"id.verification.account.name.edit": "Edit{sr}"
|
||||
}
|
||||
@@ -182,21 +182,6 @@
|
||||
"id.verification.existing.request.pending.text": "You have already submitted your verification information. You will see a message on your dashboard when the verification process is complete (usually within 5 days).",
|
||||
"id.verification.photo.take": "Take Photo",
|
||||
"id.verification.photo.retake": "Retake Photo",
|
||||
"id.verification.photo.enable.detection": "Enable Face Detection",
|
||||
"id.verification.photo.enable.detection.portrait.help.text": "If checked, a box will appear around your face. Your face can be seen clearly if the box around it is blue. If your face is not in a good position or undetectable, the box will be red.",
|
||||
"id.verification.photo.enable.detection.id.help.text": "If checked, a box will appear around the face on your ID card. The face can be seen clearly if the box around it is blue. If the face is not in a good position or undetectable, the box will be red.",
|
||||
"id.verification.photo.feedback.correct": "Face is in a good position.",
|
||||
"id.verification.photo.feedback.two.faces": "More than one face detected.",
|
||||
"id.verification.photo.feedback.no.faces": "No face detected.",
|
||||
"id.verification.photo.feedback.top.left": "Incorrect position. Top left.",
|
||||
"id.verification.photo.feedback.top.center": "Incorrect position. Top center.",
|
||||
"id.verification.photo.feedback.top.right": "Incorrect position. Top right.",
|
||||
"id.verification.photo.feedback.center.left": "Incorrect position. Center left.",
|
||||
"id.verification.photo.feedback.center.center": "Incorrect position. Too close to camera.",
|
||||
"id.verification.photo.feedback.center.right": "Incorrect position. Center right.",
|
||||
"id.verification.photo.feedback.bottom.left": "Incorrect position. Bottom left.",
|
||||
"id.verification.photo.feedback.bottom.center": "Incorrect position. Bottom center.",
|
||||
"id.verification.photo.feedback.bottom.right": "Incorrect position. Bottom right.",
|
||||
"id.verification.camera.access.title": "Camera Permissions",
|
||||
"id.verification.camera.access.title.success": "Camera Access Enabled",
|
||||
"id.verification.camera.access.title.failed": "Camera Access Failed",
|
||||
@@ -258,8 +243,7 @@
|
||||
"id.verification.id.photo.title.upload": "Upload a Photo of Your ID",
|
||||
"id.verification.id.photo.preview.alt": "Preview of photo ID.",
|
||||
"id.verification.id.photo.instructions.camera": "When your ID is in position, use the Take Photo button below to take your photo.",
|
||||
"id.verification.id.photo.instructions.upload": "Please upload an ID photo. Ensure the entire ID fits inside the frame and is well-lit. The file size must be under 10 MB. (Supported formats: .jpg, .jpeg, .png)",
|
||||
"id.verification.id.photo.instructions.upload.error": "The file you have selected is too large. Please try again with a file less than 10MB.",
|
||||
"id.verification.id.photo.instructions.upload": "Please upload an ID photo. Ensure the entire ID fits inside the frame and is well-lit. (Supported formats: .jpg, .jpeg, .png)",
|
||||
"id.verification.account.name.title": "Account Name Check",
|
||||
"id.verification.account.name.instructions": "The name on your account and the name on your ID must be an exact match. If not, please click \"No\" to update your account name.",
|
||||
"id.verification.account.name.radio.label": "Does the name on your ID match the Account Name below?",
|
||||
@@ -280,13 +264,11 @@
|
||||
"id.verification.review.id.alt": "Photo of your ID to be submitted.",
|
||||
"id.verification.review.id.retake": "Retake ID Photo",
|
||||
"id.verification.review.confirm": "Submit",
|
||||
"id.verification.review.error": "edX Support Page",
|
||||
"id.verification.submitted.title": "Identity Verification in Progress",
|
||||
"id.verification.submitted.text": "We have received your information and are verifying your identity. You will see a message on your dashboard when the verification process is complete (usually within 5 days). In the meantime, you can still access all available course content.",
|
||||
"id.verification.return.dashboard": "Return to Your Dashboard",
|
||||
"id.verification.return.course": "Return to Course",
|
||||
"id.verification.request.camera.access.instructions": "In order to take a photo using your webcam, you may receive a browser prompt for access to your camera. {clickAllow}",
|
||||
"id.verification.requirements.card.device.text": "You need a device that has a camera. If you receive a browser prompt for access to your camera, please make sure to click {allow}.",
|
||||
"idv.submission.alert.error": "\n We encountered a technical error while trying to submit ID verification.\n This might be a temporary issue, so please try again in a few minutes.\n If the problem persists,\n please go to {support_link} for help.\n ",
|
||||
"id.verification.account.name.edit": "Edit{sr}"
|
||||
}
|
||||
@@ -182,21 +182,6 @@
|
||||
"id.verification.existing.request.pending.text": "You have already submitted your verification information. You will see a message on your dashboard when the verification process is complete (usually within 5 days).",
|
||||
"id.verification.photo.take": "Take Photo",
|
||||
"id.verification.photo.retake": "Retake Photo",
|
||||
"id.verification.photo.enable.detection": "Enable Face Detection",
|
||||
"id.verification.photo.enable.detection.portrait.help.text": "If checked, a box will appear around your face. Your face can be seen clearly if the box around it is blue. If your face is not in a good position or undetectable, the box will be red.",
|
||||
"id.verification.photo.enable.detection.id.help.text": "If checked, a box will appear around the face on your ID card. The face can be seen clearly if the box around it is blue. If the face is not in a good position or undetectable, the box will be red.",
|
||||
"id.verification.photo.feedback.correct": "Face is in a good position.",
|
||||
"id.verification.photo.feedback.two.faces": "More than one face detected.",
|
||||
"id.verification.photo.feedback.no.faces": "No face detected.",
|
||||
"id.verification.photo.feedback.top.left": "Incorrect position. Top left.",
|
||||
"id.verification.photo.feedback.top.center": "Incorrect position. Top center.",
|
||||
"id.verification.photo.feedback.top.right": "Incorrect position. Top right.",
|
||||
"id.verification.photo.feedback.center.left": "Incorrect position. Center left.",
|
||||
"id.verification.photo.feedback.center.center": "Incorrect position. Too close to camera.",
|
||||
"id.verification.photo.feedback.center.right": "Incorrect position. Center right.",
|
||||
"id.verification.photo.feedback.bottom.left": "Incorrect position. Bottom left.",
|
||||
"id.verification.photo.feedback.bottom.center": "Incorrect position. Bottom center.",
|
||||
"id.verification.photo.feedback.bottom.right": "Incorrect position. Bottom right.",
|
||||
"id.verification.camera.access.title": "Camera Permissions",
|
||||
"id.verification.camera.access.title.success": "Camera Access Enabled",
|
||||
"id.verification.camera.access.title.failed": "Camera Access Failed",
|
||||
@@ -258,8 +243,7 @@
|
||||
"id.verification.id.photo.title.upload": "Upload a Photo of Your ID",
|
||||
"id.verification.id.photo.preview.alt": "Preview of photo ID.",
|
||||
"id.verification.id.photo.instructions.camera": "When your ID is in position, use the Take Photo button below to take your photo.",
|
||||
"id.verification.id.photo.instructions.upload": "Please upload an ID photo. Ensure the entire ID fits inside the frame and is well-lit. The file size must be under 10 MB. (Supported formats: .jpg, .jpeg, .png)",
|
||||
"id.verification.id.photo.instructions.upload.error": "The file you have selected is too large. Please try again with a file less than 10MB.",
|
||||
"id.verification.id.photo.instructions.upload": "Please upload an ID photo. Ensure the entire ID fits inside the frame and is well-lit. (Supported formats: .jpg, .jpeg, .png)",
|
||||
"id.verification.account.name.title": "Account Name Check",
|
||||
"id.verification.account.name.instructions": "The name on your account and the name on your ID must be an exact match. If not, please click \"No\" to update your account name.",
|
||||
"id.verification.account.name.radio.label": "Does the name on your ID match the Account Name below?",
|
||||
@@ -280,13 +264,11 @@
|
||||
"id.verification.review.id.alt": "Photo of your ID to be submitted.",
|
||||
"id.verification.review.id.retake": "Retake ID Photo",
|
||||
"id.verification.review.confirm": "Submit",
|
||||
"id.verification.review.error": "edX Support Page",
|
||||
"id.verification.submitted.title": "Identity Verification in Progress",
|
||||
"id.verification.submitted.text": "We have received your information and are verifying your identity. You will see a message on your dashboard when the verification process is complete (usually within 5 days). In the meantime, you can still access all available course content.",
|
||||
"id.verification.return.dashboard": "Return to Your Dashboard",
|
||||
"id.verification.return.course": "Return to Course",
|
||||
"id.verification.request.camera.access.instructions": "In order to take a photo using your webcam, you may receive a browser prompt for access to your camera. {clickAllow}",
|
||||
"id.verification.requirements.card.device.text": "You need a device that has a camera. If you receive a browser prompt for access to your camera, please make sure to click {allow}.",
|
||||
"idv.submission.alert.error": "\n We encountered a technical error while trying to submit ID verification.\n This might be a temporary issue, so please try again in a few minutes.\n If the problem persists,\n please go to {support_link} for help.\n ",
|
||||
"id.verification.account.name.edit": "Edit{sr}"
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import * as blazeface from '@tensorflow-models/blazeface';
|
||||
import * as cocoSsd from '@tensorflow-models/coco-ssd';
|
||||
import CameraPhoto, { FACING_MODES } from 'jslib-html5-camera-photo';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Form, Spinner } from '@edx/paragon';
|
||||
@@ -18,42 +17,22 @@ class Camera extends React.Component {
|
||||
this.setDetection = this.setDetection.bind(this);
|
||||
this.state = {
|
||||
dataUri: '',
|
||||
outlineColor: '#ff3300',
|
||||
videoHasLoaded: false,
|
||||
shouldDetect: false,
|
||||
isFinishedLoadingDetection: true,
|
||||
shouldGiveFeedback: true,
|
||||
feedback: '',
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.cameraPhoto = new CameraPhoto(this.videoRef.current);
|
||||
this.cameraPhoto.startCamera(
|
||||
this.props.isPortrait ? FACING_MODES.USER : FACING_MODES.ENVIRONMENT,
|
||||
{ width: 640, height: 480 },
|
||||
);
|
||||
this.cameraPhoto.startCamera(FACING_MODES.USER, { width: 1280 });
|
||||
}
|
||||
|
||||
async componentWillUnmount() {
|
||||
this.cameraPhoto.stopCamera();
|
||||
}
|
||||
|
||||
sendEvent() {
|
||||
let eventName = 'edx.id_verification';
|
||||
if (this.props.isPortrait) {
|
||||
eventName += '.user_photo';
|
||||
} else {
|
||||
eventName += '.id_photo';
|
||||
}
|
||||
|
||||
if (this.state.shouldDetect) {
|
||||
eventName += '.face_detection_enabled';
|
||||
} else {
|
||||
eventName += '.face_detection_disabled';
|
||||
}
|
||||
sendTrackEvent(eventName);
|
||||
}
|
||||
|
||||
setDetection() {
|
||||
this.setState(
|
||||
{ shouldDetect: !this.state.shouldDetect },
|
||||
@@ -62,7 +41,6 @@ class Camera extends React.Component {
|
||||
this.setState({ isFinishedLoadingDetection: false });
|
||||
this.startDetection();
|
||||
}
|
||||
this.sendEvent();
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -70,7 +48,7 @@ class Camera extends React.Component {
|
||||
startDetection() {
|
||||
setTimeout(() => {
|
||||
if (this.state.videoHasLoaded) {
|
||||
const loadModelPromise = blazeface.load();
|
||||
const loadModelPromise = cocoSsd.load();
|
||||
Promise.all([loadModelPromise])
|
||||
.then((values) => {
|
||||
this.setState({ isFinishedLoadingDetection: true });
|
||||
@@ -85,7 +63,7 @@ class Camera extends React.Component {
|
||||
}
|
||||
|
||||
detectFromVideoFrame = (model, video) => {
|
||||
model.estimateFaces(video).then((predictions) => {
|
||||
model.detect(video).then((predictions) => {
|
||||
if (this.state.shouldDetect && !this.state.dataUri) {
|
||||
this.showDetections(predictions);
|
||||
|
||||
@@ -104,117 +82,42 @@ class Camera extends React.Component {
|
||||
}
|
||||
// predictions is an array of objects describing each detected face
|
||||
predictions.forEach((prediction) => {
|
||||
const start = [prediction.topLeft[0], prediction.topLeft[1]];
|
||||
const end = [prediction.bottomRight[0], prediction.bottomRight[1]];
|
||||
const size = [end[0] - start[0], end[1] - start[1]];
|
||||
if (prediction.class === 'person') {
|
||||
const xAdjustment = 70;
|
||||
const yAdjustment = 55;
|
||||
const x = prediction.bbox[0] - xAdjustment;
|
||||
const y = prediction.bbox[1] - yAdjustment;
|
||||
const width = prediction.bbox[2];
|
||||
|
||||
// landmarks is an array of points representing each facial landmark (i.e. right eye, left eye, nose, etc.)
|
||||
const features = prediction.landmarks;
|
||||
let isInPosition = true;
|
||||
let isInPosition;
|
||||
|
||||
// for each of the landmarks, determine if it is in position
|
||||
for (let j = 0; j < features.length; j++) {
|
||||
const x = features[j][0];
|
||||
const y = features[j][1];
|
||||
|
||||
let isInRange;
|
||||
if (this.props.isPortrait) {
|
||||
isInRange = this.isInRangeForPortrait(x, y);
|
||||
isInPosition = this.isInRangeForPortrait(x, y, width);
|
||||
} else {
|
||||
isInRange = this.isInRangeForID(x, y);
|
||||
isInPosition = this.isInRangeForID(x, y, width);
|
||||
}
|
||||
// if it is not in range, give feedback depending on which feature is out of range
|
||||
isInPosition = isInPosition && isInRange;
|
||||
}
|
||||
|
||||
// draw a box depending on if all landmarks are in position
|
||||
if (isInPosition) {
|
||||
canvasContext.strokeStyle = '#00ffff';
|
||||
canvasContext.lineWidth = 6;
|
||||
canvasContext.strokeRect(start[0], start[1], size[0], size[1]);
|
||||
// give positive feedback here if user is in correct position
|
||||
this.giveFeedback(predictions.length, [], true);
|
||||
} else {
|
||||
canvasContext.fillStyle = 'rgba(255, 51, 0, 0.75)';
|
||||
canvasContext.fillRect(start[0], start[1], size[0], size[1]);
|
||||
this.giveFeedback(predictions.length, features[0], false);
|
||||
// set the color depending on if all landmarks are in position
|
||||
if (isInPosition) {
|
||||
this.setState({ outlineColor: '#00ffff' });
|
||||
} else {
|
||||
this.setState({ outlineColor: '#ff3300' });
|
||||
}
|
||||
|
||||
// Draw the bounding box.
|
||||
canvasContext.strokeStyle = this.state.outlineColor;
|
||||
canvasContext.lineWidth = 15;
|
||||
canvasContext.strokeRect(0, 0, canvasContext.canvas.width, canvasContext.canvas.height);
|
||||
}
|
||||
});
|
||||
|
||||
if (predictions.length === 0) {
|
||||
this.giveFeedback(predictions.length, [], false);
|
||||
}
|
||||
}
|
||||
|
||||
giveFeedback(numFaces, rightEye, isCorrect) {
|
||||
if (this.state.shouldGiveFeedback) {
|
||||
const currentFeedback = this.state.feedback;
|
||||
let newFeedback = '';
|
||||
if (numFaces === 1) {
|
||||
// only give feedback if one face is detected otherwise
|
||||
// it would be difficult to tell a user which face to move
|
||||
if (isCorrect) {
|
||||
newFeedback = this.props.intl.formatMessage(messages['id.verification.photo.feedback.correct']);
|
||||
} else {
|
||||
// give feedback based on where user is
|
||||
newFeedback = this.props.intl.formatMessage(messages[this.getGridPosition(rightEye)]);
|
||||
}
|
||||
} else if (numFaces > 1) {
|
||||
newFeedback = this.props.intl.formatMessage(messages['id.verification.photo.feedback.two.faces']);
|
||||
} else {
|
||||
newFeedback = this.props.intl.formatMessage(messages['id.verification.photo.feedback.no.faces']);
|
||||
}
|
||||
if (currentFeedback !== newFeedback) {
|
||||
// only update status if it is different, so we don't overload the user with status updates
|
||||
this.setState({ feedback: newFeedback });
|
||||
}
|
||||
// turn off feedback for one to ensure that instructions aren't disruptive/interrupting
|
||||
this.setState({ shouldGiveFeedback: false });
|
||||
setTimeout(() => {
|
||||
this.setState({ shouldGiveFeedback: true });
|
||||
}, 1000);
|
||||
}
|
||||
isInRangeForPortrait(x, y, width) {
|
||||
return x > -80 && x < 70 && y > -20 && y < 80 && width > 300 && width < 650;
|
||||
}
|
||||
|
||||
getGridPosition(coordinates) {
|
||||
// Used to determine where a face is (i.e. top-left, center-right, bottom-center, etc.)
|
||||
|
||||
const x = coordinates[0];
|
||||
const y = coordinates[1];
|
||||
|
||||
let messageBase = 'id.verification.photo.feedback';
|
||||
|
||||
const heightUpperLimit = 320;
|
||||
const heightMiddleLimit = 160;
|
||||
|
||||
if (y < heightMiddleLimit) {
|
||||
messageBase += '.top';
|
||||
} else if (y < heightUpperLimit && y >= heightMiddleLimit) {
|
||||
messageBase += '.center';
|
||||
} else {
|
||||
messageBase += '.bottom';
|
||||
}
|
||||
|
||||
const widthRightLimit = 213;
|
||||
const widthMiddleLimit = 427;
|
||||
|
||||
if (x < widthRightLimit) {
|
||||
messageBase += '.right';
|
||||
} else if (x >= widthRightLimit && x < widthMiddleLimit) {
|
||||
messageBase += '.center';
|
||||
} else {
|
||||
messageBase += '.left';
|
||||
}
|
||||
|
||||
return messageBase;
|
||||
}
|
||||
|
||||
isInRangeForPortrait(x, y) {
|
||||
return x > 47 && x < 570 && y > 100 && y < 410;
|
||||
}
|
||||
|
||||
isInRangeForID(x, y) {
|
||||
return x > 120 && x < 470 && y > 120 && y < 350;
|
||||
isInRangeForID(x, y, width) {
|
||||
return x > -60 && x < 10 && y > 0 && y < 150 && width > 230 && width < 540;
|
||||
}
|
||||
|
||||
setVideoHasLoaded() {
|
||||
@@ -225,9 +128,8 @@ class Camera extends React.Component {
|
||||
if (this.state.dataUri) {
|
||||
return this.reset();
|
||||
}
|
||||
|
||||
const config = {
|
||||
sizeFactor: this.getSizeFactor(),
|
||||
sizeFactor: 1,
|
||||
};
|
||||
|
||||
this.playShutterClick();
|
||||
@@ -236,30 +138,6 @@ class Camera extends React.Component {
|
||||
this.props.onImageCapture(dataUri);
|
||||
}
|
||||
|
||||
getSizeFactor() {
|
||||
let sizeFactor = 1;
|
||||
const settings = this.cameraPhoto.getCameraSettings();
|
||||
if (settings) {
|
||||
const videoWidth = settings.width;
|
||||
const videoHeight = settings.height;
|
||||
// need to multiply by 3 because each pixel contains 3 bytes
|
||||
const currentSize = videoWidth * videoHeight * 3;
|
||||
// chose a limit of 9,999,999 (bytes) so that result will
|
||||
// always be less than 10MB
|
||||
const ratio = 9999999 / currentSize;
|
||||
|
||||
if (ratio < 1) {
|
||||
// if the current resolution creates an image larger than 10 MB, adjust sizeFactor (resolution)
|
||||
// to ensure that image will have a file size of less than 10 MB.
|
||||
sizeFactor = ratio;
|
||||
} else if (videoWidth === 640 && videoHeight === 480) {
|
||||
// otherwise increase the resolution to try and prevent blurry images.
|
||||
sizeFactor = 2;
|
||||
}
|
||||
}
|
||||
return sizeFactor;
|
||||
}
|
||||
|
||||
playShutterClick() {
|
||||
const audio = new Audio(`data:audio/mp3;base64,${shutter.base64}`);
|
||||
audio.play();
|
||||
@@ -303,33 +181,15 @@ class Camera extends React.Component {
|
||||
autoPlay
|
||||
className="camera-video"
|
||||
onLoadedData={() => { this.setVideoHasLoaded(); }}
|
||||
style={{
|
||||
display: this.state.dataUri ? 'none' : 'block',
|
||||
WebkitTransform: 'scaleX(-1)',
|
||||
transform: 'scaleX(-1)',
|
||||
}}
|
||||
playsInline
|
||||
/>
|
||||
<canvas
|
||||
ref={this.canvasRef}
|
||||
data-testid="detection-canvas"
|
||||
className="canvas-video"
|
||||
style={{
|
||||
display: !this.state.shouldDetect || this.state.dataUri ? 'none' : 'block',
|
||||
WebkitTransform: 'scaleX(-1)',
|
||||
transform: 'scaleX(-1)',
|
||||
}}
|
||||
width="640"
|
||||
height="480"
|
||||
style={{ display: this.state.dataUri ? 'none' : 'block' }}
|
||||
/>
|
||||
<canvas ref={this.canvasRef} data-testid="detection-canvas" className="canvas-video" style={{ display: !this.state.shouldDetect || this.state.dataUri ? 'none' : 'block' }} height="375" width="500" />
|
||||
<img
|
||||
data-hj-suppress
|
||||
alt="imgCamera"
|
||||
src={this.state.dataUri}
|
||||
className="camera-video"
|
||||
style={{ display: this.state.dataUri ? 'block' : 'none' }}
|
||||
/>
|
||||
<div role="status" className="sr-only">{this.state.feedback}</div>
|
||||
</div>
|
||||
<button
|
||||
className={`btn camera-btn ${
|
||||
|
||||
@@ -35,7 +35,7 @@ function CameraHelpWithUpload(props) {
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.id.photo.instructions.upload'])}
|
||||
</p>
|
||||
<ImageFileUpload onFileChange={setAndTrackIdPhotoFile} intl={props.intl} />
|
||||
<ImageFileUpload onFileChange={setAndTrackIdPhotoFile} />
|
||||
</Collapsible>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -88,74 +88,14 @@ const messages = defineMessages({
|
||||
},
|
||||
'id.verification.photo.enable.detection.portrait.help.text': {
|
||||
id: 'id.verification.photo.enable.detection.portrait.help.text',
|
||||
defaultMessage: 'If checked, a box will appear around your face. Your face can be seen clearly if the box around it is blue. If your face is not in a good position or undetectable, the box will be red.',
|
||||
defaultMessage: 'If checked, a border will appear around the camera view. Your face can be seen clearly if the border is blue. If your face is not in a good position or undetectable, the border will be red.',
|
||||
description: 'Help text that appears for enabling face detection on the portrait photo panel.',
|
||||
},
|
||||
'id.verification.photo.enable.detection.id.help.text': {
|
||||
id: 'id.verification.photo.enable.detection.id.help.text',
|
||||
defaultMessage: 'If checked, a box will appear around the face on your ID card. The face can be seen clearly if the box around it is blue. If the face is not in a good position or undetectable, the box will be red.',
|
||||
defaultMessage: 'If checked, a border will appear around the camera view. The face can be seen clearly if the border is blue. If the face is not in a good position or undetectable, the border will be red.',
|
||||
description: 'Help text that appears for enabling face detection on the portrait photo panel.',
|
||||
},
|
||||
'id.verification.photo.feedback.correct': {
|
||||
id: 'id.verification.photo.feedback.correct',
|
||||
defaultMessage: 'Face is in a good position.',
|
||||
description: 'Text for screen reader when user\'s face is in a good position.',
|
||||
},
|
||||
'id.verification.photo.feedback.two.faces': {
|
||||
id: 'id.verification.photo.feedback.two.faces',
|
||||
defaultMessage: 'More than one face detected.',
|
||||
description: 'Text for screen reader when more than one face detected.',
|
||||
},
|
||||
'id.verification.photo.feedback.no.faces': {
|
||||
id: 'id.verification.photo.feedback.no.faces',
|
||||
defaultMessage: 'No face detected.',
|
||||
description: 'Text for screen reader when no face detected.',
|
||||
},
|
||||
'id.verification.photo.feedback.top.left': {
|
||||
id: 'id.verification.photo.feedback.top.left',
|
||||
defaultMessage: 'Incorrect position. Top left.',
|
||||
description: 'Text for screen reader when face is in a bad position.',
|
||||
},
|
||||
'id.verification.photo.feedback.top.center': {
|
||||
id: 'id.verification.photo.feedback.top.center',
|
||||
defaultMessage: 'Incorrect position. Top center.',
|
||||
description: 'Text for screen reader when face is in a bad position.',
|
||||
},
|
||||
'id.verification.photo.feedback.top.right': {
|
||||
id: 'id.verification.photo.feedback.top.right',
|
||||
defaultMessage: 'Incorrect position. Top right.',
|
||||
description: 'Text for screen reader when face is in a bad position.',
|
||||
},
|
||||
'id.verification.photo.feedback.center.left': {
|
||||
id: 'id.verification.photo.feedback.center.left',
|
||||
defaultMessage: 'Incorrect position. Center left.',
|
||||
description: 'Text for screen reader when face is in a bad position.',
|
||||
},
|
||||
'id.verification.photo.feedback.center.center': {
|
||||
id: 'id.verification.photo.feedback.center.center',
|
||||
defaultMessage: 'Incorrect position. Too close to camera.',
|
||||
description: 'Text for screen reader when face is in a bad position.',
|
||||
},
|
||||
'id.verification.photo.feedback.center.right': {
|
||||
id: 'id.verification.photo.feedback.center.right',
|
||||
defaultMessage: 'Incorrect position. Center right.',
|
||||
description: 'Text for screen reader when face is in a bad position.',
|
||||
},
|
||||
'id.verification.photo.feedback.bottom.left': {
|
||||
id: 'id.verification.photo.feedback.bottom.left',
|
||||
defaultMessage: 'Incorrect position. Bottom left.',
|
||||
description: 'Text for screen reader when face is in a bad position.',
|
||||
},
|
||||
'id.verification.photo.feedback.bottom.center': {
|
||||
id: 'id.verification.photo.feedback.bottom.center',
|
||||
defaultMessage: 'Incorrect position. Bottom center.',
|
||||
description: 'Text for screen reader when face is in a bad position.',
|
||||
},
|
||||
'id.verification.photo.feedback.bottom.right': {
|
||||
id: 'id.verification.photo.feedback.bottom.right',
|
||||
defaultMessage: 'Incorrect position. Bottom right.',
|
||||
description: 'Text for screen reader when face is in a bad position.',
|
||||
},
|
||||
'id.verification.camera.access.title': {
|
||||
id: 'id.verification.camera.access.title',
|
||||
defaultMessage: 'Camera Permissions',
|
||||
@@ -463,14 +403,9 @@ const messages = defineMessages({
|
||||
},
|
||||
'id.verification.id.photo.instructions.upload': {
|
||||
id: 'id.verification.id.photo.instructions.upload',
|
||||
defaultMessage: 'Please upload an ID photo. Ensure the entire ID fits inside the frame and is well-lit. The file size must be under 10 MB. (Supported formats: .jpg, .jpeg, .png)',
|
||||
defaultMessage: 'Please upload an ID photo. Ensure the entire ID fits inside the frame and is well-lit. (Supported formats: .jpg, .jpeg, .png)',
|
||||
description: 'Instructions for ID photo upload.',
|
||||
},
|
||||
'id.verification.id.photo.instructions.upload.error': {
|
||||
id: 'id.verification.id.photo.instructions.upload.error',
|
||||
defaultMessage: 'The file you have selected is too large. Please try again with a file less than 10MB.',
|
||||
description: 'Error message for file upload that is larger than 10MB.',
|
||||
},
|
||||
'id.verification.account.name.title': {
|
||||
id: 'id.verification.account.name.title',
|
||||
defaultMessage: 'Account Name Check',
|
||||
@@ -571,11 +506,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Submit',
|
||||
description: 'Button to confirm all information is correct and submit.',
|
||||
},
|
||||
'id.verification.review.error': {
|
||||
id: 'id.verification.review.error',
|
||||
defaultMessage: 'edX Support Page',
|
||||
description: 'Text linking to the support page.',
|
||||
},
|
||||
'id.verification.submitted.title': {
|
||||
id: 'id.verification.submitted.title',
|
||||
defaultMessage: 'Identity Verification in Progress',
|
||||
|
||||
@@ -45,9 +45,9 @@ function IdVerificationContextProvider({ children }) {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
||||
setMediaAccess(MEDIA_ACCESS.GRANTED);
|
||||
setMediaStream(stream);
|
||||
// stop the stream, as we are not using it yet
|
||||
const tracks = stream.getTracks();
|
||||
tracks.forEach(track => track.stop());
|
||||
// If we would like to stop the stream immediately. I guess we can leave it open
|
||||
// const tracks = stream.getTracks();
|
||||
// tracks.forEach(track => track.stop());
|
||||
} catch (err) {
|
||||
setMediaAccess(MEDIA_ACCESS.DENIED);
|
||||
}
|
||||
|
||||
@@ -1,52 +1,28 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { intlShape } from '@edx/frontend-platform/i18n';
|
||||
import React, { useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Alert } from '@edx/paragon';
|
||||
import messages from './IdVerification.messages';
|
||||
|
||||
|
||||
export default function ImageFileUpload({ onFileChange, intl }) {
|
||||
const [fileTooLargeError, setFileTooLargeError] = useState(false);
|
||||
const maxFileSize = 10000000;
|
||||
|
||||
export default function ImageFileUpload({ onFileChange }) {
|
||||
const handleChange = useCallback((e) => {
|
||||
if (e.target.files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileObject = e.target.files[0];
|
||||
if (fileObject.size < maxFileSize) {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.addEventListener('load', () => onFileChange(fileReader.result));
|
||||
fileReader.readAsDataURL(fileObject);
|
||||
} else {
|
||||
setFileTooLargeError(true);
|
||||
}
|
||||
const fileReader = new FileReader();
|
||||
fileReader.addEventListener('load', () => onFileChange(fileReader.result));
|
||||
fileReader.readAsDataURL(fileObject);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
data-testid="fileUpload"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{fileTooLargeError && (
|
||||
<Alert
|
||||
id="fileTooLargeError"
|
||||
variant="danger"
|
||||
tabIndex="-1"
|
||||
style={{ marginTop: '1rem' }}
|
||||
>
|
||||
{intl.formatMessage(messages['id.verification.id.photo.instructions.upload.error'])}
|
||||
</Alert>
|
||||
)}
|
||||
</>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
data-testid="fileUpload"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
ImageFileUpload.propTypes = {
|
||||
onFileChange: PropTypes.func.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ export default function ImagePreview({ src, alt, id }) {
|
||||
return (
|
||||
<div id={id} className="image-preview">
|
||||
|
||||
<img data-hj-suppress style={{ objectFit: 'contain' }} src={src} alt={alt} />
|
||||
<img style={{ objectFit: 'contain' }} src={src} alt={alt} />
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
|
||||
.canvas-video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useContext, useState, useEffect, useRef } from 'react';
|
||||
import { Form } from '@edx/paragon';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
@@ -11,7 +11,6 @@ import { IdVerificationContext } from '../IdVerificationContext';
|
||||
import messages from '../IdVerification.messages';
|
||||
|
||||
function GetNameIdPanel(props) {
|
||||
const { push } = useHistory();
|
||||
const panelSlug = 'get-name-id';
|
||||
const [nameMatches, setNameMatches] = useState(true);
|
||||
const nameInputRef = useRef();
|
||||
@@ -43,15 +42,6 @@ function GetNameIdPanel(props) {
|
||||
}
|
||||
}, [nameMatches, blankName]);
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
// If the input is empty, or if no changes have been made to the
|
||||
// mismatching name, the user should not be able to proceed.
|
||||
if (!invalidName && !blankName) {
|
||||
push(nextPanelSlug);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<BasePanel
|
||||
name={panelSlug}
|
||||
@@ -61,7 +51,7 @@ function GetNameIdPanel(props) {
|
||||
{props.intl.formatMessage(messages['id.verification.account.name.instructions'])}
|
||||
</p>
|
||||
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Form>
|
||||
<Form.Group>
|
||||
<Form.Label htmlFor="nameMatchesYes">
|
||||
{props.intl.formatMessage(messages['id.verification.account.name.radio.label'])}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { history } from '@edx/frontend-platform';
|
||||
import { Input, Button, Spinner, Alert } from '@edx/paragon';
|
||||
import { Input, Button, Spinner } from '@edx/paragon';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
|
||||
@@ -25,7 +25,6 @@ function SummaryPanel(props) {
|
||||
} = useContext(IdVerificationContext);
|
||||
const nameToBeUsed = idPhotoName || nameOnAccount || '';
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [submissionError, setSubmissionError] = useState(false);
|
||||
|
||||
function SubmitButton() {
|
||||
async function handleClick() {
|
||||
@@ -40,10 +39,6 @@ function SummaryPanel(props) {
|
||||
if (result.success) {
|
||||
stopUserMedia();
|
||||
history.push(nextPanelSlug);
|
||||
} else {
|
||||
stopUserMedia();
|
||||
setIsSubmitting(false);
|
||||
setSubmissionError(true);
|
||||
}
|
||||
}
|
||||
return (
|
||||
@@ -64,24 +59,6 @@ function SummaryPanel(props) {
|
||||
name={panelSlug}
|
||||
title={props.intl.formatMessage(messages['id.verification.review.title'])}
|
||||
>
|
||||
{submissionError &&
|
||||
<Alert
|
||||
variant="danger"
|
||||
data-testid="submission-error"
|
||||
dismissible
|
||||
onClose={() => setSubmissionError(false)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="idv.submission.alert.error"
|
||||
defaultMessage={`
|
||||
We encountered a technical error while trying to submit ID verification.
|
||||
This might be a temporary issue, so please try again in a few minutes.
|
||||
If the problem persists,
|
||||
please go to {support_link} for help.
|
||||
`}
|
||||
values={{ support_link: <Alert.Link href="https://support.edx.org/hc/en-us">{props.intl.formatMessage(messages['id.verification.review.error'])}</Alert.Link> }}
|
||||
/>
|
||||
</Alert>}
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.review.description'])}
|
||||
</p>
|
||||
|
||||
@@ -4,16 +4,12 @@ import { createMemoryHistory } from 'history';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import { render, cleanup, screen, act, fireEvent } from '@testing-library/react';
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import * as blazeface from '@tensorflow-models/blazeface';
|
||||
import * as analytics from '@edx/frontend-platform/analytics';
|
||||
import * as cocoSsd from '@tensorflow-models/coco-ssd';
|
||||
import { IdVerificationContext } from '../IdVerificationContext';
|
||||
import Camera from '../Camera';
|
||||
|
||||
jest.mock('jslib-html5-camera-photo');
|
||||
jest.mock('@tensorflow-models/blazeface');
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
|
||||
analytics.sendTrackEvent = jest.fn();
|
||||
jest.mock('@tensorflow-models/coco-ssd');
|
||||
|
||||
window.HTMLMediaElement.prototype.play = () => {};
|
||||
|
||||
@@ -85,7 +81,7 @@ describe('SubmittedPanel', () => {
|
||||
});
|
||||
|
||||
it('shows spinner when loading face detection', async () => {
|
||||
blazeface.load = jest.fn().mockResolvedValue({ estimateFaces: jest.fn().mockResolvedValue([]) });
|
||||
cocoSsd.load = jest.fn().mockResolvedValue({ detect: jest.fn().mockResolvedValue([]) });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
@@ -103,7 +99,7 @@ describe('SubmittedPanel', () => {
|
||||
});
|
||||
|
||||
it('canvas is visible when detection is enabled', async () => {
|
||||
blazeface.load = jest.fn().mockResolvedValue({ estimateFaces: jest.fn().mockResolvedValue([]) });
|
||||
cocoSsd.load = jest.fn().mockResolvedValue({ detect: jest.fn().mockResolvedValue([]) });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
@@ -122,8 +118,7 @@ describe('SubmittedPanel', () => {
|
||||
});
|
||||
|
||||
it('blazeface is called when detection is enabled', async () => {
|
||||
blazeface.load = jest.fn().mockResolvedValue({ estimateFaces: jest.fn().mockResolvedValue([]) });
|
||||
|
||||
cocoSsd.load = jest.fn().mockResolvedValue({ detect: jest.fn().mockResolvedValue([]) });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
@@ -137,48 +132,6 @@ describe('SubmittedPanel', () => {
|
||||
await fireEvent.loadedData(screen.queryByTestId('video'));
|
||||
const checkbox = await screen.findByLabelText('Enable Face Detection');
|
||||
await fireEvent.click(checkbox);
|
||||
setTimeout(() => { expect(blazeface.load).toHaveBeenCalled(); }, 2000);
|
||||
});
|
||||
|
||||
it('sends tracking events on portrait photo page', async () => {
|
||||
blazeface.load = jest.fn().mockResolvedValue({ estimateFaces: jest.fn().mockResolvedValue([]) });
|
||||
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlCamera {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
await fireEvent.loadedData(screen.queryByTestId('video'));
|
||||
const checkbox = await screen.findByLabelText('Enable Face Detection');
|
||||
await fireEvent.click(checkbox);
|
||||
expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.id_verification.user_photo.face_detection_enabled');
|
||||
await fireEvent.click(checkbox);
|
||||
expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.id_verification.user_photo.face_detection_disabled');
|
||||
});
|
||||
|
||||
it('sends tracking events on id photo page', async () => {
|
||||
blazeface.load = jest.fn().mockResolvedValue({ estimateFaces: jest.fn().mockResolvedValue([]) });
|
||||
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlCamera {...idProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
await fireEvent.loadedData(screen.queryByTestId('video'));
|
||||
const checkbox = await screen.findByLabelText('Enable Face Detection');
|
||||
await fireEvent.click(checkbox);
|
||||
expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.id_verification.id_photo.face_detection_enabled');
|
||||
await fireEvent.click(checkbox);
|
||||
expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.id_verification.id_photo.face_detection_disabled');
|
||||
setTimeout(() => { expect(cocoSsd.load).toHaveBeenCalled(); }, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import { render, cleanup, act, screen, fireEvent, waitFor } from '@testing-libra
|
||||
import '@edx/frontend-platform/analytics';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import * as dataService from '../../data/service';
|
||||
import { submitIdVerification } from '../../data/service';
|
||||
import { IdVerificationContext } from '../../IdVerificationContext';
|
||||
import SummaryPanel from '../../panels/SummaryPanel';
|
||||
|
||||
@@ -13,8 +13,9 @@ jest.mock('@edx/frontend-platform/analytics', () => ({
|
||||
sendTrackEvent: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../data/service');
|
||||
dataService.submitIdVerification = jest.fn().mockReturnValue({ success: true });
|
||||
jest.mock('../../data/service', () => ({
|
||||
submitIdVerification: jest.fn(() => ({ success: true, message: null })),
|
||||
}));
|
||||
|
||||
const IntlSummaryPanel = injectIntl(SummaryPanel);
|
||||
|
||||
@@ -73,26 +74,7 @@ describe('SummaryPanel', () => {
|
||||
it('submits', async () => {
|
||||
const button = await screen.findByTestId('submit-button');
|
||||
fireEvent.click(button);
|
||||
expect(dataService.submitIdVerification).toHaveBeenCalled();
|
||||
await waitFor(() => expect(contextValue.stopUserMedia).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('shows error when cannot submit', async () => {
|
||||
await cleanup();
|
||||
dataService.submitIdVerification = jest.fn().mockReturnValue({ success: false });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlSummaryPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByTestId('submit-button');
|
||||
await act(async () => fireEvent.click(button));
|
||||
expect(dataService.submitIdVerification).toHaveBeenCalled();
|
||||
const error = await screen.getByTestId('submission-error');
|
||||
expect(error).toBeDefined();
|
||||
expect(submitIdVerification).toHaveBeenCalled();
|
||||
await waitFor(() => expect(contextValue.stopUserMedia).toHaveBeenCalled())
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user