44 *--------------------------------------------------------------------------------------------*/
55
66import * as vscode from 'vscode' ;
7+ import { toNotificationUri } from '../common/uri' ;
78import { dispose } from '../common/utils' ;
8- import { NotificationsSortMethod , NotificationTreeItem } from './notificationItem' ;
9+ import { NotificationSubjectType } from '../github/interface' ;
10+ import { IssueModel } from '../github/issueModel' ;
11+ import { PullRequestModel } from '../github/pullRequestModel' ;
12+ import { isNotificationTreeItem , NotificationTreeDataItem , NotificationTreeItem } from './notificationItem' ;
913import { NotificationsProvider } from './notificationsProvider' ;
1014
1115export interface INotificationTreeItems {
1216 readonly notifications : NotificationTreeItem [ ] ;
1317 readonly hasNextPage : boolean
1418}
1519
16- export class NotificationsManager {
17- private readonly _onDidChangeNotifications = new vscode . EventEmitter < NotificationTreeItem [ ] > ( ) ;
18- readonly onDidChangeNotifications = this . _onDidChangeNotifications . event ;
19-
20- private _sortingMethod : NotificationsSortMethod = NotificationsSortMethod . Timestamp ;
21- public get sortingMethod ( ) : NotificationsSortMethod { return this . _sortingMethod ; }
22- public set sortingMethod ( value : NotificationsSortMethod ) {
23- if ( this . _sortingMethod === value ) {
24- return ;
25- }
20+ export enum NotificationsSortMethod {
21+ Timestamp = 'Timestamp' ,
22+ Priority = 'Priority'
23+ }
2624
27- this . _sortingMethod = value ;
28- this . _onDidChangeSortingMethod . fire ( ) ;
29- }
25+ export class NotificationsManager implements vscode . TreeDataProvider < NotificationTreeDataItem > , vscode . Disposable {
26+ private _onDidChangeTreeData : vscode . EventEmitter < NotificationTreeDataItem | undefined | void > = new vscode . EventEmitter < NotificationTreeDataItem | undefined | void > ( ) ;
27+ readonly onDidChangeTreeData : vscode . Event < NotificationTreeDataItem | undefined | void > = this . _onDidChangeTreeData . event ;
3028
31- private readonly _onDidChangeSortingMethod = new vscode . EventEmitter < void > ( ) ;
32- readonly onDidChangeSortingMethod = this . _onDidChangeSortingMethod . event ;
29+ private readonly _onDidChangeNotifications = new vscode . EventEmitter < NotificationTreeItem [ ] > ( ) ;
30+ readonly onDidChangeNotifications = this . _onDidChangeNotifications . event ;
3331
32+ private _pageCount : number = 1 ;
3433 private _hasNextPage : boolean = false ;
34+ private _dateTime : Date = new Date ( ) ;
35+ private _fetchNotifications : boolean = false ;
3536 private _notifications = new Map < string , NotificationTreeItem > ( ) ;
3637
38+ private _sortingMethod : NotificationsSortMethod = NotificationsSortMethod . Timestamp ;
39+ get sortingMethod ( ) : NotificationsSortMethod { return this . _sortingMethod ; }
40+
3741 private readonly _disposable : vscode . Disposable [ ] = [ ] ;
3842
3943 constructor ( private readonly _notificationProvider : NotificationsProvider ) {
44+ this . _disposable . push ( this . _onDidChangeTreeData ) ;
4045 this . _disposable . push ( this . _onDidChangeNotifications ) ;
4146 }
4247
4348 dispose ( ) {
4449 dispose ( this . _disposable ) ;
4550 }
4651
47- public clear ( ) {
48- if ( this . _notifications . size === 0 ) {
49- return ;
52+ //#region TreeDataProvider
53+
54+ async getChildren ( element ?: unknown ) : Promise < NotificationTreeDataItem [ ] | undefined > {
55+ if ( element !== undefined ) {
56+ return undefined ;
5057 }
51- const updates = Array . from ( this . _notifications . values ( ) ) ;
52- this . _notifications . clear ( ) ;
53- this . _onDidChangeNotifications . fire ( updates ) ;
54- }
5558
56- public async getNotifications ( compute : boolean , pageCount : number ) : Promise < INotificationTreeItems | undefined > {
57- if ( ! compute ) {
58- const notifications = Array . from ( this . _notifications . values ( ) ) ;
59+ const notificationsData = await this . getNotifications ( ) ;
60+ if ( notificationsData === undefined ) {
61+ return undefined ;
62+ }
5963
60- return {
61- notifications : this . _sortNotifications ( notifications ) ,
62- hasNextPage : this . _hasNextPage
63- } ;
64+ if ( notificationsData . hasNextPage ) {
65+ return [ ...notificationsData . notifications , { kind : 'loadMoreNotifications' } ] ;
6466 }
6567
66- // Get raw notifications
67- const notificationsData = await this . _notificationProvider . computeNotifications ( pageCount ) ;
68- if ( ! notificationsData ) {
69- return undefined ;
68+ return notificationsData . notifications ;
69+ }
70+
71+ async getTreeItem ( element : NotificationTreeDataItem ) : Promise < vscode . TreeItem > {
72+ if ( isNotificationTreeItem ( element ) ) {
73+ return this . _resolveNotificationTreeItem ( element ) ;
7074 }
75+ return this . _resolveLoadMoreNotificationsTreeItem ( ) ;
76+ }
77+
78+ private _resolveNotificationTreeItem ( element : NotificationTreeItem ) : vscode . TreeItem {
79+ const label = element . notification . subject . title ;
80+ const item = new vscode . TreeItem ( label , vscode . TreeItemCollapsibleState . None ) ;
81+ const notification = element . notification ;
82+ const model = element . model ;
83+
84+ if ( notification . subject . type === NotificationSubjectType . Issue && model instanceof IssueModel ) {
85+ item . iconPath = element . model . isOpen
86+ ? new vscode . ThemeIcon ( 'issues' , new vscode . ThemeColor ( 'issues.open' ) )
87+ : new vscode . ThemeIcon ( 'issue-closed' , new vscode . ThemeColor ( 'issues.closed' ) ) ;
88+ }
89+ if ( notification . subject . type === NotificationSubjectType . PullRequest && model instanceof PullRequestModel ) {
90+ item . iconPath = model . isOpen
91+ ? new vscode . ThemeIcon ( 'git-pull-request' , new vscode . ThemeColor ( 'pullRequests.open' ) )
92+ : new vscode . ThemeIcon ( 'git-pull-request' , new vscode . ThemeColor ( 'pullRequests.merged' ) ) ;
93+ }
94+ item . description = `${ notification . owner } /${ notification . name } ` ;
95+ item . contextValue = notification . subject . type ;
96+ item . resourceUri = toNotificationUri ( { key : element . notification . key } ) ;
97+ item . command = {
98+ command : 'notification.chatSummarizeNotification' ,
99+ title : 'Summarize Notification' ,
100+ arguments : [ element ]
101+ } ;
102+ return item ;
103+ }
104+
105+ private _resolveLoadMoreNotificationsTreeItem ( ) : vscode . TreeItem {
106+ const item = new vscode . TreeItem ( vscode . l10n . t ( 'Load More Notifications...' ) , vscode . TreeItemCollapsibleState . None ) ;
107+ item . command = {
108+ title : 'Load More Notifications' ,
109+ command : 'notifications.loadMore'
110+ } ;
111+ item . contextValue = 'loadMoreNotifications' ;
112+ return item ;
113+ }
71114
72- // Resolve notifications
73- const notificationItems = new Map < string , NotificationTreeItem > ( ) ;
74- await Promise . all ( notificationsData . notifications . map ( async notification => {
75- const cachedNotification = this . _notifications . get ( notification . key ) ;
76- if ( cachedNotification && cachedNotification . notification . updatedAd . getTime ( ) === notification . updatedAd . getTime ( ) ) {
77- notificationItems . set ( notification . key , cachedNotification ) ;
78- return ;
115+ //#endregion
116+
117+ public async getNotifications ( ) : Promise < INotificationTreeItems | undefined > {
118+ if ( this . _fetchNotifications ) {
119+ // Get raw notifications
120+ const notificationsData = await this . _notificationProvider . getNotifications ( this . _dateTime . toISOString ( ) , this . _pageCount ) ;
121+ if ( ! notificationsData ) {
122+ return undefined ;
79123 }
80124
81- const model = await this . _notificationProvider . getNotificationModel ( notification ) ;
82- if ( ! model ) {
83- return ;
125+ // Resolve notifications
126+ const notificationTreeItems = new Map < string , NotificationTreeItem > ( ) ;
127+ await Promise . all ( notificationsData . notifications . map ( async notification => {
128+ const model = await this . _notificationProvider . getNotificationModel ( notification ) ;
129+ if ( ! model ) {
130+ return ;
131+ }
132+
133+ notificationTreeItems . set ( notification . key , {
134+ notification, model, kind : 'notification'
135+ } ) ;
136+ } ) ) ;
137+
138+ for ( const [ key , value ] of notificationTreeItems . entries ( ) ) {
139+ this . _notifications . set ( key , value ) ;
84140 }
141+ this . _hasNextPage = notificationsData . hasNextPage ;
85142
86- notificationItems . set ( notification . key , {
87- notification, model, kind : 'notification'
88- } ) ;
89- } ) ) ;
143+ this . _fetchNotifications = false ;
144+ }
90145
91146 // Calculate notification priority
92- if ( this . sortingMethod === NotificationsSortMethod . Priority ) {
93- const notificationsWithoutPriority = Array . from ( notificationItems . values ( ) )
147+ if ( this . _sortingMethod === NotificationsSortMethod . Priority ) {
148+ const notificationsWithoutPriority = Array . from ( this . _notifications . values ( ) )
94149 . filter ( notification => notification . priority === undefined ) ;
95150
96151 const notificationPriorities = await this . _notificationProvider
97152 . getNotificationsPriority ( notificationsWithoutPriority ) ;
98153
99154 for ( const { key, priority, priorityReasoning } of notificationPriorities ) {
100- const notification = notificationItems . get ( key ) ;
155+ const notification = this . _notifications . get ( key ) ;
101156 if ( ! notification ) {
102157 continue ;
103158 }
104159
105160 notification . priority = priority ;
106161 notification . priorityReason = priorityReasoning ;
107162
108- notificationItems . set ( key , notification ) ;
163+ this . _notifications . set ( key , notification ) ;
109164 }
110165 }
111166
112- this . _notifications = notificationItems ;
113- this . _hasNextPage = notificationsData . hasNextPage ;
114-
115167 const notifications = Array . from ( this . _notifications . values ( ) ) ;
116168 this . _onDidChangeNotifications . fire ( notifications ) ;
117169
@@ -129,16 +181,50 @@ export class NotificationsManager {
129181 return Array . from ( this . _notifications . values ( ) ) ;
130182 }
131183
132- public async markAsRead ( notificationIdentifier : { threadId : string , notificationKey : string } ) : Promise < void > {
133- await this . _notificationProvider . markAsRead ( notificationIdentifier ) ;
184+ public refresh ( ) : void {
185+ if ( this . _notifications . size !== 0 ) {
186+ const updates = Array . from ( this . _notifications . values ( ) ) ;
187+ this . _onDidChangeNotifications . fire ( updates ) ;
188+ }
189+
190+ this . _pageCount = 1 ;
191+ this . _dateTime = new Date ( ) ;
192+ this . _notifications . clear ( ) ;
134193
194+ this . _refresh ( true ) ;
195+ }
196+
197+ public loadMore ( ) : void {
198+ this . _pageCount ++ ;
199+ this . _refresh ( true ) ;
200+ }
201+
202+ public _refresh ( fetch : boolean ) : void {
203+ this . _fetchNotifications = fetch ;
204+ this . _onDidChangeTreeData . fire ( ) ;
205+ }
206+
207+ public async markAsRead ( notificationIdentifier : { threadId : string , notificationKey : string } ) : Promise < void > {
135208 const notification = this . _notifications . get ( notificationIdentifier . notificationKey ) ;
136209 if ( notification ) {
210+ await this . _notificationProvider . markAsRead ( notificationIdentifier ) ;
211+
137212 this . _onDidChangeNotifications . fire ( [ notification ] ) ;
138213 this . _notifications . delete ( notificationIdentifier . notificationKey ) ;
214+
215+ this . _refresh ( false ) ;
139216 }
140217 }
141218
219+ public sortNotifications ( method : NotificationsSortMethod ) : void {
220+ if ( this . _sortingMethod === method ) {
221+ return ;
222+ }
223+
224+ this . _sortingMethod = method ;
225+ this . _refresh ( false ) ;
226+ }
227+
142228 private _sortNotifications ( notifications : NotificationTreeItem [ ] ) : NotificationTreeItem [ ] {
143229 if ( this . _sortingMethod === NotificationsSortMethod . Timestamp ) {
144230 return notifications . sort ( ( n1 , n2 ) => n2 . notification . updatedAd . getTime ( ) - n1 . notification . updatedAd . getTime ( ) ) ;
0 commit comments