Render animated hourglass when messages are expiring
This commit is contained in:
parent
1383dc141f
commit
5f92ccd524
9 changed files with 121 additions and 10 deletions
|
@ -157,6 +157,7 @@
|
||||||
<div class='meta'>
|
<div class='meta'>
|
||||||
<span class='timestamp' data-timestamp={{ timestamp }}></span>
|
<span class='timestamp' data-timestamp={{ timestamp }}></span>
|
||||||
<span class='status hide'></span>
|
<span class='status hide'></span>
|
||||||
|
<span class='timer hourglass'></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
1
images/hourglass_empty.svg
Normal file
1
images/hourglass_empty.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M12 5v10l9 9-9 9v10h24V33l-9-9 9-9V5H12zm20 29v5H16v-5l8-8 8 8zm-8-12l-8-8V9h16v5l-8 8z"/></svg>
|
After Width: | Height: | Size: 188 B |
1
images/hourglass_full.svg
Normal file
1
images/hourglass_full.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M32 4H12v11h.02l-.02.02L20.98 24 12 32.98l.02.02H12v11h24V33h-.02l.02-.02L27.02 24 36 15.02l-.02-.02H36V4h-4z"/></svg>
|
After Width: | Height: | Size: 210 B |
|
@ -398,15 +398,25 @@
|
||||||
|
|
||||||
this.getConversation().trigger('expired', this);
|
this.getConversation().trigger('expired', this);
|
||||||
},
|
},
|
||||||
|
isExpiring: function() {
|
||||||
|
return this.get('expireTimer') && this.get('expirationStartTimestamp');
|
||||||
|
},
|
||||||
|
msTilExpire: function() {
|
||||||
|
if (!this.isExpiring()) {
|
||||||
|
return Infinity;
|
||||||
|
}
|
||||||
|
var now = Date.now();
|
||||||
|
var start = this.get('expirationStartTimestamp');
|
||||||
|
var delta = this.get('expireTimer') * 1000;
|
||||||
|
var ms_from_now = start + delta - now;
|
||||||
|
if (ms_from_now < 0) {
|
||||||
|
ms_from_now = 0;
|
||||||
|
}
|
||||||
|
return ms_from_now;
|
||||||
|
},
|
||||||
setToExpire: function() {
|
setToExpire: function() {
|
||||||
if (this.get('expireTimer') && this.get('expirationStartTimestamp') && !this.expireTimer) {
|
if (this.isExpiring() && !this.expireTimer) {
|
||||||
var now = Date.now();
|
var ms_from_now = this.msTilExpire();
|
||||||
var start = this.get('expirationStartTimestamp');
|
|
||||||
var delta = this.get('expireTimer') * 1000;
|
|
||||||
var ms_from_now = start + delta - now;
|
|
||||||
if (ms_from_now < 0) {
|
|
||||||
ms_from_now = 0;
|
|
||||||
}
|
|
||||||
console.log('message', this.get('sent_at'), 'expires in', ms_from_now, 'ms');
|
console.log('message', this.get('sent_at'), 'expires in', ms_from_now, 'ms');
|
||||||
this.expirationTimeout = setTimeout(this.markExpired.bind(this), ms_from_now);
|
this.expirationTimeout = setTimeout(this.markExpired.bind(this), ms_from_now);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
this.listenTo(this.model, 'change:errors', this.onErrorsChanged);
|
this.listenTo(this.model, 'change:errors', this.onErrorsChanged);
|
||||||
this.listenTo(this.model, 'change:body', this.render);
|
this.listenTo(this.model, 'change:body', this.render);
|
||||||
this.listenTo(this.model, 'change:delivered', this.renderDelivered);
|
this.listenTo(this.model, 'change:delivered', this.renderDelivered);
|
||||||
|
this.listenTo(this.model, 'change:expirationStartTimestamp', this.renderExpiring);
|
||||||
this.listenTo(this.model, 'change', this.renderSent);
|
this.listenTo(this.model, 'change', this.renderSent);
|
||||||
this.listenTo(this.model, 'change:flags change:group_update', this.renderControl);
|
this.listenTo(this.model, 'change:flags change:group_update', this.renderControl);
|
||||||
this.listenTo(this.model, 'destroy', this.onDestroy);
|
this.listenTo(this.model, 'destroy', this.onDestroy);
|
||||||
|
@ -65,8 +66,11 @@
|
||||||
},
|
},
|
||||||
onExpired: function() {
|
onExpired: function() {
|
||||||
this.$el.addClass('expired');
|
this.$el.addClass('expired');
|
||||||
this.$el.find('.bubble').one('webkitAnimationEnd animationend',
|
this.$el.find('.bubble').one('webkitAnimationEnd animationend', function(e) {
|
||||||
this.remove.bind(this));
|
if (e.target === this.$('.bubble')[0]) {
|
||||||
|
this.remove();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
},
|
},
|
||||||
onDestroy: function() {
|
onDestroy: function() {
|
||||||
if (this.$el.hasClass('expired')) {
|
if (this.$el.hasClass('expired')) {
|
||||||
|
@ -129,6 +133,12 @@
|
||||||
this.$el.removeClass('control');
|
this.$el.removeClass('control');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
renderExpiring: function() {
|
||||||
|
if (this.model.isExpiring()) {
|
||||||
|
this.$('.hourglass').css('animation-duration', this.model.msTilExpire()*0.001 + 's');
|
||||||
|
this.$el.addClass('expiring');
|
||||||
|
}
|
||||||
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
var contact = this.model.isIncoming() ? this.model.getContact() : null;
|
var contact = this.model.isIncoming() ? this.model.getContact() : null;
|
||||||
this.$el.html(
|
this.$el.html(
|
||||||
|
@ -156,6 +166,8 @@
|
||||||
this.renderSent();
|
this.renderSent();
|
||||||
this.renderDelivered();
|
this.renderDelivered();
|
||||||
this.renderErrors();
|
this.renderErrors();
|
||||||
|
this.renderExpiring();
|
||||||
|
|
||||||
this.loadAttachments();
|
this.loadAttachments();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -395,6 +395,12 @@ li.entry .error-icon-container {
|
||||||
animation: shake 0.2s linear 3;
|
animation: shake 0.2s linear 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.timer { display: none; }
|
||||||
|
.expiring .timer {
|
||||||
|
display: inline-block;
|
||||||
|
@include hourglass(13px, 11px, #999);
|
||||||
|
}
|
||||||
|
|
||||||
.control {
|
.control {
|
||||||
.bubble {
|
.bubble {
|
||||||
.content {
|
.content {
|
||||||
|
|
36
stylesheets/_hourglass.scss
Normal file
36
stylesheets/_hourglass.scss
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
@mixin hourglass($width, $height, $color) {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
@include color-svg('/images/hourglass_full.svg', transparent);
|
||||||
|
background-size: 100%;
|
||||||
|
|
||||||
|
&, &:before, &:after {
|
||||||
|
width: $width;
|
||||||
|
height: $height;
|
||||||
|
}
|
||||||
|
&:before, &:after {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
&:before {
|
||||||
|
background: $color;
|
||||||
|
animation: moveDown 5s linear;
|
||||||
|
animation-duration: inherit;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
@include color-svg('/images/hourglass_empty.svg', $color);
|
||||||
|
}
|
||||||
|
@keyframes moveDown {
|
||||||
|
from {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateY($height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1221,6 +1221,49 @@ li.entry .error-icon-container {
|
||||||
.message-container .expired .bubble,
|
.message-container .expired .bubble,
|
||||||
.message-list .expired .bubble {
|
.message-list .expired .bubble {
|
||||||
animation: shake 0.2s linear 3; }
|
animation: shake 0.2s linear 3; }
|
||||||
|
.message-container .timer,
|
||||||
|
.message-list .timer {
|
||||||
|
display: none; }
|
||||||
|
.message-container .expiring .timer,
|
||||||
|
.message-list .expiring .timer {
|
||||||
|
display: inline-block;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
-webkit-mask: url("/images/hourglass_full.svg") no-repeat center;
|
||||||
|
-webkit-mask-size: 100%;
|
||||||
|
background-color: transparent;
|
||||||
|
background-size: 100%; }
|
||||||
|
.message-container .expiring .timer, .message-container .expiring .timer:before, .message-container .expiring .timer:after,
|
||||||
|
.message-list .expiring .timer,
|
||||||
|
.message-list .expiring .timer:before,
|
||||||
|
.message-list .expiring .timer:after {
|
||||||
|
width: 13px;
|
||||||
|
height: 11px; }
|
||||||
|
.message-container .expiring .timer:before, .message-container .expiring .timer:after,
|
||||||
|
.message-list .expiring .timer:before,
|
||||||
|
.message-list .expiring .timer:after {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0; }
|
||||||
|
.message-container .expiring .timer:before,
|
||||||
|
.message-list .expiring .timer:before {
|
||||||
|
background: #999;
|
||||||
|
animation: moveDown 5s linear;
|
||||||
|
animation-duration: inherit;
|
||||||
|
animation-fill-mode: forwards; }
|
||||||
|
.message-container .expiring .timer:after,
|
||||||
|
.message-list .expiring .timer:after {
|
||||||
|
-webkit-mask: url("/images/hourglass_empty.svg") no-repeat center;
|
||||||
|
-webkit-mask-size: 100%;
|
||||||
|
background-color: #999; }
|
||||||
|
@keyframes moveDown {
|
||||||
|
from {
|
||||||
|
transform: translateY(0); }
|
||||||
|
to {
|
||||||
|
transform: translateY(11px); } }
|
||||||
.message-container .control .bubble .content,
|
.message-container .control .bubble .content,
|
||||||
.message-list .control .bubble .content {
|
.message-list .control .bubble .content {
|
||||||
font-style: italic; }
|
font-style: italic; }
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
@import 'progress';
|
@import 'progress';
|
||||||
|
@import 'hourglass';
|
||||||
@import 'debugLog';
|
@import 'debugLog';
|
||||||
@import 'lightbox';
|
@import 'lightbox';
|
||||||
@import 'recorder';
|
@import 'recorder';
|
||||||
|
|
Loading…
Reference in a new issue