Move StandaloneRegistration to React
This commit is contained in:
parent
67b17ec317
commit
7c1ce3366d
20 changed files with 452 additions and 1358 deletions
|
@ -1759,6 +1759,10 @@ app.on('will-finish-launching', () => {
|
|||
if (isCaptchaHref(incomingHref, getLogger())) {
|
||||
const { captcha } = parseCaptchaHref(incomingHref, getLogger());
|
||||
challengeHandler.handleCaptcha(captcha);
|
||||
|
||||
// Show window after handling captcha
|
||||
showWindow();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,14 +85,6 @@
|
|||
<button class='finish' tabIndex='1'><span class='icon'></span></button>
|
||||
</script>
|
||||
|
||||
<script type="text/x-tmpl-mustache" id="phone-number">
|
||||
<div class='phone-input-form'>
|
||||
<div class='number-container'>
|
||||
<input type='tel' class='number' placeholder="Phone Number" />
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-tmpl-mustache" id="group-member-list">
|
||||
<div class='container' tabindex='0'>
|
||||
{{ #summary }} <div class='summary'>{{ summary }}</div>{{ /summary }}
|
||||
|
@ -198,36 +190,6 @@
|
|||
{{/isError}}
|
||||
</script>
|
||||
|
||||
<script type="text/x-tmpl-mustache" id="standalone">
|
||||
<div class='module-title-bar-drag-area'></div>
|
||||
|
||||
<div class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<div class="banner-image module-splash-screen__logo module-img--128"></div>
|
||||
<div class='header'>Create your Signal Account</div>
|
||||
<div id='phone-number-input'>
|
||||
<div class='phone-input-form'>
|
||||
<div id='number-container' class='number-container'>
|
||||
<input type='tel' class='number' placeholder='Phone Number' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearfix'>
|
||||
<a class='button' id='request-sms'>Send SMS</a>
|
||||
<a class='link' id='request-voice' tabindex='-1'>Call</a>
|
||||
</div>
|
||||
<input class='form-control' type='text' pattern='\s*[0-9]{3}-?[0-9]{3}\s*' title='Enter your 6-digit verification code. If you did not receive a code, click Call or Send SMS to request a new one' id='code' placeholder='Verification Code' autocomplete='off'>
|
||||
<div id='error' class='collapse'></div>
|
||||
<div id='status'></div>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<a class='button' id='verifyCode' data-loading-text='Please wait...'>Register</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="js/components.js"></script>
|
||||
<script type="text/javascript" src="ts/set_os_class.js"></script>
|
||||
<script
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
"node_modules/mustache/mustache.js",
|
||||
"node_modules/underscore/underscore.js",
|
||||
"components/qrcode/**/*.js",
|
||||
"node_modules/intl-tel-input/build/js/intlTelInput.js",
|
||||
"components/autosize/**/*.js",
|
||||
"components/webaudiorecorder/lib/WebAudioRecorder.js"
|
||||
]
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
"heic-convert": "^1.2.4",
|
||||
"history": "4.9.0",
|
||||
"humanize-duration": "3.26.0",
|
||||
"intl-tel-input": "12.1.15",
|
||||
"intl-tel-input": "17.0.13",
|
||||
"jquery": "3.5.0",
|
||||
"js-yaml": "3.13.1",
|
||||
"linkify-it": "2.2.0",
|
||||
|
@ -195,6 +195,7 @@
|
|||
"@types/google-libphonenumber": "7.4.14",
|
||||
"@types/history": "4.7.2",
|
||||
"@types/humanize-duration": "^3.18.1",
|
||||
"@types/intl-tel-input": "17.0.4",
|
||||
"@types/jquery": "3.5.6",
|
||||
"@types/js-yaml": "3.12.0",
|
||||
"@types/linkify-it": "2.1.0",
|
||||
|
|
|
@ -457,7 +457,6 @@ try {
|
|||
require('./ts/views/conversation_view');
|
||||
require('./ts/views/inbox_view');
|
||||
require('./ts/views/install_view');
|
||||
require('./ts/views/standalone_registration_view');
|
||||
require('./ts/SignalProtocolStore');
|
||||
require('./ts/background');
|
||||
|
||||
|
|
|
@ -570,7 +570,28 @@ $loading-height: 16px;
|
|||
@media (min-height: 750px) and (min-width: 700px) {
|
||||
font-size: 20pt;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: $color-gray-20;
|
||||
cursor: auto;
|
||||
}
|
||||
}
|
||||
|
||||
button.link {
|
||||
@include button-reset;
|
||||
|
||||
display: block;
|
||||
margin: 0.5em auto;
|
||||
text-align: center;
|
||||
text-decoration: underline;
|
||||
color: $color-ultramarine;
|
||||
|
||||
&:disabled {
|
||||
color: $color-gray-20;
|
||||
cursor: auto;
|
||||
}
|
||||
}
|
||||
|
||||
a.link {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
|
|
|
@ -5,9 +5,16 @@
|
|||
@import 'variables';
|
||||
@import '../node_modules/intl-tel-input/build/css/intlTelInput.css';
|
||||
@import 'progress';
|
||||
.iti-flag {
|
||||
|
||||
.iti__flag {
|
||||
// override intlTelInput's flags image location
|
||||
background: url('../node_modules/intl-tel-input/build/img/flags.png');
|
||||
background-image: url('../node_modules/intl-tel-input/build/img/flags.png');
|
||||
}
|
||||
|
||||
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
|
||||
.iti__flag {
|
||||
background-image: url('../node_modules/intl-tel-input/build/img/flags@2x.png');
|
||||
}
|
||||
}
|
||||
|
||||
.intl-tel-input .country-list {
|
||||
|
|
|
@ -54,14 +54,6 @@
|
|||
<button class='close'><span class='icon'></span></button>
|
||||
</script>
|
||||
|
||||
<script type="text/x-tmpl-mustache" id="phone-number">
|
||||
<div class='phone-input-form'>
|
||||
<div class='number-container'>
|
||||
<input type='tel' class='number' placeholder="Phone Number" />
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-tmpl-mustache" id="file-size-modal">
|
||||
{{ file-size-warning }}
|
||||
({{ limit }}{{ units }})
|
||||
|
@ -169,36 +161,6 @@
|
|||
{{/isError}}
|
||||
</script>
|
||||
|
||||
<script type="text/x-tmpl-mustache" id="standalone">
|
||||
<div class='module-title-bar-drag-area'></div>
|
||||
|
||||
<div class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<div class="banner-image module-splash-screen__logo module-img--128"></div>
|
||||
<div class='header'>Create your Signal Account</div>
|
||||
<div id='phone-number-input'>
|
||||
<div class='phone-input-form'>
|
||||
<div id='number-container' class='number-container'>
|
||||
<input type='tel' class='number' placeholder='Phone Number' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearfix'>
|
||||
<a class='button' id='request-sms'>Send SMS</a>
|
||||
<a class='link' id='request-voice' tabindex='-1'>Call</a>
|
||||
</div>
|
||||
<input class='form-control' type='text' pattern='\s*[0-9]{3}-?[0-9]{3}\s*' title='Enter your 6-digit verification code. If you did not receive a code, click Call or Send SMS to request a new one' id='code' placeholder='Verification Code' autocomplete='off'>
|
||||
<div id='error' class='collapse'></div>
|
||||
<div id='status'></div>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<a class='button' id='verifyCode' data-loading-text='Please wait...'>Register</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="../libtextsecure/test/fake_web_api.js"
|
||||
|
|
|
@ -2009,6 +2009,14 @@ export async function startApp(): Promise<void> {
|
|||
|
||||
window.textsecure.messaging = new window.textsecure.MessageSender(server);
|
||||
|
||||
// Update our profile key in the conversation if we just got linked.
|
||||
const profileKey = await ourProfileKeyService.get();
|
||||
if (firstRun && profileKey) {
|
||||
const me = window.ConversationController.getOurConversation();
|
||||
strictAssert(me !== undefined, "Didn't find newly created ourselves");
|
||||
await me.setProfileKey(Bytes.toBase64(profileKey));
|
||||
}
|
||||
|
||||
if (connectCount === 0) {
|
||||
try {
|
||||
// Force a re-fetch before we process our queue. We may want to turn on
|
||||
|
|
|
@ -312,6 +312,19 @@ export class ChallengeHandler {
|
|||
await this.persist();
|
||||
}
|
||||
|
||||
public async requestCaptcha(token = ''): Promise<string> {
|
||||
const request: IPCRequest = { seq: this.seq };
|
||||
this.seq += 1;
|
||||
|
||||
this.options.requestChallenge(request);
|
||||
|
||||
const response = await new Promise<ChallengeResponse>((resolve, reject) => {
|
||||
this.responseHandlers.set(request.seq, { token, resolve, reject });
|
||||
});
|
||||
|
||||
return response.captcha;
|
||||
}
|
||||
|
||||
private async persist(): Promise<void> {
|
||||
assert(
|
||||
this.isLoaded,
|
||||
|
@ -407,16 +420,10 @@ export class ChallengeHandler {
|
|||
}
|
||||
|
||||
private async solve(token: string): Promise<void> {
|
||||
const request: IPCRequest = { seq: this.seq };
|
||||
this.seq += 1;
|
||||
|
||||
this.options.setChallengeStatus('required');
|
||||
this.options.requestChallenge(request);
|
||||
this.challengeToken = token;
|
||||
|
||||
this.challengeToken = token || '';
|
||||
const response = await new Promise<ChallengeResponse>((resolve, reject) => {
|
||||
this.responseHandlers.set(request.seq, { token, resolve, reject });
|
||||
});
|
||||
const captcha = await this.requestCaptcha(token);
|
||||
|
||||
// Another `.solve()` has completed earlier than us
|
||||
if (this.challengeToken === undefined) {
|
||||
|
@ -434,7 +441,7 @@ export class ChallengeHandler {
|
|||
await this.sendChallengeResponse({
|
||||
type: 'recaptcha',
|
||||
token: lastToken,
|
||||
captcha: response.captcha,
|
||||
captcha,
|
||||
});
|
||||
} catch (error) {
|
||||
log.error(`challenge: challenge failure, error: ${error && error.stack}`);
|
||||
|
|
|
@ -18,6 +18,13 @@ type PropsType = {
|
|||
appView: AppViewType;
|
||||
renderCallManager: () => JSX.Element;
|
||||
renderGlobalModalContainer: () => JSX.Element;
|
||||
openInbox: () => void;
|
||||
requestVerification: (
|
||||
type: 'sms' | 'voice',
|
||||
number: string,
|
||||
token: string
|
||||
) => Promise<void>;
|
||||
registerSingleDevice: (number: string, code: string) => Promise<void>;
|
||||
theme: ThemeType;
|
||||
} & ComponentProps<typeof Inbox>;
|
||||
|
||||
|
@ -34,6 +41,9 @@ export const App = ({
|
|||
renderCustomizingPreferredReactionsModal,
|
||||
renderGlobalModalContainer,
|
||||
renderSafetyNumber,
|
||||
openInbox,
|
||||
requestVerification,
|
||||
registerSingleDevice,
|
||||
theme,
|
||||
verifyConversationsStoppingMessageSend,
|
||||
}: PropsType): JSX.Element => {
|
||||
|
@ -42,7 +52,17 @@ export const App = ({
|
|||
if (appView === AppViewType.Installer) {
|
||||
contents = <Install />;
|
||||
} else if (appView === AppViewType.Standalone) {
|
||||
contents = <StandaloneRegistration />;
|
||||
const onComplete = () => {
|
||||
window.removeSetupMenuItems();
|
||||
openInbox();
|
||||
};
|
||||
contents = (
|
||||
<StandaloneRegistration
|
||||
onComplete={onComplete}
|
||||
requestVerification={requestVerification}
|
||||
registerSingleDevice={registerSingleDevice}
|
||||
/>
|
||||
);
|
||||
} else if (appView === AppViewType.Inbox) {
|
||||
contents = (
|
||||
<Inbox
|
||||
|
|
|
@ -1,14 +1,268 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { BackboneHost } from './BackboneHost';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import type { Plugin } from 'intl-tel-input';
|
||||
import intlTelInput from 'intl-tel-input';
|
||||
|
||||
import { strictAssert } from '../util/assert';
|
||||
import { getChallengeURL } from '../challenge';
|
||||
|
||||
const PhoneInput = ({
|
||||
onValidation,
|
||||
onNumberChange,
|
||||
}: {
|
||||
onValidation: (isValid: boolean) => void;
|
||||
onNumberChange: (number?: string) => void;
|
||||
}): JSX.Element => {
|
||||
const [isValid, setIsValid] = useState(false);
|
||||
const pluginRef = useRef<Plugin | undefined>();
|
||||
const elemRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const onRef = useCallback((elem: HTMLInputElement | null) => {
|
||||
elemRef.current = elem;
|
||||
|
||||
if (!elem) {
|
||||
return;
|
||||
}
|
||||
|
||||
pluginRef.current?.destroy();
|
||||
|
||||
const plugin = intlTelInput(elem);
|
||||
pluginRef.current = plugin;
|
||||
}, []);
|
||||
|
||||
const validateNumber = useCallback(
|
||||
(number: string) => {
|
||||
const { current: plugin } = pluginRef;
|
||||
if (!plugin) {
|
||||
return;
|
||||
}
|
||||
|
||||
const regionCode = plugin.getSelectedCountryData().iso2;
|
||||
|
||||
const parsedNumber = window.libphonenumber.util.parseNumber(
|
||||
number,
|
||||
regionCode
|
||||
);
|
||||
|
||||
setIsValid(parsedNumber.isValidNumber);
|
||||
onValidation(parsedNumber.isValidNumber);
|
||||
|
||||
onNumberChange(
|
||||
parsedNumber.isValidNumber ? parsedNumber.e164 : undefined
|
||||
);
|
||||
},
|
||||
[setIsValid, onNumberChange, onValidation]
|
||||
);
|
||||
|
||||
const onChange = useCallback(
|
||||
(_: ChangeEvent<HTMLInputElement>) => {
|
||||
if (elemRef.current) {
|
||||
validateNumber(elemRef.current.value);
|
||||
}
|
||||
},
|
||||
[validateNumber]
|
||||
);
|
||||
|
||||
const onKeyDown = useCallback(
|
||||
(event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
// Pacify TypeScript and handle events bubbling up
|
||||
if (event.target instanceof HTMLInputElement) {
|
||||
validateNumber(event.target.value);
|
||||
}
|
||||
},
|
||||
[validateNumber]
|
||||
);
|
||||
|
||||
export const StandaloneRegistration = (): JSX.Element => {
|
||||
return (
|
||||
<BackboneHost
|
||||
className="full-screen-flow"
|
||||
View={window.Whisper.StandaloneRegistrationView}
|
||||
/>
|
||||
<div className="phone-input">
|
||||
<div className="phone-input-form">
|
||||
<div className={`number-container ${isValid ? 'valid' : 'invalid'}`}>
|
||||
<input
|
||||
className="number"
|
||||
type="tel"
|
||||
ref={onRef}
|
||||
onChange={onChange}
|
||||
onKeyDown={onKeyDown}
|
||||
placeholder="Phone Number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const StandaloneRegistration = ({
|
||||
onComplete,
|
||||
requestVerification,
|
||||
registerSingleDevice,
|
||||
}: {
|
||||
onComplete: () => void;
|
||||
requestVerification: (
|
||||
type: 'sms' | 'voice',
|
||||
number: string,
|
||||
token: string
|
||||
) => Promise<void>;
|
||||
registerSingleDevice: (number: string, code: string) => Promise<void>;
|
||||
}): JSX.Element => {
|
||||
useEffect(() => {
|
||||
window.readyForUpdates();
|
||||
}, []);
|
||||
|
||||
const [isValidNumber, setIsValidNumber] = useState(false);
|
||||
const [isValidCode, setIsValidCode] = useState(false);
|
||||
const [number, setNumber] = useState<string | undefined>(undefined);
|
||||
const [code, setCode] = useState('');
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
const [status, setStatus] = useState<string | undefined>(undefined);
|
||||
|
||||
const onRequestCode = useCallback(
|
||||
async (type: 'sms' | 'voice') => {
|
||||
if (!isValidNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!number) {
|
||||
setIsValidNumber(false);
|
||||
setError(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
document.location.href = getChallengeURL();
|
||||
const token = await window.Signal.challengeHandler.requestCaptcha();
|
||||
|
||||
try {
|
||||
requestVerification(type, number, token);
|
||||
setError(undefined);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
}
|
||||
},
|
||||
[isValidNumber, setIsValidNumber, setError, requestVerification, number]
|
||||
);
|
||||
|
||||
const onSMSClick = useCallback(
|
||||
(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
onRequestCode('sms');
|
||||
},
|
||||
[onRequestCode]
|
||||
);
|
||||
|
||||
const onVoiceClick = useCallback(
|
||||
(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
onRequestCode('voice');
|
||||
},
|
||||
[onRequestCode]
|
||||
);
|
||||
|
||||
const onChangeCode = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = event.target;
|
||||
|
||||
setIsValidCode(value.length === 6);
|
||||
setCode(value);
|
||||
},
|
||||
[setIsValidCode, setCode]
|
||||
);
|
||||
|
||||
const onVerifyCode = useCallback(
|
||||
async (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (!isValidNumber || !isValidCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
strictAssert(number && code, 'Missing number or code');
|
||||
|
||||
try {
|
||||
await registerSingleDevice(number, code);
|
||||
onComplete();
|
||||
} catch (err) {
|
||||
setStatus(err.message);
|
||||
}
|
||||
},
|
||||
[
|
||||
registerSingleDevice,
|
||||
onComplete,
|
||||
number,
|
||||
code,
|
||||
setStatus,
|
||||
isValidNumber,
|
||||
isValidCode,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="full-screen-flow">
|
||||
<div className="module-title-bar-drag-area" />
|
||||
|
||||
<div className="step">
|
||||
<div className="inner">
|
||||
<div className="step-body">
|
||||
<div className="banner-image module-splash-screen__logo module-img--128" />
|
||||
<div className="header">Create your Signal Account</div>
|
||||
<div>
|
||||
<div className="phone-input-form">
|
||||
<PhoneInput
|
||||
onValidation={setIsValidNumber}
|
||||
onNumberChange={setNumber}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="clearfix">
|
||||
<button
|
||||
type="button"
|
||||
className="button"
|
||||
disabled={!isValidNumber}
|
||||
onClick={onSMSClick}
|
||||
>
|
||||
Send SMS
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="link"
|
||||
tabIndex={-1}
|
||||
disabled={!isValidNumber}
|
||||
onClick={onVoiceClick}
|
||||
>
|
||||
Call
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
className={`form-control ${isValidCode ? 'valid' : 'invalid'}`}
|
||||
type="text"
|
||||
pattern="\s*[0-9]{3}-?[0-9]{3}\s*"
|
||||
title="Enter your 6-digit verification code. If you did not receive a code, click Call or Send SMS to request a new one"
|
||||
placeholder="Verification Code"
|
||||
autoComplete="off"
|
||||
value={code}
|
||||
onChange={onChangeCode}
|
||||
/>
|
||||
<div>{error}</div>
|
||||
<div>{status}</div>
|
||||
</div>
|
||||
<div className="nav">
|
||||
<button
|
||||
type="button"
|
||||
className="button"
|
||||
disabled={!isValidNumber || !isValidCode}
|
||||
onClick={onVerifyCode}
|
||||
>
|
||||
Register
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -38,6 +38,22 @@ const mapStateToProps = (state: StateType) => {
|
|||
renderSafetyNumber: (props: SafetyNumberProps) => (
|
||||
<SmartSafetyNumberViewer {...props} />
|
||||
),
|
||||
requestVerification: (
|
||||
type: 'sms' | 'voice',
|
||||
number: string,
|
||||
token: string
|
||||
): Promise<void> => {
|
||||
const accountManager = window.getAccountManager();
|
||||
|
||||
if (type === 'sms') {
|
||||
return accountManager.requestSMSVerification(number, token);
|
||||
}
|
||||
|
||||
return accountManager.requestVoiceVerification(number, token);
|
||||
},
|
||||
registerSingleDevice: (number: string, code: string): Promise<void> => {
|
||||
return window.getAccountManager().registerSingleDevice(number, code);
|
||||
},
|
||||
theme: getTheme(state),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -83,12 +83,12 @@ export default class AccountManager extends EventTarget {
|
|||
this.pending = Promise.resolve();
|
||||
}
|
||||
|
||||
async requestVoiceVerification(number: string) {
|
||||
return this.server.requestVerificationVoice(number);
|
||||
async requestVoiceVerification(number: string, token: string) {
|
||||
return this.server.requestVerificationVoice(number, token);
|
||||
}
|
||||
|
||||
async requestSMSVerification(number: string) {
|
||||
return this.server.requestVerificationSMS(number);
|
||||
async requestSMSVerification(number: string, token: string) {
|
||||
return this.server.requestVerificationSMS(number, token);
|
||||
}
|
||||
|
||||
encryptDeviceName(name: string, identityKey: KeyPairType) {
|
||||
|
|
|
@ -869,8 +869,8 @@ export type WebAPIType = {
|
|||
registerKeys: (genKeys: KeysType) => Promise<void>;
|
||||
registerSupportForUnauthenticatedDelivery: () => Promise<void>;
|
||||
reportMessage: (senderE164: string, serverGuid: string) => Promise<void>;
|
||||
requestVerificationSMS: (number: string) => Promise<void>;
|
||||
requestVerificationVoice: (number: string) => Promise<void>;
|
||||
requestVerificationSMS: (number: string, token: string) => Promise<void>;
|
||||
requestVerificationVoice: (number: string, token: string) => Promise<void>;
|
||||
sendMessages: (
|
||||
destination: string,
|
||||
messageArray: ReadonlyArray<MessageType>,
|
||||
|
@ -1557,19 +1557,19 @@ export function initialize({
|
|||
});
|
||||
}
|
||||
|
||||
async function requestVerificationSMS(number: string) {
|
||||
async function requestVerificationSMS(number: string, token: string) {
|
||||
await _ajax({
|
||||
call: 'accounts',
|
||||
httpType: 'GET',
|
||||
urlParameters: `/sms/code/${number}`,
|
||||
urlParameters: `/sms/code/${number}?captcha=${token}`,
|
||||
});
|
||||
}
|
||||
|
||||
async function requestVerificationVoice(number: string) {
|
||||
async function requestVerificationVoice(number: string, token: string) {
|
||||
await _ajax({
|
||||
call: 'accounts',
|
||||
httpType: 'GET',
|
||||
urlParameters: `/voice/code/${number}`,
|
||||
urlParameters: `/voice/code/${number}?captcha=${token}`,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,38 +0,0 @@
|
|||
// Copyright 2015-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
window.Whisper = window.Whisper || {};
|
||||
|
||||
export const PhoneInputView = window.Whisper.View.extend({
|
||||
tagName: 'div',
|
||||
className: 'phone-input',
|
||||
template: () => $('#phone-number').html(),
|
||||
initialize() {
|
||||
this.$('input.number').intlTelInput();
|
||||
},
|
||||
events: {
|
||||
change: 'validateNumber',
|
||||
keydown: 'validateNumber',
|
||||
},
|
||||
validateNumber() {
|
||||
const input = this.$('input.number');
|
||||
const regionCode = this.$('li.active')
|
||||
.attr('data-country-code')
|
||||
.toUpperCase();
|
||||
const number = input.val();
|
||||
|
||||
const parsedNumber = window.libphonenumber.util.parseNumber(
|
||||
number,
|
||||
regionCode
|
||||
);
|
||||
if (parsedNumber.isValidNumber) {
|
||||
this.$('.number-container').removeClass('invalid');
|
||||
this.$('.number-container').addClass('valid');
|
||||
} else {
|
||||
this.$('.number-container').removeClass('valid');
|
||||
}
|
||||
input.trigger('validation');
|
||||
|
||||
return parsedNumber.isValidNumber ? parsedNumber.e164 : undefined;
|
||||
},
|
||||
});
|
|
@ -1,117 +0,0 @@
|
|||
// Copyright 2017-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as log from '../logging/log';
|
||||
import { PhoneInputView } from './phone_input_view';
|
||||
|
||||
window.Whisper = window.Whisper || {};
|
||||
|
||||
const { Whisper } = window;
|
||||
|
||||
export const StandaloneRegistrationView = Whisper.View.extend({
|
||||
template: () => $('#standalone').html(),
|
||||
className: 'full-screen-flow',
|
||||
initialize() {
|
||||
window.readyForUpdates();
|
||||
|
||||
this.accountManager = window.getAccountManager();
|
||||
|
||||
this.render();
|
||||
|
||||
const number = window.textsecure.storage.user.getNumber();
|
||||
if (number) {
|
||||
this.$('input.number').val(number);
|
||||
}
|
||||
this.phoneView = new PhoneInputView({
|
||||
el: this.$('#phone-number-input'),
|
||||
});
|
||||
this.$('#error').hide();
|
||||
},
|
||||
events: {
|
||||
'validation input.number': 'onValidation',
|
||||
'click #request-voice': 'requestVoice',
|
||||
'click #request-sms': 'requestSMSVerification',
|
||||
'change #code': 'onChangeCode',
|
||||
'click #verifyCode': 'verifyCode',
|
||||
},
|
||||
getVerificationCode() {
|
||||
const codeHTML = $('#code').val();
|
||||
if (!codeHTML) {
|
||||
return;
|
||||
}
|
||||
return String(codeHTML).replace(/\D+/g, '');
|
||||
},
|
||||
async verifyCode() {
|
||||
const number = this.phoneView.validateNumber();
|
||||
const verificationCode = this.getVerificationCode();
|
||||
|
||||
try {
|
||||
await this.accountManager.registerSingleDevice(number, verificationCode);
|
||||
this.$el.trigger('openInbox');
|
||||
} catch (err) {
|
||||
this.log(err);
|
||||
}
|
||||
},
|
||||
log(s: Error) {
|
||||
log.info(s);
|
||||
this.$('#status').text(s);
|
||||
},
|
||||
validateCode() {
|
||||
const verificationCode = this.getVerificationCode();
|
||||
|
||||
if (verificationCode.length === 6) {
|
||||
return verificationCode;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
displayError(error: Error) {
|
||||
this.$('#error').hide().text(error).addClass('in').fadeIn();
|
||||
},
|
||||
onValidation() {
|
||||
if (this.$('#number-container').hasClass('valid')) {
|
||||
this.$('#request-sms, #request-voice').removeAttr('disabled');
|
||||
} else {
|
||||
this.$('#request-sms, #request-voice').prop('disabled', 'disabled');
|
||||
}
|
||||
},
|
||||
onChangeCode() {
|
||||
if (!this.validateCode()) {
|
||||
this.$('#code').addClass('invalid');
|
||||
} else {
|
||||
this.$('#code').removeClass('invalid');
|
||||
}
|
||||
},
|
||||
async requestVoice() {
|
||||
window.removeSetupMenuItems();
|
||||
this.$('#error').hide();
|
||||
const number = this.phoneView.validateNumber();
|
||||
if (number) {
|
||||
this.$('#step2').addClass('in').fadeIn();
|
||||
try {
|
||||
await this.accountManager.requestVoiceVerification(number);
|
||||
} catch (err) {
|
||||
this.displayError(err);
|
||||
}
|
||||
} else {
|
||||
this.$('#number-container').addClass('invalid');
|
||||
}
|
||||
},
|
||||
async requestSMSVerification() {
|
||||
window.removeSetupMenuItems();
|
||||
$('#error').hide();
|
||||
const number = this.phoneView.validateNumber();
|
||||
if (number) {
|
||||
this.$('#step2').addClass('in').fadeIn();
|
||||
try {
|
||||
await this.accountManager.requestSMSVerification(number);
|
||||
} catch (err) {
|
||||
this.displayError(err);
|
||||
}
|
||||
} else {
|
||||
this.$('#number-container').addClass('invalid');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Whisper.StandaloneRegistrationView = StandaloneRegistrationView;
|
1
ts/window.d.ts
vendored
1
ts/window.d.ts
vendored
|
@ -591,6 +591,5 @@ export type WhisperType = {
|
|||
KeyVerificationPanelView: typeof AnyViewClass;
|
||||
ReactWrapperView: typeof BasicReactWrapperViewClass;
|
||||
SafetyNumberChangeDialogView: typeof AnyViewClass;
|
||||
StandaloneRegistrationView: typeof AnyViewClass;
|
||||
View: typeof AnyViewClass;
|
||||
};
|
||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -2567,6 +2567,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/humanize-duration/-/humanize-duration-3.18.1.tgz#10090d596053703e7de0ac43a37b96cd9fc78309"
|
||||
integrity sha512-MUgbY3CF7hg/a/jogixmAufLjJBQT7WEf8Q+kYJkOc47ytngg1IuZobCngdTjAgY83JWEogippge5O5fplaQlw==
|
||||
|
||||
"@types/intl-tel-input@17.0.4":
|
||||
version "17.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/intl-tel-input/-/intl-tel-input-17.0.4.tgz#a7be083bd1989830e7e02f6d552a40428fd5ed7c"
|
||||
integrity sha512-AIX0Azxs7fAkeI2wjAsMNE4Lfoa+/NqazWDPM4k4Vm8lhcId0BNMjzC/BACEGE0nAScc71+pznlKnTxBfW5HHA==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/jquery@*":
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.0.tgz#ccb7dfd317d02d4227dd3803c75297d0c10dad68"
|
||||
|
@ -10064,10 +10071,10 @@ interpret@~1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
|
||||
integrity sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=
|
||||
|
||||
intl-tel-input@12.1.15:
|
||||
version "12.1.15"
|
||||
resolved "https://registry.yarnpkg.com/intl-tel-input/-/intl-tel-input-12.1.15.tgz#7393e6b77572731bbc65ca4585782e8ba3d74de4"
|
||||
integrity sha512-9TN9x6aGdO1eL6iGFpobuLU4UymZqjSnS9UnsOSi//LU3A8nkLOcokSYBYjak18Uu8OM59HsGYDd1jKmwRsskw==
|
||||
intl-tel-input@17.0.13:
|
||||
version "17.0.13"
|
||||
resolved "https://registry.yarnpkg.com/intl-tel-input/-/intl-tel-input-17.0.13.tgz#74a51db3b44f47ae8264df7d101a0810e53c77a6"
|
||||
integrity sha512-+jPgPKUcgbhSSRN0BZLJOdntTaMv8tqowLrVQ3CGCNGpMKb4B9PRyzOZQpwo1FeA+LTOzvErOlBhbCuQog7R3g==
|
||||
|
||||
invariant@2.2.4, invariant@^2.2.3, invariant@^2.2.4:
|
||||
version "2.2.4"
|
||||
|
|
Loading…
Add table
Reference in a new issue