1
1
mod subscriptions;
2
2
3
+ use cosmic:: cosmic_config:: { config_subscription, Config , CosmicConfigEntry } ;
3
4
use cosmic:: iced:: wayland:: popup:: { destroy_popup, get_popup} ;
4
5
use cosmic:: iced:: {
5
6
widget:: { button, column, row, text, Row , Space } ,
6
7
window, Alignment , Application , Color , Command , Length , Subscription ,
7
8
} ;
9
+ use cosmic:: iced_core:: image;
10
+ use cosmic:: iced_widget:: button:: StyleSheet ;
8
11
use cosmic_applet:: { applet_button_theme, CosmicAppletHelper } ;
9
12
10
13
use cosmic:: iced_style:: application:: { self , Appearance } ;
11
14
12
- use cosmic:: iced_widget:: Button ;
13
- use cosmic:: theme:: Svg ;
15
+ use cosmic:: iced_widget:: { horizontal_space , scrollable , Column } ;
16
+ use cosmic:: theme:: { Button , Svg } ;
14
17
use cosmic:: widget:: { divider, icon} ;
15
18
use cosmic:: Renderer ;
16
19
use cosmic:: { Element , Theme } ;
20
+ use cosmic_notifications_config:: NotificationsConfig ;
21
+ use cosmic_notifications_util:: { AppletEvent , Notification } ;
17
22
use cosmic_time:: { anim, chain, id, once_cell:: sync:: Lazy , Instant , Timeline } ;
18
- use cosmic_notifications_util:: AppletEvent ;
19
- use tracing:: info;
23
+ use std:: borrow:: Cow ;
20
24
use std:: process;
25
+ use tokio:: sync:: mpsc:: Sender ;
26
+ use tracing:: info;
21
27
22
- pub fn main ( ) -> cosmic:: iced:: Result {
28
+ #[ tokio:: main( flavor = "current_thread" ) ]
29
+ pub async fn main ( ) -> cosmic:: iced:: Result {
23
30
tracing_subscriber:: fmt:: init ( ) ;
24
31
25
32
info ! ( "Notifications applet" ) ;
@@ -34,12 +41,14 @@ static DO_NOT_DISTURB: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
34
41
struct Notifications {
35
42
applet_helper : CosmicAppletHelper ,
36
43
theme : Theme ,
44
+ config : NotificationsConfig ,
45
+ config_helper : Option < Config > ,
37
46
icon_name : String ,
38
47
popup : Option < window:: Id > ,
39
48
id_ctr : u128 ,
40
- do_not_disturb : bool ,
41
- notifications : Vec < Vec < String > > ,
49
+ notifications : Vec < Notification > ,
42
50
timeline : Timeline ,
51
+ dbus_sender : Option < Sender < subscriptions:: dbus:: Input > > ,
43
52
}
44
53
45
54
#[ derive( Debug , Clone ) ]
@@ -50,7 +59,10 @@ enum Message {
50
59
Ignore ,
51
60
Frame ( Instant ) ,
52
61
Theme ( Theme ) ,
53
- NotificationEvent ( AppletEvent )
62
+ NotificationEvent ( AppletEvent ) ,
63
+ Config ( NotificationsConfig ) ,
64
+ DbusEvent ( subscriptions:: dbus:: Output ) ,
65
+ Dismissed ( u32 ) ,
54
66
}
55
67
56
68
impl Application for Notifications {
@@ -62,11 +74,30 @@ impl Application for Notifications {
62
74
fn new ( _flags : ( ) ) -> ( Notifications , Command < Message > ) {
63
75
let applet_helper = CosmicAppletHelper :: default ( ) ;
64
76
let theme = applet_helper. theme ( ) ;
77
+ let helper = Config :: new (
78
+ cosmic_notifications_config:: ID ,
79
+ NotificationsConfig :: version ( ) ,
80
+ )
81
+ . ok ( ) ;
82
+
83
+ let config: NotificationsConfig = helper
84
+ . as_ref ( )
85
+ . map ( |helper| {
86
+ NotificationsConfig :: get_entry ( helper) . unwrap_or_else ( |( errors, config) | {
87
+ for err in errors {
88
+ tracing:: error!( "{:?}" , err) ;
89
+ }
90
+ config
91
+ } )
92
+ } )
93
+ . unwrap_or_default ( ) ;
65
94
(
66
95
Notifications {
67
96
applet_helper,
68
97
theme,
69
98
icon_name : "notification-alert-symbolic" . to_string ( ) ,
99
+ config_helper : helper,
100
+ config,
70
101
..Default :: default ( )
71
102
} ,
72
103
Command :: none ( ) ,
@@ -95,10 +126,25 @@ impl Application for Notifications {
95
126
fn subscription ( & self ) -> Subscription < Message > {
96
127
Subscription :: batch ( vec ! [
97
128
self . applet_helper. theme_subscription( 0 ) . map( Message :: Theme ) ,
129
+ config_subscription:: <u64 , NotificationsConfig >(
130
+ 0 ,
131
+ cosmic_notifications_config:: ID . into( ) ,
132
+ NotificationsConfig :: version( ) ,
133
+ )
134
+ . map( |( _, res) | match res {
135
+ Ok ( config) => Message :: Config ( config) ,
136
+ Err ( ( errors, config) ) => {
137
+ for err in errors {
138
+ tracing:: error!( "{:?}" , err) ;
139
+ }
140
+ Message :: Config ( config)
141
+ }
142
+ } ) ,
98
143
self . timeline
99
144
. as_subscription( )
100
145
. map( |( _, now) | Message :: Frame ( now) ) ,
101
- subscriptions:: notifications:: notifications( ) . map( Message :: NotificationEvent )
146
+ subscriptions:: dbus:: proxy( ) . map( Message :: DbusEvent ) ,
147
+ subscriptions:: notifications:: notifications( ) . map( Message :: NotificationEvent ) ,
102
148
] )
103
149
}
104
150
@@ -132,7 +178,12 @@ impl Application for Notifications {
132
178
}
133
179
Message :: DoNotDisturb ( chain, b) => {
134
180
self . timeline . set_chain ( chain) . start ( ) ;
135
- self . do_not_disturb = b;
181
+ self . config . do_not_disturb = b;
182
+ if let Some ( helper) = & self . config_helper {
183
+ if let Err ( err) = self . config . write_entry ( helper) {
184
+ tracing:: error!( "{:?}" , err) ;
185
+ }
186
+ }
136
187
Command :: none ( )
137
188
}
138
189
Message :: Settings => {
@@ -144,6 +195,28 @@ impl Application for Notifications {
144
195
Command :: none ( )
145
196
}
146
197
Message :: Ignore => Command :: none ( ) ,
198
+ Message :: Config ( config) => {
199
+ self . config = config;
200
+ Command :: none ( )
201
+ }
202
+ Message :: Dismissed ( id) => {
203
+ self . notifications . retain ( |n| n. id != id) ;
204
+ if let Some ( tx) = & self . dbus_sender {
205
+ let tx = tx. clone ( ) ;
206
+ tokio:: spawn ( async move {
207
+ if let Err ( err) = tx. send ( subscriptions:: dbus:: Input :: Dismiss ( id) ) . await {
208
+ tracing:: error!( "{:?}" , err) ;
209
+ }
210
+ } ) ;
211
+ }
212
+ Command :: none ( )
213
+ }
214
+ Message :: DbusEvent ( e) => match e {
215
+ subscriptions:: dbus:: Output :: Ready ( tx) => {
216
+ self . dbus_sender . replace ( tx) ;
217
+ Command :: none ( )
218
+ }
219
+ } ,
147
220
}
148
221
}
149
222
@@ -158,7 +231,7 @@ impl Application for Notifications {
158
231
DO_NOT_DISTURB ,
159
232
& self . timeline,
160
233
String :: from( "Do Not Disturb" ) ,
161
- self . do_not_disturb,
234
+ self . config . do_not_disturb,
162
235
Message :: DoNotDisturb
163
236
)
164
237
. width( Length :: Fill ) ]
@@ -176,7 +249,103 @@ impl Application for Notifications {
176
249
]
177
250
. spacing ( 12 )
178
251
} else {
179
- row ! [ text( "TODO: make app worky with notifications" ) ]
252
+ let mut notifs = Vec :: with_capacity ( self . notifications . len ( ) ) ;
253
+
254
+ for n in & self . notifications {
255
+ let summary = text ( if n. summary . len ( ) > 24 {
256
+ Cow :: from ( format ! (
257
+ "{:.26}..." ,
258
+ n. summary. lines( ) . next( ) . unwrap_or_default( )
259
+ ) )
260
+ } else {
261
+ Cow :: from ( & n. summary )
262
+ } )
263
+ . size ( 18 ) ;
264
+ let urgency = n. urgency ( ) ;
265
+
266
+ notifs. push (
267
+ cosmic:: widget:: button ( Button :: Custom {
268
+ active : Box :: new ( move |t| {
269
+ let style = if urgency > 1 {
270
+ Button :: Primary
271
+ } else {
272
+ Button :: Secondary
273
+ } ;
274
+ let cosmic = t. cosmic ( ) ;
275
+ let mut a = t. active ( & style) ;
276
+ a. border_radius = 8.0 . into ( ) ;
277
+ a. background = Some ( Color :: from ( cosmic. bg_color ( ) ) . into ( ) ) ;
278
+ a. border_color = Color :: from ( cosmic. bg_divider ( ) ) ;
279
+ a. border_width = 1.0 ;
280
+ a
281
+ } ) ,
282
+ hover : Box :: new ( move |t| {
283
+ let style = if urgency > 1 {
284
+ Button :: Primary
285
+ } else {
286
+ Button :: Secondary
287
+ } ;
288
+ let cosmic = t. cosmic ( ) ;
289
+ let mut a = t. hovered ( & style) ;
290
+ a. border_radius = 8.0 . into ( ) ;
291
+ a. background = Some ( Color :: from ( cosmic. bg_color ( ) ) . into ( ) ) ;
292
+ a. border_color = Color :: from ( cosmic. bg_divider ( ) ) ;
293
+ a. border_width = 1.0 ;
294
+ a
295
+ } ) ,
296
+ } )
297
+ . custom ( vec ! [ column!(
298
+ match n. image( ) {
299
+ Some ( cosmic_notifications_util:: Image :: File ( path) ) => {
300
+ row![ icon( path. as_path( ) , 32 ) , summary]
301
+ . spacing( 8 )
302
+ . align_items( Alignment :: Center )
303
+ }
304
+ Some ( cosmic_notifications_util:: Image :: Name ( name) ) => {
305
+ row![ icon( name. as_str( ) , 32 ) , summary]
306
+ . spacing( 8 )
307
+ . align_items( Alignment :: Center )
308
+ }
309
+ Some ( cosmic_notifications_util:: Image :: Data {
310
+ width,
311
+ height,
312
+ data,
313
+ } ) => {
314
+ let handle =
315
+ image:: Handle :: from_pixels( * width, * height, data. clone( ) ) ;
316
+ row![ icon( handle, 32 ) , summary]
317
+ . spacing( 8 )
318
+ . align_items( Alignment :: Center )
319
+ }
320
+ None => row![ summary] ,
321
+ } ,
322
+ text( if n. body. len( ) > 38 {
323
+ Cow :: from( format!(
324
+ "{:.40}..." ,
325
+ n. body. lines( ) . next( ) . unwrap_or_default( )
326
+ ) )
327
+ } else {
328
+ Cow :: from( & n. summary)
329
+ } )
330
+ . size( 14 ) ,
331
+ horizontal_space( Length :: Fixed ( 300.0 ) ) ,
332
+ )
333
+ . spacing( 8 )
334
+ . into( ) ] )
335
+ . on_press ( Message :: Dismissed ( n. id ) )
336
+ . into ( ) ,
337
+ ) ;
338
+ }
339
+
340
+ row ! ( scrollable(
341
+ Column :: with_children( notifs)
342
+ . spacing( 8 )
343
+ . width( Length :: Shrink )
344
+ . height( Length :: Shrink ) ,
345
+ )
346
+ . width( Length :: Shrink )
347
+ . height( Length :: Fixed ( 400.0 ) ) )
348
+ . width ( Length :: Shrink )
180
349
} ;
181
350
182
351
let main_content = column ! [
@@ -201,7 +370,9 @@ impl Application for Notifications {
201
370
}
202
371
203
372
// todo put into libcosmic doing so will fix the row_button's boarder radius
204
- fn row_button ( mut content : Vec < Element < Message > > ) -> Button < Message , Renderer > {
373
+ fn row_button (
374
+ mut content : Vec < Element < Message > > ,
375
+ ) -> cosmic:: iced:: widget:: Button < Message , Renderer > {
205
376
content. insert ( 0 , Space :: with_width ( Length :: Fixed ( 24.0 ) ) . into ( ) ) ;
206
377
content. push ( Space :: with_width ( Length :: Fixed ( 24.0 ) ) . into ( ) ) ;
207
378
0 commit comments