Skip to content

Commit

Permalink
Confirmaton on send, banner when 'unverified'
Browse files Browse the repository at this point in the history
Not yet using the new APIs, but ready to. Still to do:
- Send sync messages on trust decisions
- Respond to received trust decision sync messages
- Show trust decisions in the conversation history
- In that rare situation where a sent message ends up with a key error
  make it easy to retry the send.

FREEBIE
  • Loading branch information
scottnonnenberg committed Aug 4, 2017
1 parent bedf100 commit 243cbd8
Show file tree
Hide file tree
Showing 12 changed files with 438 additions and 58 deletions.
46 changes: 46 additions & 0 deletions _locales/en/messages.json
Expand Up @@ -33,6 +33,52 @@
}
}
},
"changedSinceVerifiedMultiple": {
"message": "Your safety numbers with multiple group members have changed since you last verified.",
"description": "Shown on confirmation dialog when user attempts to send a message"
},
"changedSinceVerified": {
"message": "Your safety number with $name$ has changed since you last verified.",
"description": "Shown on confirmation dialog when user attempts to send a message",
"placeholders": {
"name": {
"content": "$1",
"example": "Bob"
}
}
},
"changedRecentlyMultiple": {
"message": "Your safety numbers with multiple group members have changed in the last five seconds.",
"description": "Shown on confirmation dialog when user attempts to send a message"
},
"changedRecently": {
"message": "Your safety number with $name$ has changed in the last five seconds.",
"description": "Shown on confirmation dialog when user attempts to send a message",
"placeholders": {
"name": {
"content": "$1",
"example": "Bob"
}
}
},
"sendAnyway": {
"message": "Send Anyway",
"description": "Used on a warning dialog to specifiy "
},
"noLongerVerified": {
"message": "$name$ is no longer verified. Click to verify.",
"description": "Shown in converation banner when user's safety number has changed, but they were previously verified.",
"placeholders": {
"name": {
"content": "$1",
"example": "Bob"
}
}
},
"multipleNoLongerVerified": {
"message": "More than one member of this group is no longer verified. Click to verify.",
"description": "Shown in conversation banner when more than one group member's safety number has changed, but they were previously verified."
},
"debugLogExplanation": {
"message": "This log will be posted publicly online for contributors to view. You may examine and edit it before submitting."
},
Expand Down
7 changes: 7 additions & 0 deletions background.html
Expand Up @@ -60,6 +60,12 @@ <h3>{{ welcomeToSignal }}</h3>
</a>
{{ expiredWarning }}
</script>
<script type='text/x-tmpl-mustache' id='banner'>
<div class='body'>
{{ message }}
<img class='dismiss' src='/images/x.svg' />
</div>
</script>
<script type='text/x-tmpl-mustache' id='toast'>
{{ toastMessage }}
</script>
Expand Down Expand Up @@ -656,6 +662,7 @@ <h2 class='number'></h2>
<script type='text/javascript' src='js/views/identicon_svg_view.js'></script>
<script type='text/javascript' src='js/views/settings_view.js'></script>
<script type="text/javascript" src="js/views/install_view.js"></script>
<script type="text/javascript" src="js/views/banner_view.js"></script>

<script type='text/javascript' src='js/wall_clock_listener.js'></script>
<script type='text/javascript' src='js/rotate_signed_prekey_listener.js'></script>
Expand Down
150 changes: 126 additions & 24 deletions js/models/conversations.js
Expand Up @@ -38,6 +38,12 @@

initialize: function() {
this.ourNumber = textsecure.storage.user.getNumber();
// this.verifiedEnum = textsecure.storage.protocol.VerifiedStatus;
this.verifiedEnum = {
DEFAULT: 0,
VERIFIED: 1,
UNVERIFIED: 2,
};

this.contactCollection = new Backbone.Collection();
this.messageCollection = new Whisper.MessageCollection([], {
Expand All @@ -51,61 +57,162 @@
},

updateVerified: function() {
// TODO: replace this with the real call
function checkTrustStore() {
return Promise.resolve('default');
function checkTrustStore(value) {
return Promise.resolve(value);
}

if (this.isPrivate()) {
return Promise.all([
checkTrustStore(this.id),
//textsecure.storage.protocol.getVerified(this.id),
checkTrustStore(this.verifiedEnum.UNVERIFIED),
this.fetch()
]).then(function(results) {
var trust = results[0];
return this.save({verified: trust});
});
}.bind(this));
} else {
return this.fetchContacts().then(function() {
return Promise.all(this.contactCollection.map(function(contact) {
if (contact.id !== this.myNumber) {
if (contact.id !== this.ourNumber) {
return contact.updateVerified();
}
}.bind(this)));
}.bind(this));
}.bind(this)).then(this.onMemberVerifiedChange.bind(this));
}
},
setVerifiedDefault: function() {
function updateTrustStore() {
return Promise.resolve();
}

if (!this.isPrivate()) {
throw new Error('You cannot verify a group conversation. ' +
'You must verify individual contacts.');
}
var DEFAULT = this.verifiedEnum.DEFAULT;

// return textsecure.storage.protocol.setVerified(this.id, DEFAULT).then(function() {
return updateTrustStore(this.id, DEFAULT).then(function() {
return this.save({verified: DEFAULT});
}.bind(this));
},
setVerified: function() {
function updateTrustStore() {
return Promise.resolve();
}
var VERIFIED = this.verifiedEnum.VERIFIED;

if (!this.isPrivate()) {
throw new Error('You cannot verify a group conversation. ' +
'You must verify individual contacts.');
}

// return textsecure.storage.protocol.setVerified(this.id, VERIFIED).then(function() {
return updateTrustStore(this.id, VERIFIED).then(function() {
return this.save({verified: VERIFIED});
}.bind(this));
},
isVerified: function() {
if (this.isPrivate()) {
return this.get('verified') === 'verified';
return this.get('verified') === this.verifiedEnum.VERIFIED;
} else {
if (!this.contactCollection.length) {
return false;
}

return this.contactCollection.every(function(contact) {
if (contact.id === this.myNumber) {
if (contact.id === this.ourNumber) {
return true;
} else {
return contact.isVerified();
}
}.bind(this));
}
},
isConflict: function() {
isUnverified: function() {
if (this.isPrivate()) {
var verified = this.get('verified');
return verified !== 'verified' && verified !== 'default';
return verified !== this.verifiedEnum.VERIFIED && verified !== this.verifiedEnum.DEFAULT;
} else {
if (!this.contactCollection.length) {
return true;
}

return this.contactCollection.any(function(contact) {
if (contact.id === this.ourNumber) {
return false;
} else {
return contact.isUnverified();
}
}.bind(this));
}
},
getUnverified: function() {
if (this.isPrivate()) {
return this.isUnverified() ? new Backbone.Collection([this]) : new Backbone.Collection();
} else {
return Boolean(this.getConflicts().length);
return new Backbone.Collection(this.contactCollection.filter(function(contact) {
if (contact.id === this.ourNumber) {
return false;
} else {
return contact.isUnverified();
}
}.bind(this)));
}
},
getConflicts: function() {
isUntrusted: function() {
function getFromTrustStore() {
return Promise.resolve(true);
}

if (this.isPrivate()) {
return this.isConflict() ? [this] : [];
// return textsecure.storage.protocol.isUntrusted(this.id);
return getFromTrustStore(this.id);
} else {
return this.contactCollection.filter(function(contact) {
if (contact.id === this.myNumber) {
if (!this.contactCollection.length) {
return Promise.resolve(false);
}

return Promise.all(this.contactCollection.map(function(contact) {
if (contact.id === this.ourNumber) {
return false;
} else {
return contact.isConflict();
return contact.isUntrusted();
}
}.bind(this))).then(function(results) {
return _.any(results, function(result) {
return result;
});
});
}
},
getUntrusted: function() {
// This is a bit ugly because isUntrusted() is async. Could do the work to cache
// it locally, but we really only need it for this call.
if (this.isPrivate()) {
return this.isUntrusted().then(function(untrusted) {
if (untrusted) {
return new Backbone.Collection([this]);
}

return new Backbone.Collection();
}.bind(this));
} else {
return Promise.all(this.contactCollection.map(function(contact) {
if (contact.id === this.ourNumber) {
return [false, contact];
} else {
return Promise.all([this.isUntrusted(), contact]);
}
}.bind(this))).then(function(results) {
results = _.filter(results, function(result) {
var untrusted = result[0];
return untrusted;
});
return new Backbone.Collection(_.map(results, function(result) {
var contact = result[1];
return contact;
}));
}.bind(this));
}
},
Expand All @@ -116,15 +223,10 @@
this.trigger('change');
},
toggleVerified: function() {
if (!this.isPrivate()) {
throw new Error('You cannot verify a group conversation. ' +
'You must verify individual contacts.');
}

if (this.isVerified()) {
this.save({verified: 'default'});
return this.setVerifiedDefault();
} else {
this.save({verified: 'verified'});
return this.setVerified();
}
},

Expand Down
36 changes: 36 additions & 0 deletions js/views/banner_view.js
@@ -0,0 +1,36 @@
/*
* vim: ts=4:sw=4:expandtab
*/
(function () {
'use strict';
window.Whisper = window.Whisper || {};

Whisper.BannerView = Whisper.View.extend({
className: 'banner',
templateName: 'banner',
events: {
'click .dismiss': 'onDismiss',
'click .body': 'onClick',
},
initialize: function(options) {
this.message = options.message;
this.callbacks = {
onDismiss: options.onDismiss,
onClick: options.onClick
};
this.render();
},
render_attributes: function() {
return {
message: this.message
};
},
onDismiss: function(e) {
this.callbacks.onDismiss();
e.stopPropagation();
},
onClick: function() {
this.callbacks.onClick();
}
});
})();
9 changes: 7 additions & 2 deletions js/views/confirmation_dialog_view.js
Expand Up @@ -10,8 +10,13 @@
templateName: 'confirmation-dialog',
initialize: function(options) {
this.message = options.message;

this.resolve = options.resolve;
this.okText = options.okText || i18n('ok');

this.reject = options.reject;
this.cancelText = options.cancelText || i18n('cancel');

this.render();
},
events: {
Expand All @@ -21,8 +26,8 @@
render_attributes: function() {
return {
message: this.message,
cancel: i18n('cancel'),
ok: i18n('ok')
cancel: this.cancelText,
ok: this.okText
};
},
ok: function() {
Expand Down

0 comments on commit 243cbd8

Please sign in to comment.