Skip to content

Commit

Permalink
Let apps toggle an unread counter on app icons
Browse files Browse the repository at this point in the history
Co-authored-by: Louis Chemineau <[email protected]>
Signed-off-by: Julius Härtl <[email protected]>
  • Loading branch information
juliusknorr and artonge committed Jun 16, 2021
1 parent 51add75 commit a0f081d
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 22 deletions.
19 changes: 19 additions & 0 deletions core/css/header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,25 @@ nav[role='navigation'] {
}
}

.unread-counter {
display: none;
}
#apps .app-icon-notification,
#appmenu .app-icon-notification {
fill: var(--color-error);
}

#apps svg:not(.has-unread),
#appmenu svg:not(.has-unread) {
.app-icon-notification-mask {
display: none;
}
.app-icon-notification {
display: none;
}
}


/* Skip navigation links – show only on keyboard focus */
.skip-navigation {
padding: 11px;
Expand Down
2 changes: 1 addition & 1 deletion core/js/dist/main.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion core/js/dist/main.js.map

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions core/src/components/MainMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,26 @@ import OC from '../OC'
* If the screen is bigger, the main menu is not a toggle any more.
*/
export const setUp = () => {

Object.assign(OC, {
setNavigationCounter(id, counter) {
const appmenuElement = document.getElementById('appmenu').querySelector('[data-id="' + id + '"] svg')
const appsElement = document.getElementById('apps').querySelector('[data-id="' + id + '"] svg')
if (counter === 0) {
appmenuElement.classList.remove('has-unread')
appsElement.classList.remove('has-unread')
appmenuElement.getElementsByTagName('image')[0].style.mask = ''
appsElement.getElementsByTagName('image')[0].style.mask = ''
} else {
appmenuElement.classList.add('has-unread')
appsElement.classList.add('has-unread')
appmenuElement.getElementsByTagName('image')[0].style.mask = 'url(#hole)'
appsElement.getElementsByTagName('image')[0].style.mask = 'url(#hole)'
}
document.getElementById('appmenu').querySelector('[data-id="' + id + '"] .unread-counter').textContent = counter
document.getElementById('apps').querySelector('[data-id="' + id + '"] .unread-counter').textContent = counter
},
})
// init the more-apps menu
OC.registerMenu($('#more-apps > a'), $('#navigation'))

Expand Down
32 changes: 23 additions & 9 deletions core/templates/layout.user.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,18 @@
<a href="<?php print_unescaped($entry['href']); ?>"
<?php if ($entry['active']): ?> class="active"<?php endif; ?>
aria-label="<?php p($entry['name']); ?>">
<svg width="20" height="20" viewBox="0 0 20 20" alt="">
<?php if ($_['themingInvertMenu']) { ?>
<defs><filter id="invertMenuMain-<?php p($entry['id']); ?>"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /></filter></defs>
<?php } ?>
<image x="0" y="0" width="20" height="20" preserveAspectRatio="xMinYMin meet"<?php if ($_['themingInvertMenu']) { ?> filter="url(#invertMenuMain-<?php p($entry['id']); ?>)"<?php } ?> xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" class="app-icon"></image>
<svg width="24" height="20" viewBox="0 0 24 20" alt=""<?php if ($entry['unread'] !== 0) { ?> class="has-unread"<?php } ?>>
<defs>
<?php if ($_['themingInvertMenu']) { ?><filter id="invertMenuMain-<?php p($entry['id']); ?>"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /></filter><?php } ?>
<mask id="hole">
<rect width="100%" height="100%" fill="white"/>
<circle r="4.5" cx="21" cy="3" fill="black"/>
</mask>
</defs>
<image x="2" y="0" width="20" height="20" preserveAspectRatio="xMinYMin meet"<?php if ($_['themingInvertMenu']) { ?> filter="url(#invertMenuMain-<?php p($entry['id']); ?>)"<?php } ?> xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" style="<?php if ($entry['unread'] !== 0) { ?>mask: url("#hole");<?php } ?>" class="app-icon"></image>
<circle class="app-icon-notification" r="3" cx="21" cy="3" fill="red"/>
</svg>
<div class="unread-counter" aria-hidden="true"><?php p($entry['unread']); ?></div>
<span>
<?php p($entry['name']); ?>
</span>
Expand All @@ -87,11 +93,19 @@
<a href="<?php print_unescaped($entry['href']); ?>"
<?php if ($entry['active']): ?> class="active"<?php endif; ?>
aria-label="<?php p($entry['name']); ?>">
<svg width="16" height="16" viewBox="0 0 16 16" alt="">
<defs><filter id="invertMenuMore-<?php p($entry['id']); ?>"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"></feColorMatrix></filter></defs>
<image x="0" y="0" width="16" height="16" preserveAspectRatio="xMinYMin meet" filter="url(#invertMenuMore-<?php p($entry['id']); ?>)" xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" class="app-icon"></image>
<svg width="20" height="20" viewBox="0 0 20 20" alt=""<?php if ($entry['unread'] !== 0) { ?> class="has-unread"<?php } ?>>
<defs>
<filter id="invertMenuMore-<?php p($entry['id']); ?>"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"></feColorMatrix></filter>
<mask id="hole">
<rect width="100%" height="100%" fill="white"/>
<circle r="4.5" cx="17" cy="3" fill="black"/>
</mask>
</defs>
<image x="0" y="0" width="16" height="16" preserveAspectRatio="xMinYMin meet" filter="url(#invertMenuMore-<?php p($entry['id']); ?>)" xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" style="<?php if ($entry['unread'] !== 0) { ?>mask: url("#hole");<?php } ?>" class="app-icon"></image>
<circle class="app-icon-notification" r="3" cx="17" cy="3" fill="red"/>
</svg>
<span><?php p($entry['name']); ?></span>
<div class="unread-counter" aria-hidden="true"><?php p($entry['unread']); ?></div>
<span class="app-title"><?php p($entry['name']); ?></span>
</a>
</li>
<?php endforeach; ?>
Expand Down
12 changes: 11 additions & 1 deletion lib/private/NavigationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class NavigationManager implements INavigationManager {
protected $entries = [];
protected $closureEntries = [];
protected $activeEntry;
protected $unreadCounters = [];

/** @var bool */
protected $init = false;
/** @var IAppManager|AppManager */
Expand Down Expand Up @@ -97,7 +99,11 @@ public function add($entry) {
if (!isset($entry['type'])) {
$entry['type'] = 'link';
}
$this->entries[$entry['id']] = $entry;

$id = $entry['id'];
$entry['unread'] = isset($this->unreadCounters[$id]) ? $this->unreadCounters[$id] : 0;

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

/**
Expand Down Expand Up @@ -319,4 +325,8 @@ private function isSubadmin() {
}
return false;
}

public function setUnreadCounter(string $id, int $unreadCounter): void {
$this->unreadCounters[$id] = $unreadCounter;
}
}
9 changes: 9 additions & 0 deletions lib/public/INavigationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,13 @@ public function getActiveEntry();
* @since 14.0.0
*/
public function getAll(string $type = self::TYPE_APPS): array;

/**
* Set an unread counter for navigation entries
*
* @param string $id id of the navigation entry
* @param int $unreadCounter Number of unread entries (0 to hide the counter which is the default)
* @since 22.0.0
*/
public function setUnreadCounter(string $id, int $unreadCounter): void;
}
Loading

0 comments on commit a0f081d

Please sign in to comment.