Update attachment style

Add names and sizes for all attachments except images, and (as with
arbitrary attachments), clicking on the text will open a save dialog.
In the absence of a filename, choose something that makes sense.

Display different icons for different media types, including distinct icons
for voice notes and audio files.

In iOS theme, audio, video, voice, and files are all encapsulated in bubbles.

Closes #804
Closes #842
Closes #836

// FREEBIE
This commit is contained in:
lilia 2017-05-11 16:45:26 -07:00 committed by Lilia
parent 6c53605dfd
commit 3ea5c0435b
8 changed files with 207 additions and 47 deletions

View File

@ -52,6 +52,18 @@
"message": "Unsupported attachment type. Click to save.",
"description": "Displayed for incoming unsupported attachment"
},
"clickToSave": {
"message": "Click to save",
"description": "Hover text for attachment filenames"
},
"unnamedFile": {
"message": "Unnamed File",
"description": "Hover text for attachment filenames"
},
"voiceMessage": {
"message": "Voice Message",
"description": "Name for a voice message attachment"
},
"unsupportedFileType": {
"message": "Unsupported file type",
"description": "Displayed for outgoing unsupported attachment"

View File

@ -152,9 +152,11 @@
<a class='x close' alt='remove attachment' href='#'></a>
</script>
<script type='text/x-tmpl-mustache' id='file-view'>
<div class='icon'></div>
<div class='icon {{ mediaType }}'></div>
<div class='text'>
<div class='fileName' alt='{{ fileName }}' title='{{ altText }}'>{{ fileName }}</div>
<div class='fileName' title='{{ altText }}'>
{{ fileName }}
</div>
<div class='fileSize'>{{ fileSize }}</div>
</div>
</script>

1
images/voice.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><circle cx="18" cy="18" r="8"/><path d="M18 30c-5.34 0-16 2.68-16 8v4h32v-4c0-5.32-10.66-8-16-8zm15.52-19.27l-3.37 3.38c1.68 2.37 1.68 5.41 0 7.78l3.37 3.38c4.04-4.06 4.04-10.15 0-14.54zM40.15 4l-3.26 3.26c5.54 6.05 5.54 15.11-.01 21.47L40.15 32c7.8-7.77 7.8-19.91 0-28z"/></svg>

After

Width:  |  Height:  |  Size: 362 B

View File

@ -62,12 +62,16 @@
Whisper.AttachmentView = Backbone.View.extend({
tagName: 'span',
className: 'attachment',
className: function() {
if (this.isImage()) {
return 'attachment';
} else {
return 'attachment bubbled';
}
},
initialize: function(options) {
this.blob = new Blob([this.model.data], {type: this.model.contentType});
var parts = this.model.contentType.split('/');
this.contentType = parts[0];
if (options.timestamp) {
this.timestamp = options.timestamp;
}
@ -82,21 +86,59 @@
}
},
onclick: function(e) {
switch (this.contentType) {
case 'image':
var view = new Whisper.LightboxView({ model: this });
view.render();
view.$el.appendTo(this.el);
view.$el.trigger('show');
break;
if (this.isImage()) {
var view = new Whisper.LightboxView({ model: this });
view.render();
view.$el.appendTo(this.el);
view.$el.trigger('show');
default:
if (this.view instanceof MediaView) {
return;
}
this.saveFile();
} else {
this.saveFile();
}
},
isVoiceMessage: function() {
if (this.model.flags & textsecure.protobuf.AttachmentPointer.Flags.VOICE_MESSAGE) {
return true;
}
// Support for android legacy voice messages
if (this.isAudio() && this.model.fileName === null) {
return true;
}
},
isAudio: function() {
return this.model.contentType.startsWith('audio/');
},
isVideo: function() {
return this.model.contentType.startsWith('video/');
},
isImage: function() {
return this.model.contentType.startsWith('image/');
},
mediaType: function() {
if (this.isVoiceMessage()) {
return 'voice';
} else if (this.isAudio()) {
return 'audio';
} else if (this.isVideo()) {
return 'video';
} else if (this.isImage()) {
return 'image';
}
},
displayName: function() {
if (this.model.fileName) {
return this.model.fileName;
}
if (this.isVoiceMessage()) {
return i18n('voiceMessage');
}
if (this.isAudio() || this.isVideo()) {
return i18n('mediaMssage');
}
return i18n('unnamedFile');
},
suggestedName: function() {
if (this.model.fileName) {
return this.model.fileName;
@ -131,15 +173,21 @@
}
},
render: function() {
if (!this.isImage()) {
this.renderFileView();
}
var View;
switch(this.contentType) {
case 'image': View = ImageView; break;
case 'video': View = VideoView; break;
case 'audio': View = AudioView; break;
if (this.isImage()) {
View = ImageView;
} else if (this.isAudio()) {
View = AudioView;
} else if (this.isVideo()) {
View = VideoView;
}
if (!View || _.contains(UnsupportedFileTypes, this.model.contentType)) {
return this.renderFileView();
this.update();
return this;
}
if (!this.objectUrl) {
@ -157,20 +205,20 @@
onTimeout: function() {
// Image or media element failed to load. Fall back to FileView.
this.stopListening(this.view);
this.renderFileView();
this.update();
},
renderFileView: function() {
this.view = new FileView({
model: {
fileName: this.suggestedName(),
mediaType: this.mediaType(),
fileName: this.displayName(),
fileSize: window.filesize(this.model.size),
altText: i18n('unsupportedAttachment')
altText: i18n('clickToSave')
}
});
this.view.$el.appendTo(this.$el.empty());
this.view.render();
this.update();
return this;
},
update: function() {

View File

@ -492,6 +492,7 @@ li.entry .error-icon-container {
.icon {
margin-left: -0.5em;
margin-right: 0.5em;
display: inline-block;
vertical-align: middle;
&:before {
@ -499,7 +500,17 @@ li.entry .error-icon-container {
display: inline-block;
width: $button-height * 2;
height: $button-height * 2;
@include color-svg('/images/file.svg', black);
@include color-svg('/images/file.svg', $grey_d);
}
&.audio:before {
@include color-svg('/images/audio.svg', $grey_d);
}
&.video:before {
@include color-svg('/images/video.svg', $grey_d);
}
&.voice:before {
@include color-svg('/images/voice.svg', $grey_d);
}
}
}

View File

@ -87,11 +87,12 @@ $ios-border-color: rgba(0,0,0,0.1);
}
.message-list .attachments .fileView {
.message-list .attachments .bubbled {
border-radius: 15px;
margin-bottom: 0.25em;
padding: 10px;
position: relative;
&:before, &:after {
@ -158,7 +159,7 @@ $ios-border-color: rgba(0,0,0,0.1);
}
}
.message-list .incoming .attachment .fileView {
.message-list .incoming .bubbled {
background-color: #e6e5ea;
color: black;
float: left;
@ -187,7 +188,7 @@ $ios-border-color: rgba(0,0,0,0.1);
}
}
.outgoing {
.content, .attachments .fileView {
.content, .attachments .bubbled {
background-color: $blue;
&, .body, a {
@include invert-text-color;
@ -196,8 +197,19 @@ $ios-border-color: rgba(0,0,0,0.1);
}
}
.outgoing .attachments .fileView .icon::before {
@include color-svg('/images/file.svg', white);
.outgoing .attachments .fileView .icon {
&::before {
@include color-svg('/images/file.svg', white);
}
&.audio:before {
@include color-svg('/images/audio.svg', white);
}
&.video:before {
@include color-svg('/images/video.svg', white);
}
&.voice:before {
@include color-svg('/images/voice.svg', white);
}
}
.attachment {

View File

@ -149,12 +149,34 @@ $text-dark: #CCCCCC;
}
}
.incoming .bubble .fileView .icon::before {
@include color-svg('/images/file.svg', white);
.incoming .bubble .fileView .icon{
&::before {
@include color-svg('/images/file.svg', white);
}
&.audio:before {
@include color-svg('/images/audio.svg', white);
}
&.video:before {
@include color-svg('/images/video.svg', white);
}
&.voice:before {
@include color-svg('/images/voice.svg', white);
}
}
.outgoing .bubble .fileView .icon::before {
@include color-svg('/images/file.svg', #CCCCCC);
.outgoing .bubble .fileView .icon {
&::before {
@include color-svg('/images/file.svg', #CCCCCC);
}
&.audio:before {
@include color-svg('/images/audio.svg', #CCCCCC);
}
&.video:before {
@include color-svg('/images/video.svg', #CCCCCC);
}
&.voice:before {
@include color-svg('/images/voice.svg', #CCCCCC);
}
}
button.clock {

View File

@ -1352,6 +1352,7 @@ li.entry .error-icon-container {
.message-container .attachments .fileView .icon,
.message-list .attachments .fileView .icon {
margin-left: -0.5em;
margin-right: 0.5em;
display: inline-block;
vertical-align: middle; }
.message-container .attachments .fileView .icon:before,
@ -1362,7 +1363,22 @@ li.entry .error-icon-container {
height: 48px;
-webkit-mask: url("/images/file.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: black; }
background-color: #454545; }
.message-container .attachments .fileView .icon.audio:before,
.message-list .attachments .fileView .icon.audio:before {
-webkit-mask: url("/images/audio.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: #454545; }
.message-container .attachments .fileView .icon.video:before,
.message-list .attachments .fileView .icon.video:before {
-webkit-mask: url("/images/video.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: #454545; }
.message-container .attachments .fileView .icon.voice:before,
.message-list .attachments .fileView .icon.voice:before {
-webkit-mask: url("/images/voice.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: #454545; }
.message-container .outgoing .avatar,
.message-list .outgoing .avatar {
display: none; }
@ -1545,24 +1561,24 @@ li.entry .error-icon-container {
.ios .error-message.content,
.ios .control .content {
padding: 10px; }
.ios .message-list .attachments .fileView {
.ios .message-list .attachments .bubbled {
border-radius: 15px;
margin-bottom: 0.25em;
padding: 10px;
position: relative; }
.ios .message-list .attachments .fileView:before, .ios .message-list .attachments .fileView:after {
.ios .message-list .attachments .bubbled:before, .ios .message-list .attachments .bubbled:after {
content: '';
display: block;
border-radius: 20px;
position: absolute;
width: 10px; }
.ios .message-list .attachments .fileView:before {
.ios .message-list .attachments .bubbled:before {
right: -1px;
bottom: -3px;
height: 10px;
border-radius: 20px;
background: #2090ea; }
.ios .message-list .attachments .fileView:after {
.ios .message-list .attachments .bubbled:after {
height: 11px;
right: -6px;
bottom: -3px;
@ -1597,14 +1613,14 @@ li.entry .error-icon-container {
border: 1px solid rgba(0, 0, 0, 0.1); }
.ios .bubble .meta {
clear: both; }
.ios .message-list .incoming .attachment .fileView {
.ios .message-list .incoming .bubbled {
background-color: #e6e5ea;
color: black;
float: left; }
.ios .message-list .incoming .attachment .fileView:before {
.ios .message-list .incoming .bubbled:before {
left: -1px;
background-color: #e6e5ea; }
.ios .message-list .incoming .attachment .fileView:after {
.ios .message-list .incoming .bubbled:after {
left: -6px; }
.ios .incoming .content {
background-color: #e6e5ea;
@ -1615,18 +1631,30 @@ li.entry .error-icon-container {
background-color: #e6e5ea; }
.ios .incoming .content .body:after {
left: -6px; }
.ios .outgoing .content, .ios .outgoing .attachments .fileView {
.ios .outgoing .content, .ios .outgoing .attachments .bubbled {
background-color: #2090ea;
float: right; }
.ios .outgoing .content, .ios .outgoing .content .body, .ios .outgoing .content a, .ios .outgoing .attachments .fileView, .ios .outgoing .attachments .fileView .body, .ios .outgoing .attachments .fileView a {
.ios .outgoing .content, .ios .outgoing .content .body, .ios .outgoing .content a, .ios .outgoing .attachments .bubbled, .ios .outgoing .attachments .bubbled .body, .ios .outgoing .attachments .bubbled a {
color: white; }
.ios .outgoing .content::selection, .ios .outgoing .content .body::selection, .ios .outgoing .content a::selection, .ios .outgoing .attachments .fileView::selection, .ios .outgoing .attachments .fileView .body::selection, .ios .outgoing .attachments .fileView a::selection {
.ios .outgoing .content::selection, .ios .outgoing .content .body::selection, .ios .outgoing .content a::selection, .ios .outgoing .attachments .bubbled::selection, .ios .outgoing .attachments .bubbled .body::selection, .ios .outgoing .attachments .bubbled a::selection {
background: white;
color: #454545; }
.ios .outgoing .attachments .fileView .icon::before {
-webkit-mask: url("/images/file.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: white; }
.ios .outgoing .attachments .fileView .icon.audio:before {
-webkit-mask: url("/images/audio.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: white; }
.ios .outgoing .attachments .fileView .icon.video:before {
-webkit-mask: url("/images/video.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: white; }
.ios .outgoing .attachments .fileView .icon.voice:before {
-webkit-mask: url("/images/voice.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: white; }
.ios .attachment {
margin-bottom: 1px; }
.ios .attachment a {
@ -2010,10 +2038,34 @@ li.entry .error-icon-container {
-webkit-mask: url("/images/file.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: white; }
.android-dark .incoming .bubble .fileView .icon.audio:before {
-webkit-mask: url("/images/audio.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: white; }
.android-dark .incoming .bubble .fileView .icon.video:before {
-webkit-mask: url("/images/video.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: white; }
.android-dark .incoming .bubble .fileView .icon.voice:before {
-webkit-mask: url("/images/voice.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: white; }
.android-dark .outgoing .bubble .fileView .icon::before {
-webkit-mask: url("/images/file.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: #CCCCCC; }
.android-dark .outgoing .bubble .fileView .icon.audio:before {
-webkit-mask: url("/images/audio.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: #CCCCCC; }
.android-dark .outgoing .bubble .fileView .icon.video:before {
-webkit-mask: url("/images/video.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: #CCCCCC; }
.android-dark .outgoing .bubble .fileView .icon.voice:before {
-webkit-mask: url("/images/voice.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: #CCCCCC; }
.android-dark button.clock {
-webkit-mask: url("/images/clock.svg") no-repeat center;
-webkit-mask-size: 100%;