Skip to content

Commit 20b4009

Browse files
SebastianKrupinskist3iny
authored andcommitted
feat: adjust background sync on user activity
Signed-off-by: SebastianKrupinski <[email protected]> Signed-off-by: Richard Steinmetz <[email protected]>
1 parent 28c86a0 commit 20b4009

File tree

5 files changed

+97
-20
lines changed

5 files changed

+97
-20
lines changed

lib/BackgroundJob/SyncJob.php

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
namespace OCA\Mail\BackgroundJob;
1010

1111
use Horde_Imap_Client_Exception;
12+
use OCA\Mail\AppInfo\Application;
1213
use OCA\Mail\Exception\IncompleteSyncException;
1314
use OCA\Mail\Exception\ServiceException;
1415
use OCA\Mail\IMAP\MailboxSync;
@@ -26,12 +27,15 @@
2627
use function sprintf;
2728

2829
class SyncJob extends TimedJob {
30+
private const DEFAULT_SYNC_INTERVAL = 3600;
31+
2932
private IUserManager $userManager;
3033
private AccountService $accountService;
3134
private ImapToDbSynchronizer $syncService;
3235
private MailboxSync $mailboxSync;
3336
private LoggerInterface $logger;
3437
private IJobList $jobList;
38+
private readonly bool $forcedSyncInterval;
3539

3640
public function __construct(ITimeFactory $time,
3741
IUserManager $userManager,
@@ -40,7 +44,7 @@ public function __construct(ITimeFactory $time,
4044
ImapToDbSynchronizer $syncService,
4145
LoggerInterface $logger,
4246
IJobList $jobList,
43-
IConfig $config) {
47+
private readonly IConfig $config) {
4448
parent::__construct($time);
4549

4650
$this->userManager = $userManager;
@@ -50,12 +54,15 @@ public function __construct(ITimeFactory $time,
5054
$this->logger = $logger;
5155
$this->jobList = $jobList;
5256

53-
$this->setInterval(
54-
max(
55-
5 * 60,
56-
$config->getSystemValueInt('app.mail.background-sync-interval', 3600)
57-
),
58-
);
57+
$configuredSyncInterval = $config->getSystemValueInt('app.mail.background-sync-interval');
58+
if ($configuredSyncInterval > 0) {
59+
$this->forcedSyncInterval = true;
60+
} else {
61+
$this->forcedSyncInterval = false;
62+
$configuredSyncInterval = self::DEFAULT_SYNC_INTERVAL;
63+
}
64+
65+
$this->setInterval(max(5 * 60, $configuredSyncInterval));
5966
$this->setTimeSensitivity(self::TIME_SENSITIVE);
6067
}
6168

@@ -79,6 +86,31 @@ protected function run($argument) {
7986
return;
8087
}
8188

89+
// If an admin configured a custom sync interval, always abide by it
90+
if (!$this->forcedSyncInterval) {
91+
$now = $this->time->getTime();
92+
$heartbeat = (int)$this->config->getUserValue(
93+
$account->getUserId(),
94+
Application::APP_ID,
95+
'ui-heartbeat',
96+
0,
97+
);
98+
$lastUsed = $now - $heartbeat;
99+
if ($lastUsed > 3 * 24 * 3600) {
100+
// User did not open the app in more than three days -> defer sync
101+
$this->setInterval(6 * 3600);
102+
} elseif ($lastUsed > 24 * 3600) {
103+
// User opened the app at least once within the last three days -> default sync
104+
$this->setInterval(self::DEFAULT_SYNC_INTERVAL);
105+
} elseif ($lastUsed > 0) {
106+
// User opened the app at least once within the last 24 hours -> sync more often
107+
$this->setInterval(15 * 60);
108+
} else {
109+
// Default to the hourly interval in case there is no heartbeat
110+
$this->setInterval(self::DEFAULT_SYNC_INTERVAL);
111+
}
112+
}
113+
82114
$user = $this->userManager->get($account->getUserId());
83115
if ($user === null || !$user->isEnabled()) {
84116
$this->logger->debug(sprintf(

lib/Controller/MailboxesController.php

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
namespace OCA\Mail\Controller;
1212

1313
use Horde_Imap_Client;
14+
use NCU\Config\IUserConfig;
15+
use OCA\Mail\AppInfo\Application;
1416
use OCA\Mail\Contracts\IMailManager;
1517
use OCA\Mail\Contracts\IMailSearch;
1618
use OCA\Mail\Exception\ClientException;
@@ -27,29 +29,25 @@
2729
use OCP\AppFramework\Http\Attribute\OpenAPI;
2830
use OCP\AppFramework\Http\Attribute\UserRateLimit;
2931
use OCP\AppFramework\Http\JSONResponse;
32+
use OCP\AppFramework\Utility\ITimeFactory;
33+
use OCP\IConfig;
3034
use OCP\IRequest;
3135

3236
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
3337
class MailboxesController extends Controller {
3438
private AccountService $accountService;
35-
private ?string $currentUserId;
3639
private IMailManager $mailManager;
3740
private SyncService $syncService;
41+
private ?string $currentUserId;
3842

39-
/**
40-
* @param string $appName
41-
* @param IRequest $request
42-
* @param AccountService $accountService
43-
* @param string|null $UserId
44-
* @param IMailManager $mailManager
45-
* @param SyncService $syncService
46-
*/
4743
public function __construct(string $appName,
4844
IRequest $request,
4945
AccountService $accountService,
5046
?string $UserId,
5147
IMailManager $mailManager,
52-
SyncService $syncService) {
48+
SyncService $syncService,
49+
private readonly IConfig $config,
50+
private readonly ITimeFactory $timeFactory) {
5351
parent::__construct($appName, $request);
5452

5553
$this->accountService = $accountService;
@@ -140,6 +138,13 @@ public function sync(int $id, array $ids = [], ?int $lastMessageTimestamp = null
140138
$account = $this->accountService->find($this->currentUserId, $mailbox->getAccountId());
141139
$order = $sortOrder === 'newest' ? IMailSearch::ORDER_NEWEST_FIRST: IMailSearch::ORDER_OLDEST_FIRST;
142140

141+
$this->config->setUserValue(
142+
$this->currentUserId,
143+
Application::APP_ID,
144+
'ui-heartbeat',
145+
$this->timeFactory->getTime(),
146+
);
147+
143148
try {
144149
$syncResponse = $this->syncService->syncMailbox(
145150
$account,

lib/Service/AccountService.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
namespace OCA\Mail\Service;
1212

1313
use OCA\Mail\Account;
14+
use OCA\Mail\AppInfo\Application;
1415
use OCA\Mail\BackgroundJob\PreviewEnhancementProcessingJob;
1516
use OCA\Mail\BackgroundJob\QuotaJob;
1617
use OCA\Mail\BackgroundJob\SyncJob;
@@ -21,7 +22,9 @@
2122
use OCA\Mail\Exception\ServiceException;
2223
use OCA\Mail\IMAP\IMAPClientFactory;
2324
use OCP\AppFramework\Db\DoesNotExistException;
25+
use OCP\AppFramework\Utility\ITimeFactory;
2426
use OCP\BackgroundJob\IJobList;
27+
use OCP\IConfig;
2528
use function array_map;
2629

2730
class AccountService {
@@ -47,7 +50,9 @@ class AccountService {
4750
public function __construct(MailAccountMapper $mapper,
4851
AliasesService $aliasesService,
4952
IJobList $jobList,
50-
IMAPClientFactory $imapClientFactory) {
53+
IMAPClientFactory $imapClientFactory,
54+
private readonly IConfig $config,
55+
private readonly ITimeFactory $time) {
5156
$this->mapper = $mapper;
5257
$this->aliasesService = $aliasesService;
5358
$this->jobList = $jobList;
@@ -174,6 +179,14 @@ public function save(MailAccount $newAccount): MailAccount {
174179
$this->jobList->add(PreviewEnhancementProcessingJob::class, ['accountId' => $newAccount->getId()]);
175180
$this->jobList->add(QuotaJob::class, ['accountId' => $newAccount->getId()]);
176181

182+
// Set initial heartbeat
183+
$this->config->setUserValue(
184+
$newAccount->getUserId(),
185+
Application::APP_ID,
186+
'ui-heartbeat',
187+
$this->time->getTime(),
188+
);
189+
177190
return $newAccount;
178191
}
179192

tests/Unit/Controller/MailboxesControllerTest.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use OCA\Mail\Service\AccountService;
2121
use OCA\Mail\Service\Sync\SyncService;
2222
use OCP\AppFramework\Http\JSONResponse;
23+
use OCP\AppFramework\Utility\ITimeFactory;
24+
use OCP\IConfig;
2325
use OCP\IRequest;
2426
use PHPUnit\Framework\MockObject\MockObject;
2527

@@ -45,20 +47,28 @@ class MailboxesControllerTest extends TestCase {
4547
/** @var SyncService|MockObject */
4648
private $syncService;
4749

50+
private IConfig|MockObject $config;
51+
private ITimeFactory|MockObject $timeFactory;
52+
4853
public function setUp(): void {
4954
parent::setUp();
5055

5156
$this->request = $this->createMock(IRequest::class);
5257
$this->accountService = $this->createMock(AccountService::class);
5358
$this->mailManager = $this->createMock(IMailManager::class);
5459
$this->syncService = $this->createMock(SyncService::class);
60+
$this->config = $this->createMock(IConfig::class);
61+
$this->timeFactory = $this->createMock(ITimeFactory::class);
62+
5563
$this->controller = new MailboxesController(
5664
$this->appName,
5765
$this->request,
5866
$this->accountService,
5967
$this->userId,
6068
$this->mailManager,
61-
$this->syncService
69+
$this->syncService,
70+
$this->config,
71+
$this->timeFactory
6272
);
6373
}
6474

tests/Unit/Service/AccountServiceTest.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
use OCA\Mail\IMAP\IMAPClientFactory;
1818
use OCA\Mail\Service\AccountService;
1919
use OCA\Mail\Service\AliasesService;
20+
use OCP\AppFramework\Utility\ITimeFactory;
2021
use OCP\BackgroundJob\IJobList;
22+
use OCP\IConfig;
2123
use OCP\IL10N;
2224
use PHPUnit\Framework\MockObject\MockObject;
2325

@@ -55,6 +57,9 @@ class AccountServiceTest extends TestCase {
5557
/** @var Horde_Imap_Client_Socket|MockObject */
5658
private $client;
5759

60+
private IConfig&MockObject $config;
61+
private ITimeFactory&MockObject $time;
62+
5863
protected function setUp(): void {
5964
parent::setUp();
6065

@@ -63,11 +68,15 @@ protected function setUp(): void {
6368
$this->aliasesService = $this->createMock(AliasesService::class);
6469
$this->jobList = $this->createMock(IJobList::class);
6570
$this->imapClientFactory = $this->createMock(IMAPClientFactory::class);
71+
$this->config = $this->createMock(IConfig::class);
72+
$this->time = $this->createMock(ITimeFactory::class);
6673
$this->accountService = new AccountService(
6774
$this->mapper,
6875
$this->aliasesService,
6976
$this->jobList,
70-
$this->imapClientFactory
77+
$this->imapClientFactory,
78+
$this->config,
79+
$this->time,
7180
);
7281

7382
$this->account1 = new MailAccount();
@@ -155,12 +164,20 @@ public function testDeleteByAccountId() {
155164

156165
public function testSave() {
157166
$account = new MailAccount();
167+
$account->setUserId('user1');
158168

159169
$this->mapper->expects($this->once())
160170
->method('save')
161171
->with($account)
162172
->will($this->returnArgument(0));
163173

174+
$this->time->expects(self::once())
175+
->method('getTime')
176+
->willReturn(1755850409);
177+
$this->config->expects(self::once())
178+
->method('setUserValue')
179+
->with('user1', 'mail', 'ui-heartbeat', 1755850409);
180+
164181
$actual = $this->accountService->save($account);
165182

166183
$this->assertEquals($account, $actual);

0 commit comments

Comments
 (0)