Add option to refresh a single row
authorMarcel Werk <burntime@woltlab.com>
Tue, 17 Dec 2024 13:31:28 +0000 (14:31 +0100)
committerMarcel Werk <burntime@woltlab.com>
Tue, 17 Dec 2024 13:31:28 +0000 (14:31 +0100)
ts/WoltLabSuite/Core/Api/Gridviews/GetRow.ts [new file with mode: 0644]
ts/WoltLabSuite/Core/Component/GridView.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Api/Gridviews/GetRow.js [new file with mode: 0644]
wcfsetup/install/files/js/WoltLabSuite/Core/Component/GridView.js
wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php
wcfsetup/install/files/lib/system/endpoint/controller/core/gridViews/GetRow.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/gridView/AbstractGridView.class.php
wcfsetup/install/files/lib/system/gridView/DatabaseObjectListGridView.class.php

diff --git a/ts/WoltLabSuite/Core/Api/Gridviews/GetRow.ts b/ts/WoltLabSuite/Core/Api/Gridviews/GetRow.ts
new file mode 100644 (file)
index 0000000..1e23d9f
--- /dev/null
@@ -0,0 +1,21 @@
+import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend";
+import { ApiResult, apiResultFromError, apiResultFromValue } from "../Result";
+
+type Response = {
+  template: string;
+};
+
+export async function getRow(gridViewClass: string, objectId: string | number): Promise<ApiResult<Response>> {
+  const url = new URL(`${window.WSC_RPC_API_URL}core/grid-views/row`);
+  url.searchParams.set("gridView", gridViewClass);
+  url.searchParams.set("objectID", objectId.toString());
+
+  let response: Response;
+  try {
+    response = (await prepareRequest(url).get().allowCaching().disableLoadingIndicator().fetchAsJson()) as Response;
+  } catch (e) {
+    return apiResultFromError(e);
+  }
+
+  return apiResultFromValue(response);
+}
index a0a82ba9c58ffe582f0dd40f30a798113618bb07..9162951ddaf9a5c953fed00c9d54d75d8597492b 100644 (file)
@@ -1,3 +1,4 @@
+import { getRow } from "../Api/Gridviews/GetRow";
 import { getRows } from "../Api/Gridviews/GetRows";
 import DomChangeListener from "../Dom/Change/Listener";
 import DomUtil from "../Dom/Util";
@@ -48,6 +49,7 @@ export class GridView {
     this.#initSorting();
     this.#initActions();
     this.#initFilters();
+    this.#initEventListeners();
 
     window.addEventListener("popstate", () => {
       this.#handlePopState();
@@ -129,6 +131,12 @@ export class GridView {
     this.#initActions();
   }
 
+  async #refreshRow(row: HTMLElement): Promise<void> {
+    const response = (await getRow(this.#gridClassName, row.dataset.objectId!)).unwrap();
+    row.replaceWith(DomUtil.createFragmentFromHtml(response.template));
+    DomChangeListener.trigger();
+  }
+
   #updateQueryString(): void {
     if (!this.#baseUrl) {
       return;
@@ -282,4 +290,10 @@ export class GridView {
 
     this.#switchPage(pageNo, false);
   }
+
+  #initEventListeners(): void {
+    this.#table.addEventListener("refresh", (event) => {
+      void this.#refreshRow(event.target as HTMLElement);
+    });
+  }
 }
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Api/Gridviews/GetRow.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Api/Gridviews/GetRow.js
new file mode 100644 (file)
index 0000000..de757a5
--- /dev/null
@@ -0,0 +1,18 @@
+define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "../Result"], function (require, exports, Backend_1, Result_1) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.getRow = getRow;
+    async function getRow(gridViewClass, objectId) {
+        const url = new URL(`${window.WSC_RPC_API_URL}core/grid-views/row`);
+        url.searchParams.set("gridView", gridViewClass);
+        url.searchParams.set("objectID", objectId.toString());
+        let response;
+        try {
+            response = (await (0, Backend_1.prepareRequest)(url).get().allowCaching().disableLoadingIndicator().fetchAsJson());
+        }
+        catch (e) {
+            return (0, Result_1.apiResultFromError)(e);
+        }
+        return (0, Result_1.apiResultFromValue)(response);
+    }
+});
index addb5e72bd2c5bb42a43dcb15d2202bd219a8a75..d2ac3e8f5d8d60ac26d4e7a1de5618e2cfe65532 100644 (file)
@@ -1,4 +1,4 @@
-define(["require", "exports", "tslib", "../Api/Gridviews/GetRows", "../Dom/Change/Listener", "../Dom/Util", "../Helper/PromiseMutex", "../Ui/Dropdown/Simple", "./Dialog"], function (require, exports, tslib_1, GetRows_1, Listener_1, Util_1, PromiseMutex_1, Simple_1, Dialog_1) {
+define(["require", "exports", "tslib", "../Api/Gridviews/GetRow", "../Api/Gridviews/GetRows", "../Dom/Change/Listener", "../Dom/Util", "../Helper/PromiseMutex", "../Ui/Dropdown/Simple", "./Dialog"], function (require, exports, tslib_1, GetRow_1, GetRows_1, Listener_1, Util_1, PromiseMutex_1, Simple_1, Dialog_1) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
     exports.GridView = void 0;
@@ -38,6 +38,7 @@ define(["require", "exports", "tslib", "../Api/Gridviews/GetRows", "../Dom/Chang
             this.#initSorting();
             this.#initActions();
             this.#initFilters();
+            this.#initEventListeners();
             window.addEventListener("popstate", () => {
                 this.#handlePopState();
             });
@@ -95,6 +96,11 @@ define(["require", "exports", "tslib", "../Api/Gridviews/GetRows", "../Dom/Chang
             this.#renderFilters(response.filterLabels);
             this.#initActions();
         }
+        async #refreshRow(row) {
+            const response = (await (0, GetRow_1.getRow)(this.#gridClassName, row.dataset.objectId)).unwrap();
+            row.replaceWith(Util_1.default.createFragmentFromHtml(response.template));
+            Listener_1.default.trigger();
+        }
         #updateQueryString() {
             if (!this.#baseUrl) {
                 return;
@@ -219,6 +225,11 @@ define(["require", "exports", "tslib", "../Api/Gridviews/GetRows", "../Dom/Chang
             });
             this.#switchPage(pageNo, false);
         }
+        #initEventListeners() {
+            this.#table.addEventListener("refresh", (event) => {
+                void this.#refreshRow(event.target);
+            });
+        }
     }
     exports.GridView = GridView;
 });
index 60ddb452b2c35066c45c23f862f4439c3dd2c443..c4410b54691f6f2eb5b020d2bd01688ddce43ffd 100644 (file)
@@ -136,6 +136,7 @@ return static function (): void {
             $event->register(new \wcf\system\endpoint\controller\core\comments\responses\UpdateResponse);
             $event->register(new \wcf\system\endpoint\controller\core\exceptions\RenderException);
             $event->register(new \wcf\system\endpoint\controller\core\gridViews\GetRows);
+            $event->register(new \wcf\system\endpoint\controller\core\gridViews\GetRow);
             $event->register(new \wcf\system\endpoint\controller\core\messages\GetMentionSuggestions);
             $event->register(new \wcf\system\endpoint\controller\core\sessions\DeleteSession);
             $event->register(new \wcf\system\endpoint\controller\core\users\options\DeleteOption);
diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/gridViews/GetRow.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/gridViews/GetRow.class.php
new file mode 100644 (file)
index 0000000..ded963e
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+
+namespace wcf\system\endpoint\controller\core\gridViews;
+
+use Laminas\Diactoros\Response\JsonResponse;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use wcf\http\Helper;
+use wcf\system\endpoint\GetRequest;
+use wcf\system\endpoint\IController;
+use wcf\system\exception\PermissionDeniedException;
+use wcf\system\exception\UserInputException;
+use wcf\system\gridView\AbstractGridView;
+
+/**
+ * Retrieves a row for a grid view.
+ *
+ * @author      Marcel Werk
+ * @copyright   2001-2024 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since       6.2
+ */
+#[GetRequest('/core/grid-views/row')]
+final class GetRow implements IController
+{
+    #[\Override]
+    public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface
+    {
+        $parameters = Helper::mapApiParameters($request, GetRowParameters::class);
+
+        if (!\is_subclass_of($parameters->gridView, AbstractGridView::class)) {
+            throw new UserInputException('gridView', 'invalid');
+        }
+
+        $view = new $parameters->gridView();
+        \assert($view instanceof AbstractGridView);
+
+        if (!$view->isAccessible()) {
+            throw new PermissionDeniedException();
+        }
+
+        $view->setObjectIDFilter($parameters->objectID);
+
+        return new JsonResponse([
+            'template' => $view->renderRows(),
+        ]);
+    }
+}
+
+/** @internal */
+final class GetRowParameters
+{
+    public function __construct(
+        /** @var non-empty-string */
+        public readonly string $gridView,
+        public readonly string|int $objectID,
+    ) {}
+}
index 8486986558447566c0849165647ea6b4796fb5bd..71add3162b2dca39b8aebb1274ba6f8c9a2d4213 100644 (file)
@@ -38,6 +38,7 @@ abstract class AbstractGridView
     private string $sortOrder = 'ASC';
     private int $pageNo = 1;
     private array $activeFilters = [];
+    private string|int|null $objectIDFilter = null;
 
     /**
      * Adds a new column to the grid view.
@@ -487,6 +488,16 @@ abstract class AbstractGridView
         return '';
     }
 
+    public function setObjectIDFilter(string|int|null $objectID): void
+    {
+        $this->objectIDFilter = $objectID;
+    }
+
+    public function getObjectIDFilter(): string|int|null
+    {
+        return $this->objectIDFilter;
+    }
+
     /**
      * Fires the initialized event.
      */
index 55218b8cc02e98ac7fcfb200015fc09001c4298f..9c41b03a6249f710f09a49e141df158a1c1ffa8a 100644 (file)
@@ -72,6 +72,12 @@ abstract class DatabaseObjectListGridView extends AbstractGridView
             $this->objectList->sqlOrderBy .= ',' . $this->objectList->getDatabaseTableAlias() .
                 '.' . $this->objectList->getDatabaseTableIndexName() . ' ' . $this->getSortOrder();
         }
+        if ($this->getObjectIDFilter() !== null) {
+            $this->objectList->getConditionBuilder()->add(
+                $this->objectList->getDatabaseTableAlias() . '.' . $this->objectList->getDatabaseTableIndexName() . ' = ?',
+                [$this->getObjectIDFilter()]
+            );
+        }
         $this->applyFilters();
         $this->validate();
         $this->fireInitializedEvent();