Skip to content

Commit f86c8c3

Browse files
committed
feat(Contexts): dynamic navigation bar update on config toggle
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
1 parent 3d3a2de commit f86c8c3

File tree

6 files changed

+109
-66
lines changed

6 files changed

+109
-66
lines changed

appinfo/routes.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@
121121
['name' => 'search#all', 'url' => '/search/all', 'verb' => 'GET'],
122122
],
123123
'ocs' => [
124+
['name' => 'navigation#getAppsNavigation', 'url' => '/navigation', 'verb' => 'GET'],
125+
124126
// API v2
125127
['name' => 'ApiGeneral#index', 'url' => '/api/2/init', 'verb' => 'GET'],
126128
// -> tables
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace OCA\Tables\Controller;
4+
5+
use OCA\Tables\Service\ContextService;
6+
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
7+
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
8+
use OCP\AppFramework\Http\Attribute\OpenAPI;
9+
use OCP\AppFramework\Http\DataResponse;
10+
use OCP\INavigationManager;
11+
use OCP\IRequest;
12+
use OCP\IURLGenerator;
13+
use OCP\IUserSession;
14+
15+
/**
16+
* This is a workaround until https://github.com/nextcloud/server/pull/49904 is
17+
* settled in all covered NC versions; expected >= 31.
18+
*/
19+
class NavigationController extends \OC\Core\Controller\NavigationController {
20+
public function __construct(
21+
protected ContextService $contextService,
22+
protected IUserSession $userSession,
23+
string $appName,
24+
IRequest $request,
25+
INavigationManager $navigationManager,
26+
IURLGenerator $urlGenerator
27+
) {
28+
parent::__construct($appName, $request, $navigationManager, $urlGenerator);
29+
}
30+
31+
#[NoAdminRequired]
32+
#[NoCSRFRequired]
33+
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
34+
public function getAppsNavigation(bool $absolute = false): DataResponse {
35+
$this->contextService->addToNavigation($this->userSession->getUser()?->getUID());
36+
return parent::getAppsNavigation($absolute);
37+
}
38+
}

lib/Listener/BeforeTemplateRenderedListener.php

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,19 @@
77

88
namespace OCA\Tables\Listener;
99

10-
use OCA\Tables\AppInfo\Application;
1110
use OCA\Tables\Service\ContextService;
1211
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
1312
use OCP\EventDispatcher\Event;
1413
use OCP\EventDispatcher\IEventListener;
15-
use OCP\INavigationManager;
16-
use OCP\IURLGenerator;
1714
use OCP\IUserSession;
1815

1916
/**
2017
* @template-implements IEventListener<Event|BeforeTemplateRenderedEvent>
2118
*/
2219
class BeforeTemplateRenderedListener implements IEventListener {
2320
public function __construct(
24-
protected INavigationManager $navigationManager,
25-
protected IURLGenerator $urlGenerator,
26-
protected IUserSession $userSession,
27-
protected ContextService $contextService,
21+
protected IUserSession $userSession,
22+
protected ContextService $contextService,
2823
) {
2924
}
3025

@@ -41,27 +36,6 @@ public function handle(Event $event): void {
4136
return;
4237
}
4338

44-
$contexts = $this->contextService->findForNavigation($user->getUID());
45-
foreach ($contexts as $context) {
46-
$this->navigationManager->add(function () use ($context) {
47-
$iconRelPath = 'material/' . $context->getIcon() . '.svg';
48-
if (file_exists(__DIR__ . '/../../img/' . $iconRelPath)) {
49-
$iconUrl = $this->urlGenerator->imagePath(Application::APP_ID, $iconRelPath);
50-
} else {
51-
$iconUrl = $this->urlGenerator->imagePath('core', 'places/default-app-icon.svg');
52-
}
53-
54-
$contextUrl = $this->urlGenerator->linkToRoute('tables.page.context', ['contextId' => $context->getId()]);
55-
56-
return [
57-
'id' => Application::APP_ID . '_application_' . $context->getId(),
58-
'name' => $context->getName(),
59-
'href' => $contextUrl,
60-
'icon' => $iconUrl,
61-
'order' => 500,
62-
'type' => 'link',
63-
];
64-
});
65-
}
39+
$this->contextService->addToNavigation($user->getUID());
6640
}
6741
}

lib/Service/ContextService.php

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -27,49 +27,31 @@
2727
use OCP\DB\Exception;
2828
use OCP\EventDispatcher\IEventDispatcher;
2929
use OCP\IDBConnection;
30+
use OCP\INavigationManager;
31+
use OCP\IURLGenerator;
3032
use OCP\IUserManager;
3133
use OCP\Log\Audit\CriticalActionPerformedEvent;
3234
use Psr\Log\LoggerInterface;
3335

3436
class ContextService {
3537

36-
private ContextMapper $contextMapper;
37-
private bool $isCLI;
38-
private LoggerInterface $logger;
39-
private ContextNodeRelationMapper $contextNodeRelMapper;
40-
private PageMapper $pageMapper;
41-
private PageContentMapper $pageContentMapper;
42-
private PermissionsService $permissionsService;
43-
private IUserManager $userManager;
44-
private IEventDispatcher $eventDispatcher;
45-
private IDBConnection $dbc;
46-
private ShareService $shareService;
47-
4838
public function __construct(
49-
ContextMapper $contextMapper,
50-
ContextNodeRelationMapper $contextNodeRelationMapper,
51-
PageMapper $pageMapper,
52-
PageContentMapper $pageContentMapper,
53-
LoggerInterface $logger,
54-
PermissionsService $permissionsService,
55-
IUserManager $userManager,
56-
IEventDispatcher $eventDispatcher,
57-
IDBConnection $dbc,
58-
ShareService $shareService,
59-
bool $isCLI,
39+
private ContextMapper $contextMapper,
40+
private ContextNodeRelationMapper $contextNodeRelMapper,
41+
private PageMapper $pageMapper,
42+
private PageContentMapper $pageContentMapper,
43+
private LoggerInterface $logger,
44+
private PermissionsService $permissionsService,
45+
private IUserManager $userManager,
46+
private IEventDispatcher $eventDispatcher,
47+
private IDBConnection $dbc,
48+
private ShareService $shareService,
49+
private bool $isCLI,
50+
protected INavigationManager $navigationManager,
51+
protected IURLGenerator $urlGenerator,
6052
) {
61-
$this->contextMapper = $contextMapper;
62-
$this->isCLI = $isCLI;
63-
$this->logger = $logger;
64-
$this->contextNodeRelMapper = $contextNodeRelationMapper;
65-
$this->pageMapper = $pageMapper;
66-
$this->pageContentMapper = $pageContentMapper;
67-
$this->permissionsService = $permissionsService;
68-
$this->userManager = $userManager;
69-
$this->eventDispatcher = $eventDispatcher;
70-
$this->dbc = $dbc;
71-
$this->shareService = $shareService;
7253
}
54+
7355
use TTransactional;
7456

7557
/**
@@ -93,6 +75,31 @@ public function findForNavigation(string $userId): array {
9375
return $this->contextMapper->findForNavBar($userId);
9476
}
9577

78+
public function addToNavigation(string $userId): void {
79+
$contexts = $this->findForNavigation($userId);
80+
foreach ($contexts as $context) {
81+
$this->navigationManager->add(function () use ($context) {
82+
$iconRelPath = 'material/' . $context->getIcon() . '.svg';
83+
if (file_exists(__DIR__ . '/../../img/' . $iconRelPath)) {
84+
$iconUrl = $this->urlGenerator->imagePath(Application::APP_ID, $iconRelPath);
85+
} else {
86+
$iconUrl = $this->urlGenerator->imagePath('core', 'places/default-app-icon.svg');
87+
}
88+
89+
$contextUrl = $this->urlGenerator->linkToRoute('tables.page.context', ['contextId' => $context->getId()]);
90+
91+
return [
92+
'id' => Application::APP_ID . '_application_' . $context->getId(),
93+
'name' => $context->getName(),
94+
'href' => $contextUrl,
95+
'icon' => $iconUrl,
96+
'order' => 500,
97+
'type' => 'link',
98+
];
99+
});
100+
}
101+
}
102+
96103
/**
97104
* @throws Exception
98105
* @throws InternalError

src/modules/navigation/partials/NavigationContextItem.vue

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import Delete from 'vue-material-design-icons/Delete.vue'
5353
import permissionsMixin from '../../../shared/components/ncTable/mixins/permissionsMixin.js'
5454
import svgHelper from '../../../shared/components/ncIconPicker/mixins/svgHelper.js'
5555
import { NAV_ENTRY_MODE } from '../../../shared/constants.js'
56+
import rebuildNavigation from '../../../service/rebuild-navigation.js'
5657
5758
export default {
5859
name: 'NavigationContextItem',
@@ -115,7 +116,7 @@ export default {
115116
}
116117
return false
117118
},
118-
updateDisplayMode() {
119+
async updateDisplayMode() {
119120
const value = !this.showInNavigation
120121
const displayMode = value ? NAV_ENTRY_MODE.NAV_ENTRY_MODE_ALL : NAV_ENTRY_MODE.NAV_ENTRY_MODE_HIDDEN
121122
let hadAtLeastOneEntry = false
@@ -124,10 +125,12 @@ export default {
124125
continue
125126
}
126127
hadAtLeastOneEntry = true
127-
this.$store.dispatch('updateDisplayMode', { shareId: share.share_id, displayMode, target: 'self' })
128+
await this.$store.dispatch('updateDisplayMode', { shareId: share.share_id, displayMode, target: 'self' })
128129
this.showInNavigation = value
129130
}
130-
if (!hadAtLeastOneEntry) {
131+
if (hadAtLeastOneEntry) {
132+
await rebuildNavigation()
133+
} else {
131134
console.error('No share found ' + this.context.sharing)
132135
}
133136
},

src/service/rebuild-navigation.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
import axios from '@nextcloud/axios'
6+
import { generateOcsUrl } from '@nextcloud/router'
7+
import { emit } from '@nextcloud/event-bus'
8+
9+
export default () => {
10+
return axios.get(generateOcsUrl('apps/tables/navigation') + '?format=json')
11+
.then(({ data }) => {
12+
if (data.ocs.meta.statuscode !== 200) {
13+
return
14+
}
15+
16+
emit('nextcloud:app-menu.refresh', { apps: data.ocs.data })
17+
window.dispatchEvent(new Event('resize'))
18+
})
19+
}

0 commit comments

Comments
 (0)