FIX: Optimistically fix topic timeline state issues

FIX: Optimistically fix topic timeline state issues

This is my second try at this. The first b246a63a592 raised an issue with the event delegation not working because the topic id changed.

This adds support for delegating events to dynamic keys by passing a function where a static key would normally be needed. This means that each timeline will have its own unique state key and events will only delegate to the proper topic.

diff --git a/app/assets/javascripts/discourse/app/components/mount-widget.js b/app/assets/javascripts/discourse/app/components/mount-widget.js
index cc29cfb..9ad0535 100644
--- a/app/assets/javascripts/discourse/app/components/mount-widget.js
+++ b/app/assets/javascripts/discourse/app/components/mount-widget.js
@@ -83,6 +83,7 @@ export default Component.extend({
   afterPatch() {},
 
   eventDispatched(eventName, key, refreshArg) {
+    key = typeof key === "function" ? key(refreshArg) : key;
     const onRefresh = camelize(eventName.replace(/:/, "-"));
     this.dirtyKeys.keyDirty(key, { onRefresh, refreshArg });
     this.queueRerender();
diff --git a/app/assets/javascripts/discourse/app/components/topic-timeline.js b/app/assets/javascripts/discourse/app/components/topic-timeline.js
index 09fbfcd..a9cd6be 100644
--- a/app/assets/javascripts/discourse/app/components/topic-timeline.js
+++ b/app/assets/javascripts/discourse/app/components/topic-timeline.js
@@ -105,7 +105,10 @@ export default MountWidget.extend(Docking, {
       });
     }
 
-    this.dispatch("topic:current-post-scrolled", "timeline-scrollarea");
+    this.dispatch(
+      "topic:current-post-scrolled",
+      () => `timeline-scrollarea-${this.topic.id}`
+    );
     this.dispatch("topic:toggle-actions", "topic-admin-menu-button");
     if (!this.site.mobileView) {
       this.appEvents.on("composer:opened", this, this.queueRerender);
diff --git a/app/assets/javascripts/discourse/app/widgets/topic-timeline.js b/app/assets/javascripts/discourse/app/widgets/topic-timeline.js
index 0314981..578fe95 100644
--- a/app/assets/javascripts/discourse/app/widgets/topic-timeline.js
+++ b/app/assets/javascripts/discourse/app/widgets/topic-timeline.js
@@ -2,7 +2,6 @@ import ComponentConnector from "discourse/widgets/component-connector";
 import I18n from "I18n";
 import RawHtml from "discourse/widgets/raw-html";
 import { createWidget } from "discourse/widgets/widget";
-import { deepMerge } from "discourse-common/lib/object";
 import { h } from "virtual-dom";
 import { iconNode } from "discourse-common/lib/icon-library";
 import { later } from "@ember/runloop";
@@ -76,7 +75,7 @@ function timelineDate(date) {
 
 createWidget("timeline-scroller", {
   tagName: "div.timeline-scroller",
-  buildKey: () => `timeline-scroller`,
+  buildKey: (attrs) => `timeline-scroller-${attrs.topicId}`,
 
   defaultState() {
     return { dragging: false };
@@ -144,7 +143,7 @@ createWidget("timeline-padding", {
 
 createWidget("timeline-scrollarea", {
   tagName: "div.timeline-scrollarea",
-  buildKey: () => `timeline-scrollarea`,
+  buildKey: (attrs) => `timeline-scrollarea-${attrs.topic.id}`,
 
   buildAttributes() {
     return { style: `height: ${scrollareaHeight()}px` };
@@ -239,15 +238,15 @@ createWidget("timeline-scrollarea", {
         before + SCROLLER_HEIGHT - 5 < lastReadTop || before > lastReadTop + 25;
     }
 
+    let scrollerAttrs = position;
+    scrollerAttrs.showDockedButton =
+      !attrs.mobileView && hasBackPosition && !showButton;
+    scrollerAttrs.fullScreen = attrs.fullScreen;
+    scrollerAttrs.topicId = attrs.topic.id;
+
     const result = [
       this.attach("timeline-padding", { height: before }),
-      this.attach(
-        "timeline-scroller",
-        deepMerge(position, {
-          showDockedButton: !attrs.mobileView && hasBackPosition && !showButton,
-          fullScreen: attrs.fullScreen,
-        })
-      ),
+      this.attach("timeline-scroller", scrollerAttrs),
       this.attach("timeline-padding", { height: after }),
     ];
 
@@ -410,8 +409,7 @@ createWidget("timeline-footer-controls", {
 
 export default createWidget("topic-timeline", {
   tagName: "div.topic-timeline",
-
-  buildKey: () => "topic-timeline-area",
+  buildKey: (attrs) => `topic-timeline-area-${attrs.topic.id}`,
 
   defaultState() {
     return { position: null, excerpt: null };

GitHub sha: 7f769e9e7620faeae00266650d7c6c0a6b44cc58

This commit appears in #14328 which was approved by markvanlan. It was merged by eviltrout.