wayclip-daemon 0.2.0

Recording and management of Wayclip daemon
use chrono::Utc;
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;
use wayclip_core::{models::error::WayclipError, settings::UserSettings};

use crate::{
    common::misc::discord::{DiscordPresenceManager, DiscordPresenceState},
    linux::{
        ipc::commands::IpcCommand,
        services::{discovery::Discovery, keybinds::KeybindsService, tray::TrayManager},
        session::CurrentSession,
    },
};

pub mod discovery;
pub mod keybinds;
pub mod tray;

pub struct DaemonServices {
    pub discovery: Option<Discovery>,
    discord: Option<DiscordPresenceManager>,
    keybinds: KeybindsService,
    tray: TrayManager,
}

impl DaemonServices {
    pub fn new(
        user_settings: &UserSettings,
        cancel_token: CancellationToken,
    ) -> Result<Self, WayclipError> {
        let discovery = match user_settings.game_discovery.enabled {
            true => Some(Discovery::new()?),
            false => None,
        };

        let discord = match user_settings.game_discovery.discord_rich_presence.enabled {
            true => {
                let state = if let Some(custom) = user_settings
                    .game_discovery
                    .discord_rich_presence
                    .custom
                    .clone()
                {
                    DiscordPresenceState::Custom(custom.into())
                } else {
                    DiscordPresenceState::Default
                };

                Some(DiscordPresenceManager::new(state))
            }
            false => None,
        };

        Ok(Self {
            discord,
            discovery,
            tray: TrayManager::new(user_settings.tray.clone(), cancel_token.clone()),
            keybinds: KeybindsService::new(user_settings)?,
        })
    }

    pub fn update(&mut self, current_session: &mut CurrentSession) -> Result<(), WayclipError> {
        if let Some(ref mut disc) = self.discovery {
            if let Some(new_game) = disc.poll_changed() {
                log::info!("Game changed to: {:?}", new_game.as_ref().map(|g| &g.name));
                current_session.tick(new_game, None);
                if let Some(ref d) = self.discord {
                    d.set_recording(current_session.clone());
                }
            }
        }

        // if user saved clip, the status would've updated, but it is not changed back unless a game
        // has changed. now, we make sure to update status once last clip was more than 3 min ago
        if let Some(ref last) = current_session.last_clip
            && last
                .timestamp
                .signed_duration_since(Utc::now())
                .num_minutes()
                >= 3
        {
            if let Some(ref d) = self.discord {
                d.set_recording(current_session.clone());
            }
        }
        Ok(())
    }

    pub fn saving(&mut self, current_session: CurrentSession) -> Result<(), WayclipError> {
        if let Some(ref d) = self.discord {
            d.set_saving(current_session);
        }

        Ok(())
    }

    pub fn start_services(
        &mut self,
        user_settings: &UserSettings,
        command_sender: &mpsc::Sender<IpcCommand>,
        current_session: &mut CurrentSession,
    ) -> Result<(), WayclipError> {
        self.tray.start(command_sender);
        self.keybinds
            .setup_keybinds(user_settings, command_sender)?;
        // active it once, so even if no game is playing, discord status shows up
        if let Some(ref d) = self.discord {
            d.set_recording(current_session.clone());
        }
        Ok(())
    }

    pub async fn stop_services(&mut self) -> Result<(), WayclipError> {
        self.tray.stop().await;
        self.keybinds.stop()
    }
}