zirv-ui 0.2.1

Component library for Yew
Documentation
use std::fmt::Debug;
use std::rc::Rc;

use time::Duration;
use uuid::Uuid;
use yew::{Reducible, UseReducerDispatcher};

use super::utils::Notifiable;

#[derive(Clone, PartialEq)]
pub struct ToastManager<T: Notifiable + PartialEq + Clone> {
    pub(crate) sender: Option<UseReducerDispatcher<ToastsList<T>>>,
}

impl<T: Notifiable + PartialEq + Clone> ToastManager<T> {
    pub fn spawn(&self, toast: T) {
        if let Some(sender) = &self.sender {
            sender.dispatch(Action::New(toast));
        }
    }
}

impl<T: Notifiable + PartialEq + Clone> Default for ToastManager<T> {
    fn default() -> Self {
        Self {
            sender: Default::default(),
        }
    }
}

#[derive(Debug)]
pub enum Action<T: Notifiable + PartialEq + Clone> {
    New(T),
    Close(Uuid),
    Tick,
    Pause(Uuid),
    Continue(Uuid),
}

#[derive(Debug, Clone, PartialEq)]
pub struct ToastsList<T> {
    pub toasts: Vec<T>,
}

impl<T> Default for ToastsList<T> {
    fn default() -> Self {
        Self {
            toasts: Default::default(),
        }
    }
}

impl<T> ToastsList<T> {
    pub const TIME_TICK_MILLIS: usize = 100;
    pub const TIME_TICK_DURATION: Duration = Duration::milliseconds(100);

    pub fn is_empty(&self) -> bool {
        self.toasts.is_empty()
    }
}

impl<T: Notifiable + PartialEq + Clone> Reducible for ToastsList<T> {
    type Action = Action<T>;

    fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
        match action {
            Action::New(toast) => {
                let mut toasts = self.toasts.clone();
                toasts.push(toast);

                Rc::new(Self { toasts })
            }
            Action::Close(id) => {
                let toasts = self
                    .toasts
                    .clone()
                    .into_iter()
                    .filter(|t| t.id() != id)
                    .collect();

                Rc::new(Self { toasts })
            }
            Action::Tick => {
                let toasts = self
                    .toasts
                    .clone()
                    .into_iter()
                    .filter_map(|mut t| {
                        if t.is_paused() {
                            Some(t)
                        } else if t.is_alive() {
                            t.apply_tick(Self::TIME_TICK_DURATION);

                            Some(t)
                        } else {
                            None
                        }
                    })
                    .collect();

                Rc::new(Self { toasts })
            }
            Action::Pause(id) => {
                let toasts = self
                    .toasts
                    .clone()
                    .into_iter()
                    .map(|mut t| {
                        if t.id() == id {
                            t.mouse_in();
                        }
                        t
                    })
                    .collect();

                Rc::new(Self { toasts })
            }
            Action::Continue(id) => {
                let toasts = self
                    .toasts
                    .clone()
                    .into_iter()
                    .map(|mut t| {
                        if t.id() == id {
                            t.mouse_out();
                        }
                        t
                    })
                    .collect();

                Rc::new(Self { toasts })
            }
        }
    }
}