@@ -8,13 +8,14 @@ import { ResourceTreeDataProvider, TreeNode } from '../shared/treeview/resourceT
88import { Command , Commands } from '../shared/vscode/commands2'
99import { getIcon } from '../shared/icons'
1010import { contextKey , setContext } from '../shared/vscode/setContext'
11- import { NotificationType , ToolkitNotification } from './types'
11+ import { NotificationType , ToolkitNotification , getNotificationTelemetryId } from './types'
1212import { ToolkitError } from '../shared/errors'
1313import { isAmazonQ } from '../shared/extensionUtilities'
1414import { getLogger } from '../shared/logger/logger'
1515import { registerToolView } from '../awsexplorer/activationShared'
1616import { readonlyDocument } from '../shared/utilities/textDocumentUtilities'
1717import { openUrl } from '../shared/utilities/vsCodeUtils'
18+ import { telemetry } from '../shared/telemetry/telemetry'
1819
1920/**
2021 * Controls the "Notifications" side panel/tree in each extension. It takes purely UX actions
@@ -27,25 +28,24 @@ export class NotificationsNode implements TreeNode {
2728 public startUpNotifications : ToolkitNotification [ ] = [ ]
2829 public emergencyNotifications : ToolkitNotification [ ] = [ ]
2930
31+ /** Command executed when a notification item is clicked on in the panel. */
3032 private readonly openNotificationCmd : Command
3133 private readonly focusCmdStr : string
3234 private readonly showContextStr : contextKey
3335 private readonly startUpNodeContext : string
3436 private readonly emergencyNodeContext : string
3537
36- private readonly onDidChangeTreeItemEmitter = new vscode . EventEmitter < void > ( )
37- private readonly onDidChangeChildrenEmitter = new vscode . EventEmitter < void > ( )
38- private readonly onDidChangeVisibilityEmitter = new vscode . EventEmitter < void > ( )
39- public readonly onDidChangeTreeItem = this . onDidChangeTreeItemEmitter . event
40- public readonly onDidChangeChildren = this . onDidChangeChildrenEmitter . event
41- public readonly onDidChangeVisibility = this . onDidChangeVisibilityEmitter . event
42-
4338 static #instance: NotificationsNode
4439
4540 private constructor ( ) {
4641 this . openNotificationCmd = Commands . register (
4742 isAmazonQ ( ) ? '_aws.amazonq.notifications.open' : '_aws.toolkit.notifications.open' ,
48- async ( n : ToolkitNotification ) => this . openNotification ( n )
43+ ( n : ToolkitNotification ) => {
44+ return telemetry . ui_click . run ( ( span ) => {
45+ span . record ( { elementId : getNotificationTelemetryId ( n ) } )
46+ return this . openNotification ( n )
47+ } )
48+ }
4949 )
5050
5151 if ( isAmazonQ ( ) ) {
@@ -73,12 +73,6 @@ export class NotificationsNode implements TreeNode {
7373 const hasNotifications = this . startUpNotifications . length > 0 || this . emergencyNotifications . length > 0
7474 void setContext ( this . showContextStr , hasNotifications )
7575
76- this . onDidChangeChildrenEmitter . fire ( )
77- this . provider ?. refresh ( )
78- }
79-
80- public refreshRootNode ( ) {
81- this . onDidChangeTreeItemEmitter . fire ( )
8276 this . provider ?. refresh ( )
8377 }
8478
@@ -129,28 +123,34 @@ export class NotificationsNode implements TreeNode {
129123 * Fired when a notification is clicked on in the panel. It will run any rendering
130124 * instructions included in the notification. See {@link ToolkitNotification.uiRenderInstructions}.
131125 */
132- public async openNotification ( notification : ToolkitNotification ) {
126+ private async openNotification ( notification : ToolkitNotification ) {
133127 switch ( notification . uiRenderInstructions . onClick . type ) {
134128 case 'modal' :
135129 // Render blocking modal
136130 getLogger ( 'notifications' ) . verbose ( `rendering modal for notificaiton: ${ notification . id } ...` )
137- await this . showInformationWindow ( notification , 'modal' )
131+ await this . showInformationWindow ( notification , 'modal' , false )
138132 break
139133 case 'openUrl' :
134+ // Show open url option
140135 if ( ! notification . uiRenderInstructions . onClick . url ) {
141136 throw new ToolkitError ( 'No url provided for onclick open url' )
142137 }
143- // Show open url option
144138 getLogger ( 'notifications' ) . verbose ( `opening url for notification: ${ notification . id } ...` )
145- await openUrl ( vscode . Uri . parse ( notification . uiRenderInstructions . onClick . url ) )
139+ await openUrl (
140+ vscode . Uri . parse ( notification . uiRenderInstructions . onClick . url ) ,
141+ getNotificationTelemetryId ( notification )
142+ )
146143 break
147144 case 'openTextDocument' :
148145 // Display read-only txt document
149146 getLogger ( 'notifications' ) . verbose ( `showing txt document for notification: ${ notification . id } ...` )
150- await readonlyDocument . show (
151- notification . uiRenderInstructions . content [ 'en-US' ] . description ,
152- `Notification: ${ notification . id } `
153- )
147+ await telemetry . toolkit_invokeAction . run ( async ( ) => {
148+ telemetry . record ( { source : getNotificationTelemetryId ( notification ) , action : 'openTxt' } )
149+ await readonlyDocument . show (
150+ notification . uiRenderInstructions . content [ 'en-US' ] . description ,
151+ `Notification: ${ notification . id } `
152+ )
153+ } )
154154 break
155155 }
156156 }
@@ -160,57 +160,65 @@ export class NotificationsNode implements TreeNode {
160160 * Can be either a blocking modal or a bottom-right corner toast
161161 * Handles the button click actions based on the button type.
162162 */
163- public async showInformationWindow ( notification : ToolkitNotification , type : string = 'toast' ) {
163+ private showInformationWindow ( notification : ToolkitNotification , type : string = 'toast' , passive : boolean = false ) {
164164 const isModal = type === 'modal'
165165
166- // modal has to have defined actions(buttons)
166+ // modal has to have defined actions (buttons)
167167 const buttons = notification . uiRenderInstructions . actions ?? [ ]
168168 const buttonLabels = buttons . map ( ( actions ) => actions . displayText [ 'en-US' ] )
169169 const detail = notification . uiRenderInstructions . content [ 'en-US' ] . description
170170
171- // we use toastPreview to display as titlefor toast, since detail won't be shown
171+ // we use toastPreview to display as title for toast, since detail won't be shown
172172 const title = isModal
173173 ? notification . uiRenderInstructions . content [ 'en-US' ] . title
174174 : ( notification . uiRenderInstructions . content [ 'en-US' ] . toastPreview ??
175175 notification . uiRenderInstructions . content [ 'en-US' ] . title )
176176
177- const selectedText = await vscode . window . showInformationMessage (
178- title ,
179- { modal : isModal , detail } ,
180- ...buttonLabels
181- )
177+ telemetry . toolkit_showNotification . emit ( {
178+ id : getNotificationTelemetryId ( notification ) ,
179+ passive,
180+ component : 'editor' ,
181+ result : 'Succeeded' ,
182+ } )
182183
183- if ( selectedText ) {
184- const selectedButton = buttons . find ( ( actions ) => actions . displayText [ 'en-US' ] === selectedText )
185- // Different button options
186- if ( selectedButton ) {
187- switch ( selectedButton . type ) {
188- case 'openTxt' :
189- await readonlyDocument . show (
190- notification . uiRenderInstructions . content [ 'en-US' ] . description ,
191- `Notification: ${ notification . id } `
192- )
193- break
194- case 'updateAndReload' :
195- await this . updateAndReload ( notification . displayIf . extensionId )
196- break
197- case 'openUrl' :
198- if ( selectedButton . url ) {
199- await openUrl ( vscode . Uri . parse ( selectedButton . url ) )
200- } else {
201- throw new ToolkitError ( 'url not provided' )
184+ return vscode . window
185+ . showInformationMessage ( title , { modal : isModal , detail } , ...buttonLabels )
186+ . then ( ( response ) => {
187+ return telemetry . toolkit_invokeAction . run ( async ( span ) => {
188+ span . record ( { source : getNotificationTelemetryId ( notification ) , action : response ?? 'OK' } )
189+ if ( response ) {
190+ const selectedButton = buttons . find ( ( actions ) => actions . displayText [ 'en-US' ] === response )
191+ // Different button options
192+ if ( selectedButton ) {
193+ switch ( selectedButton . type ) {
194+ case 'openTxt' :
195+ await readonlyDocument . show (
196+ notification . uiRenderInstructions . content [ 'en-US' ] . description ,
197+ `Notification: ${ notification . id } `
198+ )
199+ break
200+ case 'updateAndReload' :
201+ await this . updateAndReload ( notification . displayIf . extensionId )
202+ break
203+ case 'openUrl' :
204+ if ( selectedButton . url ) {
205+ await openUrl ( vscode . Uri . parse ( selectedButton . url ) )
206+ } else {
207+ throw new ToolkitError ( 'url not provided' )
208+ }
209+ break
210+ default :
211+ throw new ToolkitError ( 'button action not defined' )
212+ }
202213 }
203- break
204- default :
205- throw new ToolkitError ( 'button action not defined' )
206- }
207- }
208- }
214+ }
215+ } )
216+ } )
209217 }
210218
211219 public async onReceiveNotifications ( notifications : ToolkitNotification [ ] ) {
212220 for ( const notification of notifications ) {
213- void this . showInformationWindow ( notification , notification . uiRenderInstructions . onRecieve )
221+ void this . showInformationWindow ( notification , notification . uiRenderInstructions . onRecieve , true )
214222 }
215223 }
216224
0 commit comments