Add frontend support for expiring releases

When a release expires, it gets a persistent banner notification to
upgrade, and an ephemeral toast warning when trying to send a message.

// FREEBIE
This commit is contained in:
lilia 2016-04-03 20:06:27 -07:00
parent c442a02cb6
commit 9aa429e18a
9 changed files with 104 additions and 8 deletions

View file

@ -257,5 +257,13 @@
"learnMore": { "learnMore": {
"message": "Learn more about verifying keys.", "message": "Learn more about verifying keys.",
"description": "Text that links to a support article on verifying identity keys" "description": "Text that links to a support article on verifying identity keys"
},
"expiredWarning": {
"message": "This version of Signal Desktop has expired. Please upgrade to the latest version to continue messaging.",
"description": "Warning notification that this version of the app has expired"
},
"upgrade": {
"message": "Upgrade",
"description": "Label text for button to upgrade the app to the latest version"
} }
} }

View file

@ -38,6 +38,15 @@
</div> </div>
</div> </div>
</script> </script>
<script type='text/x-tmpl-mustache' id='expired_alert'>
<a target='_blank' href='https://chrome.google.com/webstore/detail/bikioccmkafdpakkkcpdbppfkghcmihk'>
<button class='upgrade'>{{ upgrade }}</button>
</a>
{{ expiredWarning }}
</script>
<script type='text/x-tmpl-mustache' id='expired_toast'>
{{ expiredWarning }}
</script>
<script type='text/x-tmpl-mustache' id='hint'> <script type='text/x-tmpl-mustache' id='hint'>
<p> {{ content }}</p> <p> {{ content }}</p>
</script> </script>

View file

@ -94,7 +94,9 @@
var mySignalingKey = storage.get('signaling_key'); var mySignalingKey = storage.get('signaling_key');
// initialize the socket and start listening for messages // initialize the socket and start listening for messages
messageReceiver = new textsecure.MessageReceiver(SERVER_URL, USERNAME, PASSWORD, mySignalingKey, ATTACHMENT_SERVER_URL); messageReceiver = new textsecure.MessageReceiver(
SERVER_URL, USERNAME, PASSWORD, mySignalingKey, ATTACHMENT_SERVER_URL
);
messageReceiver.addEventListener('message', onMessageReceived); messageReceiver.addEventListener('message', onMessageReceived);
messageReceiver.addEventListener('receipt', onDeliveryReceipt); messageReceiver.addEventListener('receipt', onDeliveryReceipt);
messageReceiver.addEventListener('contact', onContactReceived); messageReceiver.addEventListener('contact', onContactReceived);
@ -103,7 +105,9 @@
messageReceiver.addEventListener('read', onReadReceipt); messageReceiver.addEventListener('read', onReadReceipt);
messageReceiver.addEventListener('error', onError); messageReceiver.addEventListener('error', onError);
window.textsecure.messaging = new textsecure.MessageSender(SERVER_URL, USERNAME, PASSWORD, ATTACHMENT_SERVER_URL); window.textsecure.messaging = new textsecure.MessageSender(
SERVER_URL, USERNAME, PASSWORD, ATTACHMENT_SERVER_URL
);
if (firstRun === true && textsecure.storage.user.getDeviceId() != '1') { if (firstRun === true && textsecure.storage.user.getDeviceId() != '1') {
var syncRequest = new textsecure.SyncRequest(textsecure.messaging, messageReceiver); var syncRequest = new textsecure.SyncRequest(textsecure.messaging, messageReceiver);
syncRequest.addEventListener('success', function() { syncRequest.addEventListener('success', function() {

View file

@ -6,6 +6,15 @@
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
emoji.init_colons(); emoji.init_colons();
Whisper.ExpiredToast = Whisper.ToastView.extend({
templateName: 'expired_toast',
render_attributes: function() {
return {
expiredWarning: i18n('expiredWarning')
};
}
});
Whisper.ConversationView = Whisper.View.extend({ Whisper.ConversationView = Whisper.View.extend({
className: function() { className: function() {
return [ 'conversation', this.model.get('type') ].join(' '); return [ 'conversation', this.model.get('type') ].join(' ');
@ -232,6 +241,12 @@
}, },
sendMessage: function(e) { sendMessage: function(e) {
if (extension.expired()) {
var toast = new Whisper.ExpiredToast();
toast.$el.insertAfter(this.$el);
toast.render();
return;
}
e.preventDefault(); e.preventDefault();
var input = this.$messageField; var input = this.$messageField;
var message = this.replace_colons(input.val()).trim(); var message = this.replace_colons(input.val()).trim();

View file

@ -94,14 +94,20 @@
extension.windows.onClosed(function() { extension.windows.onClosed(function() {
this.inboxListView.stopListening(); this.inboxListView.stopListening();
}.bind(this)); }.bind(this));
if (extension.expired()) {
var banner = new Whisper.ExpiredAlertBanner().render();
banner.$el.prependTo(this.$el);
this.$el.addClass('expired');
}
}, },
render_attributes: { render_attributes: {
welcomeToSignal: i18n('welcomeToSignal'), welcomeToSignal : i18n('welcomeToSignal'),
selectAContact: i18n('selectAContact'), selectAContact : i18n('selectAContact'),
searchForPeopleOrGroups: i18n('searchForPeopleOrGroups'), searchForPeopleOrGroups : i18n('searchForPeopleOrGroups'),
submitDebugLog: i18n('submitDebugLog'), submitDebugLog : i18n('submitDebugLog'),
settings: i18n('settings'), settings : i18n('settings'),
restartSignal: i18n('restartSignal') restartSignal : i18n('restartSignal'),
}, },
events: { events: {
'click': 'closeMenu', 'click': 'closeMenu',
@ -166,4 +172,15 @@
} }
}); });
Whisper.ExpiredAlertBanner = Whisper.View.extend({
templateName: 'expired_alert',
className: 'expiredAlert clearfix',
render_attributes: function() {
return {
expiredWarning: i18n('expiredWarning'),
upgrade: i18n('upgrade'),
};
}
});
})(); })();

View file

@ -496,3 +496,21 @@ input[type=text], input[type=search], textarea {
outline: 1px solid $blue; outline: 1px solid $blue;
} }
} }
.expiredAlert {
background: #F3F3A7;
padding: 10px;
line-height: 36px;
button {
float: right;
border: none;
border-radius: $border-radius;
color: white;
font-weight: bold;
line-height: 36px;
padding: 0 20px;
background: $blue;
margin-left: 20px;
}
}

View file

@ -3,6 +3,12 @@
height: 100%; height: 100%;
} }
.expired {
.conversation-stack, .gutter {
height: calc(100% - 56px);
}
}
.scrollable { .scrollable {
height: 100%; height: 100%;
overflow: auto; overflow: auto;

View file

@ -420,6 +420,21 @@ img.emoji {
input[type=text]:active, input[type=text]:focus, input[type=search]:active, input[type=search]:focus, textarea:active, textarea:focus { input[type=text]:active, input[type=text]:focus, input[type=search]:active, input[type=search]:focus, textarea:active, textarea:focus {
outline: 1px solid #2090ea; } outline: 1px solid #2090ea; }
.expiredAlert {
background: #F3F3A7;
padding: 10px;
line-height: 36px; }
.expiredAlert button {
float: right;
border: none;
border-radius: 5px;
color: white;
font-weight: bold;
line-height: 36px;
padding: 0 20px;
background: #2090ea;
margin-left: 20px; }
@keyframes progress-bar-stripes { @keyframes progress-bar-stripes {
from { from {
background-position: 40px 0; } background-position: 40px 0; }
@ -451,6 +466,9 @@ input[type=text]:active, input[type=text]:focus, input[type=search]:active, inpu
.new-conversation, .inbox, .gutter { .new-conversation, .inbox, .gutter {
height: 100%; } height: 100%; }
.expired .conversation-stack, .expired .gutter {
height: calc(100% - 56px); }
.scrollable { .scrollable {
height: 100%; height: 100%;
overflow: auto; } overflow: auto; }

View file

@ -124,6 +124,7 @@
<script type="text/javascript" src="../js/views/message_list_view.js" data-cover></script> <script type="text/javascript" src="../js/views/message_list_view.js" data-cover></script>
<script type="text/javascript" src="../js/views/conversation_list_item_view.js" data-cover></script> <script type="text/javascript" src="../js/views/conversation_list_item_view.js" data-cover></script>
<script type="text/javascript" src="../js/views/conversation_list_view.js" data-cover></script> <script type="text/javascript" src="../js/views/conversation_list_view.js" data-cover></script>
<script type="text/javascript" src="../js/views/toast_view.js" data-cover></script>
<script type="text/javascript" src="../js/views/conversation_view.js" data-cover></script> <script type="text/javascript" src="../js/views/conversation_view.js" data-cover></script>
<script type="text/javascript" src="../js/views/new_conversation_view.js" data-cover></script> <script type="text/javascript" src="../js/views/new_conversation_view.js" data-cover></script>
<script type="text/javascript" src="../js/views/conversation_search_view.js"></script> <script type="text/javascript" src="../js/views/conversation_search_view.js"></script>