diff --git a/src/app/mod.rs b/src/app/mod.rs index 7f99ad4..131074b 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -4,16 +4,44 @@ use matrix_sdk::{Client}; use accounts::Account; use crate::accounts::AccountManager; +pub struct Message { + pub author: String, + pub message: String, +} + +pub struct Room { + pub messages: Vec, +} + pub struct App { pub account_manager: accounts::AccountManager, pub client: Option, + + current_room_id: u32, + rooms: Vec, } impl App { pub fn new() -> Self { Self { account_manager: AccountManager::new(), - client: None + client: None, + current_room_id: 0, + rooms: Vec::new(), } } + + pub fn fill_test_data(&mut self) { + let mut room = Room { + messages: Vec::new() + }; + + room.messages.push(Message {author: "someone".to_string(), message: "test".to_string()}); + + self.rooms.push(room); + } + + pub fn room(&self) -> Option<&Room> { + self.rooms.get(self.current_room_id as usize) + } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 00ba119..00a48c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,15 +4,22 @@ mod app; use matrix_sdk::ruma::exports::serde_json; use tokio::time::{sleep, Duration}; +use crate::app::Message; +use crate::ui::UI; #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); let mut app = app::App::new(); + app.fill_test_data(); - let client = app.account_manager.add("https://nerdcult.net", "test", "abcd1234").await?; - app.client = Some(client); + let mut ui = UI::new(); + ui.draw_main(&app)?; + + + // let client = app.account_manager.add("https://nerdcult.net", "test", "abcd1234").await?; + // app.client = Some(client); // let client = app.account_manager.login(0).await?; // app.client = Some(client); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index b9ee46a..4124d01 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,48 +1,150 @@ -use crate::ui; +use crate::app::{App}; use crossterm::event::KeyCode::Null; use crossterm::{ event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; +use anyhow::{Error, Result}; use std::io::Stdout; use std::{io, thread, time::Duration}; -use tui::{ - backend::CrosstermBackend, - layout::{Constraint, Direction, Layout}, - widgets::{Block, Borders, Widget}, - Terminal, -}; +use tui::{backend::CrosstermBackend, layout::{Constraint, Direction, Layout, Rect}, widgets::{Block, Borders, Widget}, Terminal, Frame}; +use tui::layout::Alignment; +use tui::style::{Color, Style}; +use tui::text::{Spans, Span}; +use tui::widgets::{Paragraph, Wrap}; + pub struct UI { - backend: CrosstermBackend, terminal: Terminal>, } -pub fn ui_create() -> Result<(), io::Error> { - // setup terminal +fn terminal_prepare() -> Result { enable_raw_mode()?; let mut stdout = io::stdout(); execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; - let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend)?; - - terminal.draw(|f| { - let size = f.size(); - let block = Block::default().title("Layout design in work...").borders(Borders::ALL); - f.render_widget(block, size); - })?; - - thread::sleep(Duration::from_millis(5000)); - - // restore terminal - disable_raw_mode()?; - execute!( - terminal.backend_mut(), - LeaveAlternateScreen, - DisableMouseCapture - )?; - terminal.show_cursor()?; - - Ok(()) + return Ok(stdout); +} + +fn draw_main_status_block(frame: &mut Frame>, area: Rect) { + let panel = Block::default() + .title("Status") + .borders(Borders::ALL); + + frame.render_widget(panel, area); +} + +fn draw_main_rooms_block(frame: &mut Frame>, area: Rect) { + let panel = Block::default() + .title("Rooms") + .borders(Borders::ALL); + + frame.render_widget(panel, area); +} + +fn draw_main_messages_block(frame: &mut Frame>, area: Rect, app: &App) { + let messages = match app.room() { + None => return, + Some(r) => { + r.messages + .iter() + .rev() + .map(|msg| { + Spans::from(vec![ + Span::styled(&msg.author, Style::default().fg(Color::Cyan)), + Span::styled(": ", Style::default().fg(Color::Cyan)), + Span::styled(&msg.message, Style::default().fg(Color::White)), + ]) + }) + .collect::>() + }, + }; + + let panel = Paragraph::new(messages) + .block( + Block::default() + .title("Messages") + .borders(Borders::ALL)) + .alignment(Alignment::Left) + .wrap(Wrap {trim: false}); + + + frame.render_widget(panel, area); +} + +fn draw_main_message_compose_block(frame: &mut Frame>, area: Rect) { + let panel = Block::default() + .title("Message Compose") + .borders(Borders::ALL); + + frame.render_widget(panel, area); +} + +fn draw_main_room_info_block(frame: &mut Frame>, area: Rect) { + let panel = Block::default() + .title("Room Info") + .borders(Borders::ALL); + + frame.render_widget(panel, area); +} + +impl UI { + pub fn new() -> Self { + let stdout = terminal_prepare().expect("failed to prepare terminal"); + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend).expect("failed to initialize terminal"); + + terminal.clear().expect("failed to clear screen"); + + Self { + terminal + } + } + + pub fn draw_main(&mut self, app: &App) -> Result<()> { + let chunk = self.terminal.size()?; + + let main_chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Length(32), Constraint::Min(16), Constraint::Length(32)].as_ref()) + .split(chunk); + + let left_chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Length(5), Constraint::Min(4)].as_ref()) + .split(main_chunks[0]); + + let middle_chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Min(4), Constraint::Length(3)].as_ref()) + .split(main_chunks[1]); + + let right_chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Min(4)].as_ref()) + .split(main_chunks[2]); + + + self.terminal.draw(|frame| { + draw_main_status_block(frame, left_chunks[0]); + draw_main_rooms_block(frame, left_chunks[1]); + draw_main_messages_block(frame, middle_chunks[0], app); + draw_main_message_compose_block(frame, middle_chunks[1]); + draw_main_room_info_block(frame, right_chunks[0]); + })?; + + thread::sleep(Duration::from_millis(10000)); + Ok(()) + } + + pub fn close(&mut self) -> Result<()>{ + disable_raw_mode()?; + execute!( + self.terminal.backend_mut(), + LeaveAlternateScreen, + DisableMouseCapture + )?; + self.terminal.show_cursor()?; + Ok(()) + } }