Skip to content

Commit

Permalink
Improve unsupported file type handling
Browse files Browse the repository at this point in the history
Make arbitrary files look nicer and display the filename.

If an audio or video element fails to load for any reason, timeout after
a few seconds and render it as an arbitrary file. Also short circuit to
this treatment for common audio and video file types that we know are
going to fail, e.g., proprietary formats from apple.

// FREEBIE
  • Loading branch information
liliakai committed Apr 18, 2017
1 parent 27a1c9f commit a27ea20
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 23 deletions.
4 changes: 4 additions & 0 deletions background.html
Expand Up @@ -151,6 +151,10 @@ <h3>{{ welcomeToSignal }}</h3>
<img src='{{ source }}' class='preview' />
<a class='x close' alt='remove attachment' href='#'></a>
</script>
<script type='text/x-tmpl-mustache' id='file-view'>
<span class='icon'></span>
<span class='fileName' alt='{{ fileName }}' title='{{ altText }}'>{{ fileName }}</a>
</script>
<script type='text/x-tmpl-mustache' id='hasRetry'>
{{ messageNotSent }}
<span href='#' class='retry'>{{ resend }}</span>
Expand Down
1 change: 1 addition & 0 deletions images/file.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 46 additions & 19 deletions js/views/attachment_view.js
Expand Up @@ -4,16 +4,15 @@
(function () {
'use strict';

var FileView = Backbone.View.extend({
tagName: 'a',
initialize: function(dataUrl) {
this.dataUrl = dataUrl;
this.$el.text(i18n('unsupportedAttachment'));
},
render: function() {
this.$el.attr('href', this.dataUrl);
this.trigger('update');
return this;
var FileView = Whisper.View.extend({
tagName: 'div',
className: 'fileView',
templateName: 'file-view',
render_attributes: function() {
return {
fileName : this.model.fileName,
altText : i18n('unsupportedAttachment')
};
}
});

Expand Down Expand Up @@ -58,6 +57,12 @@
var AudioView = MediaView.extend({ tagName: 'audio' });
var VideoView = MediaView.extend({ tagName: 'video' });

// Blacklist common file types known to be unsupported in Chrome
var UnsupportedFileTypes = [
'audio/aiff',
'video/quicktime'
];

Whisper.AttachmentView = Backbone.View.extend({
tagName: 'span',
className: 'attachment',
Expand All @@ -76,16 +81,17 @@
},
onclick: function(e) {
switch (this.contentType) {
case 'audio':
case 'video':
return;
case 'image':
var view = new Whisper.LightboxView({ model: this });
view.render();
view.$el.appendTo(this.el);
view.$el.trigger('show');
break;

case 'video':
if (this.view instanceof MediaView) {
return;
}
default:
this.saveFile();
}
Expand Down Expand Up @@ -126,18 +132,39 @@
var View;
switch(this.contentType) {
case 'image': View = ImageView; break;
case 'audio': View = AudioView; break;
case 'video': View = VideoView; break;
default : View = FileView; break;
case 'audio': View = AudioView; break;
}

if (!View || _.contains(UnsupportedFileTypes, this.model.contentType)) {
return this.renderFileView();
}

if (!this.objectUrl) {
this.objectUrl = window.URL.createObjectURL(this.blob);
}
var view = new View(this.objectUrl, this.model.contentType);
view.$el.appendTo(this.$el);
view.on('update', this.trigger.bind(this, 'update'));
view.render();
this.view = new View(this.objectUrl, this.model.contentType);
this.view.$el.appendTo(this.$el);
this.listenTo(this.view, 'update', this.update);
this.view.render();
this.timeout = setTimeout(this.onTimeout.bind(this), 3000);
return this;
},
onTimeout: function() {
// Image or media element failed to load. Fall back to FileView.
this.stopListening(this.view);
this.renderFileView();
},
renderFileView: function() {
this.view = new FileView({model: {fileName: this.suggestedName()}});
this.view.$el.appendTo(this.$el.empty());
this.view.render();
this.update();
return this;
},
update: function() {
clearTimeout(this.timeout);
this.trigger('update');
}
});

Expand Down
33 changes: 33 additions & 0 deletions stylesheets/_conversation.scss
Expand Up @@ -465,6 +465,39 @@ li.entry .error-icon-container {
cursor: pointer;
}

.fileView {
position: relative;
padding: 5px;
padding-right: 10px;
background-color: #fafafa;
border: 1px solid $grey_l2;
border-radius: $border-radius;
cursor: pointer;
color: $grey_d;

.icon, .fileName {
opacity: 0.75;
}

&:hover {
.icon, .fileName {
opacity: 1.0;
}
}

.icon {
display: inline-block;
vertical-align: middle;
&:before {
content: '';
display: inline-block;
width: $button-height;
height: $button-height;
@include color-svg('/images/file.svg', black);
}
}
}

}

.outgoing .avatar {
Expand Down
2 changes: 1 addition & 1 deletion stylesheets/_lightbox.scss
Expand Up @@ -6,7 +6,7 @@

.content {
margin: 0;
padding: 0 50px;
padding: 0 60px;
max-width: 100%;
height: 100%;
box-shadow: none;
Expand Down
2 changes: 1 addition & 1 deletion stylesheets/android-dark.scss
Expand Up @@ -83,7 +83,7 @@ $text-dark: #CCCCCC;
display: inline-block;
@include color-svg('/images/double-check.svg', white);
}
.paperclip:before {
.file-input .paperclip:before {
content: '';
display: inline-block;
width: $button-height;
Expand Down
35 changes: 33 additions & 2 deletions stylesheets/manifest.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit a27ea20

Please sign in to comment.