@@ -9,6 +9,7 @@ const Applet = imports.ui.applet;
99const Cinnamon = imports . gi . Cinnamon ;
1010const Main = imports . ui . main ;
1111const DND = imports . ui . dnd ;
12+ const NotificationDestroyedReason = imports . ui . messageTray . NotificationDestroyedReason ;
1213const { AppletSettings} = imports . ui . settings ;
1314const { SignalManager} = imports . misc . signalManager ;
1415const { throttle, unref, trySpawnCommandLine} = imports . misc . util ;
@@ -161,6 +162,124 @@ class PinnedFavs {
161162 }
162163}
163164
165+ class Notifications {
166+ // As an app can have multiple appgroups (multiple windows, different workspaces, multiple instances), all
167+ // appGroups with the same appId should always display the same number of notifications.
168+ constructor ( ) {
169+ this . _appGroupList = [ ] ; //stores all appGroups from all workspaces
170+ Main . messageTray . connect ( 'notify-applet-update' , ( mtray , notification ) => this . _notificationReceived ( mtray , notification ) ) ;
171+ }
172+
173+ _notificationReceived ( mtray , notification ) {
174+ const guessFlatpakAppIdFromDesktopEntryHint = ( desktopEntry ) => {
175+ let tryAppId = desktopEntry + '.desktop:flatpak' ;
176+ if ( this . _appGroupList . some ( appGroup => appGroup . groupState . appId === tryAppId ) ) {
177+ return tryAppId ;
178+ }
179+ const exceptions = {
180+ "vivaldi-stable" : "com.vivaldi.Vivaldi" ,
181+ "brave-browser" : "com.brave.Browser" ,
182+ "google-chrome" : "com.google.Chrome" ,
183+ "microsoft-edge" : "com.microsoft.Edge" ,
184+ "opera" : "com.opera.Opera"
185+ } ;
186+ if ( exceptions [ desktopEntry ] ) {
187+ tryAppId = exceptions [ desktopEntry ] + '.desktop:flatpak' ;
188+ if ( this . _appGroupList . some ( appGroup => appGroup . groupState . appId === tryAppId ) ) {
189+ return tryAppId ;
190+ }
191+ }
192+ } ;
193+
194+ let appId = notification . source . app ?. get_id ( ) ;
195+ if ( ! appId ) {
196+ appId = guessFlatpakAppIdFromDesktopEntryHint ( notification . desktopEntry ) ;
197+ }
198+ if ( ! appId ) {
199+ global . logError ( 'GWL: Failed to find appId for notification with desktopEntry hint: '
200+ + notification . desktopEntry ) ;
201+ return ;
202+ }
203+
204+ // Add notification to all appgroups with appId
205+ let notificationAdded = false ;
206+ this . _appGroupList . forEach ( appGroup => {
207+ if ( ! appGroup . groupState || appGroup . groupState . willUnmount ) return ;
208+ if ( appId === appGroup . groupState . appId ) {
209+ appGroup . notifications . push ( notification ) ;
210+ notificationAdded = true ;
211+ this . updateNotificationsBadge ( appGroup ) ;
212+ }
213+ } ) ;
214+ if ( notificationAdded ) {
215+ notification . appId = appId ;
216+ notification . connect ( 'destroy' , ( ) => this . _removeNotification ( notification ) ) ;
217+ }
218+ }
219+
220+ _removeNotification ( notification ) {
221+ this . _appGroupList . forEach ( appGroup => {
222+ if ( ! appGroup . groupState || appGroup . groupState . willUnmount ) return ;
223+ if ( notification . appId === appGroup . groupState . appId ) {
224+ const index = appGroup . notifications . indexOf ( notification ) ;
225+ if ( index > - 1 ) {
226+ appGroup . notifications . splice ( index , 1 )
227+ this . updateNotificationsBadge ( appGroup ) ;
228+ }
229+ }
230+ } ) ;
231+ }
232+
233+ // Called when an app is focused to remove all notifications from all instances of an app (with given appId)
234+ removeAllNotifications ( appId ) {
235+ this . _appGroupList . forEach ( appGroup => {
236+ if ( ! appGroup . groupState || appGroup . groupState . willUnmount ) return ;
237+ if ( appId === appGroup . groupState . appId ) {
238+ // iterate backwards due to in place array modification
239+ for ( let i = appGroup . notifications . length - 1 ; i >= 0 ; i -- ) {
240+ if ( appGroup . notifications [ i ] && ! appGroup . notifications [ i ] . _destroyed ) {
241+ appGroup . notifications [ i ] . destroy ( NotificationDestroyedReason . DISMISSED ) ;
242+ }
243+ }
244+ appGroup . notifications = [ ] ;
245+ this . updateNotificationsBadge ( appGroup ) ;
246+ }
247+ } ) ;
248+ }
249+
250+ updateNotificationsBadge ( appGroup ) {
251+ if ( appGroup . notifications . length > 0 ) {
252+ appGroup . notificationsBadgeLabel . text = appGroup . notifications . length . toString ( ) ;
253+ appGroup . notificationsBadge . show ( ) ;
254+ } else {
255+ appGroup . notificationsBadge . hide ( ) ;
256+ }
257+ }
258+
259+ // Called from AppGroup constructor so that we always have a list of all appgroups from all workspaces.
260+ addAppGroup ( newAppGroup ) {
261+ newAppGroup . notifications = [ ] ;
262+
263+ //Copy notifications from any existing appGroup with the same appId.
264+ this . _appGroupList . some ( appGroup => {
265+ if ( ! appGroup . groupState || appGroup . groupState . willUnmount ) return false ;
266+ if ( appGroup . groupState . appId === newAppGroup . groupState . appId ) {
267+ newAppGroup . notifications = appGroup . notifications . slice ( ) ; //shallow copy
268+ return true ;
269+ }
270+ } )
271+
272+ this . _appGroupList . push ( newAppGroup ) ;
273+
274+ //remove old deleted appgroups
275+ this . _appGroupList = this . _appGroupList . filter ( appGroup =>
276+ appGroup !== null &&
277+ appGroup !== undefined &&
278+ appGroup . groupState &&
279+ ! appGroup . groupState . willUnmount ) ;
280+ }
281+ }
282+
164283class GroupedWindowListApplet extends Applet . Applet {
165284 constructor ( metadata , orientation , panel_height , instance_id ) {
166285 super ( orientation , panel_height , instance_id ) ;
@@ -191,6 +310,7 @@ class GroupedWindowListApplet extends Applet.Applet {
191310 appletReady : false ,
192311 willUnmount : false ,
193312 settings : { } ,
313+ notifications : new Notifications ( ) ,
194314 homeDir : GLib . get_home_dir ( ) ,
195315 lastOverlayPreview : null ,
196316 lastCycled : - 1 ,
@@ -307,7 +427,6 @@ class GroupedWindowListApplet extends Applet.Applet {
307427 { key : 'super-num-hotkeys' , value : 'SuperNumHotkeys' , cb : this . bindAppKeys } ,
308428 { key : 'title-display' , value : 'titleDisplay' , cb : this . updateTitleDisplay } ,
309429 { key : 'launcher-animation-effect' , value : 'launcherAnimationEffect' , cb : null } ,
310- { key : 'number-display' , value : 'numDisplay' , cb : this . updateWindowNumberState } ,
311430 { key : 'enable-app-button-dragging' , value : 'enableDragging' , cb : this . draggableSettingChanged } ,
312431 { key : 'thumbnail-scroll-behavior' , value : 'thumbnailScrollBehavior' , cb : null } ,
313432 { key : 'show-thumbnails' , value : 'showThumbs' , cb : this . updateVerticalThumbnailState } ,
@@ -584,12 +703,6 @@ class GroupedWindowListApplet extends Applet.Applet {
584703 } ) ;
585704 }
586705
587- updateWindowNumberState ( ) {
588- this . workspaces . forEach (
589- workspace => workspace . calcAllWindowNumbers ( )
590- ) ;
591- }
592-
593706 updateAttentionState ( display , window ) {
594707 this . workspaces . forEach (
595708 workspace => workspace . updateAttentionState ( display , window )
0 commit comments