Overhauled user notification settings, unified CSS
authorAlexander Ebert <ebert@woltlab.com>
Sat, 20 Jun 2015 09:38:56 +0000 (11:38 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Sat, 20 Jun 2015 09:38:56 +0000 (11:38 +0200)
com.woltlab.wcf/templates/notificationSettings.tpl
wcfsetup/install/files/acp/templates/booleanOptionType.tpl
wcfsetup/install/files/acp/templates/userGroupBooleanOptionType.tpl
wcfsetup/install/files/js/WoltLab/WCF/Controller/User/Notification/Settings.js [new file with mode: 0644]
wcfsetup/install/files/js/WoltLab/WCF/UI/Dropdown/Simple.js
wcfsetup/install/files/style/form.less
wcfsetup/install/files/style/user.less
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 98ba80b80abcd19c873a6b095cfa3e51aedab9c3..1da6aeca6314de89b08132488d449fa5bcf6c9d2 100644 (file)
@@ -6,20 +6,15 @@
        {include file='headInclude'}
        
        <script data-relocate="true">
-               //<![CDATA[
-               $(function() {
-                       $('#notificationSettings > fieldset > dl > dd > label > input').each(function(index, value) {
-                               var $input = $(value);
-                               $input.on('click', function(event) {
-                                       var $input = $(event.currentTarget);
-                                       $input.parents('dd').find('.jsMailNotificationType').toggle();
-                               });
-                               if (!$input.is(':checked')) {
-                                       $input.parents('dd').find('.jsMailNotificationType').hide();
-                               }
+               require(['Language', 'WoltLab/WCF/Controller/User/Notification/Settings'], function(Language, ControllerUserNotificationSettings) {
+                       Language.addObject({
+                               'wcf.user.notification.mailNotificationType.daily': '{lang}wcf.user.notification.mailNotificationType.daily{/lang}',
+                               'wcf.user.notification.mailNotificationType.instant': '{lang}wcf.user.notification.mailNotificationType.instant{/lang}',
+                               'wcf.user.notification.mailNotificationType.none': '{lang}wcf.user.notification.mailNotificationType.none{/lang}'
                        });
+                       
+                       ControllerUserNotificationSettings.setup();
                });
-               //]]>
        </script>
 </head>
 
@@ -31,6 +26,7 @@
 
 <header class="boxHeadline">
        <h1>{lang}wcf.user.menu.settings{/lang}: {lang}wcf.user.notification.notifications{/lang}</h1>
+       <p>{lang}wcf.user.notification.notifications.description{/lang}
 </header>
 
 {include file='userNotice'}
                                
                                <dl>
                                        {foreach from=$eventList item=event}
+                                               <dt>{lang}wcf.user.notification.{$event->objectType}.{$event->eventName}{/lang}</dt>
                                                <dd>
-                                                       <label><input type="checkbox" name="settings[{@$event->eventID}][enabled]" value="1"{if !$settings[$event->eventID][enabled]|empty} checked="checked"{/if} /> {lang}wcf.user.notification.{$event->objectType}.{$event->eventName}{/lang}</label>
-                                                       {hascontent}<small>{content}{lang __optional=true}wcf.user.notification.{$event->objectType}.{$event->eventName}.description{/lang}{/content}</small>{/hascontent}
-                                                       {if $event->supportsEmailNotification()}
-                                                               <small class="jsMailNotificationType">
-                                                                       <select name="settings[{@$event->eventID}][mailNotificationType]">
-                                                                               <option value="none">{lang}wcf.user.notification.mailNotificationType.none{/lang}</option>
-                                                                               <option value="instant"{if $settings[$event->eventID][mailNotificationType] == 'instant'} selected="selected"{/if}>{lang}wcf.user.notification.mailNotificationType.instant{/lang}</option>
-                                                                               <option value="daily"{if $settings[$event->eventID][mailNotificationType] == 'daily'} selected="selected"{/if}>{lang}wcf.user.notification.mailNotificationType.daily{/lang}</option>
-                                                                       </select>
-                                                               </small>
-                                                       {else}
-                                                               <small class="jsMailNotificationType">{lang}wcf.user.notification.mailNotificationType.notSupported{/lang}</small>
-                                                       {/if}
+                                                       <ol class="flexibleButtonGroup" data-object-id="{@$event->eventID}">
+                                                               <li>
+                                                                       <input type="radio" id="settings_{@$event->eventID}_disabled" name="settings[{@$event->eventID}][enabled]" value="0"{if $settings[$event->eventID][enabled]|empty} checked="checked"{/if}>
+                                                                       <label for="settings_{@$event->eventID}_disabled" class="red">
+                                                                               <span class="icon icon16 fa-times"></span>
+                                                                               {lang}wcf.user.notification.notifications.disabled{/lang}
+                                                                       </label>
+                                                               </li>
+                                                               <li class="spaceAfter">
+                                                                       <input type="radio" id="settings_{@$event->eventID}_enabled" name="settings[{@$event->eventID}][enabled]" value="1"{if !$settings[$event->eventID][enabled]|empty} checked="checked"{/if}>
+                                                                       <label for="settings_{@$event->eventID}_enabled" class="green">
+                                                                               <span class="icon icon16 fa-bell"></span>
+                                                                               {lang}wcf.user.notification.notifications.enabled{/lang}
+                                                                       </label>
+                                                               </li>
+                                                               {if $event->supportsEmailNotification()}
+                                                                       <li class="notificationSettingsEmail{if !$settings[$event->eventID][enabled]|empty} active{/if}">
+                                                                               <input type="hidden" id="settings_{$event->eventID}_mailNotificationType" name="settings[{@$event->eventID}][mailNotificationType]" value="{$settings[$event->eventID][mailNotificationType]}">
+                                                                               <a{if $settings[$event->eventID][mailNotificationType] !== 'none'} class="active yellow"{/if}>
+                                                                                       <span class="icon icon16 fa-envelope-o"></span>
+                                                                                       <span class="title">{lang}wcf.user.notification.mailNotificationType.{$settings[$event->eventID][mailNotificationType]}{/lang}</span>
+                                                                                       <span class="icon icon16 fa-caret-down"></span>
+                                                                               </a>
+                                                                       </li>
+                                                               {/if}
+                                                       </ol>
                                                </dd>
                                        {/foreach}
                                </dl>
index 22338b3f9a1212d8678277e9859d0d1e7fdeab63..f6c30b9cf73b8458753b2ec38a32d50cec53a178 100644 (file)
@@ -1,10 +1,10 @@
-<ol class="optionTypeBoolean">
+<ol class="flexibleButtonGroup">
        <li>
                <input type="radio" id="{$option->optionName}_yes"{if $value == 1} checked="checked"{/if} name="values[{$option->optionName}]" value="1"{if $disableOptions || $enableOptions} class="jsEnablesOptions" data-is-boolean="true" data-disable-options="[ {@$disableOptions}]" data-enable-options="[ {@$enableOptions}]"{/if}>
-               <label for="{$option->optionName}_yes" class="yes"><span class="icon icon16 fa-check"></span> {lang}wcf.acp.option.type.boolean.yes{/lang}</label>
+               <label for="{$option->optionName}_yes" class="green"><span class="icon icon16 fa-check"></span> {lang}wcf.acp.option.type.boolean.yes{/lang}</label>
        </li>
        <li>
                <input type="radio" id="{$option->optionName}_no"{if $value == 0} checked="checked"{/if} name="values[{$option->optionName}]" value="0"{if $disableOptions || $enableOptions} class="jsEnablesOptions" data-is-boolean="true" data-disable-options="[ {@$disableOptions}]" data-enable-options="[ {@$enableOptions}]"{/if}>
-               <label for="{$option->optionName}_no" class="no"><span class="icon icon16 fa-times"></span> {lang}wcf.acp.option.type.boolean.no{/lang}</label>
+               <label for="{$option->optionName}_no" class="red"><span class="icon icon16 fa-times"></span> {lang}wcf.acp.option.type.boolean.no{/lang}</label>
        </li>
 </ol>
index 91583a7f462936465f6b31c23817c41bbe81e2bb..787f75e8997cee37276c1d915751a350368cf526 100644 (file)
@@ -1,16 +1,16 @@
-<ol class="optionTypeBoolean">
+<ol class="flexibleButtonGroup">
        <li>
                <input type="radio" id="{$option->optionName}_yes"{if $value == 1} checked="checked"{/if} name="values[{$option->optionName}]" value="1"{if $disableOptions || $enableOptions} class="jsEnablesOptions" data-is-boolean="true" data-disable-options="[ {@$disableOptions}]" data-enable-options="[ {@$enableOptions}]"{/if}>
-               <label for="{$option->optionName}_yes" class="yes"><span class="icon icon16 fa-check"></span> {lang}wcf.acp.option.type.boolean.yes{/lang}</label>
+               <label for="{$option->optionName}_yes" class="green"><span class="icon icon16 fa-check"></span> {lang}wcf.acp.option.type.boolean.yes{/lang}</label>
        </li>
        <li>
                <input type="radio" id="{$option->optionName}_no"{if $value == 0} checked="checked"{/if} name="values[{$option->optionName}]" value="0"{if $disableOptions || $enableOptions} class="jsEnablesOptions" data-is-boolean="true" data-disable-options="[ {@$disableOptions}]" data-enable-options="[ {@$enableOptions}]"{/if}>
-               <label for="{$option->optionName}_no" class="no"><span class="icon icon16 fa-times"></span> {lang}wcf.acp.option.type.boolean.no{/lang}</label>
+               <label for="{$option->optionName}_no" class="red"><span class="icon icon16 fa-times"></span> {lang}wcf.acp.option.type.boolean.no{/lang}</label>
        </li>
        {if $group === null || !$group->isEveryone()}
                <li>
                        <input type="radio" id="{$option->optionName}_never"{if $value == -1} checked="checked"{/if} name="values[{$option->optionName}]" value="-1"{if $disableOptions || $enableOptions} class="jsEnablesOptions" data-is-boolean="true" data-disable-options="[ {@$disableOptions}]" data-enable-options="[ {@$enableOptions}]"{/if}>
-                       <label for="{$option->optionName}_never" class="never"><span class="icon icon16 fa-ban"></span> {lang}wcf.acp.option.type.boolean.never{/lang}</label>
+                       <label for="{$option->optionName}_never" class="yellow"><span class="icon icon16 fa-ban"></span> {lang}wcf.acp.option.type.boolean.never{/lang}</label>
                </li>
        {/if}
 </ol>
diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Controller/User/Notification/Settings.js b/wcfsetup/install/files/js/WoltLab/WCF/Controller/User/Notification/Settings.js
new file mode 100644 (file)
index 0000000..cdc6fbc
--- /dev/null
@@ -0,0 +1,153 @@
+/**
+ * Handles email notification type for user notification settings.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLab/WCF/Controller/User/Notification/Settings
+ */
+define(['Dictionary', 'Language', 'DOM/Traverse', 'UI/SimpleDropdown'], function(Dictionary, Language, DOMTraverse, UISimpleDropdown) {
+       "use strict";
+       
+       var _data = new Dictionary();
+       
+       var _callbackClick = null;
+       var _callbackSelectType = null;
+       
+       /**
+        * @exports     WoltLab/WCF/Controller/User/Notification/Settings
+        */
+       var ControllerUserNotificationSettings = {
+               /**
+                * Binds event listeners for all notifications supporting emails.
+                */
+               setup: function() {
+                       _callbackClick = this._click.bind(this);
+                       _callbackSelectType = this._selectType.bind(this);
+                       
+                       var group, mailSetting, groups = document.querySelectorAll('#notificationSettings .flexibleButtonGroup');
+                       for (var i = 0, length = groups.length; i < length; i++) {
+                               group = groups[i];
+                               
+                               mailSetting = group.querySelector('.notificationSettingsEmail');
+                               if (mailSetting === null) {
+                                       continue;
+                               }
+                               
+                               this._initGroup(group, mailSetting);
+                       }
+               },
+               
+               /**
+                * Initializes a setting.
+                * 
+                * @param       {Element}       group           button group element
+                * @param       {Element}       mailSetting     mail settings element
+                */
+               _initGroup: function(group, mailSetting) {
+                       var groupId = ~~group.getAttribute('data-object-id');
+                       
+                       var disabledNotification = document.getElementById('settings_' + groupId + '_disabled');
+                       disabledNotification.addEventListener('click', function() { mailSetting.classList.remove('active'); });
+                       var enabledNotification = document.getElementById('settings_' + groupId + '_enabled');
+                       enabledNotification.addEventListener('click', function() { mailSetting.classList.add('active'); });
+                       
+                       var mailValue = DOMTraverse.childByTag(mailSetting, 'INPUT');
+                       
+                       var button = DOMTraverse.childByTag(mailSetting, 'A');
+                       button.setAttribute('data-object-id', groupId);
+                       button.addEventListener('click', _callbackClick);
+                       
+                       _data.set(groupId, {
+                               button: button,
+                               dropdownMenu: null,
+                               mailSetting: mailSetting,
+                               mailValue: mailValue
+                       });
+               },
+               
+               /**
+                * Creates and displays the email type dropdown.
+                * 
+                * @param       {Object}        event           event object
+                */
+               _click: function(event) {
+                       event.preventDefault();
+                       
+                       var button = event.currentTarget;
+                       var objectId = ~~button.getAttribute('data-object-id');
+                       var data = _data.get(objectId);
+                       if (data.dropdownMenu === null) {
+                               data.dropdownMenu = this._createDropdown(objectId, data.mailValue.value);
+                               
+                               button.parentNode.classList.add('dropdown');
+                               button.parentNode.appendChild(data.dropdownMenu);
+                               
+                               UISimpleDropdown.init(button, true);
+                       }
+                       else {
+                               var items = DOMTraverse.childrenByTag(data.dropdownMenu, 'LI'), value = data.mailValue.value;
+                               for (var i = 0; i < 4; i++) {
+                                       items[i].classList[(items[i].getAttribute('data-value') === value) ? 'add' : 'remove']('active');
+                               }
+                       }
+               },
+               
+               /**
+                * Creates the email type dropdown.
+                * 
+                * @param       {integer}       objectId        notification event id
+                * @param       {string}        initialValue    initial email type
+                * @returns     {Element}       dropdown menu object
+                */
+               _createDropdown: function(objectId, initialValue) {
+                       var dropdownMenu = document.createElement('ul');
+                       dropdownMenu.className = 'dropdownMenu';
+                       dropdownMenu.setAttribute('data-object-id', objectId);
+                       
+                       var link, listItem, value, items = ['instant', 'daily', 'divider', 'none'];
+                       for (var i = 0; i < 4; i++) {
+                               value = items[i];
+                               
+                               listItem = document.createElement('li');
+                               if (value === 'divider') {
+                                       listItem.className = 'dropdownDivider';
+                               }
+                               else {
+                                       link = document.createElement('a');
+                                       link.textContent = Language.get('wcf.user.notification.mailNotificationType.' + value);
+                                       listItem.appendChild(link);
+                                       listItem.setAttribute('data-value', value);
+                                       listItem.addEventListener('click', _callbackSelectType);
+                                       
+                                       if (initialValue === value) {
+                                               listItem.className = 'active';
+                                       }
+                               }
+                               
+                               dropdownMenu.appendChild(listItem);
+                       }
+                       
+                       return dropdownMenu;
+               },
+               
+               /**
+                * Sets the selected email notification type.
+                * 
+                * @param       {Object}        event           event object
+                */
+               _selectType: function(event) {
+                       var value = event.currentTarget.getAttribute('data-value');
+                       var groupId = ~~event.currentTarget.parentNode.getAttribute('data-object-id');
+                       
+                       var data = _data.get(groupId);
+                       data.mailValue.value = value;
+                       data.mailSetting.querySelector('span.title').textContent = Language.get('wcf.user.notification.mailNotificationType.' + value);
+                       
+                       data.button.classList[(value === 'none') ? 'remove' : 'add']('yellow');
+                       data.button.classList[(value === 'none') ? 'remove' : 'add']('active');
+               }
+       };
+       
+       return ControllerUserNotificationSettings;
+});
index 1e8c4726af584ca96bc6bcf6d20c0d2efc60c3d2..ca345a62c5b19ecdb5000cc231bb13140a22821d 100644 (file)
@@ -98,7 +98,7 @@ define(
                        button.setAttribute('data-target', containerId);
                        
                        if (isLazyInitialization) {
-                               Core.triggerEvent(button, 'click');
+                               setTimeout(function() { Core.triggerEvent(button, 'click'); }, 10);
                        }
                },
                
index cace6b93a0bd1805cc715e64895d2635db9c49ec..1f653fe4c2b539fd9b5503fb8681a872be75658c 100644 (file)
@@ -619,29 +619,34 @@ select > option {
        }
 }
 
-/* BooleanUserGroupOptionType */
-.optionTypeBoolean {
-       display: flex;
+.flexibleButtonGroup {
+       display: inline-flex;
        
        > li {
                border: 1px solid @wcfContainerBorderColor;
                display: flex;
-               flex: 0 1 100px;
+               flex: 1 auto;
+               overflow: hidden;
                
                &:not(:last-child) {
                        border-right-width: 0;
                }
                
+               &.spaceAfter + li,
                &:first-child {
-                       border-radius: 3px 0 0 3px;
+                       border-bottom-left-radius: 3px;
+                       border-top-left-radius: 3px;
                }
                
+               &.spaceAfter,
                &:last-child {
-                       border-radius: 0 3px 3px 0;
+                       border-bottom-right-radius: 3px;
+                       border-top-right-radius: 3px;
                }
                
-               &:hover > label {
-                       opacity: 1 !important;
+               &.spaceAfter {
+                       border-right-width: 1px;
+                       margin-right: @wcfGapMedium;
                }
                
                > input {
@@ -653,54 +658,65 @@ select > option {
                        
                        &:checked + label {
                                opacity: 1;
-                               
-                               &.yes {
-                                       background-color: rgb(223, 240, 216);
-                                       color: rgb(60, 118, 61);
-                                       
-                                       > .icon {
-                                               color: rgb(60, 118, 61);
-                                       }
-                               }
-                               
-                               &.no {
-                                       background-color: rgb(242, 222, 222);
-                                       color: rgb(169, 68, 66);
-                                       
-                                       > .icon {
-                                               color: rgb(169, 68, 66);
-                                       }
-                               }
-                               
-                               &.never {
-                                       background-color: rgb(252, 248, 227);
-                                       color: rgb(138, 109, 59);
-                                       
-                                       > .icon {
-                                               color: rgb(138, 109, 59);
-                                       }
-                               }
                        }
                }
                
-               > label {
+               > a,
+               > label{
+                       color: @wcfColor;
                        cursor: pointer;
-                       flex: 1 auto;
                        font-size: .85rem;
-                       padding: @wcfGapTiny;
+                       opacity: .6;
+                       padding: @wcfGapTiny @wcfGapSmall;
                        text-align: center;
                        
                        transition: all .3s linear;
                        
+                       &:hover {
+                               opacity: 1 !important;
+                       }
+                       
                        > .icon {
                                cursor: pointer !important;
                        }
                }
+               
+               > a {
+                       &.active,
+                       &:hover {
+                               opacity: 1;
+                               text-decoration: none;
+                       }
+               }
+               
+               > input:checked + label.green,
+               > a.active.green {
+                       background-color: rgb(223, 240, 216);
+                       color: rgb(60, 118, 61);
+                       
+                       > .icon {
+                               color: rgb(60, 118, 61);
+                       }
+               }
+               
+               > input:checked + label.red,
+               > a.active.red {
+                       background-color: rgb(242, 222, 222);
+                       color: rgb(169, 68, 66);
+                       
+                       > .icon {
+                               color: rgb(169, 68, 66);
+                       }
+               }
+               
+               > input:checked + label.yellow,
+               > a.active.yellow {
+                       background-color: rgb(252, 248, 227);
+                       color: rgb(138, 109, 59);
+                       
+                       > .icon {
+                               color: rgb(138, 109, 59);
+                       }
+               }
        }
 }
-
-@media only screen and (max-width: 800px) {
-       .optionTypeBoolean > li {
-               flex: 1;
-       }
-}
\ No newline at end of file
index 200358c3fd0e90b3206a9077231fd4b2f121a244..8f20b11d3bd385d0e6b8a98355834d053a12ca76 100644 (file)
        }
 }
 
+/* user notification settings */
+#notificationSettings .notificationSettingsEmail {
+       &:not(.active) {
+               display: none;
+       }
+       
+       &.dropdownOpen > a {
+               opacity: 1;
+       }
+}
+
 .dashboardBoxRecentActivityHeadline {
        overflow: visible;
        position: relative;
index 246ec3c73939ca822d925512e4e02d9a1221b59b..efe52883efda710816b9adb4c8f8cb231ec8ed5c 100644 (file)
@@ -3185,6 +3185,9 @@ Möchten Sie diese E-Mail-Benachrichtigung in Zukunft nicht mehr erhalten, könn
                <item name="wcf.user.notification.noMoreNotifications"><![CDATA[Keine aktuellen Benachrichtigungen]]></item>
                <item name="wcf.user.notification.noNotifications"><![CDATA[Sie haben keine Benachrichtigungen.]]></item>
                <item name="wcf.user.notification.notifications"><![CDATA[Benachrichtigungen]]></item>
+               <item name="wcf.user.notification.notifications.description"><![CDATA[E-Mail-Benachrichtigungen werden nicht von allen verfügbaren Benachrichtigungen unterstützt.]]></item>
+               <item name="wcf.user.notification.notifications.disabled"><![CDATA[Deaktiviert]]></item>
+               <item name="wcf.user.notification.notifications.enabled"><![CDATA[Aktiviert]]></item>
                <item name="wcf.user.notification.showAll"><![CDATA[Alle Benachrichtigungen anzeigen]]></item>
                <item name="wcf.user.notification.com.woltlab.wcf.user"><![CDATA[Benutzer-Profile]]></item>
                <item name="wcf.user.notification.com.woltlab.wcf.user.follow.following"><![CDATA[Jemand folgt Ihnen]]></item>
index e4c900f41f76250e2b52eee876abc1afe49cc94a..5044b0bd43419448a5e0afb9b7d36f11a16abc92 100644 (file)
@@ -3176,6 +3176,9 @@ If you do not want to receive further email notifications for this event, you ca
                <item name="wcf.user.notification.noMoreNotifications"><![CDATA[No recent notifications]]></item>
                <item name="wcf.user.notification.noNotifications"><![CDATA[You have no notifications.]]></item>
                <item name="wcf.user.notification.notifications"><![CDATA[Notifications]]></item>
+               <item name="wcf.user.notification.notifications.description"><![CDATA[Email notifications may not be supported for every available notification]]></item>
+               <item name="wcf.user.notification.notifications.disabled"><![CDATA[Disabled]]></item>
+               <item name="wcf.user.notification.notifications.enabled"><![CDATA[Enabled]]></item>
                <item name="wcf.user.notification.showAll"><![CDATA[Show All Notifications]]></item>
                <item name="wcf.user.notification.com.woltlab.wcf.user"><![CDATA[User Profiles]]></item>
                <item name="wcf.user.notification.com.woltlab.wcf.user.follow.following"><![CDATA[New follower]]></item>