Skip to content

Commit be14138

Browse files
authored
Merge pull request #63104 from nextcloud/feat/app-menu-order-by-topic
App menu sorting improvement
2 parents 68a1867 + 2c34ed8 commit be14138

2 files changed

Lines changed: 78 additions & 2 deletions

File tree

lib/private/NavigationManager.php

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,32 @@
2828
* @psalm-import-type NavigationEntryOutput from INavigationManager
2929
*/
3030
class NavigationManager implements INavigationManager {
31+
/**
32+
* Default app menu order, grouped by topic with four apps per row.
33+
* Apps ship very different orders in their info.xml, so the values are
34+
* negative to keep this group in front of everything else.
35+
* Apps that are not listed keep their own order.
36+
*/
37+
private const DEFAULT_APP_ORDER = [
38+
// Basics
39+
'dashboard' => -100,
40+
'files' => -99,
41+
'office' => -98,
42+
'photos' => -97,
43+
// Collaboration
44+
'spreed' => -96,
45+
'mail' => -95,
46+
'calendar' => -94,
47+
'contacts' => -93,
48+
// Productivity
49+
'deck' => -92,
50+
'collectives' => -91,
51+
'tables' => -90,
52+
'circles' => -89,
53+
// All other apps follow, starting with Activity
54+
'activity' => -88,
55+
];
56+
3157
/** @var array<string, NavigationEntryOutput> */
3258
protected array $entries = [];
3359
/** @var list<callable(): NavigationEntry> */
@@ -80,8 +106,11 @@ public function add(array|callable $entry): void {
80106
$entry['app'] = $id;
81107
}
82108

83-
// Set order from user defined app order
84-
$entry['order'] = (int)($this->customAppOrder[$id]['order'] ?? $entry['order'] ?? 100);
109+
// Set order from user defined app order, then the default app order.
110+
// The default order is skipped for users that sorted the apps themselves,
111+
// so a newly installed app does not jump to the front of their order.
112+
$defaultOrder = $this->customAppOrder === [] ? (self::DEFAULT_APP_ORDER[$id] ?? null) : null;
113+
$entry['order'] = (int)($this->customAppOrder[$id]['order'] ?? $defaultOrder ?? $entry['order'] ?? 100);
85114
}
86115

87116
$this->entries[$id] = $entry;

tests/lib/NavigationManagerTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,53 @@ function (string $userId, string $appName, string $key, mixed $default = '') use
480480
$this->assertEquals($expected, $entries);
481481
}
482482

483+
/**
484+
* Known apps get a default order, all other apps keep the order from their info.xml.
485+
*/
486+
public function testDefaultAppOrder(): void {
487+
$this->userSession->method('isLoggedIn')->willReturn(false);
488+
$this->appManager->method('getEnabledApps')->willReturn([]);
489+
$this->appManager->method('isEnabledForUser')->willReturn(true);
490+
491+
// order as shipped by the apps themselves
492+
$apps = ['circles' => 80, 'activity' => 1, 'other' => 2, 'spreed' => -5, 'files' => 0, 'dashboard' => -10];
493+
foreach ($apps as $id => $order) {
494+
$this->navigationManager->add(['id' => $id, 'name' => $id, 'href' => '/', 'order' => $order]);
495+
}
496+
497+
$this->assertSame(
498+
['dashboard', 'files', 'spreed', 'circles', 'activity', 'other'],
499+
array_keys($this->navigationManager->getAll()),
500+
);
501+
}
502+
503+
/**
504+
* Users that sorted the apps themselves keep their order, also for apps they never sorted.
505+
*/
506+
public function testDefaultAppOrderIsSkippedForCustomOrder(): void {
507+
$user = $this->createMock(IUser::class);
508+
$user->method('getUID')->willReturn('user001');
509+
$this->userSession->method('getUser')->willReturn($user);
510+
$this->userSession->method('isLoggedIn')->willReturn(true);
511+
$this->appManager->method('getEnabledAppsForUser')->willReturn([]);
512+
$this->appManager->method('isEnabledForUser')->willReturn(true);
513+
$this->config->method('getUserValue')
514+
->willReturnCallback(static function (string $userId, string $appName, string $key, mixed $default = '') {
515+
return $key === 'apporder' ? json_encode(['other' => ['app' => 'other', 'order' => 0]]) : $default;
516+
});
517+
518+
// `circles` is not part of the user order, so it keeps the order from its info.xml
519+
// instead of moving to the front of the user order
520+
foreach (['other' => 2, 'circles' => 80] as $id => $order) {
521+
$this->navigationManager->add(['id' => $id, 'name' => $id, 'href' => '/', 'order' => $order]);
522+
}
523+
524+
$this->assertSame(
525+
['other', 'circles'],
526+
array_keys($this->navigationManager->getAll()),
527+
);
528+
}
529+
483530
/**
484531
* Navigation entries of enabled apps that are not booted yet must not be resolved.
485532
*/

0 commit comments

Comments
 (0)