diff options
author | Duda Dornelles <ddornell@thoughtworks.com> | 2014-10-16 13:53:16 +0200 |
---|---|---|
committer | Duda Dornelles <ddornell@thoughtworks.com> | 2014-10-16 13:54:03 +0200 |
commit | 0de9d8dc31298921f7272d74cdd3c5d4217b5841 (patch) | |
tree | 468ba5005379151eff72c5aaaf37db9ba29b3ef7 | |
parent | 761e85e523ec61f5629a9f1f6e4196e23ce96673 (diff) |
decreasing count on tag and tag shortcut when email is read. we were
decreasing only tags that were also in the read mail, but we have also
to look at the mailbox and compare that to the tags in the case of
default tags #95
-rw-r--r-- | service/pixelated/adapter/mail.py | 7 | ||||
-rw-r--r-- | service/pixelated/adapter/mail_service.py | 3 | ||||
-rw-r--r-- | service/pixelated/controllers/mails_controller.py | 7 | ||||
-rw-r--r-- | service/test/unit/adapter/mail_test.py | 24 | ||||
-rw-r--r-- | web-ui/app/js/mail_list/ui/mail_items/generic_mail_item.js | 3 | ||||
-rw-r--r-- | web-ui/app/js/mail_list/ui/mail_items/mail_item.js | 3 | ||||
-rw-r--r-- | web-ui/app/js/tags/ui/tag.js | 6 | ||||
-rw-r--r-- | web-ui/app/js/tags/ui/tag_list.js | 4 | ||||
-rw-r--r-- | web-ui/app/js/tags/ui/tag_shortcut.js | 29 | ||||
-rw-r--r-- | web-ui/test/spec/mail_list/ui/mail_items/generic_mail_item.spec.js | 8 | ||||
-rw-r--r-- | web-ui/test/spec/tags/ui/tag.spec.js | 51 | ||||
-rw-r--r-- | web-ui/test/test_data.js | 3 |
12 files changed, 113 insertions, 35 deletions
diff --git a/service/pixelated/adapter/mail.py b/service/pixelated/adapter/mail.py index 513a07d8..5cafa36a 100644 --- a/service/pixelated/adapter/mail.py +++ b/service/pixelated/adapter/mail.py @@ -52,6 +52,10 @@ class Mail: return self.fdoc.content.get('flags') @property + def mailbox_name(self): + return self.fdoc.content.get('mbox') + + @property def _mime_multipart(self): if self._mime: return self._mime @@ -75,7 +79,8 @@ class Mail: 'tags': list(self.tags), 'status': list(self.status), 'security_casing': {}, - 'body': self.body + 'body': self.body, + 'mailbox': self.mailbox_name.lower() } diff --git a/service/pixelated/adapter/mail_service.py b/service/pixelated/adapter/mail_service.py index e908feb7..cc03ab3a 100644 --- a/service/pixelated/adapter/mail_service.py +++ b/service/pixelated/adapter/mail_service.py @@ -35,7 +35,8 @@ class MailService: if len(reserved_words): raise ValueError('None of the following words can be used as tags: ' + ' '.join(reserved_words)) mail = self.mail(mail_id) - return mail.update_tags(set(new_tags)) + mail.update_tags(set(new_tags)) + return mail def mail(self, mail_id): return self.mailboxes.mail(mail_id) diff --git a/service/pixelated/controllers/mails_controller.py b/service/pixelated/controllers/mails_controller.py index 6bd2fe99..f6414f27 100644 --- a/service/pixelated/controllers/mails_controller.py +++ b/service/pixelated/controllers/mails_controller.py @@ -92,11 +92,12 @@ class MailsController: def mail_tags(self, mail_id): new_tags = map(lambda tag: tag.lower(), request.get_json()['newtags']) try: - tags = self._mail_service.update_tags(mail_id, new_tags) - self._search_engine.index_mail(self._mail_service.mail(mail_id)) + self._mail_service.update_tags(mail_id, new_tags) + mail = self._mail_service.mail(mail_id) + self._search_engine.index_mail(mail) except ValueError as ve: return respond_json(ve.message, 403) - return respond_json(list(tags)) + return respond_json(mail.as_dict()) def update_draft(self): _mail = InputMail.from_dict(request.json) diff --git a/service/test/unit/adapter/mail_test.py b/service/test/unit/adapter/mail_test.py index 20038e96..94578a05 100644 --- a/service/test/unit/adapter/mail_test.py +++ b/service/test/unit/adapter/mail_test.py @@ -19,10 +19,10 @@ import pixelated.support.date from pixelated.adapter.mail import PixelatedMail, InputMail from mockito import * from test.support import test_helper +import dateutil.parser as dateparser class TestPixelatedMail(unittest.TestCase): - def setUp(self): self.querier = mock() @@ -68,6 +68,28 @@ class TestPixelatedMail(unittest.TestCase): self.assertEquals(mail.fdoc.content['flags'], []) + def test_as_dict(self): + fdoc, hdoc, bdoc = test_helper.leap_mail(flags=['\\Recent']) + hdoc.content['headers']['Subject'] = 'The subject' + hdoc.content['headers']['From'] = 'me@pixelated.org' + + mail = PixelatedMail.from_soledad(fdoc, hdoc, bdoc, soledad_querier=self.querier) + + _dict = mail.as_dict() + + self.assertEquals(_dict, {'body': 'body', + 'header': { + 'date': dateparser.parse(hdoc.content['date']).isoformat(), + 'from': 'me@pixelated.org', + 'subject': 'The subject' + }, + 'ident': 'chash', + 'mailbox': 'inbox', + 'security_casing': {}, + 'status': ['recent'], + 'tags': []} + ) + class InputMailTest(unittest.TestCase): mail_dict = lambda x: { diff --git a/web-ui/app/js/mail_list/ui/mail_items/generic_mail_item.js b/web-ui/app/js/mail_list/ui/mail_items/generic_mail_item.js index 3d426447..d5dfbf28 100644 --- a/web-ui/app/js/mail_list/ui/mail_items/generic_mail_item.js +++ b/web-ui/app/js/mail_list/ui/mail_items/generic_mail_item.js @@ -51,7 +51,8 @@ define( function updateMailStatusToRead() { if (!_.contains(this.attr.mail.status, this.status.READ)) { - this.trigger(document, events.mail.read, { ident: this.attr.ident, tags: this.attr.mail.tags }); + var mail_read_data = { ident: this.attr.ident, tags: this.attr.tags, mailbox: this.attr.mailbox }; + this.trigger(document, events.mail.read, mail_read_data); this.attr.mail.status.push(this.status.READ); this.$node.addClass(viewHelpers.formatStatusClasses(this.attr.mail.status)); } diff --git a/web-ui/app/js/mail_list/ui/mail_items/mail_item.js b/web-ui/app/js/mail_list/ui/mail_items/mail_item.js index 5a4192e6..5f1f354a 100644 --- a/web-ui/app/js/mail_list/ui/mail_items/mail_item.js +++ b/web-ui/app/js/mail_list/ui/mail_items/mail_item.js @@ -1,6 +1,6 @@ /* * Copyright (c) 2014 ThoughtWorks, Inc. - * + * * Pixelated is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -64,6 +64,7 @@ define( this.attr.ident = mail.ident; this.attr.statuses = viewHelper.formatStatusClasses(mail.status); this.attr.tags = mail.tags; + this.attr.mailbox = mail.mailbox; this.attr.header.formattedDate = this.formattedDate(mail.header.date); }; diff --git a/web-ui/app/js/tags/ui/tag.js b/web-ui/app/js/tags/ui/tag.js index c2b7f588..2cf45764 100644 --- a/web-ui/app/js/tags/ui/tag.js +++ b/web-ui/app/js/tags/ui/tag.js @@ -52,9 +52,13 @@ define( }; this.decreaseReadCountIfMatchingTag = function (ev, data) { - if (_.contains(data.tags, this.attr.tag.name)) { + var mailbox_and_tags = _.flatten([data.tags, data.mailbox]); + if (_.contains(mailbox_and_tags, this.attr.tag.name)) { this.attr.tag.counts.read++; this.$node.html(this.viewFor(this.attr.tag, templates.tags.tagInner)); + if (!_.isUndefined(this.attr.shortcut)) { + this.attr.shortcut.reRender(); + } } }; diff --git a/web-ui/app/js/tags/ui/tag_list.js b/web-ui/app/js/tags/ui/tag_list.js index 5bbac8ca..b5b4d555 100644 --- a/web-ui/app/js/tags/ui/tag_list.js +++ b/web-ui/app/js/tags/ui/tag_list.js @@ -55,7 +55,7 @@ define( }); this.renderShortcut = function (tag, tagComponent) { - TagShortcut.appendedTo($('#tags-shortcuts'), { linkTo: tag, trigger: tagComponent}); + return TagShortcut.appendedTo($('#tags-shortcuts'), { linkTo: tag, trigger: tagComponent}); }; function renderTag(tag, defaultList, customList) { @@ -63,7 +63,7 @@ define( var tagComponent = Tag.appendedTo(list, {tag: tag}); if (_.contains(_.keys(ORDER), tag.name)) { - this.renderShortcut(tag, tagComponent); + tagComponent.attr.shortcut = this.renderShortcut(tag, tagComponent); } } diff --git a/web-ui/app/js/tags/ui/tag_shortcut.js b/web-ui/app/js/tags/ui/tag_shortcut.js index 5710592e..0fe92550 100644 --- a/web-ui/app/js/tags/ui/tag_shortcut.js +++ b/web-ui/app/js/tags/ui/tag_shortcut.js @@ -26,7 +26,7 @@ define( function (describeComponent, templates, events, tagBase, utils) { - var TagShortcut = describeComponent(tagShortcut, tagBase); + var TagShortcut = describeComponent(tagShortcut, tagBase); TagShortcut.appendedTo = function (parent, data) { var res = new this(); @@ -38,9 +38,7 @@ define( function tagShortcut() { - this.renderAndAttach = function (parent, options) { - var linkTo = options.linkTo; - + this.renderTemplate = function (linkTo) { var model = { tagName: linkTo.name, displayBadge: this.displayBadge(linkTo), @@ -48,11 +46,16 @@ define( count: this.badgeType(linkTo) === 'total' ? linkTo.counts.total : (linkTo.counts.total - linkTo.counts.read), icon: iconFor[linkTo.name] }; + return templates.tags.shortcut(model); + }; - var rendered = templates.tags.shortcut(model); - parent.append(rendered); + this.renderAndAttach = function (parent, options) { + parent.append(this.renderTemplate(options.linkTo)); + this.initialize(parent.children().last(), options); + }; - this.initialize(parent.children().last(),options); + this.reRender = function () { + this.$node.html(this.renderTemplate(this.attr.linkTo)); }; var iconFor = { @@ -64,8 +67,12 @@ define( }; this.selectTag = function (ev, data) { - if(data.tag === this.attr.linkTo.name) { this.doSelect(); } - else { this.doUnselect(); } + if (data.tag === this.attr.linkTo.name) { + this.doSelect(); + } + else { + this.doUnselect(); + } }; this.doUnselect = function () { @@ -83,7 +90,9 @@ define( }; this.after('initialize', function () { - this.on('click', function () { this.attr.trigger.triggerSelect(); }); + this.on('click', function () { + this.attr.trigger.triggerSelect(); + }); this.on(document, events.ui.tag.select, this.selectTag); this.on(document, events.tags.shortcuts.teardown, this.doTeardown); }); diff --git a/web-ui/test/spec/mail_list/ui/mail_items/generic_mail_item.spec.js b/web-ui/test/spec/mail_list/ui/mail_items/generic_mail_item.spec.js index ecb2b854..b03babb5 100644 --- a/web-ui/test/spec/mail_list/ui/mail_items/generic_mail_item.spec.js +++ b/web-ui/test/spec/mail_list/ui/mail_items/generic_mail_item.spec.js @@ -5,7 +5,8 @@ describeComponent('mail_list/ui/mail_items/generic_mail_item', function () { beforeEach(function () { mail = Pixelated.testData().parsedMail.simpleTextPlain; - mail.tags = ['inbox']; + mail.tags = []; + mail.mailbox = 'inbox'; setupComponent('<li></li>', { mail: mail, @@ -95,7 +96,7 @@ describeComponent('mail_list/ui/mail_items/generic_mail_item', function () { expect(this.component.attr.mail.status).toContain(this.component.status.READ); expect(this.$node.attr('class')).toMatch('status-read'); - expect(mailReadEvent).toHaveBeenTriggeredOnAndWith(document, { ident: mail.ident, tags: ['inbox'] }); + expect(mailReadEvent).toHaveBeenTriggeredOnAndWith(document, { ident: mail.ident, tags: [], mailbox: 'inbox' }); }); }); @@ -110,7 +111,8 @@ describeComponent('mail_list/ui/mail_items/generic_mail_item', function () { this.$node.find('a').click(); - expect(mailReadEvent).toHaveBeenTriggeredOnAndWith(document, jasmine.objectContaining({ident: mail.ident})); + var expectedEventData = {ident: mail.ident}; + expect(mailReadEvent).toHaveBeenTriggeredOnAndWith(document, jasmine.objectContaining(expectedEventData)); }); it('should not trigger mail:read event when clicking mail that is already read', function () { diff --git a/web-ui/test/spec/tags/ui/tag.spec.js b/web-ui/test/spec/tags/ui/tag.spec.js index de78ba75..48f761da 100644 --- a/web-ui/test/spec/tags/ui/tag.spec.js +++ b/web-ui/test/spec/tags/ui/tag.spec.js @@ -4,7 +4,7 @@ describeComponent('tags/ui/tag', function () { 'use strict'; - describe('inbox tag', function() { + describe('inbox tag', function () { beforeEach(function () { setupComponent('<li></li>', { tag: { @@ -46,11 +46,42 @@ describeComponent('tags/ui/tag', function () { expect(tagSelectedEvent).toHaveBeenTriggeredOnAndWith(document, { tag: 'drafts'}); }); - it('increases the read count when there is an email read and the email has that tag', function () { + describe('increasing count read when email is read', function () { + it('doesnt update if mail.tags or mail.mailbox dont match the tag name', function () { + setupComponent('<li></li>', { + tag: { name: 'sometag', ident: '1', counts: { total: 100, read: 0 } } + }); + + $(document).trigger(Pixelated.events.mail.read, { tags: ['someothertag'], mailbox: 'inbox' }); + + expect(this.component.attr.tag.counts.read).toEqual(0); + }); + + it('looks at the mail mailbox attr for default tags', function () { + $(document).trigger(Pixelated.events.mail.read, { tags: [], mailbox: 'inbox' }); + + expect(this.component.attr.tag.counts.read).toEqual(1); + expect(this.$node.html()).toMatch('<span class="unread-count">99</span>'); + }); + + it('looks at the mail tags for non default tags', function () { + setupComponent('<li></li>', { + tag: { name: 'tag', ident: '1', counts: { total: 100, read: 0 } } + }); + + $(document).trigger(Pixelated.events.mail.read, { tags: ['tag'], mailbox: 'inbox' }); + + expect(this.component.attr.tag.counts.read).toEqual(1); + expect(this.$node.html()).toMatch('<span class="unread-count">99</span>'); + }); + }); + + it('re-renders the tag shortcut linked to it when increasing the read count if there is a shortcut', function () { + this.component.attr.shortcut = jasmine.createSpyObj('shortcut', ['reRender']); + $(document).trigger(Pixelated.events.mail.read, { tags: ['inbox'] }); - expect(this.component.attr.tag.counts.read).toEqual(1); - expect(this.$node.html()).toMatch('<span class="unread-count">99</span>'); + expect(this.component.attr.shortcut.reRender).toHaveBeenCalled(); }); it('doesnt increase the read count when the read email doesnt have the tag', function () { @@ -89,7 +120,7 @@ describeComponent('tags/ui/tag', function () { }); }); - describe('all tag', function(){ + describe('all tag', function () { beforeEach(function () { setupComponent('<li></li>', { tag: { @@ -103,18 +134,18 @@ describeComponent('tags/ui/tag', function () { }); }); - it('adds searching class when user is doing a search', function(){ + it('adds searching class when user is doing a search', function () { $(document).trigger(Pixelated.events.search.perform, {}); expect(this.$node.attr('class')).toMatch('searching'); }); - it('removes searching class when user searches for empty string', function(){ + it('removes searching class when user searches for empty string', function () { $(document).trigger(Pixelated.events.search.perform, {}); $(document).trigger(Pixelated.events.search.empty); expect(this.$node.attr('class')).not.toMatch('searching'); }); - it('removes searching class when user clicks in any tag', function(){ + it('removes searching class when user clicks in any tag', function () { $(document).trigger(Pixelated.events.search.perform, {}); this.$node.click(); expect(this.$node.attr('class')).not.toMatch('searching'); @@ -122,8 +153,8 @@ describeComponent('tags/ui/tag', function () { }); - _.each(['sent', 'trash'], function(tag_name) { - describe(tag_name + ' tag', function() { + _.each(['sent', 'trash'], function (tag_name) { + describe(tag_name + ' tag', function () { beforeEach(function () { setupComponent('<li></li>', { tag: { diff --git a/web-ui/test/test_data.js b/web-ui/test/test_data.js index bf3626bb..194cd02d 100644 --- a/web-ui/test/test_data.js +++ b/web-ui/test/test_data.js @@ -84,7 +84,8 @@ define(function() { date:'2014-06-04T14:41:13-03:00' }, ident:1, - tags:['textplain','inbox'], + tags:['textplain'], + mailbox: ['inbox'], status:[], body: 'Hello Everyone', isSentMail: function() { return false; }, |