DEV: reduces jquery usage and memory leaks in composer (#14924)

DEV: reduces jquery usage and memory leaks in composer (#14924)

Removes more than 60 jquery function leaks in one Acceptance: Composer run.

diff --git a/app/assets/javascripts/discourse/app/components/composer-body.js b/app/assets/javascripts/discourse/app/components/composer-body.js
index 035da95..fb8bf92 100644
--- a/app/assets/javascripts/discourse/app/components/composer-body.js
+++ b/app/assets/javascripts/discourse/app/components/composer-body.js
@@ -11,9 +11,9 @@ import discourseDebounce from "discourse-common/lib/debounce";
 import { headerHeight } from "discourse/components/site-header";
 import positioningWorkaround from "discourse/lib/safari-hacks";
 
-const START_EVENTS = "touchstart mousedown";
-const DRAG_EVENTS = "touchmove mousemove";
-const END_EVENTS = "touchend mouseup";
+const START_DRAG_EVENTS = ["touchstart", "mousedown"];
+const DRAG_EVENTS = ["touchmove", "mousemove"];
+const END_DRAG_EVENTS = ["touchend", "mouseup"];
 
 const THROTTLE_RATE = 20;
 
@@ -54,17 +54,15 @@ export default Component.extend(KeyEnterEscape, {
   },
 
   movePanels(size) {
-    $("#main-outlet").css("padding-bottom", size ? size : "");
+    document.querySelector("#main-outlet").style.paddingBottom = size
+      ? `${size}px`
+      : "";
 
     // signal the progress bar it should move!
     this.appEvents.trigger("composer:resized");
   },
 
-  @observes(
-    "composeState",
-    "composer.action",
-    "composer.canEditTopicFeaturedLink"
-  )
+  @observes("composeState", "composer.{action,canEditTopicFeaturedLink}")
   resize() {
     schedule("afterRender", () => {
       if (!this.element || this.isDestroying || this.isDestroyed) {
@@ -76,8 +74,11 @@ export default Component.extend(KeyEnterEscape, {
   },
 
   debounceMove() {
-    const h = $("#reply-control:not(.saving)").height() || 0;
-    this.movePanels(h);
+    let height = 0;
+    if (!this.element.classList.contains("saving")) {
+      height = this.element.offsetHeight;
+    }
+    this.movePanels(height);
   },
 
   keyUp() {
@@ -105,45 +106,13 @@ export default Component.extend(KeyEnterEscape, {
   },
 
   setupComposerResizeEvents() {
-    const $composer = $(this.element);
-    const $grippie = $(this.element.querySelector(".grippie"));
-    const $document = $(document);
-    let origComposerSize = 0;
-    let lastMousePos = 0;
-
-    const performDrag = (event) => {
-      $composer.trigger("div-resizing");
-      this.appEvents.trigger("composer:div-resizing");
-      $composer.addClass("clear-transitions");
-      const currentMousePos = mouseYPos(event);
-      let size = origComposerSize + (lastMousePos - currentMousePos);
-
-      const winHeight = $(window).height();
-      size = Math.min(size, winHeight - headerHeight());
-      this.movePanels(size);
-      $composer.height(size);
-    };
+    this.origComposerSize = 0;
+    this.lastMousePos = 0;
 
-    const throttledPerformDrag = ((event) => {
-      event.preventDefault();
-      throttle(this, performDrag, event, THROTTLE_RATE);
-    }).bind(this);
-
-    const endDrag = (() => {
-      this.appEvents.trigger("composer:resize-ended");
-      $document.off(DRAG_EVENTS, throttledPerformDrag);
-      $document.off(END_EVENTS, endDrag);
-      $composer.removeClass("clear-transitions");
-      $composer.focus();
-    }).bind(this);
-
-    $grippie.on(START_EVENTS, (event) => {
-      event.preventDefault();
-      origComposerSize = $composer.height();
-      lastMousePos = mouseYPos(event);
-      $document.on(DRAG_EVENTS, throttledPerformDrag);
-      $document.on(END_EVENTS, endDrag);
-      this.appEvents.trigger("composer:resize-started");
+    START_DRAG_EVENTS.forEach((startDragEvent) => {
+      this.element
+        .querySelector(".grippie")
+        ?.addEventListener(startDragEvent, this.startDragHandler);
     });
 
     if (this._visualViewportResizing()) {
@@ -153,6 +122,58 @@ export default Component.extend(KeyEnterEscape, {
   },
 
   @bind
+  performDragHandler() {
+    this.appEvents.trigger("composer:div-resizing");
+    this.element.classList.add("clear-transitions");
+    const currentMousePos = mouseYPos(event);
+    let size = this.origComposerSize + (this.lastMousePos - currentMousePos);
+
+    size = Math.min(size, window.innerHeight - headerHeight());
+    this.movePanels(size);
+    this.element.style.height = size ? `${size}px` : "";
+  },
+
+  @bind
+  startDragHandler(event) {
+    event.preventDefault();
+
+    this.origComposerSize = this.element.offsetHeight;
+    this.lastMousePos = mouseYPos(event);
+
+    DRAG_EVENTS.forEach((dragEvent) => {
+      document.addEventListener(dragEvent, this.throttledPerformDrag);
+    });
+
+    END_DRAG_EVENTS.forEach((endDragEvent) => {
+      document.addEventListener(endDragEvent, this.endDragHandler);
+    });
+
+    this.appEvents.trigger("composer:resize-started");
+  },
+
+  @bind
+  endDragHandler() {
+    this.appEvents.trigger("composer:resize-ended");
+
+    DRAG_EVENTS.forEach((dragEvent) => {
+      document.removeEventListener(dragEvent, this.throttledPerformDrag);
+    });
+
+    END_DRAG_EVENTS.forEach((endDragEvent) => {
+      document.removeEventListener(endDragEvent, this.endDragHandler);
+    });
+
+    this.element.classList.remove("clear-transitions");
+    this.element.focus();
+  },
+
+  @bind
+  throttledPerformDrag(event) {
+    event.preventDefault();
+    throttle(this, this.performDragHandler, event, THROTTLE_RATE);
+  },
+
+  @bind
   viewportResize() {
     const composerVH = window.visualViewport.height * 0.01,
       doc = document.documentElement;
@@ -207,10 +228,17 @@ export default Component.extend(KeyEnterEscape, {
 
   willDestroyElement() {
     this._super(...arguments);
+
     if (this._visualViewportResizing()) {
       window.visualViewport.removeEventListener("resize", this.viewportResize);
     }
 
+    START_DRAG_EVENTS.forEach((startDragEvent) => {
+      this.element
+        .querySelector(".grippie")
+        ?.removeEventListener(startDragEvent, this.startDragHandler);
+    });
+
     cancel(this._lastKeyTimeout);
   },
 
diff --git a/app/assets/javascripts/discourse/app/components/composer-editor.js b/app/assets/javascripts/discourse/app/components/composer-editor.js
index a98df73..fe511fd 100644
--- a/app/assets/javascripts/discourse/app/components/composer-editor.js
+++ b/app/assets/javascripts/discourse/app/components/composer-editor.js
@@ -12,6 +12,7 @@ import {
   tinyAvatar,
 } from "discourse/lib/utilities";
 import discourseComputed, {
+  bind,
   observes,
   on,
 } from "discourse-common/utils/decorators";
@@ -138,9 +139,7 @@ export default Component.extend(ComposerUpload, {
 
   @discourseComputed
   showLink() {
-    return (
-      this.currentUser && this.currentUser.get("link_posting_access") !== "none"
-    );
+    return this.currentUser && this.currentUser.link_posting_access !== "none";
   },
 
   @observes("focusTarget")
@@ -189,7 +188,8 @@ export default Component.extend(ComposerUpload, {
     };
   },
 
-  userSearchTerm(term) {
+  @bind
+  _userSearchTerm(term) {
     const topicId = this.get("topic.id");
     // maybe this is a brand new topic, so grab category from composer
     const categoryId =
@@ -218,34 +218,42 @@ export default Component.extend(ComposerUpload, {
     return extensions.map((ext) => `.${ext}`).join();
   },
 
+  @bind
+  _afterMentionComplete(value) {
+    this.composer.set("reply", value);
+
+    // ensures textarea scroll position is correct
+    schedule("afterRender", () => {
+      const input = this.element.querySelector(".d-editor-input");
+      input?.blur();
+      input?.focus();
+    });
+  },
+
   @on("didInsertElement")
   _composerEditorInit() {
     const $input = $(this.element.querySelector(".d-editor-input"));
-    const $preview = $(this.element.querySelector(".d-editor-preview-wrapper"));
 
     if (this.siteSettings.enable_mentions) {
       $input.autocomplete({
         template: findRawTemplate("user-selector-autocomplete"),
-        dataSource: (term) => this.userSearchTerm.call(this, term),
+        dataSource: this._userSearchTerm,
         key: "@",

[... diff too long, it was truncated ...]

GitHub sha: ae16b0a9d40f179f0c30b355b5d5d99a41c4451b

This commit appears in #14924 which was approved by CvX. It was merged by jjaffeux.