Skip to content

Commit

Permalink
feat: notifications (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
wolf4ood committed Sep 19, 2024
1 parent 0c9e6a7 commit b91d6a2
Show file tree
Hide file tree
Showing 14 changed files with 229 additions and 48 deletions.
32 changes: 27 additions & 5 deletions edc-connector-tui/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::rc::Rc;
use std::{rc::Rc, time::Duration};
mod action;
mod fetch;
pub mod model;
Expand All @@ -17,7 +17,8 @@ use crate::{
assets::AssetsComponent, connectors::ConnectorsComponent,
contract_definitions::ContractDefinitionsComponent, footer::Footer,
header::HeaderComponent, launch_bar::LaunchBar, policies::PolicyDefinitionsComponent,
Component, ComponentEvent, ComponentMsg, ComponentReturn,
Action, Component, ComponentEvent, ComponentMsg, ComponentReturn, Notification,
NotificationMsg,
},
config::{AuthKind, Config, ConnectorConfig},
types::{
Expand All @@ -29,6 +30,8 @@ use crate::{

use self::{model::AppFocus, msg::AppMsg};

const SERVICE: &str = "edc-connector-tui";

pub struct App {
connectors: ConnectorsComponent,
policies: PolicyDefinitionsComponent,
Expand All @@ -46,8 +49,7 @@ impl App {
match cfg.auth() {
AuthKind::NoAuth => (ConnectorStatus::Connected, Auth::NoAuth),
AuthKind::Token { token_alias } => {
let entry =
Entry::new("edc-tui", &token_alias).and_then(|entry| entry.get_password());
let entry = Entry::new(SERVICE, token_alias).and_then(|entry| entry.get_password());

match entry {
Ok(pwd) => (ConnectorStatus::Connected, Auth::api_token(pwd)),
Expand Down Expand Up @@ -105,6 +107,25 @@ impl App {
.key_binding("<:q>", "Quit")
}

pub fn show_notification(
&mut self,
noty: Notification,
) -> anyhow::Result<ComponentReturn<AppMsg>> {
let timeout = noty.timeout();
self.footer.show_notification(noty);

let action = Action::spawn(async move {
tokio::time::sleep(Duration::from_secs(timeout)).await;
Ok(Action::ClearNotification)
});
Ok(ComponentReturn::action(action))
}

pub fn clear_notification(&mut self) -> anyhow::Result<ComponentReturn<AppMsg>> {
self.footer.clear_notification();
Ok(ComponentReturn::empty())
}

pub fn change_sheet(&mut self) -> anyhow::Result<ComponentReturn<AppMsg>> {
let component_sheet = match self.header.selected_menu() {
Menu::Connectors => InfoSheet::default(),
Expand All @@ -127,7 +148,6 @@ impl App {
self.launch_bar.clear();
self.header.set_selected_menu(nav);
self.change_sheet()?;

match self.header.selected_menu() {
Menu::Connectors => {
self.focus = AppFocus::ConnectorList;
Expand Down Expand Up @@ -240,6 +260,8 @@ impl Component for App {
}
AppMsg::RoutingMsg(nav) => self.handle_routing(nav).await,
AppMsg::ChangeSheet => self.change_sheet(),
AppMsg::NontificationMsg(NotificationMsg::Show(noty)) => self.show_notification(noty),
AppMsg::NontificationMsg(NotificationMsg::Clear) => self.clear_notification(),
}
}

Expand Down
8 changes: 8 additions & 0 deletions edc-connector-tui/src/app/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ impl ActionHandler for App {
(AppFocus::LaunchBar, Action::Esc) => Ok(vec![AppMsg::HideLaunchBar.into()]),
(_, Action::NavTo(nav)) => Ok(vec![AppMsg::RoutingMsg(nav).into()]),
(_, Action::ChangeSheet) => Ok(vec![AppMsg::ChangeSheet.into()]),
(_, Action::Notification(noty)) => Ok(vec![AppMsg::NontificationMsg(
crate::components::NotificationMsg::Show(noty),
)
.into()]),
(_, Action::ClearNotification) => Ok(vec![AppMsg::NontificationMsg(
crate::components::NotificationMsg::Clear,
)
.into()]),
_ => Ok(vec![]),
}
}
Expand Down
3 changes: 2 additions & 1 deletion edc-connector-tui/src/app/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
components::{
assets::AssetsMsg, connectors::msg::ConnectorsMsg,
contract_definitions::ContractDefinitionsMsg, header::msg::HeaderMsg,
launch_bar::msg::LaunchBarMsg, policies::PoliciesMsg,
launch_bar::msg::LaunchBarMsg, policies::PoliciesMsg, NotificationMsg,
},
types::nav::Nav,
};
Expand All @@ -18,5 +18,6 @@ pub enum AppMsg {
ContractDefinitions(ContractDefinitionsMsg),
HeaderMsg(HeaderMsg),
RoutingMsg(Nav),
NontificationMsg(NotificationMsg),
ChangeSheet,
}
76 changes: 74 additions & 2 deletions edc-connector-tui/src/components.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{fmt::Debug, sync::Arc};
use std::{fmt::Debug, future::Future, sync::Arc};

use crossterm::event::Event;
use futures::{future::BoxFuture, FutureExt};
Expand Down Expand Up @@ -107,12 +107,83 @@ impl<'a, T: Debug> Debug for ComponentReturn<'a, T> {
}
}

#[derive(Debug, Clone)]
pub enum Action {
Quit,
Esc,
NavTo(Nav),
ChangeSheet,
Notification(Notification),
ClearNotification,
Spawn(BoxFuture<'static, anyhow::Result<Action>>),
}

impl Action {
pub fn spawn(fut: impl Future<Output = anyhow::Result<Action>> + Send + 'static) -> Action {
Action::Spawn(fut.boxed())
}
}

impl Debug for Action {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Quit => write!(f, "Quit"),
Self::Esc => write!(f, "Esc"),
Self::NavTo(arg0) => f.debug_tuple("NavTo").field(arg0).finish(),
Self::ChangeSheet => write!(f, "ChangeSheet"),
Self::Notification(arg0) => f.debug_tuple("Notification").field(arg0).finish(),
Self::Spawn(_arg0) => f.debug_tuple("Spawn").finish(),
Self::ClearNotification => f.debug_tuple("ClearNotification").finish(),
}
}
}

#[derive(Debug, Clone)]
pub struct Notification {
msg: String,
kind: NotificationKind,
timeout: u64,
}

#[derive(Debug, Clone)]
pub enum NotificationMsg {
Show(Notification),
Clear,
}

impl Notification {
pub fn error(msg: String) -> Notification {
Notification {
msg,
kind: NotificationKind::Error,
timeout: 5,
}
}

pub fn info(msg: String) -> Notification {
Notification {
msg,
kind: NotificationKind::Info,
timeout: 5,
}
}

pub fn msg(&self) -> &str {
&self.msg
}

pub fn kind(&self) -> &NotificationKind {
&self.kind
}

pub fn timeout(&self) -> u64 {
self.timeout
}
}

#[derive(Debug, Clone)]
pub enum NotificationKind {
Error,
Info,
}

impl<T> ComponentMsg<T> {
Expand All @@ -136,6 +207,7 @@ impl<'a, T: 'a> ComponentReturn<'a, T> {
actions: vec![],
}
}

pub fn empty() -> ComponentReturn<'a, T> {
ComponentReturn {
msgs: vec![],
Expand Down
43 changes: 38 additions & 5 deletions edc-connector-tui/src/components/footer.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
use ratatui::{
layout::Rect,
widgets::{Block, Borders},
layout::{Alignment, Rect},
style::{Color, Style},
text::{Span, Text},
widgets::{Block, Borders, Paragraph},
Frame,
};

use super::Component;
use super::{Component, Notification, NotificationKind};

pub mod msg;

#[derive(Default)]
pub struct Footer {}
pub struct Footer {
noty: Option<Notification>,
}

#[async_trait::async_trait]
impl Component for Footer {
Expand All @@ -18,6 +22,35 @@ impl Component for Footer {

fn view(&mut self, f: &mut Frame, rect: Rect) {
let block = Block::default().borders(Borders::all());
f.render_widget(block, rect)

let content = self.noty.as_ref().map(Notification::msg).unwrap_or("");

let style = self
.noty
.as_ref()
.map(Footer::map_color)
.unwrap_or_default();

let text = Text::from(Span::styled(content, style));
let p = Paragraph::new(text)
.block(block)
.alignment(Alignment::Center);
f.render_widget(p, rect)
}
}

impl Footer {
pub fn show_notification(&mut self, noty: Notification) {
self.noty = Some(noty);
}
pub fn clear_notification(&mut self) {
self.noty = None;
}

fn map_color(noty: &Notification) -> Style {
match noty.kind() {
NotificationKind::Error => Style::default().fg(Color::Red),
NotificationKind::Info => Style::default().fg(Color::Cyan),
}
}
}
2 changes: 1 addition & 1 deletion edc-connector-tui/src/components/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl Component for HeaderComponent {
HeaderMsg::NextTab => {
let current = self.menu.clone();
let idx = (self.menu.ordinal() + 1) % Menu::VALUES.len();
self.menu = Menu::from_ordinal(idx).unwrap_or_else(|| current);
self.menu = Menu::from_ordinal(idx).unwrap_or(current);
Ok(ComponentReturn::action(super::Action::NavTo(
self.menu.clone().into(),
)))
Expand Down
13 changes: 10 additions & 3 deletions edc-connector-tui/src/components/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{fmt::Debug, sync::Arc};
use self::{msg::ResourcesMsg, resource::ResourceComponent};
use super::{
table::{msg::TableMsg, TableEntry, UiTable},
Action, Component, ComponentEvent, ComponentMsg, ComponentReturn,
Action, Component, ComponentEvent, ComponentMsg, ComponentReturn, Notification,
};
use crate::types::{connector::Connector, info::InfoSheet};
use crossterm::event::{Event, KeyCode};
Expand Down Expand Up @@ -84,8 +84,12 @@ impl<T: DrawableResource + TableEntry + Send + Sync> Component for ResourcesComp
if let Some(on_fetch) = self.on_fetch.as_ref() {
Ok(ComponentReturn::cmd(
async move {
let elements = on_fetch(&connector).await?;
Ok(vec![ResourcesMsg::ResourcesFetched(elements).into()])
match on_fetch(&connector).await {
Ok(elements) => Ok(vec![ResourcesMsg::ResourcesFetched(elements).into()]),
Err(err) => Ok(vec![
ResourcesMsg::ResourcesFetchFailed(err.to_string()).into()
]),
}
}
.boxed(),
))
Expand Down Expand Up @@ -126,6 +130,9 @@ impl<T: DrawableResource + TableEntry + Send + Sync> Component for ResourcesComp
Self::forward_update(&mut self.resource, msg.into(), ResourcesMsg::ResourceMsg)
.await
}
ResourcesMsg::ResourcesFetchFailed(error) => Ok(ComponentReturn::action(
Action::Notification(Notification::error(error)),
)),
}
}

Expand Down
1 change: 1 addition & 0 deletions edc-connector-tui/src/components/resources/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ pub enum ResourcesMsg<T> {
TableEvent(TableMsg<Box<ResourcesMsg<T>>>),
ResourceMsg(ResourceMsg),
ResourcesFetched(Vec<T>),
ResourcesFetchFailed(String),
}
6 changes: 3 additions & 3 deletions edc-connector-tui/src/components/resources/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ impl<T: DrawableResource> ResourceComponent<T> {

fn handle_key(&self, key: KeyEvent) -> Vec<ComponentMsg<ResourceMsg>> {
match key.code {
KeyCode::Char('j') => vec![(ComponentMsg(ResourceMsg::MoveDown.into()))],
KeyCode::Char('k') => vec![(ComponentMsg(ResourceMsg::MoveUp.into()))],
KeyCode::Char('y') => vec![(ComponentMsg(ResourceMsg::Yank.into()))],
KeyCode::Char('j') => vec![(ComponentMsg(ResourceMsg::MoveDown))],
KeyCode::Char('k') => vec![(ComponentMsg(ResourceMsg::MoveUp))],
KeyCode::Char('y') => vec![(ComponentMsg(ResourceMsg::Yank))],
_ => vec![],
}
}
Expand Down
2 changes: 1 addition & 1 deletion edc-connector-tui/src/components/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ impl<T: TableEntry, M> UiTable<T, M> {
if let Some(cb) = self.on_select.as_ref() {
if let Some(idx) = self.table_state.selected() {
if let Some(element) = self.elements.get(idx) {
vec![ComponentMsg(TableMsg::Outer(cb(&element)))]
vec![ComponentMsg(TableMsg::Outer(cb(element)))]
} else {
vec![]
}
Expand Down
2 changes: 1 addition & 1 deletion edc-connector-tui/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl Config {

let config: Result<Config, toml::de::Error> = toml::from_str(&contents);
match config {
Ok(config) => return Ok(config),
Ok(config) => Ok(config),
Err(e) => panic!("fail to parse config file: {}", e),
}
}
Expand Down
Loading

0 comments on commit b91d6a2

Please sign in to comment.