1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! A dead simple notification daemon.

/// Error handler.
pub mod error;

/// D-Bus handler.
pub mod dbus;

/// Configuration.
pub mod config;

/// Notification manager.
pub mod notification;

/// Socket handler.
pub mod socket;

/// Rofi handler
pub mod rofi;

use crate::config::Config;
use crate::dbus::DbusServer;
use crate::error::Result;
use crate::notification::{Action, NOTIFICATION_MESSAGE_TEMPLATE};
use notification::Manager;
use socket::Socket;
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use tera::Tera;
use tracing_subscriber::EnvFilter;
use xdg::BaseDirectories;

/// Runs `nofi`.
pub fn run() -> Result<()> {
    let xdg_dirs = BaseDirectories::with_prefix(env!("CARGO_PKG_NAME"))?;
    let config = Arc::new(Config::parse(&xdg_dirs)?);

    tracing_subscriber::fmt()
        .with_env_filter(
            EnvFilter::builder()
                .with_default_directive(config.global.log_verbosity.into())
                .from_env_lossy(),
        )
        .init();
    tracing::trace!("{:#?}", config);
    tracing::info!("starting");

    let dbus_server = DbusServer::init()?;
    let timeout = Duration::from_millis(1000);

    let unix_socket = Socket::init(&xdg_dirs)?;
    let notifications = Manager::init();
    let unix_socket = Arc::new(unix_socket);
    let config_cloned = Arc::clone(&config);
    let mut template = Tera::default();
    template.add_raw_template(
        NOTIFICATION_MESSAGE_TEMPLATE,
        &config_cloned.global.template,
    )?;
    let config_cloned = Arc::clone(&config);
    let menu = Arc::new(rofi::Menu::init(config_cloned, template));

    let (sender, receiver) = mpsc::channel();

    thread::spawn(move || {
        tracing::debug!("registering D-Bus handler");
        dbus_server
            .register_notification_handler(sender, timeout)
            .expect("failed to register D-Bus notification handler");
    });

    loop {
        match receiver.recv()? {
            Action::Show(notification) => {
                tracing::debug!("received notification: {}", notification.id);
                notifications.add(notification);
                unix_socket.update(notifications.counts());
            }
            Action::ShowLast => {
                tracing::debug!("listing notifications");
                let notifications = notifications.clone();
                let all_notifications = notifications.all_unread();
                let menu_cloned = menu.clone();
                let unix_socket = unix_socket.clone();

                thread::spawn(move || {
                    let selected_notification = menu_cloned.list(&all_notifications);

                    if let Some(id) = selected_notification {
                        notifications.mark_as_read(id);
                        unix_socket.update(notifications.counts());
                    }
                });
            }
            Action::Close(id) => {
                if let Some(id) = id {
                    tracing::debug!("closing notification: {}", id);
                    notifications.mark_as_read(id);
                } else {
                    tracing::debug!("closing the last notification");
                    notifications.mark_last_as_read();
                }
                unix_socket.update(notifications.counts());
            }
            Action::CloseAll => {
                tracing::debug!("closing all notifications");
                notifications.mark_all_as_read();
                unix_socket.update(notifications.counts());
            }
        }
    }
}