@@ -9,7 +9,9 @@ use std::{
9
9
10
10
use lazy_static:: lazy_static;
11
11
use parking_lot:: Mutex ;
12
- use seelen_core:: system_state:: { AppNotification , Toast , ToastBindingEntry } ;
12
+ use seelen_core:: system_state:: {
13
+ AppNotification , Toast , ToastActionActivationType , ToastBindingChild , ToastText ,
14
+ } ;
13
15
use tauri:: Manager ;
14
16
use windows:: {
15
17
ApplicationModel :: AppInfo ,
@@ -27,10 +29,10 @@ use crate::{
27
29
app:: get_app_handle,
28
30
error:: Result ,
29
31
event_manager, log_error,
30
- modules:: uwp:: get_hightest_quality_posible,
32
+ modules:: { start :: application :: START_MENU_MANAGER , uwp:: get_hightest_quality_posible} ,
31
33
trace_lock,
32
34
utils:: { convert_file_to_src, icon_extractor:: extract_and_save_icon_umid, spawn_named_thread} ,
33
- windows_api:: traits:: EventRegistrationTokenExt ,
35
+ windows_api:: { traits:: EventRegistrationTokenExt , types :: AppUserModelId , WindowsApi } ,
34
36
} ;
35
37
36
38
lazy_static ! {
@@ -183,20 +185,26 @@ impl NotificationManager {
183
185
}
184
186
185
187
fn clean_toast ( toast : & mut Toast , umid : & str ) -> Result < ( ) > {
188
+ if toast. launch . is_none ( ) {
189
+ toast. launch = Some ( format ! ( "shell:AppsFolder\\ {umid}" ) ) ;
190
+ toast. activation_type = ToastActionActivationType :: Protocol ;
191
+ }
192
+
186
193
let package_path = AppInfo :: GetFromAppUserModelId ( & umid. into ( ) )
187
194
. and_then ( |info| info. Package ( ) )
188
195
. and_then ( |package| package. InstalledPath ( ) )
189
196
. map ( |path| PathBuf :: from ( path. to_os_string ( ) ) ) ;
190
197
191
- for entry in & mut toast. visual . binding . entries {
192
- let ToastBindingEntry :: Image ( image) = entry else {
198
+ for entry in & mut toast. visual . binding . children {
199
+ let ToastBindingChild :: Image ( image) = entry else {
193
200
continue ;
194
201
} ;
195
202
196
203
if image. src . is_empty ( ) {
197
204
continue ;
198
205
}
199
206
207
+ log:: trace!( "Resolving image src:e \" {}\" " , image. src) ;
200
208
let uri = Uri :: CreateUri ( & image. src . clone ( ) . into ( ) ) ?;
201
209
let scheme = uri. SchemeName ( ) ?. to_string_lossy ( ) ;
202
210
let uri_path = PathBuf :: from (
@@ -252,7 +260,14 @@ impl NotificationManager {
252
260
image. src = convert_file_to_src ( & path) ;
253
261
}
254
262
"file" => {
255
- image. src = convert_file_to_src ( & uri_path) ;
263
+ image. src = if uri_path. exists ( ) {
264
+ convert_file_to_src ( & uri_path)
265
+ } else {
266
+ // telegram desktop from ms store uses file intead of ms-appdata
267
+ // so this causes the image to be missing, windows doesn't show image as well
268
+ // so we decide to follow same behavior.
269
+ "" . to_string ( )
270
+ }
256
271
}
257
272
_ => { }
258
273
}
@@ -280,15 +295,13 @@ impl NotificationManager {
280
295
let app_umid = app_info. AppUserModelId ( ) ?;
281
296
282
297
let visuals = notification. Visual ( ) ?;
283
- let text_sequence = visuals
284
- . GetBinding ( & KnownNotificationBindings :: ToastGeneric ( ) ?) ?
285
- . GetTextElements ( ) ?;
286
- let mut notification_text = Vec :: new ( ) ;
287
- for text in text_sequence {
288
- let text = text. Text ( ) ?. to_string_lossy ( ) . trim ( ) . to_string ( ) ;
289
- if !text. is_empty ( ) {
290
- notification_text. push ( text) ;
291
- }
298
+ let binding = visuals. GetBinding ( & KnownNotificationBindings :: ToastGeneric ( ) ?) ?;
299
+ let text_sequence = binding. GetTextElements ( ) ?;
300
+
301
+ let mut notification_text = String :: new ( ) ;
302
+ for text in & text_sequence {
303
+ let text = text. Text ( ) ?. to_string_lossy ( ) . trim ( ) . replace ( "\r \n " , "\n " ) ;
304
+ notification_text. push_str ( & text) ;
292
305
}
293
306
294
307
let history = self . manager . History ( ) ?;
@@ -304,29 +317,46 @@ impl NotificationManager {
304
317
for toast_notification in toast_notifications {
305
318
// this can be null when the notification count is bigger than the max allowed by default 20
306
319
if let Ok ( content) = toast_notification. Content ( ) {
307
- let data = content. GetXml ( ) ?. to_string ( ) ;
308
- let mut toast: Toast = quick_xml:: de:: from_str ( & data) ?;
309
-
310
- let mut toast_text = Vec :: new ( ) ;
311
- for entry in & toast. visual . binding . entries {
312
- if let ToastBindingEntry :: Text ( text) = entry {
313
- if !text. content . is_empty ( ) {
314
- toast_text. push ( text. content . clone ( ) . replace ( "\r \n " , "\n " ) ) ;
315
- }
316
- }
317
- }
320
+ let toast_xml = content. GetXml ( ) ?. to_string ( ) ;
321
+ let mut toast: Toast = quick_xml:: de:: from_str ( & toast_xml) ?;
322
+ let toast_text = get_text_from_toast ( & toast) ;
318
323
319
324
if notification_text == toast_text {
320
325
Self :: clean_toast ( & mut toast, & app_umid. to_string ( ) ) ?;
321
326
notification_content = Some ( toast) ;
327
+
328
+ println ! (
329
+ "??????????? Group {:?} - Tag: {:?}" ,
330
+ toast_notification. Group ( ) ,
331
+ toast_notification. Tag ( )
332
+ ) ;
322
333
break ;
323
334
}
324
335
}
325
336
}
326
337
327
- if notification_content. is_none ( ) {
328
- log:: debug!( "NONE FOR {notification_text:#?}" ) ;
329
- }
338
+ let notification_content = match notification_content {
339
+ Some ( content) => content,
340
+ None => {
341
+ log:: debug!( "Toast content not found, generating one from plain text" ) ;
342
+ let mut toast = Toast :: default ( ) ;
343
+ let content = & mut toast. visual . binding . children ;
344
+ for text in text_sequence {
345
+ let text = text
346
+ . Text ( ) ?
347
+ . to_string_lossy ( )
348
+ . replace ( "\r \n " , "\n " )
349
+ . trim ( )
350
+ . to_owned ( ) ;
351
+ content. push ( ToastBindingChild :: Text ( ToastText {
352
+ id : None ,
353
+ content : text,
354
+ } ) ) ;
355
+ }
356
+ Self :: clean_toast ( & mut toast, & app_umid. to_string ( ) ) ?;
357
+ toast
358
+ }
359
+ } ;
330
360
331
361
// pre-extraction to avoid flickering on the ui
332
362
extract_and_save_icon_umid ( & app_umid. to_string ( ) . into ( ) ) ;
@@ -337,9 +367,36 @@ impl NotificationManager {
337
367
app_name : display_info. DisplayName ( ) ?. to_string ( ) ,
338
368
app_description : display_info. Description ( ) ?. to_string ( ) ,
339
369
date : u_notification. CreationTime ( ) ?. UniversalTime ,
340
- content : notification_content. ok_or ( "Failed to get notification content" ) ? ,
370
+ content : notification_content,
341
371
} ) ;
342
372
self . notifications . sort_by ( |a, b| b. date . cmp ( & a. date ) ) ;
343
373
Ok ( ( ) )
344
374
}
345
375
}
376
+
377
+ fn get_text_from_toast ( toast : & Toast ) -> String {
378
+ let mut text = String :: new ( ) ;
379
+ for entry in & toast. visual . binding . children {
380
+ // text inside groups are intended to be ignored for the comparison
381
+ if let ToastBindingChild :: Text ( entry) = entry {
382
+ text. push_str ( entry. content . replace ( "\r \n " , "\n " ) . trim ( ) ) ;
383
+ }
384
+ }
385
+ text
386
+ }
387
+
388
+ pub fn get_toast_activator_clsid ( app_umid : & AppUserModelId ) -> Result < String > {
389
+ match app_umid {
390
+ AppUserModelId :: PropertyStore ( umid) => {
391
+ let guard = START_MENU_MANAGER . load ( ) ;
392
+ if let Some ( item) = guard. get_by_file_umid ( umid) {
393
+ let clsid = WindowsApi :: get_file_toast_activator ( & item. path ) ?;
394
+ return Ok ( clsid) ;
395
+ }
396
+ }
397
+ _ => {
398
+ // todo search for the clsid in the AppManifest
399
+ }
400
+ } ;
401
+ Err ( format ! ( "Unable to get toast activator clsid for: {app_umid:?}" ) . into ( ) )
402
+ }
0 commit comments