Overhaul of the user object watch button
authorMarcel Werk <burntime@woltlab.com>
Mon, 15 Aug 2022 14:18:47 +0000 (16:18 +0200)
committerMarcel Werk <burntime@woltlab.com>
Mon, 15 Aug 2022 14:18:47 +0000 (16:18 +0200)
com.woltlab.wcf/templates/__userObjectWatchButton.tpl [new file with mode: 0644]
com.woltlab.wcf/templates/categoryArticleList.tpl
ts/WoltLabSuite/Core/Ui/User/ObjectWatch.ts [new file with mode: 0644]
wcfsetup/install/files/js/WCF.User.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/User/ObjectWatch.js [new file with mode: 0644]
wcfsetup/install/files/style/ui/dropdown.scss
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

diff --git a/com.woltlab.wcf/templates/__userObjectWatchButton.tpl b/com.woltlab.wcf/templates/__userObjectWatchButton.tpl
new file mode 100644 (file)
index 0000000..4bc2afa
--- /dev/null
@@ -0,0 +1,29 @@
+{if $__wcf->user->userID}
+       <div class="dropdown contentInteractionButton">
+               <a class="jsTooltip button small dropdownToggle jsSubscribeButton userObjectWatchDropdownToggle{if $isSubscribed} active{/if}" data-object-type="{$objectType}" data-object-id="{$objectID}" data-is-subscribed="{if $isSubscribed}1{else}0{/if}">
+                       <span class="icon icon16 fa-bookmark{if !$isSubscribed}-o{/if}"></span>
+                       <span>{if $isSubscribed}{lang}wcf.user.objectWatch.button.subscribed{/lang}{else}{lang}wcf.user.objectWatch.button.subscribe{/lang}{/if}</span>
+               </a>
+               <ul class="dropdownMenu userObjectWatchDropdown" data-object-type="{$objectType}" data-object-id="{$objectID}">
+                       <li class="userObjectWatchSelect{if !$isSubscribed} active{/if}" data-subscribe="0">
+                               <span class="userObjectWatchSelectHeader">{lang}wcf.user.objectWatch.notSubscribed{/lang}</span>
+                               <span class="userObjectWatchSelectDescription">{lang}wcf.user.objectWatch.notSubscribed.description{/lang}</span>
+                       </li>
+                       <li class="userObjectWatchSelect{if $isSubscribed} active{/if}" data-subscribe="1">
+                               <span class="userObjectWatchSelectHeader">{lang}wcf.user.objectWatch.subscribed{/lang}</span>
+                               <span class="userObjectWatchSelectDescription">{lang}wcf.user.objectWatch.subscribed.description{/lang}</span>
+                       </li>
+               </ul>
+       </div>
+    
+    <script data-relocate="true">
+               require([ 'WoltLabSuite/Core/Ui/User/ObjectWatch', 'Language'], function (ObjectWatch, Language) {
+                       Language.addObject({
+                               'wcf.user.objectWatch.button.subscribe': '{jslang}wcf.user.objectWatch.button.subscribe{/jslang}',
+                'wcf.user.objectWatch.button.subscribed': '{jslang}wcf.user.objectWatch.button.subscribed{/jslang}',
+                       })
+
+                       ObjectWatch.setup();
+               });
+    </script>
+{/if}
index e2743f7e2146f87a7370dc8ec110074111bd027a..fecbe3989e9c44fd4e7d884e749b0e0b027a2582 100644 (file)
@@ -63,9 +63,8 @@
 {/capture}
 
 {capture assign='contentInteractionButtons'}
-       {if $__wcf->user->userID}
-               <a href="#" class="contentInteractionButton jsSubscribeButton jsOnly button small{if $category->isSubscribed()} active{/if}" data-object-type="com.woltlab.wcf.article.category" data-object-id="{@$category->categoryID}"><span class="icon icon16 fa-bookmark{if !$category->isSubscribed()}-o{/if}"></span> <span>{lang}wcf.user.objectWatch.button.subscribe{/lang}</span></a>
-       {/if}
+       {include file='__userObjectWatchButton' isSubscribed=$category->isSubscribed() objectType="com.woltlab.wcf.article.category" objectID=$category->categoryID}
+       
        {if ARTICLE_ENABLE_VISIT_TRACKING}
                <a href="#" class="markAllAsReadButton contentInteractionButton button small jsOnly"><span class="icon icon16 fa-check"></span> <span>{lang}wcf.global.button.markAllAsRead{/lang}</span></a>
        {/if}
        </script>
 {/if}
 
-<script data-relocate="true">
-       $(function() {
-               WCF.Language.addObject({
-                       'wcf.user.objectWatch.manageSubscription': '{jslang}wcf.user.objectWatch.manageSubscription{/jslang}'
-               });
-               
-               new WCF.User.ObjectWatch.Subscribe();
-       });
-</script>
-
 {include file='articleAddDialog'}
 
 {include file='footer'}
diff --git a/ts/WoltLabSuite/Core/Ui/User/ObjectWatch.ts b/ts/WoltLabSuite/Core/Ui/User/ObjectWatch.ts
new file mode 100644 (file)
index 0000000..8304a6d
--- /dev/null
@@ -0,0 +1,93 @@
+/**
+ * Handles the object watch button.
+ *
+ * @author     Marcel Werk
+ * @copyright  2001-2022 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLabSuite/Core/Ui/User/ObjectWatch
+ * @since 6.0
+ */
+
+import * as Ajax from "../../Ajax";
+import * as UiNotification from "../Notification";
+import * as Language from "../../Language";
+import * as EventHandler from "../../Event/Handler";
+
+const dropdowns = new Map<number, Set<HTMLElement>>();
+
+async function click(element: HTMLElement): Promise<void> {
+  const dropdown = element.closest(".userObjectWatchDropdown") as HTMLElement;
+  const subscribe = parseInt(element.dataset.subscribe!, 10);
+  const objectID = parseInt(dropdown.dataset.objectId!, 10);
+  const objectType = dropdown.dataset.objectType!;
+
+  await Ajax.dboAction("saveSubscription", "wcf\\data\\user\\object\\watch\\UserObjectWatchAction")
+    .payload({
+      enableNotification: 1,
+      objectID,
+      objectType,
+      subscribe,
+    })
+    .dispatch();
+
+  if (dropdowns.has(objectID)) {
+    dropdowns.get(objectID)!.forEach((element) => {
+      element.querySelectorAll(".userObjectWatchSelect").forEach((li) => {
+        li.classList.remove("active");
+      });
+
+      element.querySelector(`.userObjectWatchSelect[data-subscribe="${subscribe}"]`)?.classList.add("active");
+    });
+  }
+
+  document
+    .querySelectorAll(`.userObjectWatchDropdownToggle[data-object-type="${objectType}"][data-object-id="${objectID}"]`)
+    .forEach((element: HTMLElement) => {
+      const icon = element.querySelector(".icon")!;
+      const label = element.querySelector("span:not(.icon)")!;
+
+      if (subscribe) {
+        element.classList.add("active");
+        icon.classList.remove("fa-bookmark-o");
+        icon.classList.add("fa-bookmark");
+        label.textContent = Language.get(`wcf.user.objectWatch.button.subscribed`);
+      } else {
+        element.classList.remove("active");
+        icon.classList.remove("fa-bookmark");
+        icon.classList.add("fa-bookmark-o");
+        label.textContent = Language.get("wcf.user.objectWatch.button.subscribe");
+      }
+
+      element.dataset.isSubscribed = subscribe.toString();
+    });
+
+  EventHandler.fire("com.woltlab.wcf.objectWatch", "updatedSubscription");
+  UiNotification.show();
+}
+
+export function setup(): void {
+  document.querySelectorAll(".userObjectWatchDropdown").forEach((element: HTMLElement) => {
+    if (!element.dataset.objectId) {
+      throw new Error("Missing objectId for '.userObjectWatchDropdown' element.");
+    }
+
+    const objectId = parseInt(element.dataset.objectId, 10);
+
+    if (!dropdowns.has(objectId)) {
+      dropdowns.set(objectId, new Set());
+    }
+
+    dropdowns.get(objectId)!.add(element);
+
+    element.querySelectorAll(".userObjectWatchSelect").forEach((element: HTMLElement) => {
+      if (!element.dataset.subscribe) {
+        throw new Error("Missing 'data-subscribe' attribute for '.userObjectWatchSelect' element.");
+      }
+
+      element.addEventListener("click", (event) => {
+        event.preventDefault();
+        void click(element);
+      });
+    });
+  });
+}
index 63fdce1837b85aef3916e2f500ec37fbb7ab9773..0bd48341ddce77b87a9b15a3a375b1600e47d906 100644 (file)
@@ -2072,6 +2072,8 @@ WCF.User.ObjectWatch = {};
 if (COMPILER_TARGET_DEFAULT) {
        /**
         * Handles subscribe/unsubscribe links.
+        * 
+        * @deprecated  since 6.0, use `WoltLabSuite/Core/Ui/User/ObjectWatch` instead.
         */
        WCF.User.ObjectWatch.Subscribe = Class.extend({
                /**
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/User/ObjectWatch.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/User/ObjectWatch.js
new file mode 100644 (file)
index 0000000..c9beb67
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * Handles the object watch button.
+ *
+ * @author     Marcel Werk
+ * @copyright  2001-2022 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLabSuite/Core/Ui/User/ObjectWatch
+ * @since 6.0
+ */
+define(["require", "exports", "tslib", "../../Ajax", "../Notification", "../../Language", "../../Event/Handler"], function (require, exports, tslib_1, Ajax, UiNotification, Language, EventHandler) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.setup = void 0;
+    Ajax = tslib_1.__importStar(Ajax);
+    UiNotification = tslib_1.__importStar(UiNotification);
+    Language = tslib_1.__importStar(Language);
+    EventHandler = tslib_1.__importStar(EventHandler);
+    const dropdowns = new Map();
+    async function click(element) {
+        const dropdown = element.closest(".userObjectWatchDropdown");
+        const subscribe = parseInt(element.dataset.subscribe, 10);
+        const objectID = parseInt(dropdown.dataset.objectId, 10);
+        const objectType = dropdown.dataset.objectType;
+        await Ajax.dboAction("saveSubscription", "wcf\\data\\user\\object\\watch\\UserObjectWatchAction")
+            .payload({
+            enableNotification: 1,
+            objectID,
+            objectType,
+            subscribe,
+        })
+            .dispatch();
+        if (dropdowns.has(objectID)) {
+            dropdowns.get(objectID).forEach((element) => {
+                var _a;
+                element.querySelectorAll(".userObjectWatchSelect").forEach((li) => {
+                    li.classList.remove("active");
+                });
+                (_a = element.querySelector(`.userObjectWatchSelect[data-subscribe="${subscribe}"]`)) === null || _a === void 0 ? void 0 : _a.classList.add("active");
+            });
+        }
+        document
+            .querySelectorAll(`.userObjectWatchDropdownToggle[data-object-type="${objectType}"][data-object-id="${objectID}"]`)
+            .forEach((element) => {
+            const icon = element.querySelector(".icon");
+            const label = element.querySelector("span:not(.icon)");
+            if (subscribe) {
+                element.classList.add("active");
+                icon.classList.remove("fa-bookmark-o");
+                icon.classList.add("fa-bookmark");
+                label.textContent = Language.get(`wcf.user.objectWatch.button.subscribed`);
+            }
+            else {
+                element.classList.remove("active");
+                icon.classList.remove("fa-bookmark");
+                icon.classList.add("fa-bookmark-o");
+                label.textContent = Language.get("wcf.user.objectWatch.button.subscribe");
+            }
+            element.dataset.isSubscribed = subscribe.toString();
+        });
+        EventHandler.fire("com.woltlab.wcf.objectWatch", "updatedSubscription");
+        UiNotification.show();
+    }
+    function setup() {
+        document.querySelectorAll(".userObjectWatchDropdown").forEach((element) => {
+            if (!element.dataset.objectId) {
+                throw new Error("Missing objectId for '.userObjectWatchDropdown' element.");
+            }
+            const objectId = parseInt(element.dataset.objectId, 10);
+            if (!dropdowns.has(objectId)) {
+                dropdowns.set(objectId, new Set());
+            }
+            dropdowns.get(objectId).add(element);
+            element.querySelectorAll(".userObjectWatchSelect").forEach((element) => {
+                if (!element.dataset.subscribe) {
+                    throw new Error("Missing 'data-subscribe' attribute for '.userObjectWatchSelect' element.");
+                }
+                element.addEventListener("click", (event) => {
+                    event.preventDefault();
+                    void click(element);
+                });
+            });
+        });
+    }
+    exports.setup = setup;
+});
index c1b33f9afdc3e82f225878f57cc66ab06f8d19ad..6360a227d09b0194481dfe061921355b3573bcb8 100644 (file)
                display: inline-flex !important;
        }
 }
+
+.userObjectWatchSelect {
+       .userObjectWatchSelectHeader {
+        font-weight: 600;
+               padding-bottom: 0;
+       }
+
+       .userObjectWatchSelectDescription {
+               @include wcfFontSmall;
+
+               color: $wcfContentDimmedText;
+               padding-top: 0;
+               white-space: normal;
+       }
+}
index 1d1cbafdc64a2631787b4066b99eecb35aaca2cb..0577afa07dc28b150a30e552eebb5e60cd55ec57 100644 (file)
@@ -5467,6 +5467,11 @@ Benachrichtigungen auf <a href="{link isHtmlEmail=true}{/link}">{PAGE_TITLE|phra
                <item name="wcf.user.objectWatch.subscribe.com.woltlab.wcf.article.category"><![CDATA[Kategorie abonnieren]]></item>
                <item name="wcf.user.objectWatch.enableNotification.com.woltlab.wcf.article.category"><![CDATA[Benachrichtigung über neue Artikel aus dieser Kategorie aktivieren]]></item>
                <item name="wcf.user.objectWatch.unsubscribe.com.woltlab.wcf.article.category"><![CDATA[Kategorie nicht abonnieren]]></item>
+               <item name="wcf.user.objectWatch.button.subscribed"><![CDATA[Abonniert]]></item>
+               <item name="wcf.user.objectWatch.notSubscribed"><![CDATA[Nicht abonniert]]></item>
+               <item name="wcf.user.objectWatch.notSubscribed.description"><![CDATA[Erhalte{if !LANGUAGE_USE_INFORMAL_VARIANT}n Sie{/if} Benachrichtigungen für Erwähnungen, Zitate und Reaktionen.]]></item>
+               <item name="wcf.user.objectWatch.subscribed"><![CDATA[Abonniert]]></item>
+               <item name="wcf.user.objectWatch.subscribed.description"><![CDATA[Erhalte{if !LANGUAGE_USE_INFORMAL_VARIANT}n Sie{/if} Benachrichtigungen über neue Inhalte, Erwähnungen, Zitate und Reaktionen.]]></item>
        </category>
        <category name="wcf.user.option">
                <item name="wcf.user.option.aboutMe"><![CDATA[Über mich]]></item>
index b7172fd06d0fb0d0cc0e5325dc01c5a73f14e991..5e4ffffcf587358afdadb1edce74d8525a54d182 100644 (file)
@@ -5469,6 +5469,11 @@ your notifications on <a href="{link isHtmlEmail=true}{/link}">{PAGE_TITLE|phras
                <item name="wcf.user.objectWatch.subscribe.com.woltlab.wcf.article.category"><![CDATA[Watch this category]]></item>
                <item name="wcf.user.objectWatch.enableNotification.com.woltlab.wcf.article.category"><![CDATA[Notify me of new articles.]]></item>
                <item name="wcf.user.objectWatch.unsubscribe.com.woltlab.wcf.article.category"><![CDATA[Unwatch this category]]></item>
+               <item name="wcf.user.objectWatch.button.subscribed"><![CDATA[Subscribed]]></item>
+               <item name="wcf.user.objectWatch.notSubscribed"><![CDATA[Not subscribed]]></item>
+               <item name="wcf.user.objectWatch.notSubscribed.description"><![CDATA[Receive notifications for mentions, quotes and reactions.]]></item>
+               <item name="wcf.user.objectWatch.subscribed"><![CDATA[Subscribed]]></item>
+               <item name="wcf.user.objectWatch.subscribed.description"><![CDATA[Receive notifications for new content, mentions, quotes and reactions.]]></item>
        </category>
        <category name="wcf.user.option">
                <item name="wcf.user.option.aboutMe"><![CDATA[About Me]]></item>