#89 Composer move sign/encrypt buttons from menu to view

This commit is contained in:
the-djmaze 2022-02-04 16:21:29 +01:00
parent 26d3509e28
commit 5b051745fe
6 changed files with 152 additions and 174 deletions

View file

@ -133,13 +133,13 @@ export const GnuPGUserStore = new class {
/** /**
* Checks if verifying/encrypting a message is possible with given email addresses. * Checks if verifying/encrypting a message is possible with given email addresses.
*/ */
hasPublicKeyForEmails(recipients, all) { hasPublicKeyForEmails(recipients) {
const count = recipients.length, const count = recipients.length,
length = count ? recipients.filter(email => length = count ? recipients.filter(email =>
// (key.can_verify || key.can_encrypt) && // (key.can_verify || key.can_encrypt) &&
this.publicKeys.find(key => key.emails.includes(email)) this.publicKeys.find(key => key.emails.includes(email))
).length : 0; ).length : 0;
return length && (!all || length === count); return length && length === count;
} }
getPrivateKeyFor(query, sign) { getPrivateKeyFor(query, sign) {

View file

@ -145,12 +145,12 @@ export const OpenPGPUserStore = new class {
/** /**
* Checks if verifying/encrypting a message is possible with given email addresses. * Checks if verifying/encrypting a message is possible with given email addresses.
*/ */
hasPublicKeyForEmails(recipients, all) { hasPublicKeyForEmails(recipients) {
const count = recipients.length, const count = recipients.length,
length = count ? recipients.filter(email => length = count ? recipients.filter(email =>
this.publicKeys().find(key => key.emails.includes(email)) this.publicKeys().find(key => key.emails.includes(email))
).length : 0; ).length : 0;
return length && (!all || length === count); return length && length === count;
} }
getPrivateKeyFor(query/*, sign*/) { getPrivateKeyFor(query/*, sign*/) {

View file

@ -74,33 +74,28 @@ export const PgpUserStore = new class {
return 0 === text.trim().indexOf('-----BEGIN PGP MESSAGE-----'); return 0 === text.trim().indexOf('-----BEGIN PGP MESSAGE-----');
} }
async mailvelopeHasPublicKeyForEmails(recipients, all) { async mailvelopeHasPublicKeyForEmails(recipients) {
const const
keyring = this.mailvelopeKeyring, keyring = this.mailvelopeKeyring,
mailvelope = keyring && await keyring.validKeyForAddress(recipients) mailvelope = keyring && await keyring.validKeyForAddress(recipients)
/*.then(LookupResult => Object.entries(LookupResult))*/, /*.then(LookupResult => Object.entries(LookupResult))*/,
entries = mailvelope && Object.entries(mailvelope); entries = mailvelope && Object.entries(mailvelope);
return !!(entries && (all ? (entries.filter(value => value[1]).length === recipients.length) : entries.length)); return entries && entries.filter(value => value[1]).length === recipients.length;
} }
/** /**
* Checks if verifying/encrypting a message is possible with given email addresses. * Checks if verifying/encrypting a message is possible with given email addresses.
* Returns the first library that can. * Returns the first library that can.
*/ */
async hasPublicKeyForEmails(recipients, all) { async hasPublicKeyForEmails(recipients) {
const count = recipients.length; const count = recipients.length;
if (count) { if (count) {
if (OpenPGPUserStore.hasPublicKeyForEmails(recipients, all)) { if (OpenPGPUserStore.hasPublicKeyForEmails(recipients)) {
return 'openpgp'; return 'openpgp';
} }
if (GnuPGUserStore.hasPublicKeyForEmails(recipients)) {
if (GnuPGUserStore.hasPublicKeyForEmails(recipients, all)) {
return 'gnupg'; return 'gnupg';
} }
if (await this.mailvelopeHasPublicKeyForEmails(recipients, all)) {
return 'mailvelope';
}
} }
return false; return false;
} }

View file

@ -113,10 +113,6 @@
} }
} }
> div {
line-height: 30px;
}
textarea, input[type="text"] { textarea, input[type="text"] {
width: 100%; width: 100%;
} }

View file

@ -134,8 +134,6 @@ class ComposePopupView extends AbstractViewPopup {
this.bSkipNextHide = false; this.bSkipNextHide = false;
this.capaOpenPGP = PgpUserStore.isSupported();
this.addObservables({ this.addObservables({
identitiesDropdownTrigger: false, identitiesDropdownTrigger: false,
@ -183,8 +181,6 @@ class ComposePopupView extends AbstractViewPopup {
viewArea: 'body', viewArea: 'body',
composeUploaderButton: null, // initDom
composeUploaderDropPlace: null, // initDom
attacheMultipleAllowed: false, attacheMultipleAllowed: false,
addAttachmentEnabled: false, addAttachmentEnabled: false,
@ -283,7 +279,10 @@ class ComposePopupView extends AbstractViewPopup {
currentIdentity: value => { currentIdentity: value => {
this.canPgpSign(false); this.canPgpSign(false);
value && PgpUserStore.getKeyForSigning(value.email()).then(result => { value && PgpUserStore.getKeyForSigning(value.email()).then(result => {
console.log({canPgpSign:result}); console.log({
email: value.email(),
canPgpSign:result
});
this.canPgpSign(result) this.canPgpSign(result)
}); });
}, },
@ -1120,126 +1119,123 @@ class ComposePopupView extends AbstractViewPopup {
} }
} }
onBuild() { onBuild(dom) {
// initUploader // initUploader
const oJua = new Jua({
action: serverRequest('Upload'),
clickElement: dom.querySelector('#composeUploadButton'),
dragAndDropElement: dom.querySelector('.b-attachment-place')
}),
uploadCache = {},
attachmentSizeLimit = pInt(SettingsGet('AttachmentLimit'));
if (this.composeUploaderButton()) { oJua
const oJua = new Jua({ // .on('onLimitReached', (limit) => {
action: serverRequest('Upload'), // alert(limit);
clickElement: this.composeUploaderButton(), // })
dragAndDropElement: this.composeUploaderDropPlace() .on('onDragEnter', () => {
}), this.dragAndDropOver(true);
uploadCache = {}, })
attachmentSizeLimit = pInt(SettingsGet('AttachmentLimit')); .on('onDragLeave', () => {
this.dragAndDropOver(false);
oJua })
// .on('onLimitReached', (limit) => { .on('onBodyDragEnter', () => {
// alert(limit); this.attachmentsArea();
// }) this.dragAndDropVisible(true);
.on('onDragEnter', () => { })
this.dragAndDropOver(true); .on('onBodyDragLeave', () => {
}) this.dragAndDropVisible(false);
.on('onDragLeave', () => { })
this.dragAndDropOver(false); .on('onProgress', (id, loaded, total) => {
}) let item = uploadCache[id];
.on('onBodyDragEnter', () => { if (!item) {
this.attachmentsArea(); item = this.getAttachmentById(id);
this.dragAndDropVisible(true);
})
.on('onBodyDragLeave', () => {
this.dragAndDropVisible(false);
})
.on('onProgress', (id, loaded, total) => {
let item = uploadCache[id];
if (!item) {
item = this.getAttachmentById(id);
if (item) {
uploadCache[id] = item;
}
}
if (item) { if (item) {
item.progress(Math.floor((loaded / total) * 100)); uploadCache[id] = item;
} }
}) }
.on('onSelect', (sId, oData) => {
this.dragAndDropOver(false);
const fileName = undefined === oData.FileName ? '' : oData.FileName.toString(), if (item) {
size = pInt(oData.Size, null), item.progress(Math.floor((loaded / total) * 100));
attachment = new ComposeAttachmentModel(sId, fileName, size); }
})
.on('onSelect', (sId, oData) => {
this.dragAndDropOver(false);
attachment.cancel = this.cancelAttachmentHelper(sId, oJua); const fileName = undefined === oData.FileName ? '' : oData.FileName.toString(),
size = pInt(oData.Size, null),
attachment = new ComposeAttachmentModel(sId, fileName, size);
this.attachments.push(attachment); attachment.cancel = this.cancelAttachmentHelper(sId, oJua);
this.attachmentsArea(); this.attachments.push(attachment);
if (0 < size && 0 < attachmentSizeLimit && attachmentSizeLimit < size) { this.attachmentsArea();
if (0 < size && 0 < attachmentSizeLimit && attachmentSizeLimit < size) {
attachment
.waiting(false)
.uploading(true)
.complete(true)
.error(i18n('UPLOAD/ERROR_FILE_IS_TOO_BIG'));
return false;
}
return true;
})
.on('onStart', (id) => {
let item = uploadCache[id];
if (!item) {
item = this.getAttachmentById(id);
if (item) {
uploadCache[id] = item;
}
}
if (item) {
item
.waiting(false)
.uploading(true)
.complete(false);
}
})
.on('onComplete', (id, result, data) => {
const attachment = this.getAttachmentById(id),
response = (data && data.Result) || {},
errorCode = response.ErrorCode,
attachmentJson = result && response.Attachment;
let error = '';
if (null != errorCode) {
error = getUploadErrorDescByCode(errorCode);
} else if (!attachmentJson) {
error = i18n('UPLOAD/ERROR_UNKNOWN');
}
if (attachment) {
if (error) {
attachment attachment
.waiting(false) .waiting(false)
.uploading(true) .uploading(false)
.complete(true) .complete(true)
.error(i18n('UPLOAD/ERROR_FILE_IS_TOO_BIG')); .error(error + '\n' + response.ErrorMessage);
} else if (attachmentJson) {
return false; attachment
}
return true;
})
.on('onStart', (id) => {
let item = uploadCache[id];
if (!item) {
item = this.getAttachmentById(id);
if (item) {
uploadCache[id] = item;
}
}
if (item) {
item
.waiting(false) .waiting(false)
.uploading(true) .uploading(false)
.complete(false); .complete(true);
}
})
.on('onComplete', (id, result, data) => {
const attachment = this.getAttachmentById(id),
response = (data && data.Result) || {},
errorCode = response.ErrorCode,
attachmentJson = result && response.Attachment;
let error = ''; attachment.initByUploadJson(attachmentJson);
if (null != errorCode) {
error = getUploadErrorDescByCode(errorCode);
} else if (!attachmentJson) {
error = i18n('UPLOAD/ERROR_UNKNOWN');
} }
if (attachment) { if (undefined === uploadCache[id]) {
if (error) { delete uploadCache[id];
attachment
.waiting(false)
.uploading(false)
.complete(true)
.error(error + '\n' + response.ErrorMessage);
} else if (attachmentJson) {
attachment
.waiting(false)
.uploading(false)
.complete(true);
attachment.initByUploadJson(attachmentJson);
}
if (undefined === uploadCache[id]) {
delete uploadCache[id];
}
} }
}); }
});
this.addAttachmentEnabled(true); this.addAttachmentEnabled(true);
}
shortcuts.add('q', 'meta', Scope.Compose, ()=>false); shortcuts.add('q', 'meta', Scope.Compose, ()=>false);
shortcuts.add('w', 'meta', Scope.Compose, ()=>false); shortcuts.add('w', 'meta', Scope.Compose, ()=>false);
@ -1514,7 +1510,7 @@ class ComposePopupView extends AbstractViewPopup {
quotedMailHeader: '', // header to be added before the quoted mail quotedMailHeader: '', // header to be added before the quoted mail
keepAttachments: false, // add attachments of quotedMail to editor (default: false) keepAttachments: false, // add attachments of quotedMail to editor (default: false)
*/ */
signMsg: confirm('Also sign this message?') signMsg: this.pgpSign() || confirm('Sign this message?')
}).then(editor => this.mailvelope = editor); }).then(editor => this.mailvelope = editor);
} }
this.viewArea('mailvelope'); this.viewArea('mailvelope');
@ -1542,11 +1538,12 @@ class ComposePopupView extends AbstractViewPopup {
} }
initPgpEncrypt() { initPgpEncrypt() {
PgpUserStore.hasPublicKeyForEmails(this.allRecipients(), 1).then(result => { const recipients = this.allRecipients();
PgpUserStore.hasPublicKeyForEmails(recipients).then(result => {
console.log({canPgpEncrypt:result}); console.log({canPgpEncrypt:result});
this.canPgpEncrypt(result); this.canPgpEncrypt(result);
}); });
PgpUserStore.mailvelopeHasPublicKeyForEmails(this.allRecipients(), 1).then(result => { PgpUserStore.mailvelopeHasPublicKeyForEmails(recipients).then(result => {
console.log({canMailvelope:result}); console.log({canMailvelope:result});
this.canMailvelope(result); this.canMailvelope(result);
if (!result) { if (!result) {

View file

@ -40,7 +40,7 @@
<a class="btn" data-i18n="GLOBAL/BCC" <a class="btn" data-i18n="GLOBAL/BCC"
data-bind="visible: !showBcc(), click: function () { showBcc(true); }"></a> data-bind="visible: !showBcc(), click: function () { showBcc(true); }"></a>
<a class="btn fontastic" data-bind="visible: allowContacts, command: contactsCommand" data-i18n="[title]GLOBAL/CONTACTS">📇</a> <a class="btn fontastic" data-bind="visible: allowContacts, command: contactsCommand" data-i18n="[title]GLOBAL/CONTACTS">📇</a>
<div class="btn-group dropdown" data-bind="registerBootstrapDropdown: true" style="display:inline;vertical-align:top"> <div class="btn-group dropdown" data-bind="registerBootstrapDropdown: true" style="display:inline-block;vertical-align:top">
<a class="btn dropdown-toggle fontastic"></a> <a class="btn dropdown-toggle fontastic"></a>
<ul class="dropdown-menu right-edge" role="menu"> <ul class="dropdown-menu right-edge" role="menu">
<li data-bind="click: function () { showBcc(!showBcc()); }"> <li data-bind="click: function () { showBcc(!showBcc()); }">
@ -79,20 +79,6 @@
<span data-i18n="COMPOSE/BUTTON_MARK_AS_IMPORTANT"></span> <span data-i18n="COMPOSE/BUTTON_MARK_AS_IMPORTANT"></span>
</a> </a>
</li> </li>
<!-- ko if: capaOpenPGP -->
<li class="dividerbar" data-bind="click: togglePgpSign, css: {'disabled': !canPgpSign()}">
<a>
<i class="fontastic" data-bind="text: pgpSign() ? '✍' : '☐'"></i>
<span data-i18n="OPENPGP/LABEL_SIGN"></span>
</a>
</li>
<li data-bind="click: togglePgpEncrypt, css: {'disabled': !canPgpEncrypt()}">
<a>
<i class="fontastic" data-bind="text: pgpEncrypt() ? '🔒' : '☐'"></i>
<span data-i18n="OPENPGP/LABEL_ENCRYPT"></span>
</a>
</li>
<!-- /ko -->
</ul> </ul>
</div> </div>
</span> </span>
@ -131,41 +117,45 @@
<input type="text" size="70" autocomplete="off" data-bind="textInput: subject" /> <input type="text" size="70" autocomplete="off" data-bind="textInput: subject" />
</td> </td>
</tr> </tr>
<tr>
<td></td>
<td style="display:flex">
<div class="btn-group" style="flex-grow:1" id="area-toggle">
<button type="button" class="btn" data-bind="click: bodyArea,
css: { 'active': 'body' == viewArea() }">
<i class="icon-file-text"></i>
</button>
<button type="button" class="btn" data-bind="click: attachmentsArea,
css: { 'btn-danger': attachmentsInErrorCount(), 'active': 'attachments' == viewArea() },
tooltipErrorTip: attachmentsErrorTooltip">
<span data-bind="visible: attachmentsCount()">
<b data-bind="text: attachmentsCount"></b>
&nbsp;&nbsp;
</span>
<i data-bind="css: { 'icon-attachment': !attachmentsInProcessCount(), 'icon-spinner': attachmentsInProcessCount()}"></i>
</button>
<button type="button" class="btn" data-bind="visible: canMailvelope, click: mailvelopeArea, css: { 'active': 'mailvelope' == viewArea() }">
<i class="mailvelope-icon"></i>
</button>
</div>
<div class="btn-group">
<a class="btn"
style="padding-left: 10px; padding-right: 10px;"
data-bind="visible: addAttachmentEnabled(), initDom: composeUploaderButton" data-i18n="[title]COMPOSE/ATTACH_FILES">
<sup style="font-weight: bold; font-size: 100%; top: -0.3em;">+</sup><i class="icon-attachment"></i>
</a>
</div>
</td>
</tr>
</table> </table>
<div style="display:flex">
<div class="btn-group" style="flex-grow:1" id="area-toggle">
<button type="button" class="btn" data-bind="click: bodyArea,
css: { 'active': 'body' == viewArea() }">
<i class="icon-file-text"></i>
</button>
<button type="button" class="btn" data-bind="click: attachmentsArea,
css: { 'btn-danger': attachmentsInErrorCount(), 'active': 'attachments' == viewArea() },
tooltipErrorTip: attachmentsErrorTooltip">
<span data-bind="visible: attachmentsCount()">
<b data-bind="text: attachmentsCount"></b>
&nbsp;&nbsp;
</span>
<i data-bind="css: { 'icon-attachment': !attachmentsInProcessCount(), 'icon-spinner': attachmentsInProcessCount()}"></i>
</button>
<button type="button" class="btn" data-bind="visible: canMailvelope, click: mailvelopeArea, css: { 'active': 'mailvelope' == viewArea() }">
<i class="mailvelope-icon"></i>
</button>
</div>
<div class="btn-group">
<a class="btn" data-bind="click: togglePgpSign, visible: canPgpSign, css: {'btn-success': pgpSign()}">
<i class="fontastic" data-bind="text: pgpSign() ? '☑' : '☐'"></i>
<span data-i18n="OPENPGP/LABEL_SIGN"></span>
</a>
<a class="btn" data-bind="click: togglePgpEncrypt, visible: canPgpEncrypt, css: {'btn-success': pgpEncrypt()}">
<i class="fontastic" data-bind="text: pgpEncrypt() ? '☑' : '☐'"></i>
<span data-i18n="OPENPGP/LABEL_ENCRYPT"></span>
</a>
<a class="btn fontastic" style="padding-left: 10px; padding-right: 10px;" id="composeUploadButton"
data-bind="visible: addAttachmentEnabled()" data-i18n="[title]COMPOSE/ATTACH_FILES">
⁺📎
</a>
</div>
</div>
</div> </div>
<div class="attachmentAreaParent" data-bind="visible: 'attachments' == viewArea()"> <div class="attachmentAreaParent" data-bind="visible: 'attachments' == viewArea()">
<div class="b-attachment-place" data-bind="visible: addAttachmentEnabled() && dragAndDropVisible(), initDom: composeUploaderDropPlace, css: {'dragAndDropOver': dragAndDropOver}" <div class="b-attachment-place" data-bind="visible: addAttachmentEnabled() && dragAndDropVisible(), css: {'dragAndDropOver': dragAndDropOver}"
data-i18n="COMPOSE/ATTACH_DROP_FILES_DESC"></div> data-i18n="COMPOSE/ATTACH_DROP_FILES_DESC"></div>
<ul class="attachmentList" data-bind="foreach: attachments"> <ul class="attachmentList" data-bind="foreach: attachments">
<li class="attachmentItem" data-bind="attr: { 'title': title }, css: { 'waiting': waiting, 'error': '' !== error() }"> <li class="attachmentItem" data-bind="attr: { 'title': title }, css: { 'waiting': waiting, 'error': '' !== error() }">