Open images in a lightbox

Adds file-write permission for saving to disk from lightbox.

Fixes #810

// FREEBIE
This commit is contained in:
lilia 2016-06-20 11:37:26 -07:00
parent f34f6eedef
commit 9091233950
8 changed files with 131 additions and 9 deletions

View File

@ -106,6 +106,15 @@
</div>
</div>
</script>
<script type='text/x-tmpl-mustache' id='lightbox'>
<div class='content'>
<div class='controls'>
<a class='x close' alt='Close image.' href='#'></a>
<a class='save' alt='Save as...' href='#'></a>
</div>
<img class='image' src='{{ url }}' />
</div>
</script>
<script type='text/x-tmpl-mustache' id='confirmation-dialog'>
<div class="content">
<div class='message'>{{ message }}</div>

1
images/save.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"><path d="M34 6H10c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h28c2.21 0 4-1.79 4-4V14l-8-8zM24 38c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm6-20H10v-8h20v8z"/></svg>

After

Width:  |  Height:  |  Size: 255 B

View File

@ -31,14 +31,10 @@
},
events: {
'load': 'update',
'click': 'open'
},
update: function() {
this.trigger('update');
},
open: function () {
window.open(this.dataUrl, '_blank');
},
render: function() {
this.$el.attr('src', this.dataUrl);
return this;
@ -72,16 +68,42 @@
Whisper.AttachmentView = Backbone.View.extend({
tagName: 'span',
className: 'attachment',
initialize: function() {
this.blob = new Blob([this.model.data], {type: this.model.contentType});
var parts = this.model.contentType.split('/');
this.contentType = parts[0];
this.fileType = parts[1];
},
events: {
'click': 'onclick'
},
onclick: function(e) {
if (this.contentType === 'image') {
var view = new Whisper.LightboxView({
model: {
url : this.objectUrl,
blob : this.blob,
fileType : this.fileType
}
});
view.render();
view.$el.appendTo(this.el);
view.$el.trigger('show');
}
},
render: function() {
var View;
switch(this.model.contentType.split('/')[0]) {
switch(this.contentType) {
case 'image': View = ImageView; break;
case 'audio': View = AudioView; break;
case 'video': View = VideoView; break;
default : View = FileView; break;
}
var blob = new Blob([this.model.data], {type: this.model.contentType});
var view = new View(window.URL.createObjectURL(blob), this.model.contentType);
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();
@ -89,4 +111,46 @@
}
});
Whisper.LightboxView = Whisper.View.extend({
templateName: 'lightbox',
className: 'modal lightbox',
events: {
'click .save': 'save',
'click .close': 'remove',
'click': 'onclick'
},
save: function(e) {
var suggestedName;
if (this.model.fileType) {
suggestedName = 'signal.' + this.model.fileType;
}
var w = extension.windows.getViews()[0];
if (w && w.chrome && w.chrome.fileSystem) {
w.chrome.fileSystem.chooseEntry({
type: 'saveFile', suggestedName: suggestedName
}, function(entry) {
if (!entry) {
return;
}
entry.createWriter(function(fileWriter) {
fileWriter.write(this.model.blob);
}.bind(this));
}.bind(this));
} else {
console.log('Failed to get window');
}
},
onclick: function(e) {
var $el = this.$(e.target);
if (!$el.hasClass('image') && !$el.closest('.controls').length ) {
e.preventDefault();
this.remove();
return false;
}
},
render_attributes: function() {
return { url: this.model.url };
}
});
})();

View File

@ -118,7 +118,8 @@
'click .settings': 'showSettings',
'select .gutter .conversation-list-item': 'openConversation',
'input input.search': 'filterContacts',
'click .restart-signal': 'reloadBackgroundPage'
'click .restart-signal': 'reloadBackgroundPage',
'show .lightbox': 'showLightbox'
},
focusConversation: function(e) {
if (e && this.$(e.target).closest('.placeholder').length) {
@ -163,6 +164,9 @@
this.$('.debug-log').remove();
new Whisper.DebugLogView().$el.appendTo(this.el);
},
showLightbox: function(e) {
this.$el.append(e.target);
},
closeMenu: function(e) {
if (e && this.$(e.target).parent('.global-menu').length > 0 ) {
return;

View File

@ -12,7 +12,7 @@
"permissions": [
"unlimitedStorage",
"notifications",
"fileSystem",
{"fileSystem": ["write"]},
"alarms",
"fullscreen"
],

1
stylesheets/_lightbox.scss Symbolic link
View File

@ -0,0 +1 @@
../components/lightbox2/dist/css/lightbox.css

View File

@ -496,6 +496,48 @@ input[type=text]:active, input[type=text]:focus, input[type=search]:active, inpu
border: solid 1px #ccc;
border-right: none; }
.lightbox.modal {
padding: 30px;
text-align: center;
background-color: rgba(0, 0, 0, 0.8); }
.lightbox.modal .content {
margin: 0;
padding: 0 50px;
max-width: 100%;
height: 100%;
box-shadow: none;
background: transparent; }
.lightbox.modal .content img {
display: block;
margin: auto;
max-width: 100%;
max-height: 100%; }
.lightbox.modal .controls {
position: absolute;
top: 0;
right: 0;
width: 50px; }
.lightbox.modal a {
background: transparent;
width: 50px;
height: 50px;
margin-bottom: 10px;
display: inline-block;
cursor: pointer;
border-radius: 50%;
padding: 3px; }
.lightbox.modal a:before {
content: '';
display: block;
width: 100%;
height: 100%; }
.lightbox.modal a:hover {
background: #616161; }
.lightbox.modal .save:before {
-webkit-mask: url("/images/save.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: white; }
.conversation-stack,
.new-conversation, .inbox, .gutter {
height: 100%; }

View File

@ -6,6 +6,7 @@
// Components
@import 'progress';
@import 'debugLog';
@import 'lightbox';
// Build the main view
@import 'index';