diff options
Diffstat (limited to 'web-ui')
-rw-r--r-- | web-ui/app/js/helpers/view_helper.js | 13 | ||||
-rw-r--r-- | web-ui/app/js/mail_list/ui/mail_item_factory.js | 6 | ||||
-rw-r--r-- | web-ui/app/js/mixins/with_mail_edit_base.js | 4 | ||||
-rw-r--r-- | web-ui/app/js/services/model/mail.js | 176 | ||||
-rw-r--r-- | web-ui/app/js/tags/data/tags.js | 1 | ||||
-rw-r--r-- | web-ui/app/js/tags/ui/tag_list.js | 1 | ||||
-rw-r--r-- | web-ui/app/js/views/i18n.js | 8 | ||||
-rw-r--r-- | web-ui/app/js/views/templates.js | 3 | ||||
-rw-r--r-- | web-ui/app/locales/en-us/translation.json | 2 | ||||
-rw-r--r-- | web-ui/app/locales/en/translation.json | 2 | ||||
-rw-r--r-- | web-ui/app/scss/_mixins.scss | 10 | ||||
-rw-r--r-- | web-ui/app/scss/_security.scss | 12 | ||||
-rw-r--r-- | web-ui/app/templates/mails/full_view.hbs | 12 | ||||
-rw-r--r-- | web-ui/app/templates/mails/single.hbs | 1 | ||||
-rw-r--r-- | web-ui/app/templates/mails/trash.hbs | 26 | ||||
-rw-r--r-- | web-ui/test/spec/helpers/view_helper.spec.js | 8 | ||||
-rw-r--r-- | web-ui/test/spec/tags/data/tags.spec.js | 8 | ||||
-rw-r--r-- | web-ui/test/spec/tags/ui/tag_list.spec.js | 7 |
18 files changed, 175 insertions, 125 deletions
diff --git a/web-ui/app/js/helpers/view_helper.js b/web-ui/app/js/helpers/view_helper.js index 6755b891..a682ae5e 100644 --- a/web-ui/app/js/helpers/view_helper.js +++ b/web-ui/app/js/helpers/view_helper.js @@ -22,7 +22,7 @@ define( 'quoted-printable/quoted-printable', 'utf8/utf8' ], - function(contentType, htmlWhitelister, i18n_lib, quotedPrintable, utf8) { + function(contentType, htmlWhitelister, i18n, quotedPrintable, utf8) { 'use strict'; function formatStatusClasses(ss) { @@ -109,12 +109,15 @@ define( }, 1); } - function quoteMail(mail) { - return '\n\n' + mail.textPlainBody.replace(/^/mg, '> '); + function prependFrom(mail) { + return i18n( + 'On __date__, <__from__> wrote:\n', + {'date': new Date(mail.header.date).toString(), 'from': mail.header.from} + ); } - function i18n(text) { - return i18n_lib.get(text); + function quoteMail(mail) { + return '\n\n' + prependFrom(mail) + mail.textPlainBody.replace(/^/mg, '> '); } return { diff --git a/web-ui/app/js/mail_list/ui/mail_item_factory.js b/web-ui/app/js/mail_list/ui/mail_item_factory.js index 87a883b0..3c815401 100644 --- a/web-ui/app/js/mail_list/ui/mail_item_factory.js +++ b/web-ui/app/js/mail_list/ui/mail_item_factory.js @@ -26,12 +26,14 @@ define( var MAIL_ITEM_TYPE = { 'drafts': DraftItem, - 'sent': SentItem + 'sent': SentItem, + 'trash': GenericMailItem }; var TEMPLATE_TYPE = { 'drafts': 'draft', - 'sent': 'sent' + 'sent': 'sent', + 'trash': 'trash' }; var createAndAttach = function (nodeToAttachTo, mail, currentMailIdent, currentTag, isChecked) { diff --git a/web-ui/app/js/mixins/with_mail_edit_base.js b/web-ui/app/js/mixins/with_mail_edit_base.js index 9942e747..5efb8967 100644 --- a/web-ui/app/js/mixins/with_mail_edit_base.js +++ b/web-ui/app/js/mixins/with_mail_edit_base.js @@ -134,7 +134,7 @@ define( } else { this.trigger( events.ui.userAlerts.displayMessage, - {message: i18n.get('One or more of the recipients are not valid emails')} + {message: i18n('One or more of the recipients are not valid emails')} ); this.trigger(events.mail.send_failed); } @@ -176,7 +176,7 @@ define( this.draftSaved = function(event, data) { this.attr.ident = data.ident; if(!this.attr.silent) { - this.trigger(document, events.ui.userAlerts.displayMessage, { message: i18n.get('Saved as draft.') }); + this.trigger(document, events.ui.userAlerts.displayMessage, { message: i18n('Saved as draft.') }); } delete this.attr.silent; }; diff --git a/web-ui/app/js/services/model/mail.js b/web-ui/app/js/services/model/mail.js index 34cba610..c41bcff9 100644 --- a/web-ui/app/js/services/model/mail.js +++ b/web-ui/app/js/services/model/mail.js @@ -16,121 +16,111 @@ */ 'use strict'; -define(['helpers/contenttype'], - function (contentType) { - - var asMail = (function () { - - function isSentMail() { - return _.has(this, 'mailbox') && this.mailbox.toUpperCase() === 'SENT'; - } - - function isDraftMail() { - return _.has(this, 'mailbox') && this.mailbox.toUpperCase() === 'DRAFTS'; - } - - function isInTrash() { - return _.has(this, 'mailbox') && this.mailbox.toUpperCase() === 'TRASH'; - } - - function setDraftReplyFor(ident) { - this.draft_reply_for = ident; - } - - function replyToAddress() { - return { - to: [this.replying.single], - cc: [] - }; - } - - function replyToAllAddress() { - return { - to: this.replying.all['to-field'], - cc: this.replying.all['cc-field'] - }; - } +define(['helpers/contenttype'], function (contentType) { + function isSentMail() { + return _.has(this, 'mailbox') && this.mailbox.toUpperCase() === 'SENT'; + } + + function isDraftMail() { + return _.has(this, 'mailbox') && this.mailbox.toUpperCase() === 'DRAFTS'; + } + + function isInTrash() { + return _.has(this, 'mailbox') && this.mailbox.toUpperCase() === 'TRASH'; + } + + function setDraftReplyFor(ident) { + this.draft_reply_for = ident; + } + + function replyToAddress() { + return { + to: [this.replying.single], + cc: [] + }; + } - function getHeadersFromMailPart (rawBody) { - var lines, headerLines, endOfHeaders, headers; + function replyToAllAddress() { + return { + to: this.replying.all['to-field'], + cc: this.replying.all['cc-field'] + }; + } - lines = rawBody.split('\n'); - endOfHeaders = _.indexOf(lines, ''); - headerLines = lines.slice(0, endOfHeaders); + function getHeadersFromMailPart (rawBody) { + var lines, headerLines, endOfHeaders, headers; - headers = _.map(headerLines, function (headerLine) { - return _.map(headerLine.split(':'), function(elem){return elem.trim();}); - }); + lines = rawBody.split('\n'); + endOfHeaders = _.indexOf(lines, ''); + headerLines = lines.slice(0, endOfHeaders); - return _.object(headers); - } + headers = _.map(headerLines, function (headerLine) { + return _.map(headerLine.split(':'), function(elem){return elem.trim();}); + }); - function getBodyFromMailPart (rawBody) { - var lines, endOfHeaders; + return _.object(headers); + } - lines = rawBody.split('\n'); - endOfHeaders = _.indexOf(lines, ''); + function getBodyFromMailPart (rawBody) { + var lines, endOfHeaders; - return lines.slice(endOfHeaders + 1).join('\n'); - } + lines = rawBody.split('\n'); + endOfHeaders = _.indexOf(lines, ''); - function parseWithHeaders(rawBody) { - return {headers: getHeadersFromMailPart(rawBody), body: getBodyFromMailPart(rawBody)}; - } + return lines.slice(endOfHeaders + 1).join('\n'); + } - function getMailMultiParts () { - var mediaType = this.getMailMediaType(); - var boundary = '--' + mediaType.params.boundary + '\n'; - var finalBoundary = '--' + mediaType.params.boundary + '--'; + function parseWithHeaders(rawBody) { + return {headers: getHeadersFromMailPart(rawBody), body: getBodyFromMailPart(rawBody)}; + } - var bodyParts = this.body.split(finalBoundary)[0].split(boundary); + function getMailMultiParts () { + var mediaType = this.getMailMediaType(); + var boundary = '--' + mediaType.params.boundary + '\n'; + var finalBoundary = '--' + mediaType.params.boundary + '--'; - bodyParts = _.reject(bodyParts, function(bodyPart) { return _.isEmpty(bodyPart.trim()); }); + var bodyParts = this.body.split(finalBoundary)[0].split(boundary); - return _.map(bodyParts, parseWithHeaders); - } + bodyParts = _.reject(bodyParts, function(bodyPart) { return _.isEmpty(bodyPart.trim()); }); - function getMailMediaType () { - return new contentType.MediaType(this.header.content_type); - } + return _.map(bodyParts, parseWithHeaders); + } - function isMailMultipartAlternative () { - return this.getMailMediaType().type === 'multipart/alternative'; - } + function getMailMediaType () { + return new contentType.MediaType(this.header.content_type); + } - function availableBodyPartsContentType () { - var bodyParts = this.getMailMultiParts(); + function isMailMultipartAlternative () { + return this.getMailMediaType().type === 'multipart/alternative'; + } - return _.pluck(_.pluck(bodyParts, 'headers'), 'Content-Type'); - } + function availableBodyPartsContentType () { + var bodyParts = this.getMailMultiParts(); - function getMailPartByContentType (contentType) { - var bodyParts = this.getMailMultiParts(); + return _.pluck(_.pluck(bodyParts, 'headers'), 'Content-Type'); + } - return _.findWhere(bodyParts, {headers: {'Content-Type': contentType}}); - } + function getMailPartByContentType (contentType) { + var bodyParts = this.getMailMultiParts(); - return function () { - this.isSentMail = isSentMail; - this.isDraftMail = isDraftMail; - this.isInTrash = isInTrash; - this.setDraftReplyFor = setDraftReplyFor; - this.replyToAddress = replyToAddress; - this.replyToAllAddress = replyToAllAddress; - this.getMailMediaType = getMailMediaType; - this.isMailMultipartAlternative = isMailMultipartAlternative; - this.getMailMultiParts = getMailMultiParts; - this.availableBodyPartsContentType = availableBodyPartsContentType; - this.getMailPartByContentType = getMailPartByContentType; - return this; - }; - }()); + return _.findWhere(bodyParts, {headers: {'Content-Type': contentType}}); + } return { create: function (mail) { - if (mail) { - asMail.apply(mail); - } + if (!mail) { return; } + + mail.isSentMail = isSentMail; + mail.isDraftMail = isDraftMail; + mail.isInTrash = isInTrash; + mail.setDraftReplyFor = setDraftReplyFor; + mail.replyToAddress = replyToAddress; + mail.replyToAllAddress = replyToAllAddress; + mail.getMailMediaType = getMailMediaType; + mail.isMailMultipartAlternative = isMailMultipartAlternative; + mail.getMailMultiParts = getMailMultiParts; + mail.availableBodyPartsContentType = availableBodyPartsContentType; + mail.getMailPartByContentType = getMailPartByContentType; return mail; } }; diff --git a/web-ui/app/js/tags/data/tags.js b/web-ui/app/js/tags/data/tags.js index 401b41f7..348c1832 100644 --- a/web-ui/app/js/tags/data/tags.js +++ b/web-ui/app/js/tags/data/tags.js @@ -57,6 +57,7 @@ define(['flight/lib/component', 'page/events', 'helpers/monitored_ajax', 'mixins this.after('initialize', function () { this.on(document, events.tags.want, this.fetchTags); + this.on(document, events.mail.sent, this.fetchTags); }); } diff --git a/web-ui/app/js/tags/ui/tag_list.js b/web-ui/app/js/tags/ui/tag_list.js index a2172c6d..d6f6f76c 100644 --- a/web-ui/app/js/tags/ui/tag_list.js +++ b/web-ui/app/js/tags/ui/tag_list.js @@ -98,6 +98,7 @@ define( this.after('initialize', function() { this.on(document, events.tags.received, this.displayTags); this.on(document, events.ui.tag.select, this.updateCurrentTag); + this.on(document, events.ui.tag.select, events.ui.mails.uncheckAll); this.renderTagListTemplate(); }); } diff --git a/web-ui/app/js/views/i18n.js b/web-ui/app/js/views/i18n.js index b09490f5..568e7635 100644 --- a/web-ui/app/js/views/i18n.js +++ b/web-ui/app/js/views/i18n.js @@ -17,15 +17,11 @@ define(['i18next'], function(i18n) { 'use strict'; - var self = function(str) { - return i18n.t(str); - }; - - self.get = self; + var self = i18n.t; self.init = function(path) { i18n.init({detectLngQS: 'lang', fallbackLng: 'en', lowerCaseLng: true, getAsync: false, resGetPath: path + 'locales/__lng__/__ns__.json'}); - Handlebars.registerHelper('t', self.get.bind(self)); + Handlebars.registerHelper('t', self.bind(self)); }; return self; diff --git a/web-ui/app/js/views/templates.js b/web-ui/app/js/views/templates.js index 470c6e51..64e269ca 100644 --- a/web-ui/app/js/views/templates.js +++ b/web-ui/app/js/views/templates.js @@ -41,7 +41,8 @@ define(['hbs/templates'], function (templates) { fullView: window.Pixelated['app/templates/mails/full_view.hbs'], mailActions: window.Pixelated['app/templates/mails/mail_actions.hbs'], draft: window.Pixelated['app/templates/mails/draft.hbs'], - sent: window.Pixelated['app/templates/mails/sent.hbs'] + sent: window.Pixelated['app/templates/mails/sent.hbs'], + trash: window.Pixelated['app/templates/mails/trash.hbs'] }, mailActions: { actionsBox: window.Pixelated['app/templates/mail_actions/actions_box.hbs'], diff --git a/web-ui/app/locales/en-us/translation.json b/web-ui/app/locales/en-us/translation.json index 2a474c80..b6fd6f0a 100644 --- a/web-ui/app/locales/en-us/translation.json +++ b/web-ui/app/locales/en-us/translation.json @@ -46,7 +46,7 @@ "encrypted encryption-error": "Unable to decrypt", "encrypted encryption-valid": "Encrypted", "not-encrypted": "Not Encrypted", - "signed": "Certified sender.", + "signed": "Certified sender", "signed signature-revoked": "Sender could not be securely identified.", "signed signature-expired": "Sender could not be securely identified.", "signed signature-not-trusted": "Sender and/or message cannot be trusted.", diff --git a/web-ui/app/locales/en/translation.json b/web-ui/app/locales/en/translation.json index 2a474c80..b6fd6f0a 100644 --- a/web-ui/app/locales/en/translation.json +++ b/web-ui/app/locales/en/translation.json @@ -46,7 +46,7 @@ "encrypted encryption-error": "Unable to decrypt", "encrypted encryption-valid": "Encrypted", "not-encrypted": "Not Encrypted", - "signed": "Certified sender.", + "signed": "Certified sender", "signed signature-revoked": "Sender could not be securely identified.", "signed signature-expired": "Sender could not be securely identified.", "signed signature-not-trusted": "Sender and/or message cannot be trusted.", diff --git a/web-ui/app/scss/_mixins.scss b/web-ui/app/scss/_mixins.scss index 14a1679f..2ce4999a 100644 --- a/web-ui/app/scss/_mixins.scss +++ b/web-ui/app/scss/_mixins.scss @@ -81,6 +81,12 @@ } @mixin tags { + i.tags-label { + vertical-align: bottom; + font-size: medium; + color: #c2c2c2; + } + ul.tags { li { font-size: 0.6rem; @@ -105,7 +111,8 @@ background: transparent; border: 1px solid #DDD; line-height: 0; - padding: 3px 2px 2px 2px; + padding: 1px 2px; + margin-left: -5px; @include border-radius(2px); &:hover { opacity: 1; @@ -114,6 +121,7 @@ i { &:before { vertical-align: middle; + font-size: smaller; } } } diff --git a/web-ui/app/scss/_security.scss b/web-ui/app/scss/_security.scss index be306d86..0c2baa52 100644 --- a/web-ui/app/scss/_security.scss +++ b/web-ui/app/scss/_security.scss @@ -12,35 +12,35 @@ } &.encrypted { &:before { - content: "\f023 \f00c"; + content: "\f023"; } &.encryption-error { background: $attention; &:before { - content: "\f023 \f12a"; + content: "\f023 \f057"; } } } &.signed { &:before { - content: "\f007 \f00c"; + content: "\f00c"; } &.signature-not-trusted { background: $error; &:before { - content: "\f007 \f05e"; + content: "\f05e"; } } } &[class^=not-], &.signature-expired, &.signature-revoked { background: $attention; &:before { - content: "\f007 \f12a" + content: "\f05e" } } &.not-encrypted { &:before { - content: "\f023 \f12a"; + content: "\f13e "; } } } diff --git a/web-ui/app/templates/mails/full_view.hbs b/web-ui/app/templates/mails/full_view.hbs index c72b3af9..55d116d2 100644 --- a/web-ui/app/templates/mails/full_view.hbs +++ b/web-ui/app/templates/mails/full_view.hbs @@ -10,7 +10,7 @@ <div style="display:inline-block;padding-top: 5px;width:95%;flex-shrink:1" > - <div class="column large-10 no-padding security-status"> + <div class="column large-12 no-padding security-status"> {{#if signatureStatus}} <span class="{{signatureStatus}}"> {{t signatureStatus }} @@ -22,10 +22,7 @@ </span> {{/if}} </div> - <div class="column large-2 no-padding text-right"> - <span class="received-date">{{ header.formattedDate }}</span> - </div> - <div class="recipients column large-12 no-padding"> + <div class="recipients column large-10 no-padding"> <span class="from"> {{#if header.from }} {{ header.from }} @@ -36,13 +33,16 @@ <i class="fa fa-long-arrow-right"></i> {{{formatRecipients header}}} </div> - + <div class="recipients column large-2 text-right"> + <span class="received-date">{{ header.formattedDate }}</span> + </div> <div> <h3 class="subjectArea column large-10 no-padding"> <span class="subject">{{ header.subject }}</span> <div class="tagsArea"> <ul class="tags"> + <i class="tags-label fa fa-tags"></i> {{#each tags }} <li class="tag" data-tag="{{this}}">{{ this }}</li> {{/each }} diff --git a/web-ui/app/templates/mails/single.hbs b/web-ui/app/templates/mails/single.hbs index a74c9606..90023713 100644 --- a/web-ui/app/templates/mails/single.hbs +++ b/web-ui/app/templates/mails/single.hbs @@ -12,7 +12,6 @@ </span> <div class="from">{{#if header.from }}{{ header.from }}{{else}}{{t "you"}}{{/if}}</div> <div class="subject-and-tags"> - <i class="fa fa-trash-o"></i> {{ header.subject }} </div> <div class="subject-and-tags"> diff --git a/web-ui/app/templates/mails/trash.hbs b/web-ui/app/templates/mails/trash.hbs new file mode 100644 index 00000000..a74c9606 --- /dev/null +++ b/web-ui/app/templates/mails/trash.hbs @@ -0,0 +1,26 @@ +<span> + <input type="checkbox" {{#if isChecked }}checked="true"{{/if}} /> +</span> +<span> + <a href="/#/{{ tag }}/mail/{{ ident }}"> + <span class="received-date">{{ header.formattedDate }} + {{#if attachments}} + <div class="attachment-indicator"> + <i class="fa fa-paperclip"></i> + </div> + {{/if}} + </span> + <div class="from">{{#if header.from }}{{ header.from }}{{else}}{{t "you"}}{{/if}}</div> + <div class="subject-and-tags"> + <i class="fa fa-trash-o"></i> + {{ header.subject }} + </div> + <div class="subject-and-tags"> + <ul class="tags"> + {{#each tagsForListView }} + <li class="tag" data-tag="{{this}}">{{ this }}</li> + {{/each }} + </ul> + </div> + </a> +</span> diff --git a/web-ui/test/spec/helpers/view_helper.spec.js b/web-ui/test/spec/helpers/view_helper.spec.js index 7b5b960b..888c6cda 100644 --- a/web-ui/test/spec/helpers/view_helper.spec.js +++ b/web-ui/test/spec/helpers/view_helper.spec.js @@ -15,6 +15,14 @@ define(['helpers/view_helper'], function (viewHelper) { expect(quotedMail).toContain('> First Line\n> Second Line'); }); + + it('should add the mail sender information', function() { + testData.rawMail.mail.textPlainBody = 'First Line\nSecond Line'; + + var quotedMail = viewHelper.quoteMail(testData.rawMail.mail); + + expect(quotedMail).toContain('On Wed Jun 04 2014 17:41:13 GMT+0000 (UTC), <laurel@hamill.info> wrote'); + }); }); describe('getFormmattedDate', function() { diff --git a/web-ui/test/spec/tags/data/tags.spec.js b/web-ui/test/spec/tags/data/tags.spec.js index 469ab0ce..53978fb8 100644 --- a/web-ui/test/spec/tags/data/tags.spec.js +++ b/web-ui/test/spec/tags/data/tags.spec.js @@ -47,4 +47,12 @@ describeComponent('tags/data/tags', function () { tags.push(this.component.all); expect(eventSpy.mostRecentCall.data).toEqual(jasmine.objectContaining({tags: tags})); }); + + it('should reload taglist on mail sent', function(){ + spyOn($, 'ajax').and.returnValue($.Deferred()); + + $(document).trigger(Pixelated.events.mail.sent); + + expect($.ajax.calls.mostRecent().args[0]).toEqual('/tags'); + }); }); diff --git a/web-ui/test/spec/tags/ui/tag_list.spec.js b/web-ui/test/spec/tags/ui/tag_list.spec.js index f92f72af..e84c68aa 100644 --- a/web-ui/test/spec/tags/ui/tag_list.spec.js +++ b/web-ui/test/spec/tags/ui/tag_list.spec.js @@ -62,6 +62,13 @@ describeComponent('tags/ui/tag_list', function () { expect(this.component.attr.currentTag).toEqual('amazing'); }); + it('should uncheck all emails when a new tag is selected', function () { + var uncheckAllEvent = spyOnEvent(document, Pixelated.events.ui.mails.uncheckAll); + $(document).trigger(Pixelated.events.ui.tag.select, { tag: 'amazing'}); + + expect(uncheckAllEvent).toHaveBeenTriggeredOn(document); + }); + it('resets the tag lists when loading tags', function () { var tagList = [tag('tag1', 1, false), tag('tag2', 2, true), tag('tag3', 3, true)]; $(document).trigger(Pixelated.events.tags.received, {tags: tagList}); |