From a545699a884e6af225a05d5da60508995a67c460 Mon Sep 17 00:00:00 2001
From: Sam Fink <finks@amazon.com>
Date: Fri, 31 Jan 2025 10:37:29 -0800
Subject: [PATCH] Adds event listener for notifying UI that AB feature
 configurations have been resolved (#5308)

Adds a new event listener object and handler to allow the application to notify the UI when feature configs have been resolved. For now, this is just the highlight command as that is all the UI needs. In the future we can use this class to plumb through additional features.
---
 ...-634f17f8-0782-4c9b-a57e-0c9431eb4eb4.json |  4 ++++
 .../toolkits/jetbrains/services/cwc/App.kt    | 19 ++++++++++++++++++
 .../services/cwc/messages/CwcMessage.kt       |  9 +++++++++
 .../src/mynah-ui/ui/apps/cwChatConnector.ts   | 14 ++++++++++++-
 .../mynah-ui/src/mynah-ui/ui/connector.ts     |  5 ++++-
 .../amazonq/mynah-ui/src/mynah-ui/ui/main.ts  | 11 ++++++++++
 .../src/mynah-ui/ui/tabs/generator.ts         |  2 +-
 .../CodeWhispererFeatureConfigListener.kt     | 20 +++++++++++++++++++
 .../CodeWhispererFeatureConfigService.kt      |  1 +
 9 files changed, 82 insertions(+), 3 deletions(-)
 create mode 100644 .changes/next-release/feature-634f17f8-0782-4c9b-a57e-0c9431eb4eb4.json
 create mode 100644 plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/CodeWhispererFeatureConfigListener.kt

diff --git a/.changes/next-release/feature-634f17f8-0782-4c9b-a57e-0c9431eb4eb4.json b/.changes/next-release/feature-634f17f8-0782-4c9b-a57e-0c9431eb4eb4.json
new file mode 100644
index 0000000000..4d6dc149bc
--- /dev/null
+++ b/.changes/next-release/feature-634f17f8-0782-4c9b-a57e-0c9431eb4eb4.json
@@ -0,0 +1,4 @@
+{
+  "type" : "feature",
+  "description" : "Adds event listener for notifying UI that AB feature configurations have been resolved"
+}
\ No newline at end of file
diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/App.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/App.kt
index f871a24710..b206595f9e 100644
--- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/App.kt
+++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/App.kt
@@ -3,17 +3,21 @@
 
 package software.aws.toolkits.jetbrains.services.cwc
 
+import com.intellij.openapi.application.ApplicationManager
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.launch
 import software.aws.toolkits.jetbrains.core.coroutines.disposableCoroutineScope
+import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigListener
 import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQApp
 import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
 import software.aws.toolkits.jetbrains.services.amazonq.messages.AmazonQMessage
 import software.aws.toolkits.jetbrains.services.amazonq.onboarding.OnboardingPageInteraction
+import software.aws.toolkits.jetbrains.services.amazonq.util.highlightCommand
 import software.aws.toolkits.jetbrains.services.cwc.commands.ActionRegistrar
 import software.aws.toolkits.jetbrains.services.cwc.commands.CodeScanIssueActionMessage
 import software.aws.toolkits.jetbrains.services.cwc.commands.ContextMenuActionMessage
 import software.aws.toolkits.jetbrains.services.cwc.controller.ChatController
+import software.aws.toolkits.jetbrains.services.cwc.messages.FeatureConfigsAvailableMessage
 import software.aws.toolkits.jetbrains.services.cwc.messages.IncomingCwcMessage
 
 class App : AmazonQApp {
@@ -56,6 +60,21 @@ class App : AmazonQApp {
                 scope.launch { handleMessage(message, inboundAppMessagesHandler) }
             }
         }
+
+        ApplicationManager.getApplication().messageBus.connect(this).subscribe(
+            CodeWhispererFeatureConfigListener.TOPIC,
+            object : CodeWhispererFeatureConfigListener {
+                override fun publishFeatureConfigsAvailble() {
+                    scope.launch {
+                        context.messagesFromAppToUi.publish(
+                            FeatureConfigsAvailableMessage(
+                                highlightCommand = highlightCommand()
+                            )
+                        )
+                    }
+                }
+            }
+        )
     }
 
     private suspend fun handleMessage(message: AmazonQMessage, inboundAppMessagesHandler: InboundAppMessagesHandler) {
diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/messages/CwcMessage.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/messages/CwcMessage.kt
index 85b3075c96..f695ba4993 100644
--- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/messages/CwcMessage.kt
+++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/messages/CwcMessage.kt
@@ -17,6 +17,7 @@ import software.amazon.awssdk.services.codewhispererstreaming.model.UserIntent
 import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthFollowUpType
 import software.aws.toolkits.jetbrains.services.amazonq.messages.AmazonQMessage
 import software.aws.toolkits.jetbrains.services.amazonq.onboarding.OnboardingPageInteractionType
+import software.aws.toolkits.jetbrains.services.amazonq.util.HighlightCommand
 import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.FollowUpType
 import java.time.Instant
 
@@ -264,6 +265,14 @@ data class ErrorMessage(
     type = "errorMessage",
 )
 
+data class FeatureConfigsAvailableMessage(
+    val highlightCommand: HighlightCommand?,
+
+) : UiMessage(
+    null,
+    type = "featureConfigsAvailableMessage",
+)
+
 data class QuickActionMessage(
     val message: String,
     @JsonProperty("triggerID") val triggerId: String,
diff --git a/plugins/amazonq/mynah-ui/src/mynah-ui/ui/apps/cwChatConnector.ts b/plugins/amazonq/mynah-ui/src/mynah-ui/ui/apps/cwChatConnector.ts
index ffbca2fb06..7f478f0fbe 100644
--- a/plugins/amazonq/mynah-ui/src/mynah-ui/ui/apps/cwChatConnector.ts
+++ b/plugins/amazonq/mynah-ui/src/mynah-ui/ui/apps/cwChatConnector.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import { ChatItemAction, ChatItemType, FeedbackPayload } from '@aws/mynah-ui-chat'
+import {ChatItemAction, ChatItemType, FeedbackPayload, QuickActionCommand} from '@aws/mynah-ui-chat'
 import { ExtensionMessage } from '../commands'
 import { CodeReference } from './amazonqCommonsConnector'
 import { TabOpenType, TabsStorage } from '../storages/tabsStorage'
@@ -23,6 +23,9 @@ export interface ConnectorProps {
     onError: (tabID: string, message: string, title: string) => void
     onWarning: (tabID: string, message: string, title: string) => void
     onOpenSettingsMessage: (tabID: string) => void
+    onFeatureConfigsAvailable: (
+        highlightCommand?: QuickActionCommand
+    ) => void
     tabsStorage: TabsStorage
 }
 
@@ -33,6 +36,7 @@ export class Connector {
     private readonly onChatAnswerReceived
     private readonly onCWCContextCommandMessage
     private readonly onOpenSettingsMessage
+    private readonly onFeatureConfigsAvailable
     private readonly followUpGenerator: FollowUpGenerator
 
     constructor(props: ConnectorProps) {
@@ -42,6 +46,7 @@ export class Connector {
         this.onError = props.onError
         this.onCWCContextCommandMessage = props.onCWCContextCommandMessage
         this.onOpenSettingsMessage = props.onOpenSettingsMessage
+        this.onFeatureConfigsAvailable = props.onFeatureConfigsAvailable
         this.followUpGenerator = new FollowUpGenerator()
     }
 
@@ -373,5 +378,12 @@ export class Connector {
             await this.processOpenSettingsMessage(messageData)
             return
         }
+
+        if (messageData.type === 'featureConfigsAvailableMessage') {
+            this.onFeatureConfigsAvailable(
+                messageData.highlightCommand,
+            )
+            return
+        }
     }
 }
diff --git a/plugins/amazonq/mynah-ui/src/mynah-ui/ui/connector.ts b/plugins/amazonq/mynah-ui/src/mynah-ui/ui/connector.ts
index 3405a59f32..143b3f017a 100644
--- a/plugins/amazonq/mynah-ui/src/mynah-ui/ui/connector.ts
+++ b/plugins/amazonq/mynah-ui/src/mynah-ui/ui/connector.ts
@@ -10,7 +10,7 @@ import {
     Engagement,
     NotificationType,
     ProgressField,
-    ChatPrompt,
+    ChatPrompt, QuickActionCommand,
 } from '@aws/mynah-ui-chat'
 import { Connector as CWChatConnector } from './apps/cwChatConnector'
 import { Connector as FeatureDevChatConnector } from './apps/featureDevChatConnector'
@@ -84,6 +84,9 @@ export interface ConnectorProps {
         codeTestEnabled: boolean,
         authenticatingTabIDs: string[]
     ) => void
+    onFeatureConfigsAvailable: (
+        highlightCommand?: QuickActionCommand
+    ) => void
     onNewTab: (tabType: TabType) => void
     onStartNewTransform: (tabID: string) => void
     onCodeTransformCommandMessageReceived: (message: ChatItem, command?: string) => void
diff --git a/plugins/amazonq/mynah-ui/src/mynah-ui/ui/main.ts b/plugins/amazonq/mynah-ui/src/mynah-ui/ui/main.ts
index 685c496d1a..1be8f151a7 100644
--- a/plugins/amazonq/mynah-ui/src/mynah-ui/ui/main.ts
+++ b/plugins/amazonq/mynah-ui/src/mynah-ui/ui/main.ts
@@ -552,6 +552,17 @@ export const createMynahUI = (
                     tabsStorage.updateTabStatus(tabID, 'free')
                 }
             }
+        },
+        onFeatureConfigsAvailable: (
+            highlightCommand?: QuickActionCommand
+        ): void => {
+            tabDataGenerator.highlightCommand = highlightCommand
+
+            for (const tab of tabsStorage.getTabs()) {
+                mynahUI.updateStore(tab.id, {
+                    contextCommands: tabDataGenerator.getTabData(tab.type, true).contextCommands
+                })
+            }
         }
     })
 
diff --git a/plugins/amazonq/mynah-ui/src/mynah-ui/ui/tabs/generator.ts b/plugins/amazonq/mynah-ui/src/mynah-ui/ui/tabs/generator.ts
index b5fb3d047f..63682d596e 100644
--- a/plugins/amazonq/mynah-ui/src/mynah-ui/ui/tabs/generator.ts
+++ b/plugins/amazonq/mynah-ui/src/mynah-ui/ui/tabs/generator.ts
@@ -21,7 +21,7 @@ export interface TabDataGeneratorProps {
 export class TabDataGenerator {
     private followUpsGenerator: FollowUpGenerator
     public quickActionsGenerator: QuickActionGenerator
-    private highlightCommand?: QuickActionCommand
+    public highlightCommand?: QuickActionCommand
 
     private tabTitle: Map<TabType, string> = new Map([
         ['unknown', 'Chat'],
diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/CodeWhispererFeatureConfigListener.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/CodeWhispererFeatureConfigListener.kt
new file mode 100644
index 0000000000..2ae46a8285
--- /dev/null
+++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/CodeWhispererFeatureConfigListener.kt
@@ -0,0 +1,20 @@
+// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.jetbrains.services.amazonq
+
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.util.messages.Topic
+
+interface CodeWhispererFeatureConfigListener {
+    fun publishFeatureConfigsAvailble() {}
+
+    companion object {
+        @Topic.AppLevel
+        val TOPIC = Topic.create("feature configs listener", CodeWhispererFeatureConfigListener::class.java)
+
+        fun notifyUiFeatureConfigsAvailable() {
+            ApplicationManager.getApplication().messageBus.syncPublisher(TOPIC).publishFeatureConfigsAvailble()
+        }
+    }
+}
diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/CodeWhispererFeatureConfigService.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/CodeWhispererFeatureConfigService.kt
index 736879327c..84a75038ad 100644
--- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/CodeWhispererFeatureConfigService.kt
+++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/CodeWhispererFeatureConfigService.kt
@@ -83,6 +83,7 @@ class CodeWhispererFeatureConfigService {
                     featureConfigs.remove(CUSTOMIZATION_ARN_OVERRIDE_NAME)
                 }
             }
+            CodeWhispererFeatureConfigListener.notifyUiFeatureConfigsAvailable()
         } catch (e: Exception) {
             LOG.debug(e) { "Error when fetching feature configs" }
         }