diff --git a/Gruntfile.js b/Gruntfile.js
index 5bfc8cc5..6cac4725 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -127,7 +127,8 @@ module.exports = function(grunt) {
sass: {
stylesheets: {
files: {
- 'stylesheets/manifest.css': 'stylesheets/manifest.scss'
+ 'stylesheets/manifest.css': 'stylesheets/manifest.scss',
+ 'stylesheets/options.css': 'stylesheets/options.scss'
}
}
},
diff --git a/images/appstore.svg b/images/appstore.svg
new file mode 100644
index 00000000..ac111e59
--- /dev/null
+++ b/images/appstore.svg
@@ -0,0 +1,129 @@
+
+
+
+
diff --git a/images/playstore.png b/images/playstore.png
new file mode 100644
index 00000000..9a50affd
Binary files /dev/null and b/images/playstore.png differ
diff --git a/js/options.js b/js/options.js
index ba774c26..e9df26ae 100644
--- a/js/options.js
+++ b/js/options.js
@@ -13,176 +13,52 @@
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
-
;(function() {
- function validateCode() {
- var verificationCode = $('#code').val().replace(/\D/g, '');
- if (verificationCode.length == 6) {
- return verificationCode;
- }
- }
-
- function displayError(error) {
- $('#error').hide().text(error).addClass('in').fadeIn();
- }
-
+ 'use strict';
$(function() {
- var phoneView = new Whisper.PhoneInputView({el: $('#phone-number-input')});
- phoneView.render();
if (textsecure.registration.isDone()) {
- $('#complete-number').text(textsecure.utils.unencodeNumber(textsecure.storage.getUnencrypted("number_id"))[0]);//TODO: no
+ $('#complete-number').text(
+ textsecure.utils.unencodeNumber(
+ textsecure.storage.getUnencrypted("number_id")
+ )[0]
+ );//TODO: no
$('#setup-complete').show().addClass('in');
} else {
- $('#choose-setup').show().addClass('in');
-
- $('input.number').on('validation', function() {
- if ($('#number-container').hasClass('valid')) {
- $('#request-sms, #request-voice').removeAttr('disabled');
- } else {
- $('#request-sms, #request-voice').prop('disabled', 'disabled');
- }
- });
-
- $('#code').on('change', function() {
- if (!validateCode())
- $('#code').addClass('invalid');
- else
- $('#code').removeClass('invalid');
- });
-
- $('#request-voice').click(function() {
- $('#error').hide();
- var number = phoneView.validateNumber();
- if (number) {
- textsecure.api.requestVerificationVoice(number).catch(displayError);
- $('#step2').addClass('in').fadeIn();
- } else {
- $('#number-container').addClass('invalid');
- }
- });
-
- $('#request-sms').click(function() {
- $('#error').hide();
- var number = phoneView.validateNumber();
- if (number) {
- textsecure.api.requestVerificationSMS(number).catch(displayError);
- $('#step2').addClass('in').fadeIn();
- } else {
- $('#number-container').addClass('invalid');
- }
- });
-
- $('#new-account').click(function(){
- $('#choose-setup').fadeOut(function() {
- $('#single-device').addClass('in').fadeIn();
- });
-
- $('#single-device .back').click(function() {
- $('#single-device').fadeOut(function() {
- $('#choose-setup').addClass('in').fadeIn();
- $('input.number').removeClass('invalid');
- });
- });
-
- $('#single-device form').submit(function(e) {
- e.preventDefault();
- $('#error').hide();
- var number = phoneView.validateNumber();
- var verificationCode = validateCode();
- if (number && verificationCode) {
- $('#verifyCode').prop('disabled', 'disabled');
- $('#verify *').hide();
- $('#verify').show().addClass('in');
- $('#verify2').show();
-
- textsecure.registerSingleDevice(number, verificationCode, function(step) {
+ $('#init-setup').show().addClass('in');
+ $('#status').text("Connecting...");
+ axolotl.protocol.createIdentityKeyRecvSocket().then(function(cryptoInfo) {
+ var qrCode = new QRCode(document.getElementById('qr'));
+ var socket = textsecure.api.getTempWebsocket();
+ new WebSocketResource(socket, function(request) {
+ if (request.path == "/v1/address" && request.verb == "PUT") {
+ var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body);
+ var url = [ 'tsdevice:/', '?uuid=', proto.uuid, '&pub_key=',
+ encodeURIComponent(btoa(getString(cryptoInfo.pubKey))) ].join('');
+ $('#status').text('');
+ qrCode.makeCode(url);
+ request.respond(200, 'OK');
+ } else if (request.path == "/v1/message" && request.verb == "PUT") {
+ $('#qr').hide();
+ textsecure.registerSecondDevice(request.body, cryptoInfo, function(step) {
switch(step) {
case 1:
- $('#verify3').show();
+ $('#status').text('Registering new device...');
break;
case 2:
- $('#verify4').show();
+ $('#status').text('Generating keys...');
break;
case 3:
- $('#complete-number').text(number);
- $('#verify').hide();
- $('#init-setup').hide().removeClass('in');
- $('#setup-complete').show().addClass('in');
+ $('#status').text('Uploading keys...');
+ break;
+ case 4:
+ $('#status').text('All done!');
textsecure.registration.done();
+ $('#init-setup').hide();
+ $('#setup-complete').show().addClass('in');
}
- }).catch(function(error) {
- $('#verify *').hide();
- $('#verifyCode').removeAttr('disabled');
- if (error.humanError)
- displayError(error.humanError);
- else
- displayError(error); //XXX
});
- }
- });
- });
-
- $('#new-device').click(function(){
- $('#choose-setup').fadeOut(function() {
- $('#multi-device').addClass('in').fadeIn();
- });
-
- $('#multi-device .back').click(function() {
- $('#multi-device').fadeOut(function() {
- $('#choose-setup').addClass('in').fadeIn();
- $('input.number').removeClass('invalid');
- });
- });
-
- $('#multi-device .status').text("Connecting...");
- $('#setup-qr').html('');
- axolotl.protocol.createIdentityKeyRecvSocket().then(function(cryptoInfo) {
- var qrCode = new QRCode(document.getElementById('setup-qr'));
-
- var socket = textsecure.api.getTempWebsocket();
- new WebSocketResource(socket, function(request) {
- if (request.path == "/v1/address" && request.verb == "PUT") {
- var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body);
- qrCode.makeCode('tsdevice:/' +
- '?uuid=' + proto.uuid +
- '&pub_key=' + encodeURIComponent(btoa(getString(cryptoInfo.pubKey))));
- $('img').removeAttr('style');
- $('#multi-device .status').text("Use your phone to scan the QR code.");
- request.respond(200, 'OK');
- } else if (request.path == "/v1/message" && request.verb == "PUT") {
- $('#init-setup').hide();
- $('#verify1done').text('');
- $('#verify2done').text('');
- $('#verify3done').text('');
- $('#verify4done').text('');
- $('#verify5done').text('');
- $('#verify').show().addClass('in');
-
- textsecure.registerSecondDevice(request.body, cryptoInfo, function(step) {
- switch(step) {
- case 1:
- $('#verify1done').text('done');
- break;
- case 2:
- $('#verify2done').text('done');
- break;
- case 3:
- $('#verify3done').text('done');
- break;
- case 4:
- //XXX: User needs to verify number before we continue
- $('#verify4done').text('done');
- //$('#complete-number').text(parsedNumber);
- textsecure.registration.done();
- //case 5: //TODO: Do sync to get 5!
- $('#verify').hide();
- $('#setup-complete').show().addClass('in');
- textsecure.registration.done();
- }
- });
- } else
- console.log(request.path);
- });
+ } else
+ console.log(request.path);
});
});
}
diff --git a/js/register.js b/js/register.js
new file mode 100644
index 00000000..5ff9dd50
--- /dev/null
+++ b/js/register.js
@@ -0,0 +1,122 @@
+/* vim: ts=4:sw=4
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+;(function() {
+ 'use strict';
+
+ function log(s) {
+ console.log(s);
+ $('#status').text(s);
+ }
+
+ function validateCode() {
+ var verificationCode = $('#code').val().replace(/\D/g, '');
+ if (verificationCode.length == 6) {
+ return verificationCode;
+ }
+ }
+
+ function displayError(error) {
+ $('#error').hide().text(error).addClass('in').fadeIn();
+ }
+
+ var phoneView = new Whisper.PhoneInputView({el: $('#phone-number-input')});
+ phoneView.$el.find('input.number').intlTelInput();
+
+ var number = textsecure.storage.getUnencrypted('number_id');
+ if (number) {
+ $('input.number').val(number.split('.')[0]);
+ }
+
+ $('input.number').on('validation', function() {
+ if ($('#number-container').hasClass('valid')) {
+ $('#request-sms, #request-voice').removeAttr('disabled');
+ } else {
+ $('#request-sms, #request-voice').prop('disabled', 'disabled');
+ }
+ });
+
+ $('#code').on('change', function() {
+ if (!validateCode())
+ $('#code').addClass('invalid');
+ else
+ $('#code').removeClass('invalid');
+ });
+
+ $('#request-voice').click(function() {
+ $('#error').hide();
+ var number = phoneView.validateNumber();
+ if (number) {
+ textsecure.api.requestVerificationVoice(number).catch(displayError);
+ $('#step2').addClass('in').fadeIn();
+ } else {
+ $('#number-container').addClass('invalid');
+ }
+ });
+
+ $('#request-sms').click(function() {
+ $('#error').hide();
+ var number = phoneView.validateNumber();
+ if (number) {
+ textsecure.api.requestVerificationSMS(number).catch(displayError);
+ $('#step2').addClass('in').fadeIn();
+ } else {
+ $('#number-container').addClass('invalid');
+ }
+ });
+
+ $('#form').submit(function(e) {
+ e.preventDefault();
+ log('registering');
+ var number = $('input.number').val();
+ var verificationCode = $('#code').val();
+ var signalingKey = textsecure.crypto.getRandomBytes(32 + 20);
+
+ var password = btoa(getString(textsecure.crypto.getRandomBytes(16)));
+ password = password.substring(0, password.length - 2);
+
+ var registrationId = new Uint16Array(textsecure.crypto.getRandomBytes(2))[0];
+ registrationId = registrationId & 0x3fff;
+
+ log('clearing data');
+ localStorage.clear();
+
+ localStorage.setItem('first_install_ran', 1);
+ textsecure.storage.putUnencrypted('registrationId', registrationId);
+ textsecure.storage.putEncrypted('signaling_key', signalingKey);
+ textsecure.storage.putEncrypted('password', password);
+ textsecure.storage.putUnencrypted('number_id', number + '.1');
+ textsecure.storage.putUnencrypted('regionCode', libphonenumber.util.getRegionCodeForNumber(number));
+
+ log('verifying code');
+ return textsecure.api.confirmCode(
+ number, verificationCode, password, signalingKey, registrationId, true
+ ).then(function() {
+ log('generating keys');
+ return axolotl.protocol.generateKeys().then(function(keys) {
+ log('uploading keys');
+ return textsecure.api.registerKeys(keys).then(function() {
+ textsecure.registration.done();
+ log('done');
+ chrome.runtime.reload();
+ });
+ });
+ }).catch(function(e) {
+ log(e);
+ });
+ });
+
+})();
diff --git a/options.html b/options.html
index d394801d..e0ba36c7 100644
--- a/options.html
+++ b/options.html
@@ -19,79 +19,82 @@
-
-
-
TextSecure
-
-
-
-
I'm new to TextSecure
-
-
-
I have TextSecure installed on my phone.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
Welcome to Signal for Chrome
+ Private messaging from your web browser.
+
+
+
+
+
+
+
+
Step 1
+ Download the Mobile App
+
+
+
Step 2
+ Pair Your Device
+
+
+
+
+
+ To get started, download the TextSecure app for your Android phone.
+ Once you've installed it, proceed to Step 2.
+
+
+
+
+ Using a QR code scanning app on your phone, scan the QR code below, then
+ open the link to pair your device.
+
+
+
+
-
-
-
-
Receiving identity key...
-
Verifying number and setup code...
-
Generating keys...
-
Registering...
-
Syncing with existing devices...
+
+
+
+
+
+
+
+
+
+
Signal for Chrome
+ Private messaging from your web browser.
+
-
-
+
+
You are registered on TextSecure with number
-
diff --git a/register.html b/register.html
new file mode 100644
index 00000000..8e65a2f2
--- /dev/null
+++ b/register.html
@@ -0,0 +1,79 @@
+
+
+
+
+
+
Re-register TextSecure
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Create your Signal Account
+ Private messaging from your web browser.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stylesheets/options.css b/stylesheets/options.css
index 44f1c165..cea87b85 100644
--- a/stylesheets/options.css
+++ b/stylesheets/options.css
@@ -13,51 +13,74 @@
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
.
*/
+@font-face {
+ font-family: 'Roboto-Light';
+ src: url("/fonts/Roboto-Light.ttf") format("truetype"); }
+@font-face {
+ font-family: 'Roboto';
+ src: url("/fonts/Roboto-Regular.ttf") format("truetype"); }
+@font-face {
+ font-family: 'Roboto';
+ src: url("/fonts/Roboto-Italic.ttf") format("truetype");
+ font-style: italic; }
+@font-face {
+ font-family: 'Roboto';
+ src: url("/fonts/Roboto-Bold.ttf") format("truetype");
+ font-weight: bold; }
+body {
+ font-family: Roboto, "Helvetica Neue", Arial, Helvetica, sans-serif; }
-* {
- font-family: Ubuntu, Segoe, 'Lucidia Grande', sans-serif;
-}
-
-.paper {
- background-color: #fafafa;
- @include box-shadow(5px 0 5px -2px #ddd, -5px 0 5px -2px #ddd);
-}
+header {
+ background: #2a92e7;
+ color: white;
+ padding-bottom: 2em;
+ margin-bottom: 2em; }
+.container {
+ min-width: 650px; }
h1 {
font-size: 30pt;
font-weight: normal;
- padding-bottom: 10px;
-}
+ padding-bottom: 10px; }
-h2 {
- font-size: 12pt;
- font-weight: normal;
-}
-
-.left-column {
- float: left;
- width: 45%;
-}
-
-.right-column {
- float: right;
- width: 50%;
-}
-
-.hidden {
- display: none;
-}
+.tagline {
+ font-style: italic; }
#textsecure-icon {
- width: 50px;
- height: 50px;
-}
+ float: left;
+ margin-top: 20px;
+ max-width: 100%; }
-#setup-qr {
- max-width: 256px;
- margin-top: 1em;
-}
+#step1, #step2 {
+ color: #2a92e7;
+ font-weight: bold;
+ font-size: small;
+ text-transform: uppercase; }
+
+h3.step {
+ margin-top: 0;
+ font-weight: bold; }
+
+.cta {
+ text-align: center;
+ border: 2px solid #f3f3f3;
+ border-radius: 10px;
+ padding: 1em 0;
+ min-height: 293px; }
+
+.help {
+ border-top: 2px solid #f3f3f3;
+ padding: 1.5em 0.1em; }
+
+.install {
+ display: inline-block;
+ margin-top: 90px; }
+
+#qr {
+ display: inline-block; }
+ #qr canvas {
+ display: none; }
#verifyCode,
#code,
@@ -65,35 +88,28 @@ h2 {
box-sizing: border-box;
width: 100%;
display: block;
- margin-bottom: 0.5em;
-}
+ margin-bottom: 0.5em; }
#request-voice,
#request-sms {
- box-sizing: border-box;
-}
+ box-sizing: border-box; }
+
#request-sms {
width: 57%;
- float: right;
-}
+ float: right; }
+
#request-voice {
width: 40%;
- float: left;
-}
-
-.regionCode {
- width: 100%;
- margin-bottom: 1em;
-}
+ float: left; }
.number-container {
position: relative;
- margin-bottom: 0.5em;
-}
+ margin-bottom: 0.5em; }
+
.number-container .intl-tel-input,
.number-container .number {
- width: 100%;
-}
+ width: 100%; }
+
.number-container::after {
visibility: hidden;
content: ' ';
@@ -107,47 +123,37 @@ h2 {
top: 0;
left: 100%;
margin: 3px 8px;
- text-align: center;
-}
+ text-align: center; }
+
.number-container.valid::after {
visibility: visible;
content: '✓';
background-color: #0f9d58;
- color: #ffffff;
-}
+ color: #ffffff; }
+
.number-container.invalid::after {
visibility: visible;
content: '!';
background-color: #f44336;
- color: #ffffff;
-}
+ color: #ffffff; }
#error {
color: white;
font-weight: bold;
padding: 0.5em;
- text-align: center;
-}
-#error { background-color: #f44336; }
+ text-align: center; }
+
+#error {
+ background-color: #f44336; }
+
#error:before {
content: '\26a0';
- padding-right: 0.5em;
-}
+ padding-right: 0.5em; }
+
.narrow {
box-sizing: border-box;
- margin: 0 auto 5px;
width: 275px;
- max-width: 100%;
-}
-
-#single-device form {
- margin: 2em 0;
-}
-
-input.form-control, select.form-control {
- border-radius: 0;
-}
+ max-width: 100%; }
ul.country-list {
- min-width: 197px !important;
-}
+ min-width: 197px !important; }
diff --git a/stylesheets/options.scss b/stylesheets/options.scss
new file mode 100644
index 00000000..20c775fe
--- /dev/null
+++ b/stylesheets/options.scss
@@ -0,0 +1,164 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see
.
+ */
+
+@import 'variables';
+
+body {
+ font-family: $roboto;
+}
+
+header {
+ background: $blue;
+ color: white;
+ padding-bottom: 2em;
+ margin-bottom: 2em;
+}
+
+.container {
+ min-width: 650px;
+}
+
+h1 {
+ font-size: 30pt;
+ font-weight: normal;
+ padding-bottom: 10px;
+}
+
+.tagline {
+ font-style: italic;
+}
+
+#textsecure-icon {
+ float: left;
+ margin-top: 20px;
+ max-width: 100%;
+}
+
+#step1, #step2 {
+ color: $blue;
+ font-weight: bold;
+ font-size: small;
+ text-transform: uppercase;
+}
+
+h3.step {
+ margin-top: 0;
+ font-weight: bold;
+}
+
+.cta {
+ text-align: center;
+ border: 2px solid $grey_l;
+ border-radius: 10px;
+ padding: 1em 0;
+ min-height: 293px;
+}
+
+.help {
+ border-top: 2px solid $grey_l;
+ padding: 1.5em 0.1em;
+}
+
+.install {
+ display: inline-block;
+ margin-top: 90px;
+}
+
+#qr {
+ display: inline-block;
+
+ canvas {
+ display: none;
+ }
+}
+
+#verifyCode,
+#code,
+#number {
+ box-sizing: border-box;
+ width: 100%;
+ display: block;
+ margin-bottom: 0.5em;
+}
+
+#request-voice,
+#request-sms {
+ box-sizing: border-box;
+}
+#request-sms {
+ width: 57%;
+ float: right;
+}
+#request-voice {
+ width: 40%;
+ float: left;
+}
+
+.number-container {
+ position: relative;
+ margin-bottom: 0.5em;
+}
+.number-container .intl-tel-input,
+.number-container .number {
+ width: 100%;
+}
+.number-container::after {
+ visibility: hidden;
+ content: ' ';
+ display: inline-block;
+ border-radius: 1.5em;
+ width: 1.5em;
+ height: 1.5em;
+ line-height: 1.5em;
+ color: #ffffff;
+ position: absolute;
+ top: 0;
+ left: 100%;
+ margin: 3px 8px;
+ text-align: center;
+}
+.number-container.valid::after {
+ visibility: visible;
+ content: '✓';
+ background-color: #0f9d58;
+ color: #ffffff;
+}
+.number-container.invalid::after {
+ visibility: visible;
+ content: '!';
+ background-color: #f44336;
+ color: #ffffff;
+}
+
+#error {
+ color: white;
+ font-weight: bold;
+ padding: 0.5em;
+ text-align: center;
+}
+#error { background-color: #f44336; }
+#error:before {
+ content: '\26a0';
+ padding-right: 0.5em;
+}
+.narrow {
+ box-sizing: border-box;
+ width: 275px;
+ max-width: 100%;
+}
+
+ul.country-list {
+ min-width: 197px !important;
+}