Nest key verification inside conflict dialogs
// FREEBIE
This commit is contained in:
parent
c4fcbd8cbe
commit
6fe9c3f964
8 changed files with 123 additions and 122 deletions
|
@ -22,16 +22,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"acceptNewKey": {
|
"acceptNewKey": {
|
||||||
"message": "Accept new key"
|
"message": "Accept",
|
||||||
|
"description": "Label for a button to accept a new identity key"
|
||||||
},
|
},
|
||||||
"verify": {
|
"verify": {
|
||||||
"message": "Verify"
|
"message": "Verify"
|
||||||
},
|
},
|
||||||
"newIdentity": {
|
"newIdentity": {
|
||||||
"message": "New Identity"
|
"message": "New Identity",
|
||||||
|
"description": "Header for a key change dialog"
|
||||||
},
|
},
|
||||||
"identityChanged": {
|
"identityChanged": {
|
||||||
"message": "This contact's identity key has changed. This could either mean that someone is trying to intercept your communication, or this contact simply re-installed Signal and now has a new identity key."
|
"message": "This contact is using a new Signal identity. This could either mean that someone is trying to intercept your communication, or this contact simply re-installed Signal. You may wish to verify their new identity."
|
||||||
},
|
},
|
||||||
"outgoingKeyConflict": {
|
"outgoingKeyConflict": {
|
||||||
"message": "This contact's identity key has changed. Click to process and display."
|
"message": "This contact's identity key has changed. Click to process and display."
|
||||||
|
@ -242,5 +244,13 @@
|
||||||
"messageNotSent": {
|
"messageNotSent": {
|
||||||
"message": "Message not sent.",
|
"message": "Message not sent.",
|
||||||
"description": "Informational label, appears on messages that failed to send"
|
"description": "Informational label, appears on messages that failed to send"
|
||||||
|
},
|
||||||
|
"showMore": {
|
||||||
|
"message": "Details",
|
||||||
|
"description": "Displays the details of a key change"
|
||||||
|
},
|
||||||
|
"showLess": {
|
||||||
|
"message": "Hide details",
|
||||||
|
"description": "Hides the details of a key change"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,8 +235,12 @@
|
||||||
<script type='text/x-tmpl-mustache' id='group-member-list'>
|
<script type='text/x-tmpl-mustache' id='group-member-list'>
|
||||||
<div class='container'></div>
|
<div class='container'></div>
|
||||||
</script>
|
</script>
|
||||||
<script type='text/x-tmpl-mustache' id='key-verification'>
|
<script type='text/x-tmpl-mustache' id='key_verification_panel'>
|
||||||
<div class='container'>
|
<div class='container'>
|
||||||
|
{{> key_verification }}
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
<script type='text/x-tmpl-mustache' id='key_verification'>
|
||||||
<p> {{theirIdentity}} </p>
|
<p> {{theirIdentity}} </p>
|
||||||
{{ ^their_key }}
|
{{ ^their_key }}
|
||||||
<div class='placeholder'>{{ their_key_unknown }}</div>
|
<div class='placeholder'>{{ their_key_unknown }}</div>
|
||||||
|
@ -250,7 +254,6 @@
|
||||||
<div class='key'>
|
<div class='key'>
|
||||||
{{ #your_key }} <span>{{ . }}</span> {{ /your_key }}
|
{{ #your_key }} <span>{{ . }}</span> {{ /your_key }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</script>
|
</script>
|
||||||
<!-- index -->
|
<!-- index -->
|
||||||
<script type='text/x-tmpl-mustache' id='group_info_input'>
|
<script type='text/x-tmpl-mustache' id='group_info_input'>
|
||||||
|
@ -305,15 +308,13 @@
|
||||||
<div class='clearfix'>
|
<div class='clearfix'>
|
||||||
<h3>{{ newIdentity }}</h3>
|
<h3>{{ newIdentity }}</h3>
|
||||||
{{> avatar }}
|
{{> avatar }}
|
||||||
<span class='name'>{{ name }}
|
<span class='name'>{{ name }}</span>
|
||||||
<button class='conflict'><span>{{ verify }}</span></button>
|
<button class='resolve'>{{ resolve }}</button>
|
||||||
<button class='cancel hide'><span>{{ cancel }}</span></button>
|
<span class='hideKeys hide'> {{ hideKeys }} </span>
|
||||||
</span>
|
<span class='showKeys'> {{ showKeys }} </span>
|
||||||
</div>
|
</div>
|
||||||
<div class='content hide'>
|
<div class='keys hide'>
|
||||||
<p> {{ message }} </p>
|
<p> {{ message }} </p>
|
||||||
<p> {{{ verifyContact }}} </p>
|
|
||||||
<p><button class='resolve'>{{ resolve }}</button></p>
|
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
<script type='text/x-tmpl-mustache' id='window-controls'>
|
<script type='text/x-tmpl-mustache' id='window-controls'>
|
||||||
|
|
|
@ -162,7 +162,7 @@
|
||||||
var our_number = textsecure.storage.user.getNumber();
|
var our_number = textsecure.storage.user.getNumber();
|
||||||
textsecure.storage.axolotl.getIdentityKey(their_number).then(function(their_key) {
|
textsecure.storage.axolotl.getIdentityKey(their_number).then(function(their_key) {
|
||||||
textsecure.storage.axolotl.getIdentityKey(our_number).then(function(our_key) {
|
textsecure.storage.axolotl.getIdentityKey(our_number).then(function(our_key) {
|
||||||
var view = new Whisper.KeyVerificationView({
|
var view = new Whisper.KeyVerificationPanelView({
|
||||||
model: { their_key: their_key, your_key: our_key }
|
model: { their_key: their_key, your_key: our_key }
|
||||||
}).render();
|
}).render();
|
||||||
this.listenBack(view);
|
this.listenBack(view);
|
||||||
|
|
|
@ -11,42 +11,48 @@
|
||||||
className: 'key-conflict-dialogue clearfix',
|
className: 'key-conflict-dialogue clearfix',
|
||||||
initialize: function(options) {
|
initialize: function(options) {
|
||||||
this.contact = options.contact;
|
this.contact = options.contact;
|
||||||
this.conflict = options.conflict;
|
|
||||||
this.conversation = options.conversation;
|
this.conversation = options.conversation;
|
||||||
|
textsecure.storage.axolotl.getIdentityKey(textsecure.storage.user.getNumber()).then(function(our_key) {
|
||||||
|
this.your_key = our_key;
|
||||||
|
this.render();
|
||||||
|
}.bind(this));
|
||||||
|
textsecure.storage.axolotl.getIdentityKey(textsecure.storage.user.getNumber()).then(function(our_key) {
|
||||||
|
var view = new Whisper.KeyVerificationView({
|
||||||
|
model: {
|
||||||
|
their_key : this.model.identityKey,
|
||||||
|
your_key : our_key
|
||||||
|
}
|
||||||
|
});
|
||||||
|
view.render().$el.appendTo(this.$('.keys'));
|
||||||
|
}.bind(this));
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
'click .conflict': 'showDialog',
|
'click .showKeys': 'showKeys',
|
||||||
'click .cancel' : 'cancel',
|
'click .hideKeys': 'hideKeys',
|
||||||
'click .verify' : 'triggerVerify',
|
|
||||||
'click .resolve' : 'resolve'
|
'click .resolve' : 'resolve'
|
||||||
},
|
},
|
||||||
triggerVerify: function() {
|
hideKeys: function() {
|
||||||
this.trigger('verify', {identityKey: this.model.identityKey});
|
this.$('.keys, .hideKeys').hide();
|
||||||
|
this.$('.showKeys').show();
|
||||||
|
},
|
||||||
|
showKeys: function() {
|
||||||
|
this.$('.keys, .hideKeys').show();
|
||||||
|
this.$('.showKeys').hide();
|
||||||
},
|
},
|
||||||
resolve: function() {
|
resolve: function() {
|
||||||
this.trigger('resolve');
|
|
||||||
this.remove();
|
this.remove();
|
||||||
this.conversation.resolveConflicts(this.model);
|
this.conversation.resolveConflicts(this.model);
|
||||||
},
|
},
|
||||||
showDialog: function() {
|
|
||||||
this.$('.conflict').hide();
|
|
||||||
this.$('.cancel, .content').show();
|
|
||||||
},
|
|
||||||
cancel: function() {
|
|
||||||
this.$('.cancel, .content').hide();
|
|
||||||
this.$('.conflict').show();
|
|
||||||
},
|
|
||||||
render_attributes: function() {
|
render_attributes: function() {
|
||||||
return {
|
return {
|
||||||
name : this.contact.getTitle(),
|
name : this.contact.getTitle(),
|
||||||
avatar : this.contact.getAvatar(),
|
avatar : this.contact.getAvatar(),
|
||||||
conflict : this.conflict,
|
conflict : this.model,
|
||||||
verify : i18n('verify'),
|
|
||||||
cancel : i18n('cancel'),
|
|
||||||
newIdentity : i18n('newIdentity'),
|
newIdentity : i18n('newIdentity'),
|
||||||
message : i18n('identityChanged'),
|
message : i18n('identityChanged'),
|
||||||
resolve : i18n('acceptNewKey'),
|
resolve : i18n('acceptNewKey'),
|
||||||
verifyContact: i18n('verifyContact')
|
showKeys : i18n('showMore'),
|
||||||
|
hideKeys : i18n('showLess')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
window.Whisper = window.Whisper || {};
|
window.Whisper = window.Whisper || {};
|
||||||
|
|
||||||
Whisper.KeyVerificationView = Whisper.View.extend({
|
Whisper.KeyVerificationView = Whisper.View.extend({
|
||||||
className: 'key-verification panel',
|
className: 'key-verification',
|
||||||
templateName: 'key-verification',
|
templateName: 'key_verification',
|
||||||
events: {
|
events: {
|
||||||
'click .back': 'goBack'
|
'click .back': 'goBack'
|
||||||
},
|
},
|
||||||
|
@ -36,4 +36,8 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Whisper.KeyVerificationPanelView = Whisper.KeyVerificationView.extend({
|
||||||
|
className: 'key-verification panel',
|
||||||
|
templateName: 'key_verification_panel',
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -43,19 +43,6 @@
|
||||||
goBack: function() {
|
goBack: function() {
|
||||||
this.trigger('back');
|
this.trigger('back');
|
||||||
},
|
},
|
||||||
verify: function(their_key) {
|
|
||||||
textsecure.storage.axolotl.getIdentityKey(textsecure.storage.user.getNumber()).then(function(our_key) {
|
|
||||||
var view = new Whisper.KeyVerificationView({
|
|
||||||
model: { their_key: their_key, your_key: our_key }
|
|
||||||
});
|
|
||||||
this.$el.hide();
|
|
||||||
view.render().$el.insertAfter(this.el);
|
|
||||||
this.listenTo(view, 'back', function() {
|
|
||||||
view.remove();
|
|
||||||
this.$el.show();
|
|
||||||
}.bind(this));
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
contacts: function() {
|
contacts: function() {
|
||||||
if (this.model.isIncoming()) {
|
if (this.model.isIncoming()) {
|
||||||
var number = this.model.get('source');
|
var number = this.model.get('source');
|
||||||
|
@ -83,14 +70,6 @@
|
||||||
conversation: this.conversation
|
conversation: this.conversation
|
||||||
}).render();
|
}).render();
|
||||||
this.$('.conflicts').append(view.el);
|
this.$('.conflicts').append(view.el);
|
||||||
this.listenTo(view, 'verify', function(data) {
|
|
||||||
this.verify(conflict.identityKey);
|
|
||||||
});
|
|
||||||
/*
|
|
||||||
this.listenTo(view, 'resolve', function() {
|
|
||||||
this.render();
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
this.errors = _.groupBy(this.model.get('errors'), 'number');
|
this.errors = _.groupBy(this.model.get('errors'), 'number');
|
||||||
|
|
|
@ -47,17 +47,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.key-verification {
|
.key-verification {
|
||||||
background: white;
|
&.panel {
|
||||||
padding: 20px 10px;
|
padding: 20px 10px;
|
||||||
|
|
||||||
p {
|
|
||||||
padding: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.key, .placeholder {
|
.key, .placeholder {
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
-webkit-user-select: text;
|
-webkit-user-select: text;
|
||||||
}
|
}
|
||||||
.key {
|
.key {
|
||||||
|
display: inline-block;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
|
@ -71,39 +70,36 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-detail {
|
.message-detail {
|
||||||
|
& > .container > * {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
.key-conflict-dialogue {
|
.key-conflict-dialogue {
|
||||||
background: #F3F3A7;
|
background: #F3F3A7;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 1em;
|
padding: 20px;
|
||||||
margin: 1em;
|
margin: 20px;
|
||||||
|
|
||||||
button {
|
button.resolve {
|
||||||
outline: none;
|
outline: none;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0.5em 1em;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 18px;
|
line-height: 36px;
|
||||||
|
padding: 0 20px;
|
||||||
span {
|
float: right;
|
||||||
vertical-align: middle;
|
background: $blue;
|
||||||
}
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.key-conflict-dialogue {
|
.hideKeys, .showKeys {
|
||||||
.content p {
|
|
||||||
max-width: 40em;
|
|
||||||
margin: 1em auto;
|
|
||||||
}
|
|
||||||
.verify {
|
|
||||||
color: $blue;
|
color: $blue;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
float: right;
|
||||||
.resolve {
|
line-height: 36px;
|
||||||
background: $blue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,12 +442,6 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
|
||||||
height: 36px;
|
|
||||||
width: 36px;
|
|
||||||
line-height: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.end-session {
|
.end-session {
|
||||||
font: small;
|
font: small;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
@ -471,6 +461,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-list,
|
||||||
|
.message-container,
|
||||||
|
.key-conflict-dialogue {
|
||||||
|
.avatar {
|
||||||
|
height: 36px;
|
||||||
|
width: 36px;
|
||||||
|
line-height: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.bottom-bar {
|
.bottom-bar {
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
$button-width: 36px;
|
$button-width: 36px;
|
||||||
|
|
|
@ -657,48 +657,47 @@ input.search {
|
||||||
padding: 2em 20px 0;
|
padding: 2em 20px 0;
|
||||||
overflow-y: auto; }
|
overflow-y: auto; }
|
||||||
|
|
||||||
.key-verification {
|
.key-verification.panel {
|
||||||
background: white;
|
|
||||||
padding: 20px 10px; }
|
padding: 20px 10px; }
|
||||||
.key-verification p {
|
.key-verification .key, .key-verification .placeholder {
|
||||||
padding: 1em; }
|
padding: 0 1em;
|
||||||
.key-verification .key, .key-verification .placeholder {
|
-webkit-user-select: text; }
|
||||||
padding: 0 1em;
|
.key-verification .key {
|
||||||
-webkit-user-select: text; }
|
display: inline-block;
|
||||||
.key-verification .key {
|
font-family: monospace;
|
||||||
font-family: monospace;
|
padding: 10px;
|
||||||
padding: 10px;
|
margin: 10px;
|
||||||
margin: 10px;
|
background: #f3f3f3;
|
||||||
background: #f3f3f3;
|
border: solid 1px #d9d9d9;
|
||||||
border: solid 1px #d9d9d9;
|
border-radius: 5px; }
|
||||||
border-radius: 5px; }
|
.key-verification .placeholder {
|
||||||
.key-verification .placeholder {
|
font-weight: bold; }
|
||||||
font-weight: bold; }
|
|
||||||
|
|
||||||
|
.message-detail > .container > * {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto; }
|
||||||
.message-detail .key-conflict-dialogue {
|
.message-detail .key-conflict-dialogue {
|
||||||
background: #F3F3A7;
|
background: #F3F3A7;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 1em;
|
padding: 20px;
|
||||||
margin: 1em; }
|
margin: 20px; }
|
||||||
.message-detail .key-conflict-dialogue button {
|
.message-detail .key-conflict-dialogue button.resolve {
|
||||||
outline: none;
|
outline: none;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0.5em 1em;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 18px; }
|
line-height: 36px;
|
||||||
.message-detail .key-conflict-dialogue button span {
|
padding: 0 20px;
|
||||||
vertical-align: middle; }
|
float: right;
|
||||||
.message-detail .key-conflict-dialogue .content p {
|
background: #2090ea;
|
||||||
max-width: 40em;
|
margin-left: 20px; }
|
||||||
margin: 1em auto; }
|
.message-detail .key-conflict-dialogue .hideKeys, .message-detail .key-conflict-dialogue .showKeys {
|
||||||
.message-detail .key-conflict-dialogue .verify {
|
color: #2090ea;
|
||||||
color: #2090ea;
|
text-decoration: underline;
|
||||||
text-decoration: underline;
|
cursor: pointer;
|
||||||
cursor: pointer; }
|
float: right;
|
||||||
.message-detail .key-conflict-dialogue .resolve {
|
line-height: 36px; }
|
||||||
background: #2090ea; }
|
|
||||||
.message-detail .message-container {
|
.message-detail .message-container {
|
||||||
background: white;
|
background: white;
|
||||||
padding: 20px; }
|
padding: 20px; }
|
||||||
|
@ -972,11 +971,6 @@ input.search {
|
||||||
.message-container .outgoing .avatar,
|
.message-container .outgoing .avatar,
|
||||||
.message-list .outgoing .avatar {
|
.message-list .outgoing .avatar {
|
||||||
display: none; }
|
display: none; }
|
||||||
.message-container .avatar,
|
|
||||||
.message-list .avatar {
|
|
||||||
height: 36px;
|
|
||||||
width: 36px;
|
|
||||||
line-height: 36px; }
|
|
||||||
.message-container .end-session,
|
.message-container .end-session,
|
||||||
.message-list .end-session {
|
.message-list .end-session {
|
||||||
font: small;
|
font: small;
|
||||||
|
@ -992,6 +986,13 @@ input.search {
|
||||||
.message-list .key-conflict button {
|
.message-list .key-conflict button {
|
||||||
margin-top: 5px; }
|
margin-top: 5px; }
|
||||||
|
|
||||||
|
.message-list .avatar,
|
||||||
|
.message-container .avatar,
|
||||||
|
.key-conflict-dialogue .avatar {
|
||||||
|
height: 36px;
|
||||||
|
width: 36px;
|
||||||
|
line-height: 36px; }
|
||||||
|
|
||||||
.bottom-bar {
|
.bottom-bar {
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
padding: 5px 5px 5px 0;
|
padding: 5px 5px 5px 0;
|
||||||
|
|
Loading…
Reference in a new issue