From 7c65ced2267b04e049cbd05f90a8c71ace9e1347 Mon Sep 17 00:00:00 2001 From: Moritz Strohm <strohm@data-quest.de> Date: Wed, 19 Oct 2022 13:09:10 +0000 Subject: [PATCH] fix for BIESt 109, closes #109 Closes #109 Merge request studip/studip!832 --- .../bootstrap/studip_helper_attributes.js | 18 +++- resources/assets/javascripts/lib/gettext.js | 3 +- .../javascripts/lib/personal_notifications.js | 13 ++- .../less/personal-notifications.less | 82 ++++++++++--------- templates/header.php | 11 ++- .../personal_notifications/notification.php | 28 ++++--- 6 files changed, 89 insertions(+), 66 deletions(-) diff --git a/resources/assets/javascripts/bootstrap/studip_helper_attributes.js b/resources/assets/javascripts/bootstrap/studip_helper_attributes.js index a733638a91d..19d78867c94 100644 --- a/resources/assets/javascripts/bootstrap/studip_helper_attributes.js +++ b/resources/assets/javascripts/bootstrap/studip_helper_attributes.js @@ -237,9 +237,19 @@ $(document).on('keydown', '.enter-accessible', function(event) { } }); -$(document).on('click', '[data-toggles]', function (event) { - const target = event.currentTarget.dataset.toggles; - $(target).toggle(); +$(document).on('click keydown', '[data-toggles]', function (event) { + if ((event.type === 'keydown' && event.key === 'Enter') || event.type === 'click') { + const target = event.currentTarget.dataset.toggles; + //Check if the target is a checkbox. These have to be toggled differently than + //other elements: + if ($(target).is(':checkbox')) { + //Toggle the checked state of the checkbox: + $(target).prop('checked', !$(target).prop('checked')); + } else { + //Do the normal toggle operation: + $(target).toggle(); + } - event.preventDefault(); + event.preventDefault(); + } }); diff --git a/resources/assets/javascripts/lib/gettext.js b/resources/assets/javascripts/lib/gettext.js index 699136fee25..1bc19b8bd2d 100644 --- a/resources/assets/javascripts/lib/gettext.js +++ b/resources/assets/javascripts/lib/gettext.js @@ -8,9 +8,10 @@ const DEFAULT_LANG_NAME = 'Deutsch'; const state = getInitialState(); const $gettext = translate.gettext.bind(translate); +const $ngettext = translate.ngettext.bind(translate); const $gettextInterpolate = translate.gettextInterpolate.bind(translate); -export { $gettext, $gettextInterpolate, translate, getLocale, setLocale, getVueConfig }; +export { $gettext, $ngettext, $gettextInterpolate, translate, getLocale, setLocale, getVueConfig }; function getLocale() { return state.locale; diff --git a/resources/assets/javascripts/lib/personal_notifications.js b/resources/assets/javascripts/lib/personal_notifications.js index 4a0a6e317e5..2729f553d85 100644 --- a/resources/assets/javascripts/lib/personal_notifications.js +++ b/resources/assets/javascripts/lib/personal_notifications.js @@ -1,6 +1,7 @@ import Favico from 'favico.js'; import Cache from './cache.js'; import PageLayout from './page_layout.js'; +import { $gettextInterpolate, $ngettext } from './gettext'; var stack = {}; var audio_notification = false; @@ -91,7 +92,7 @@ function process_notifications({ notifications }) { const PersonalNotifications = { initialize () { - if ($('#notification_marker').length > 0) { + if ($('#notification_marker .count').length > 0) { $('#notification_list .notification').map(function() { var data = $(this).data(); stack[data.id] = data; @@ -154,7 +155,7 @@ const PersonalNotifications = { }, update () { var count = _.values(stack).length; - var old_count = parseInt($('#notification_marker').text(), 10); + var old_count = parseInt($('#notification_marker .count').text(), 10); var really_new = 0; $('#notification_list > ul > li').each(function() { if (parseInt($(this).data('timestamp'), 10) > parseInt($('#notification_marker').data('lastvisit'), 10)) { @@ -172,16 +173,20 @@ const PersonalNotifications = { } if (count) { $('#notification_container').addClass('hoverable'); + $('#notification_marker').prop('disabled', false); if (count > old_count && audio_notification !== false) { audio_notification.play(); } } else { $('#notification_container').removeClass('hoverable'); + $('#notification_marker').prop('disabled', true); } if (old_count !== count) { - $('#notification_marker').text(count); + $('#notification_marker .count').text(count); + let notification_text = $ngettext('%{ count } Benachrichtigung', '%{ count } Benachrichtigungen', count); + $('#notification_marker').attr('title', $gettextInterpolate(notification_text, {count: count})); updateFavicon(count); - $('#notification_container .mark-all-as-read').toggleClass('notification_hidden', count < 2); + $('#notification_container .mark-all-as-read').toggleClass('invisible', count < 2); } }, isVisited () { diff --git a/resources/assets/stylesheets/less/personal-notifications.less b/resources/assets/stylesheets/less/personal-notifications.less index 10554a395a7..a2b849b9934 100644 --- a/resources/assets/stylesheets/less/personal-notifications.less +++ b/resources/assets/stylesheets/less/personal-notifications.less @@ -1,15 +1,14 @@ #notification_marker { - width: 48px; margin-left: 0px; padding-left: 0px; margin-right: 0px; padding-right: 0px; - height: 28px; + width: 100%; + height: 100%; font-size: 0.8em; color: @base-color; text-align: center; line-height: 28px; - vertical-align: text-bottom; background-color: @dark-gray-color-10; border: 1px solid @dark-gray-color-40; @@ -58,6 +57,15 @@ &.hoverable:hover { .list { display: block; } } + + #notification_checkbox { + display: none; + } + + &.hoverable #notification_checkbox:checked + #notification_list { + display: block; + } + #notification_list { z-index: 1001; margin-top: 10px; @@ -71,6 +79,11 @@ } } .list { + + ul { + list-style-type: none; + } + // Creates an arrow pointing from the list to the triggering element #arrow > .top-border(10px, @white, 1px, @light-gray-color-80); @@ -114,7 +127,6 @@ @padding: 5px; border-top: thin solid @light-gray-color-60; line-height: 20px; - display: block; height: auto; padding: @padding; white-space: normal; @@ -136,26 +148,33 @@ &:first-child { border-top: 0; } - .content { + + .main { display: flex; flex-direction: row; - flex-wrap: nowrap; - - .avatar { - @avatar-size: 40px; - margin-right: 10px; - margin-left: 0px; - background-position: center center; - background-size: 100%; - background-repeat: no-repeat; - width: @avatar-size; - height: @avatar-size; - min-width: @avatar-size; + + .content { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + flex-grow: 1; + + .avatar { + @avatar-size: 40px; + margin-right: 10px; + margin-left: 0; + background-position: center center; + background-size: 100%; + background-repeat: no-repeat; + width: @avatar-size; + height: @avatar-size; + min-width: @avatar-size; + } } } } - a { + a:not(.mark-all-as-read) { color: @brand-color-dark; display: block; padding: 0px; @@ -163,9 +182,11 @@ } .options { + border: 0; + background: none; cursor: pointer; - float: right; padding-top: 4px; + height: 24px; > img { vertical-align: top; } @@ -175,10 +196,11 @@ .item:hover .options.hidden { visibility: visible; } } - a.mark-all-as-read, + a.mark-all-as-read:not(.invisible), a.enable-desktop-notifications { background-color: @dark-gray-color-15; border-bottom: thin solid @dark-gray-color-45; + display: block; max-height: 31px; padding: 5px 5px 5px 14px; z-index: 3; @@ -205,26 +227,6 @@ // Create blind effect to hide/display this links smoothly transition: all 300ms; - - &.notification_hidden { - border-bottom-width: 0; - max-height: 0; - opacity: 0; - padding-bottom: 0; - padding-top: 0; - pointer-events: none; - - + .enable-desktop-notifications { - // Creates an arrow pointing from the list to the triggering element - #arrow > .top-border(10px, @light-gray-color-20, 1px, @light-gray-color-80); - &::before, - &::after { - left: (@list-width - 20px); - z-index: 2; - } - margin: 0; - } - } } a.enable-desktop-notifications { .background-icon('notification', 'clickable'); diff --git a/templates/header.php b/templates/header.php index 8f2992130ae..5d7034957df 100644 --- a/templates/header.php +++ b/templates/header.php @@ -85,11 +85,14 @@ if (isset($_COOKIE['navigation-length'])) { $alert = true; } } ?> - <div id="notification_marker"<?= $alert ? ' class="alert"' : "" ?> title="<?= _("Benachrichtigungen") ?>" data-lastvisit="<?= $lastvisit ?>"> - <?= count($notifications) ?> - </div> + <button id="notification_marker" data-toggles="#notification_checkbox" <?= !empty($alert) ? ' class="alert"' : "" ?> + title="<?= _('Benachrichtigungen') ?>" data-lastvisit="<?= $lastvisit ?>" + <?= count($notifications) == 0 ? 'disabled' : '' ?>> + <span class="count" aria-hidden="true"><?= count($notifications) ?></span> + </button> + <input type="checkbox" id="notification_checkbox"> <div class="list below" id="notification_list"> - <a class="mark-all-as-read <? if (count($notifications) < 2) echo 'notification_hidden'; ?>" href="<?= URLHelper::getLink('dispatch.php/jsupdater/mark_notification_read/all', ['return_to' => $_SERVER['REQUEST_URI']]) ?>"> + <a class="mark-all-as-read <? if (count($notifications) < 2) echo 'invisible'; ?>" href="<?= URLHelper::getLink('dispatch.php/jsupdater/mark_notification_read/all', ['return_to' => $_SERVER['REQUEST_URI']]) ?>"> <?= _('Alle Benachrichtigungen als gelesen markieren') ?> </a> <a class="enable-desktop-notifications" href="#" style="display: none;"> diff --git a/templates/personal_notifications/notification.php b/templates/personal_notifications/notification.php index 0dfc63c79dc..8a10ae91a9e 100644 --- a/templates/personal_notifications/notification.php +++ b/templates/personal_notifications/notification.php @@ -1,16 +1,18 @@ <li class="notification item" data-id="<?= $notification['personal_notification_id'] ?>" data-timestamp="<?= (int) $notification['mkdate'] ?>"> - <a class="options mark_as_read" href="#"> - <?= Icon::create('decline')->asImg(12, ['title' => _('Als gelesen markieren')]) ?> - </a> - <a class="content" href="<?= URLHelper::getLink('dispatch.php/jsupdater/mark_notification_read/' . $notification['personal_notification_id']) ?>"<?= $notification['dialog'] ? ' data-dialog' : '' ?>> - <? if ($notification['avatar']): ?> - <div class="avatar" style="background-image: url(<?= $notification['avatar'] ?>);"></div> - <? endif; ?> - <?= htmlReady($notification['text']) ?> - </a> -<? if ($notification->more_unseen > 0): ?> - <div class="more"> - <?= htmlReady(sprintf(_('... und %u weitere'), $notification->more_unseen)) ?> + <div class="main"> + <a class="content" href="<?= URLHelper::getLink('dispatch.php/jsupdater/mark_notification_read/' . $notification['personal_notification_id']) ?>"<?= $notification['dialog'] ? ' data-dialog' : '' ?>> + <? if ($notification['avatar']): ?> + <div class="avatar" style="background-image: url(<?= $notification['avatar'] ?>);"></div> + <? endif ?> + <?= htmlReady($notification['text']) ?> + </a> + <button class="options mark_as_read"> + <?= Icon::create('decline')->asImg(12, ['title' => _('Als gelesen markieren')]) ?> + </button> </div> -<? endif; ?> + <? if ($notification->more_unseen > 0): ?> + <div class="more"> + <?= htmlReady(sprintf(_('... und %u weitere'), $notification->more_unseen)) ?> + </div> + <? endif ?> </li> -- GitLab