@@ -8,13 +8,14 @@ import { ResourceTreeDataProvider, TreeNode } from '../shared/treeview/resourceT
8
8
import { Command , Commands } from '../shared/vscode/commands2'
9
9
import { getIcon } from '../shared/icons'
10
10
import { contextKey , setContext } from '../shared/vscode/setContext'
11
- import { NotificationType , ToolkitNotification } from './types'
11
+ import { NotificationType , ToolkitNotification , getNotificationTelemetryId } from './types'
12
12
import { ToolkitError } from '../shared/errors'
13
13
import { isAmazonQ } from '../shared/extensionUtilities'
14
14
import { getLogger } from '../shared/logger/logger'
15
15
import { registerToolView } from '../awsexplorer/activationShared'
16
16
import { readonlyDocument } from '../shared/utilities/textDocumentUtilities'
17
17
import { openUrl } from '../shared/utilities/vsCodeUtils'
18
+ import { telemetry } from '../shared/telemetry/telemetry'
18
19
19
20
/**
20
21
* Controls the "Notifications" side panel/tree in each extension. It takes purely UX actions
@@ -27,25 +28,24 @@ export class NotificationsNode implements TreeNode {
27
28
public startUpNotifications : ToolkitNotification [ ] = [ ]
28
29
public emergencyNotifications : ToolkitNotification [ ] = [ ]
29
30
31
+ /** Command executed when a notification item is clicked on in the panel. */
30
32
private readonly openNotificationCmd : Command
31
33
private readonly focusCmdStr : string
32
34
private readonly showContextStr : contextKey
33
35
private readonly startUpNodeContext : string
34
36
private readonly emergencyNodeContext : string
35
37
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
-
43
38
static #instance: NotificationsNode
44
39
45
40
private constructor ( ) {
46
41
this . openNotificationCmd = Commands . register (
47
42
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
+ }
49
49
)
50
50
51
51
if ( isAmazonQ ( ) ) {
@@ -73,12 +73,6 @@ export class NotificationsNode implements TreeNode {
73
73
const hasNotifications = this . startUpNotifications . length > 0 || this . emergencyNotifications . length > 0
74
74
void setContext ( this . showContextStr , hasNotifications )
75
75
76
- this . onDidChangeChildrenEmitter . fire ( )
77
- this . provider ?. refresh ( )
78
- }
79
-
80
- public refreshRootNode ( ) {
81
- this . onDidChangeTreeItemEmitter . fire ( )
82
76
this . provider ?. refresh ( )
83
77
}
84
78
@@ -129,28 +123,34 @@ export class NotificationsNode implements TreeNode {
129
123
* Fired when a notification is clicked on in the panel. It will run any rendering
130
124
* instructions included in the notification. See {@link ToolkitNotification.uiRenderInstructions}.
131
125
*/
132
- public async openNotification ( notification : ToolkitNotification ) {
126
+ private async openNotification ( notification : ToolkitNotification ) {
133
127
switch ( notification . uiRenderInstructions . onClick . type ) {
134
128
case 'modal' :
135
129
// Render blocking modal
136
130
getLogger ( 'notifications' ) . verbose ( `rendering modal for notificaiton: ${ notification . id } ...` )
137
- await this . showInformationWindow ( notification , 'modal' )
131
+ await this . showInformationWindow ( notification , 'modal' , false )
138
132
break
139
133
case 'openUrl' :
134
+ // Show open url option
140
135
if ( ! notification . uiRenderInstructions . onClick . url ) {
141
136
throw new ToolkitError ( 'No url provided for onclick open url' )
142
137
}
143
- // Show open url option
144
138
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
+ )
146
143
break
147
144
case 'openTextDocument' :
148
145
// Display read-only txt document
149
146
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
+ } )
154
154
break
155
155
}
156
156
}
@@ -160,57 +160,65 @@ export class NotificationsNode implements TreeNode {
160
160
* Can be either a blocking modal or a bottom-right corner toast
161
161
* Handles the button click actions based on the button type.
162
162
*/
163
- public async showInformationWindow ( notification : ToolkitNotification , type : string = 'toast' ) {
163
+ private showInformationWindow ( notification : ToolkitNotification , type : string = 'toast' , passive : boolean = false ) {
164
164
const isModal = type === 'modal'
165
165
166
- // modal has to have defined actions(buttons)
166
+ // modal has to have defined actions (buttons)
167
167
const buttons = notification . uiRenderInstructions . actions ?? [ ]
168
168
const buttonLabels = buttons . map ( ( actions ) => actions . displayText [ 'en-US' ] )
169
169
const detail = notification . uiRenderInstructions . content [ 'en-US' ] . description
170
170
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
172
172
const title = isModal
173
173
? notification . uiRenderInstructions . content [ 'en-US' ] . title
174
174
: ( notification . uiRenderInstructions . content [ 'en-US' ] . toastPreview ??
175
175
notification . uiRenderInstructions . content [ 'en-US' ] . title )
176
176
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
+ } )
182
183
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
+ }
202
213
}
203
- break
204
- default :
205
- throw new ToolkitError ( 'button action not defined' )
206
- }
207
- }
208
- }
214
+ }
215
+ } )
216
+ } )
209
217
}
210
218
211
219
public async onReceiveNotifications ( notifications : ToolkitNotification [ ] ) {
212
220
for ( const notification of notifications ) {
213
- void this . showInformationWindow ( notification , notification . uiRenderInstructions . onRecieve )
221
+ void this . showInformationWindow ( notification , notification . uiRenderInstructions . onRecieve , true )
214
222
}
215
223
}
216
224
0 commit comments