diff options
author | Steffen Lindner <mail@steffen-lindner.de> | 2017-08-23 16:13:39 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-23 16:13:39 +0300 |
commit | e0579bd8b670134d49e38808065a6293103bc488 (patch) | |
tree | 2deb40e440068dce6d1c5b1365555ed6e7542f12 | |
parent | 7ffd0d6e98b17709b1f51b22f22a29a7f8f4d515 (diff) | |
parent | 959095ded37df49af560eca2a9d6ac642ac77cb7 (diff) |
Merge pull request #413 from nextcloud/refactor/background-syncnightly-20170823
Fix background synchronization
-rw-r--r-- | appinfo/routes.php | 2 | ||||
-rw-r--r-- | js/app.js | 30 | ||||
-rw-r--r-- | js/radio.js | 8 | ||||
-rw-r--r-- | js/service/backgroundsyncservice.js | 67 | ||||
-rw-r--r-- | js/service/foldersyncservice.js | 10 | ||||
-rw-r--r-- | js/tests/service/backgroundsyncservice_spec.js | 49 | ||||
-rw-r--r-- | js/util/notificationhandler.js | 35 | ||||
-rw-r--r-- | js/views/messagesview.js | 2 | ||||
-rw-r--r-- | lib/Account.php | 52 | ||||
-rw-r--r-- | lib/Service/IAccount.php | 6 | ||||
-rw-r--r-- | lib/Service/UnifiedAccount.php | 38 |
11 files changed, 175 insertions, 124 deletions
diff --git a/appinfo/routes.php b/appinfo/routes.php index 7b8f72049..69c75d962 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -87,7 +87,7 @@ $app->registerRoutes($this, ], 'resources' => [ 'autoComplete' => ['url' => '/autoComplete'], - 'localAttachments' => ['url' => '/attachments'], + 'localAttachments' => ['url' => '/attachments'], 'accounts' => ['url' => '/accounts'], 'folders' => ['url' => '/accounts/{accountId}/folders'], 'messages' => ['url' => '/accounts/{accountId}/folders/{folderId}/messages'], @@ -40,15 +40,22 @@ define(function(require) { require('controller/foldercontroller'); require('controller/messagecontroller'); require('service/accountservice'); + require('service/aliasesservice'); require('service/attachmentservice'); require('service/davservice'); require('service/folderservice'); require('service/foldersyncservice'); require('service/messageservice'); - require('service/aliasesservice'); + require('service/backgroundsyncservice'); require('util/notificationhandler'); var Mail = Marionette.Application.extend({ + + /** + * Register the mailto protocol handler + * + * @returns {undefined} + */ registerProtocolHandler: function() { if (window.navigator.registerProtocolHandler) { var url = window.location.protocol + '//' + @@ -61,11 +68,31 @@ define(function(require) { } } }, + + /** + * @returns {undefined} + */ requestNotificationPermissions: function() { Radio.ui.trigger('notification:request'); }, + + /** + * Register the actual search module in the search proxy + * + * @returns {undefined} + */ setUpSearch: function() { SearchProxy.setFilter(require('search').filter); + }, + + /** + * Start syncing accounts in the background + * + * @param {AccountCollection} accounts + * @returns {undefined} + */ + startBackgroundSync: function(accounts) { + Radio.sync.trigger('start', accounts); } }); @@ -87,6 +114,7 @@ define(function(require) { controller: new RouteController(accounts) }); Backbone.history.start(); + _this.startBackgroundSync(accounts); }); /** diff --git a/js/radio.js b/js/radio.js index c38151fa3..c26b06a1a 100644 --- a/js/radio.js +++ b/js/radio.js @@ -16,16 +16,17 @@ define(function(require) { var channelNames = [ 'account', + 'aliases', 'attachment', 'folder', 'dav', + 'keyboard', 'message', 'navigation', 'notification', 'state', - 'ui', - 'keyboard', - 'aliases' + 'sync', + 'ui' ]; var channels = {}; @@ -37,4 +38,3 @@ define(function(require) { return channels; }); - diff --git a/js/service/backgroundsyncservice.js b/js/service/backgroundsyncservice.js new file mode 100644 index 000000000..a86d2c9aa --- /dev/null +++ b/js/service/backgroundsyncservice.js @@ -0,0 +1,67 @@ +/** + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * + * Mail + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +define(function(require) { + 'use strict'; + + var Radio = require('radio'); + var _timer = null; + var _accounts = null; + + Radio.sync.on('start', startBackgroundSync); + + var SYNC_INTERVAL = 30 * 1000; // twice a minute + + function startBackgroundSync(accounts) { + _accounts = accounts; + clearTimeout(_timer); + triggerNextSync(); + } + + function triggerNextSync() { + _timer = setTimeout(function() { + var account; + if (require('state').accounts.length === 0) { + account = _accounts.first(); + } else { + account = _accounts.get(-1); + } + sync(account); + }, SYNC_INTERVAL); + } + + /** + * @param {Account} account + * @returns {Promise} + */ + function sync(account) { + return Radio.sync.request('sync:folder', account.folders.first()) + .then(function(newMessages) { + Radio.ui.trigger('notification:mail:show', newMessages); + }) + .catch(function(e) { + console.error(e); + }) + .then(triggerNextSync); + } + + return { + sync: sync + }; +}); diff --git a/js/service/foldersyncservice.js b/js/service/foldersyncservice.js index 927c73f70..121122c27 100644 --- a/js/service/foldersyncservice.js +++ b/js/service/foldersyncservice.js @@ -27,7 +27,7 @@ define(function(require) { var OC = require('OC'); var Radio = require('radio'); - Radio.message.reply('sync', syncFolder); + Radio.sync.reply('sync:folder', syncFolder); /** * @private @@ -86,6 +86,8 @@ define(function(require) { folder.messages.remove(id); }); + + return newMessages; }); } @@ -112,7 +114,11 @@ define(function(require) { return acc.concat(f); }, []).map(function(folder) { return syncSingleFolder(folder, unifiedFolder); - })); + })).then(function(results) { + return results.reduce(function(acc, newMessages) { + return acc.concat(newMessages); + }, []); + }); } else { var unifiedAccount = allAccounts.get(-1); if (unifiedAccount) { diff --git a/js/tests/service/backgroundsyncservice_spec.js b/js/tests/service/backgroundsyncservice_spec.js new file mode 100644 index 000000000..4d1f50a38 --- /dev/null +++ b/js/tests/service/backgroundsyncservice_spec.js @@ -0,0 +1,49 @@ +/* global expect, Promise, spyOn */ + +/** + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * + * Mail + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +define([ + 'radio', + 'models/account', + 'models/folder', + 'service/backgroundsyncservice' +], function(Radio, Account, Folder, BackgroundSyncService) { + describe('Background sync service', function() { + it('fails', function(done) { + spyOn(Radio.sync, 'request').and.returnValue(Promise.resolve([])); + spyOn(Radio.ui, 'trigger'); + var account = new Account({ + accountId: -1, + isUnified: true + }); + var folder = new Folder({ + account: account + }); + account.addFolder(folder); + + BackgroundSyncService.sync(account).then(function() { + expect(Radio.sync.request).toHaveBeenCalledWith('sync:folder', folder); + expect(Radio.ui.trigger). + toHaveBeenCalledWith('notification:mail:show', []); + done(); + }).catch(done.fail); + }); + }); +}); diff --git a/js/util/notificationhandler.js b/js/util/notificationhandler.js index d01c1750b..922ff7cbf 100644 --- a/js/util/notificationhandler.js +++ b/js/util/notificationhandler.js @@ -35,8 +35,7 @@ define(function(require) { } } - /*jshint maxparams: 6 */ - function showNotification(title, body, tag, icon, account, folder) { + function showNotification(title, body, icon) { // notifications not supported -> go away if (typeof Notification === 'undefined') { return; @@ -50,20 +49,22 @@ define(function(require) { title, { body: body, - tag: tag, icon: icon } ); notification.onclick = function() { - Radio.navigation.trigger('folder', account.get('accountId'), folder.get('id'), false); window.focus(); }; } - function showMailNotification(email, folder) { - if (Notification.permission === 'granted' && folder.messages.length > 0) { - var from = _.map(folder.messages, function(m) { - return m.from; + /** + * @param {array<Message>} messages + * @returns {undefined} + */ + function showMailNotification(messages) { + if (Notification.permission === 'granted' && messages.length > 0) { + var from = _.map(messages, function(m) { + return m.get('from'); }); from = _.uniq(from); if (from.length > 2) { @@ -74,9 +75,9 @@ define(function(require) { } // special layout if there is only 1 new message var body = ''; - if (folder.messages.length === 1) { - var subject = _.map(folder.messages, function(m) { - return m.subject; + if (messages.length === 1) { + var subject = _.map(messages, function(m) { + return m.get('subject'); }); body = t('mail', '{from}\n{subject}', { @@ -85,19 +86,15 @@ define(function(require) { }); } else { body = n('mail', - '%n new message in {folderName} \nfrom {from}', - '%n new messages in {folderName} \nfrom {from}', - folder.messages.length, { - folderName: folder.name, + '%n new message \nfrom {from}', + '%n new messages \nfrom {from}', + messages.length, { from: from.join() }); } // If it's okay let's create a notification - var State = require('state'); - var tag = 'not-' + folder.accountId + '-' + folder.name; var icon = OC.filePath('mail', 'img', 'mail-notification.png'); - var account = State.accounts.get(folder.accountId); - showNotification(email, body, tag, icon, account, folder.id); + showNotification(t('mail', 'Nextcloud Mail'), body, icon); } } diff --git a/js/views/messagesview.js b/js/views/messagesview.js index e20b2db54..fbb04cb57 100644 --- a/js/views/messagesview.js +++ b/js/views/messagesview.js @@ -233,7 +233,7 @@ define(function(require) { ); var folder = require('state').currentFolder; - return Radio.message.request('sync', folder) + return Radio.sync.request('sync:folder', folder) .catch(function(e) { console.error(e); Radio.ui.trigger('error:show', t('mail', 'Error while refreshing messages.')); diff --git a/lib/Account.php b/lib/Account.php index 6a084c305..0d0896b5b 100644 --- a/lib/Account.php +++ b/lib/Account.php @@ -680,58 +680,6 @@ class Account implements IAccount { } /** - * @param $query - * @return array - */ - public function getChangedMailboxes($query) { - $imp = $this->getImapConnection(); - $allBoxes = $this->getMailboxes(); - $allBoxesMap = []; - foreach ($allBoxes as $mb) { - $allBoxesMap[$mb->getFolderId()] = $mb; - } - - // filter non existing mailboxes - $mailBoxNames = array_filter(array_keys($query), function($folderId) use ($allBoxesMap) { - return isset($allBoxesMap[$folderId]); - }); - - $status = $imp->status($mailBoxNames); - - // filter for changed mailboxes - $changedBoxes = []; - foreach ($status as $folderId => $s) { - $uidValidity = $query[$folderId]['uidvalidity']; - $uidNext = $query[$folderId]['uidnext']; - - if ($uidNext === $s['uidnext'] && - $uidValidity === $s['uidvalidity']) { - continue; - } - // get unread messages - if (isset($allBoxesMap[$folderId])) { - /** @var Mailbox $m */ - $m = $allBoxesMap[$folderId]; - $role = $m->getSpecialRole(); - if (is_null($role) || $role === 'inbox') { - $newMessages = $m->getMessagesSince($uidNext, $s['uidnext']); - // only trigger updates in case new messages are actually available - if (!empty($newMessages)) { - $changedBoxes[$folderId] = $m->serialize($this->getId(), $s); - $changedBoxes[$folderId]['messages'] = $newMessages; - $newUnreadMessages = array_filter($newMessages, function($m) { - return $m['flags']['unseen']; - }); - $changedBoxes[$folderId]['newUnReadCounter'] = count($newUnreadMessages); - } - } - } - } - - return $changedBoxes; - } - - /** } /** diff --git a/lib/Service/IAccount.php b/lib/Service/IAccount.php index 7c476d122..c9029db4e 100644 --- a/lib/Service/IAccount.php +++ b/lib/Service/IAccount.php @@ -50,12 +50,6 @@ interface IAccount extends JsonSerializable { public function moveMessage($sourceFolderId, $messageId, $destFolderId); /** - * @param string[] $query - * @return array - */ - public function getChangedMailboxes($query); - - /** * @return IMailBox */ public function getInbox(); diff --git a/lib/Service/UnifiedAccount.php b/lib/Service/UnifiedAccount.php index 091fbd2f2..6119575ac 100644 --- a/lib/Service/UnifiedAccount.php +++ b/lib/Service/UnifiedAccount.php @@ -151,44 +151,6 @@ class UnifiedAccount implements IAccount { } /** - * @param string[] $query - * @return array - */ - public function getChangedMailboxes($query) { - $accounts = $this->accountService->findByUserId($this->userId); - $changedBoxes = []; - - foreach($accounts as $account) { - /** @var IAccount $account */ - if ($account->getId() === UnifiedAccount::ID) { - continue; - } - $inbox = $account->getInbox(); - $inboxName = $inbox->getFolderId(); - $changes = $account->getChangedMailboxes([$inboxName => [ - 'uidvalidity' => $query[self::INBOX_ID]['uidvalidity'][$account->getId()], - 'uidnext' => $query[self::INBOX_ID]['uidnext'][$account->getId()], - ]]); - if (!isset($changes[$inboxName])) { - continue; - } - if (!isset($changedBoxes[self::INBOX_ID])) { - $changedBoxes[self::INBOX_ID] = $this->buildInbox(); - $changedBoxes[self::INBOX_ID]['messages'] = []; - $changedBoxes[self::INBOX_ID]['newUnReadCounter'] = 0; - } - // Create special unified inbox message IDs - foreach ($changes[$inboxName]['messages'] as &$message) { - $id = base64_encode(json_encode([$account->getId(), $message['id']])); - $message['id'] = $id; - } - $changedBoxes[self::INBOX_ID]['messages'] = array_merge($changedBoxes[self::INBOX_ID]['messages'], $changes[$inboxName]['messages']); - $changedBoxes[self::INBOX_ID]['newUnReadCounter'] += $changes[$inboxName]['newUnReadCounter']; - } - return $changedBoxes; - } - - /** * @return IMailBox */ public function getInbox() { |