forked from trinitrix/core
Refactor(events): Split up event handling into multiple files
This commit is contained in:
parent
05d4b4d097
commit
ef5afcda02
384
src/app/event.rs
384
src/app/event.rs
|
@ -1,384 +0,0 @@
|
|||
use anyhow::{Error, Result};
|
||||
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use matrix_sdk::{config::SyncSettings, Client, LoopCtrl};
|
||||
use tokio::{sync::mpsc, time::Duration};
|
||||
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::{
|
||||
app::{status::State, App},
|
||||
ui,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EventStatus {
|
||||
Ok,
|
||||
Finished,
|
||||
Terminate,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Event {
|
||||
input_event: Option<crossterm::event::Event>,
|
||||
matrix_event: Option<matrix_sdk::deserialized_responses::SyncResponse>,
|
||||
}
|
||||
|
||||
pub struct EventBuilder {
|
||||
event: Event,
|
||||
}
|
||||
|
||||
impl Default for Event {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
input_event: None,
|
||||
matrix_event: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EventBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
event: Event::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventBuilder {
|
||||
fn input_event(&mut self, input_event: crossterm::event::Event) -> &Self {
|
||||
self.event.input_event = Some(input_event);
|
||||
self
|
||||
}
|
||||
|
||||
fn matrix_event(
|
||||
&mut self,
|
||||
matrix_event: matrix_sdk::deserialized_responses::SyncResponse,
|
||||
) -> &Self {
|
||||
self.event.matrix_event = Some(matrix_event);
|
||||
self
|
||||
}
|
||||
|
||||
fn build(&self) -> Event {
|
||||
Event {
|
||||
input_event: self.event.input_event.clone(),
|
||||
matrix_event: self.event.matrix_event.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub async fn handle(&self, app: &mut App<'_>) -> Result<EventStatus> {
|
||||
if self.matrix_event.is_some() {
|
||||
return self.handle_matrix(app).await;
|
||||
}
|
||||
|
||||
let status = match app.status.state() {
|
||||
State::None => EventStatus::Ok,
|
||||
State::Main => self.handle_main(app).await?,
|
||||
State::Setup => self.handle_setup(app).await?,
|
||||
};
|
||||
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
async fn handle_matrix(&self, app: &mut App<'_>) -> Result<EventStatus> {
|
||||
let sync = self.matrix_event.clone().unwrap();
|
||||
for (m_room_id, m_room) in sync.rooms.join.iter() {
|
||||
let room = match app.status.get_room_mut(m_room_id) {
|
||||
Some(r) => r,
|
||||
None => continue,
|
||||
};
|
||||
for m_event in m_room.timeline.events.clone() {
|
||||
let event = m_event
|
||||
.event
|
||||
.deserialize()
|
||||
.unwrap()
|
||||
.into_full_event(m_room_id.clone());
|
||||
room.timeline_add(event);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(EventStatus::Ok)
|
||||
}
|
||||
|
||||
async fn handle_main(&self, app: &mut App<'_>) -> Result<EventStatus> {
|
||||
if self.input_event.is_some() {
|
||||
match self.input_event.clone().unwrap() {
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Esc, ..
|
||||
}) => return Ok(EventStatus::Terminate),
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Tab, ..
|
||||
}) => {
|
||||
app.ui.cycle_main_input_position();
|
||||
}
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::BackTab,
|
||||
..
|
||||
}) => {
|
||||
app.ui.cycle_main_input_position_rev();
|
||||
}
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Char('c'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
}) => {
|
||||
app.ui.cli_enable();
|
||||
}
|
||||
input => match app.ui.input_position() {
|
||||
ui::MainInputPosition::MessageCompose => {
|
||||
match input {
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Enter,
|
||||
modifiers: KeyModifiers::ALT,
|
||||
..
|
||||
}) => {
|
||||
match app.status.room_mut() {
|
||||
Some(room) => {
|
||||
room.send(app.ui.message_compose.lines().join("\n"))
|
||||
.await?;
|
||||
app.ui.message_compose_clear();
|
||||
}
|
||||
None => (),
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
app.ui
|
||||
.message_compose
|
||||
.input(tui_textarea::Input::from(input));
|
||||
}
|
||||
};
|
||||
}
|
||||
ui::MainInputPosition::Rooms => {
|
||||
match input {
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Up, ..
|
||||
}) => {
|
||||
let i = match app.ui.rooms_state.selected() {
|
||||
Some(cur) => {
|
||||
if cur > 0 {
|
||||
cur - 1
|
||||
} else {
|
||||
cur
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
app.ui.rooms_state.select(Some(i));
|
||||
app.status.set_room_by_index(i)?;
|
||||
}
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Down,
|
||||
..
|
||||
}) => {
|
||||
let i = match app.ui.rooms_state.selected() {
|
||||
Some(cur) => {
|
||||
if cur < app.status.rooms().len() - 1 {
|
||||
cur + 1
|
||||
} else {
|
||||
cur
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
app.ui.rooms_state.select(Some(i));
|
||||
app.status.set_room_by_index(i)?;
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
ui::MainInputPosition::Messages => {
|
||||
match input {
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Up, ..
|
||||
}) => {
|
||||
match app.status.room_mut() {
|
||||
Some(room) => {
|
||||
let len = room.timeline().len();
|
||||
let i = match room.view_scroll() {
|
||||
Some(i) => i + 1,
|
||||
None => 0,
|
||||
};
|
||||
if i < len {
|
||||
room.set_view_scroll(Some(i))
|
||||
}
|
||||
if i <= len - 5 {
|
||||
room.poll_old_timeline().await?;
|
||||
}
|
||||
}
|
||||
None => (),
|
||||
};
|
||||
}
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Down,
|
||||
..
|
||||
}) => {
|
||||
match app.status.room_mut() {
|
||||
Some(room) => {
|
||||
match room.view_scroll() {
|
||||
Some(i) => {
|
||||
if i == 0 {
|
||||
room.set_view_scroll(None);
|
||||
} else {
|
||||
room.set_view_scroll(Some(i - 1));
|
||||
}
|
||||
}
|
||||
None => (),
|
||||
};
|
||||
}
|
||||
None => (),
|
||||
};
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
ui::MainInputPosition::CLI => {
|
||||
if let Some(cli) = &mut app.ui.cli {
|
||||
match input {
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Enter,
|
||||
..
|
||||
}) => {
|
||||
let cli_event = cli.lines()[0].clone();
|
||||
app.status.cli_event(cli_event);
|
||||
app.ui.cli_disable();
|
||||
}
|
||||
_ => {
|
||||
cli.input(tui_textarea::Input::from(input));
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
};
|
||||
}
|
||||
Ok(EventStatus::Ok)
|
||||
}
|
||||
|
||||
async fn handle_setup(&self, app: &mut App<'_>) -> Result<EventStatus> {
|
||||
let ui = match &mut app.ui.setup_ui {
|
||||
Some(ui) => ui,
|
||||
None => return Err(Error::msg("SetupUI instance not found")),
|
||||
};
|
||||
|
||||
if self.input_event.is_some() {
|
||||
match self.input_event.clone().unwrap() {
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Esc, ..
|
||||
}) => return Ok(EventStatus::Terminate),
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Tab, ..
|
||||
}) => {
|
||||
ui.cycle_input_position();
|
||||
}
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::BackTab,
|
||||
..
|
||||
}) => {
|
||||
ui.cycle_input_position_rev();
|
||||
}
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
code: KeyCode::Enter,
|
||||
..
|
||||
}) => {
|
||||
match ui.input_position() {
|
||||
ui::SetupInputPosition::Ok => {
|
||||
let homeserver = ui.homeserver.lines()[0].clone();
|
||||
let username = ui.username.lines()[0].clone();
|
||||
let password = ui.password_data.lines()[0].clone();
|
||||
let login = app.login(&homeserver, &username, &password).await;
|
||||
if login.is_ok() {
|
||||
return Ok(EventStatus::Finished);
|
||||
}
|
||||
}
|
||||
_ => ui.cycle_input_position(),
|
||||
};
|
||||
}
|
||||
input => match ui.input_position() {
|
||||
ui::SetupInputPosition::Homeserver => {
|
||||
ui.homeserver.input(input);
|
||||
}
|
||||
ui::SetupInputPosition::Username => {
|
||||
ui.username.input(input);
|
||||
}
|
||||
ui::SetupInputPosition::Password => {
|
||||
let textarea_input = tui_textarea::Input::from(input);
|
||||
ui.password_data.input(textarea_input.clone());
|
||||
match textarea_input.key {
|
||||
tui_textarea::Key::Char(_) => {
|
||||
ui.password.input(tui_textarea::Input {
|
||||
key: tui_textarea::Key::Char('*'),
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
ui.password.input(textarea_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
};
|
||||
}
|
||||
Ok(EventStatus::Ok)
|
||||
}
|
||||
}
|
||||
|
||||
async fn poll_input_events_stage_2(channel: mpsc::Sender<Event>) -> Result<()> {
|
||||
loop {
|
||||
if crossterm::event::poll(Duration::from_millis(100))? {
|
||||
let event = EventBuilder::default()
|
||||
.input_event(crossterm::event::read()?)
|
||||
.build();
|
||||
|
||||
channel.send(event).await?;
|
||||
} else {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn poll_input_events(
|
||||
channel: mpsc::Sender<Event>,
|
||||
kill: CancellationToken,
|
||||
) -> Result<()> {
|
||||
tokio::select! {
|
||||
output = poll_input_events_stage_2(channel) => output,
|
||||
_ = kill.cancelled() => Err(Error::msg("received kill signal"))
|
||||
}
|
||||
}
|
||||
|
||||
async fn poll_matrix_events_stage_2(channel: mpsc::Sender<Event>, client: Client) -> Result<()> {
|
||||
let sync_settings = SyncSettings::default();
|
||||
// .token(sync_token)
|
||||
// .timeout(Duration::from_secs(30));
|
||||
|
||||
let tx = &channel;
|
||||
|
||||
client
|
||||
.sync_with_callback(sync_settings, |response| async move {
|
||||
let event = EventBuilder::default().matrix_event(response).build();
|
||||
|
||||
match tx.send(event).await {
|
||||
Ok(_) => LoopCtrl::Continue,
|
||||
Err(_) => LoopCtrl::Break,
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn poll_matrix_events(
|
||||
channel: mpsc::Sender<Event>,
|
||||
kill: CancellationToken,
|
||||
client: Client,
|
||||
) -> Result<()> {
|
||||
tokio::select! {
|
||||
output = poll_matrix_events_stage_2(channel, client) => output,
|
||||
_ = kill.cancelled() => Err(Error::msg("received kill signal")),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
use anyhow::Result;
|
||||
use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyEvent, KeyModifiers};
|
||||
|
||||
use crate::{
|
||||
app::{events::event_types::EventStatus, App},
|
||||
ui,
|
||||
};
|
||||
|
||||
pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<EventStatus> {
|
||||
match input_event {
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Esc, ..
|
||||
}) => return Ok(EventStatus::Terminate),
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Tab, ..
|
||||
}) => {
|
||||
app.ui.cycle_main_input_position();
|
||||
}
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::BackTab,
|
||||
..
|
||||
}) => {
|
||||
app.ui.cycle_main_input_position_rev();
|
||||
}
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Char('c'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
}) => {
|
||||
app.ui.cli_enable();
|
||||
}
|
||||
input => match app.ui.input_position() {
|
||||
ui::MainInputPosition::MessageCompose => {
|
||||
match input {
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Enter,
|
||||
modifiers: KeyModifiers::ALT,
|
||||
..
|
||||
}) => {
|
||||
match app.status.room_mut() {
|
||||
Some(room) => {
|
||||
room.send(app.ui.message_compose.lines().join("\n")).await?;
|
||||
app.ui.message_compose_clear();
|
||||
}
|
||||
None => (),
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
app.ui
|
||||
.message_compose
|
||||
.input(tui_textarea::Input::from(input.to_owned()));
|
||||
}
|
||||
};
|
||||
}
|
||||
ui::MainInputPosition::Rooms => {
|
||||
match input {
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Up, ..
|
||||
}) => {
|
||||
let i = match app.ui.rooms_state.selected() {
|
||||
Some(cur) => {
|
||||
if cur > 0 {
|
||||
cur - 1
|
||||
} else {
|
||||
cur
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
app.ui.rooms_state.select(Some(i));
|
||||
app.status.set_room_by_index(i)?;
|
||||
}
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Down,
|
||||
..
|
||||
}) => {
|
||||
let i = match app.ui.rooms_state.selected() {
|
||||
Some(cur) => {
|
||||
if cur < app.status.rooms().len() - 1 {
|
||||
cur + 1
|
||||
} else {
|
||||
cur
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
app.ui.rooms_state.select(Some(i));
|
||||
app.status.set_room_by_index(i)?;
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
ui::MainInputPosition::Messages => {
|
||||
match input {
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Up, ..
|
||||
}) => {
|
||||
match app.status.room_mut() {
|
||||
Some(room) => {
|
||||
let len = room.timeline().len();
|
||||
let i = match room.view_scroll() {
|
||||
Some(i) => i + 1,
|
||||
None => 0,
|
||||
};
|
||||
if i < len {
|
||||
room.set_view_scroll(Some(i))
|
||||
}
|
||||
if i <= len - 5 {
|
||||
room.poll_old_timeline().await?;
|
||||
}
|
||||
}
|
||||
None => (),
|
||||
};
|
||||
}
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Down,
|
||||
..
|
||||
}) => {
|
||||
match app.status.room_mut() {
|
||||
Some(room) => {
|
||||
match room.view_scroll() {
|
||||
Some(i) => {
|
||||
if i == 0 {
|
||||
room.set_view_scroll(None);
|
||||
} else {
|
||||
room.set_view_scroll(Some(i - 1));
|
||||
}
|
||||
}
|
||||
None => (),
|
||||
};
|
||||
}
|
||||
None => (),
|
||||
};
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
ui::MainInputPosition::CLI => {
|
||||
todo!();
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
};
|
||||
Ok(EventStatus::Ok)
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
use matrix_sdk::deserialized_responses::SyncResponse;
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::app::{events::event_types::EventStatus, App};
|
||||
|
||||
pub async fn handle<'a>(app: &mut App<'a>, sync: &SyncResponse) -> Result<EventStatus> {
|
||||
for (m_room_id, m_room) in sync.rooms.join.iter() {
|
||||
let room = match app.status.get_room_mut(m_room_id) {
|
||||
Some(r) => r,
|
||||
None => continue,
|
||||
};
|
||||
for m_event in m_room.timeline.events.clone() {
|
||||
let event = m_event
|
||||
.event
|
||||
.deserialize()
|
||||
.unwrap()
|
||||
.into_full_event(m_room_id.clone());
|
||||
room.timeline_add(event);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(EventStatus::Ok)
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
pub mod matrix;
|
||||
pub mod setup;
|
||||
pub mod main;
|
|
@ -0,0 +1,75 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyEvent};
|
||||
|
||||
use crate::{
|
||||
app::{events::event_types::EventStatus, App},
|
||||
ui,
|
||||
};
|
||||
|
||||
pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<EventStatus> {
|
||||
let ui = match &mut app.ui.setup_ui {
|
||||
Some(ui) => ui,
|
||||
None => bail!("SetupUI instance not found"),
|
||||
};
|
||||
|
||||
match input_event {
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Esc, ..
|
||||
}) => return Ok(EventStatus::Terminate),
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Tab, ..
|
||||
}) => {
|
||||
ui.cycle_input_position();
|
||||
}
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::BackTab,
|
||||
..
|
||||
}) => {
|
||||
ui.cycle_input_position_rev();
|
||||
}
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Enter,
|
||||
..
|
||||
}) => {
|
||||
match ui.input_position() {
|
||||
ui::SetupInputPosition::Ok => {
|
||||
let homeserver = ui.homeserver.lines()[0].clone();
|
||||
let username = ui.username.lines()[0].clone();
|
||||
let password = ui.password_data.lines()[0].clone();
|
||||
app.login(&homeserver, &username, &password)
|
||||
.await
|
||||
.context("Failed to login")?;
|
||||
// We bailed in the line above, thus login must have succeeded
|
||||
return Ok(EventStatus::Finished);
|
||||
}
|
||||
_ => ui.cycle_input_position(),
|
||||
};
|
||||
}
|
||||
input => match ui.input_position() {
|
||||
ui::SetupInputPosition::Homeserver => {
|
||||
ui.homeserver.input(input.to_owned());
|
||||
}
|
||||
ui::SetupInputPosition::Username => {
|
||||
ui.username.input(input.to_owned());
|
||||
}
|
||||
ui::SetupInputPosition::Password => {
|
||||
let textarea_input = tui_textarea::Input::from(input.to_owned());
|
||||
ui.password_data.input(textarea_input.clone());
|
||||
match textarea_input.key {
|
||||
tui_textarea::Key::Char(_) => {
|
||||
ui.password.input(tui_textarea::Input {
|
||||
key: tui_textarea::Key::Char('*'),
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
ui.password.input(textarea_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
};
|
||||
Ok(EventStatus::Ok)
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
mod handlers;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use crossterm::event::Event as CrosstermEvent;
|
||||
|
||||
use crate::app::{status::State, App};
|
||||
|
||||
use self::handlers::{main, matrix, setup};
|
||||
|
||||
use super::EventStatus;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Event {
|
||||
pub(super) input_event: Option<CrosstermEvent>,
|
||||
pub(super) matrix_event: Option<matrix_sdk::deserialized_responses::SyncResponse>,
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub async fn handle(&self, app: &mut App<'_>) -> Result<EventStatus> {
|
||||
if let Some(matrix_event) = &self.matrix_event {
|
||||
return matrix::handle(app, matrix_event)
|
||||
.await
|
||||
.with_context(|| format!("Failed to handle matrix event: `{:#?}`", matrix_event));
|
||||
}
|
||||
|
||||
if let Some(input_event) = &self.input_event {
|
||||
let status = match app.status.state() {
|
||||
State::None => EventStatus::Ok,
|
||||
State::Main => main::handle(app, input_event).await.with_context(|| {
|
||||
format!("Failed to handle input event: `{:#?}`", input_event)
|
||||
})?,
|
||||
State::Setup => setup::handle(app, input_event).await.with_context(|| {
|
||||
format!("Failed to handle input event: `{:#?}`", input_event)
|
||||
})?,
|
||||
};
|
||||
return Ok(status);
|
||||
}
|
||||
|
||||
Ok(EventStatus::Ok)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
use super::Event;
|
||||
|
||||
pub struct EventBuilder {
|
||||
event: Event,
|
||||
}
|
||||
|
||||
impl Default for Event {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
input_event: None,
|
||||
matrix_event: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EventBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
event: Event::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventBuilder {
|
||||
pub fn input_event(&mut self, input_event: crossterm::event::Event) -> &Self {
|
||||
self.event.input_event = Some(input_event);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn matrix_event(
|
||||
&mut self,
|
||||
matrix_event: matrix_sdk::deserialized_responses::SyncResponse,
|
||||
) -> &Self {
|
||||
self.event.matrix_event = Some(matrix_event);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self) -> Event {
|
||||
Event {
|
||||
input_event: self.event.input_event.to_owned(),
|
||||
matrix_event: self.event.matrix_event.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#[derive(Debug)]
|
||||
pub enum EventStatus {
|
||||
Ok,
|
||||
Finished,
|
||||
Terminate,
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
pub mod event_builder;
|
||||
pub mod event;
|
||||
pub mod event_status;
|
||||
|
||||
pub use self::event_builder::*;
|
||||
pub use self::event::*;
|
||||
pub use self::event_status::*;
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
pub mod event_types;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use matrix_sdk::{config::SyncSettings, Client, LoopCtrl};
|
||||
use tokio::{sync::mpsc, time::Duration};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::app::events::event_types::EventBuilder;
|
||||
|
||||
use self::event_types::Event;
|
||||
|
||||
pub async fn poll_input_events(
|
||||
channel: mpsc::Sender<Event>,
|
||||
kill: CancellationToken,
|
||||
) -> Result<()> {
|
||||
async fn poll_input_events_stage_2(channel: mpsc::Sender<Event>) -> Result<()> {
|
||||
loop {
|
||||
if crossterm::event::poll(Duration::from_millis(100))? {
|
||||
let event = EventBuilder::default()
|
||||
.input_event(crossterm::event::read()?)
|
||||
.build();
|
||||
|
||||
channel.send(event).await?;
|
||||
} else {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
tokio::select! {
|
||||
output = poll_input_events_stage_2(channel) => output,
|
||||
_ = kill.cancelled() => bail!("received kill signal")
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn poll_matrix_events(
|
||||
channel: mpsc::Sender<Event>,
|
||||
kill: CancellationToken,
|
||||
client: Client,
|
||||
) -> Result<()> {
|
||||
async fn poll_matrix_events_stage_2(
|
||||
channel: mpsc::Sender<Event>,
|
||||
client: Client,
|
||||
) -> Result<()> {
|
||||
let sync_settings = SyncSettings::default();
|
||||
// .token(sync_token)
|
||||
// .timeout(Duration::from_secs(30));
|
||||
|
||||
let tx = &channel;
|
||||
|
||||
client
|
||||
.sync_with_callback(sync_settings, |response| async move {
|
||||
let event = EventBuilder::default().matrix_event(response).build();
|
||||
|
||||
match tx.send(event).await {
|
||||
Ok(_) => LoopCtrl::Continue,
|
||||
Err(_) => LoopCtrl::Break,
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
tokio::select! {
|
||||
output = poll_matrix_events_stage_2(channel, client) => output,
|
||||
_ = kill.cancelled() => bail!("received kill signal"),
|
||||
}
|
||||
}
|
|
@ -1,33 +1,33 @@
|
|||
pub mod event;
|
||||
pub mod command_interface;
|
||||
pub mod events;
|
||||
pub mod status;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use accounts::{Account, AccountsManager};
|
||||
use anyhow::{Error, Result};
|
||||
use anyhow::{Context, Error, Result};
|
||||
use cli_log::info;
|
||||
use matrix_sdk::Client;
|
||||
use rlua::Lua;
|
||||
use status::{State, Status};
|
||||
use tokio::sync::mpsc;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::{accounts, ui};
|
||||
use crate::{accounts, app::command_interface::generate_ci_functions, ui};
|
||||
|
||||
pub struct App<'a> {
|
||||
ui: ui::UI<'a>,
|
||||
use self::events::event_types;
|
||||
|
||||
pub struct App<'ui> {
|
||||
ui: ui::UI<'ui>,
|
||||
accounts_manager: accounts::AccountsManager,
|
||||
status: Status,
|
||||
|
||||
channel_tx: mpsc::Sender<event::Event>,
|
||||
channel_rx: mpsc::Receiver<event::Event>,
|
||||
channel_tx: mpsc::Sender<event_types::Event>,
|
||||
channel_rx: mpsc::Receiver<event_types::Event>,
|
||||
input_listener_killer: CancellationToken,
|
||||
matrix_listener_killer: CancellationToken,
|
||||
}
|
||||
|
||||
impl Drop for App<'_> {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
impl App<'_> {
|
||||
pub fn new() -> Result<Self> {
|
||||
let path: &std::path::Path = Path::new("userdata/accounts.json");
|
||||
|
@ -54,7 +54,7 @@ impl App<'_> {
|
|||
|
||||
pub async fn run(&mut self) -> Result<()> {
|
||||
// Spawn input event listener
|
||||
tokio::task::spawn(event::poll_input_events(
|
||||
tokio::task::spawn(events::poll_input_events(
|
||||
self.channel_tx.clone(),
|
||||
self.input_listener_killer.clone(),
|
||||
));
|
||||
|
@ -71,14 +71,14 @@ impl App<'_> {
|
|||
self.status.set_state(State::Main);
|
||||
self.ui.update(&self.status).await?;
|
||||
|
||||
let event: event::Event = match self.channel_rx.recv().await {
|
||||
let event: event_types::Event = match self.channel_rx.recv().await {
|
||||
Some(e) => e,
|
||||
None => return Err(Error::msg("Event channel has no senders")),
|
||||
};
|
||||
|
||||
match event.handle(self).await? {
|
||||
event::EventStatus::Ok => (),
|
||||
event::EventStatus::Terminate => break,
|
||||
event_types::EventStatus::Ok => (),
|
||||
event_types::EventStatus::Terminate => break,
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
@ -94,15 +94,15 @@ impl App<'_> {
|
|||
self.status.set_state(State::Setup);
|
||||
self.ui.update_setup().await?;
|
||||
|
||||
let event: event::Event = match self.channel_rx.recv().await {
|
||||
let event: event_types::Event = match self.channel_rx.recv().await {
|
||||
Some(e) => e,
|
||||
None => return Err(Error::msg("Event channel has no senders")),
|
||||
};
|
||||
|
||||
match event.handle(self).await? {
|
||||
event::EventStatus::Ok => (),
|
||||
event::EventStatus::Finished => return Ok(()),
|
||||
event::EventStatus::Terminate => return Err(Error::msg("Terminated by user")),
|
||||
event_types::EventStatus::Ok => (),
|
||||
event_types::EventStatus::Finished => return Ok(()),
|
||||
event_types::EventStatus::Terminate => return Err(Error::msg("Terminated by user")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ impl App<'_> {
|
|||
self.matrix_listener_killer = CancellationToken::new();
|
||||
|
||||
// Spawn Matrix Event Listener
|
||||
tokio::task::spawn(event::poll_matrix_events(
|
||||
tokio::task::spawn(events::poll_matrix_events(
|
||||
self.channel_tx.clone(),
|
||||
self.matrix_listener_killer.clone(),
|
||||
client.clone(),
|
||||
|
|
|
@ -204,8 +204,4 @@ impl Status {
|
|||
pub fn set_state(&mut self, state: State) {
|
||||
self.state = state;
|
||||
}
|
||||
|
||||
pub fn cli_event(&mut self, event: String) {
|
||||
info!("CLI Event: {}", event);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue