Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions .cargo-husky/hooks/pre-push

This file was deleted.

47 changes: 31 additions & 16 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,45 @@
mod app;
mod state;
mod ui;

use std::time::Duration;

use app::App;
use ratatui::crossterm::event::{self, Event};
use ratatui::crossterm::event::{self, Event, KeyCode, KeyEvent};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut terminal = ratatui::init();
let servers = &*state::MOCK_SERVERS;
ratatui::run(|terminal| {
loop {
terminal.draw(|f| ui::render_state(f, servers)).unwrap();
if event::poll(Duration::from_millis(16)).unwrap()
&& let Event::Key(k) = event::read().unwrap()
&& k.code == KeyCode::Char('q')
{
break;
}
}
});

let mut app = App::new().await?;
// let mut terminal = ratatui::init();
// let mut app = App::new().await?;

loop {
terminal.draw(|f| ui::render(f, &app))?;
// loop {
// terminal.draw(|f| ui::render(f, &app))?;

// Handle Keyboard Events
if event::poll(std::time::Duration::from_millis(16))?
&& let Event::Key(key) = event::read()?
{
app.handle_key_event(key).await?;
// // Handle Keyboard Events
// if event::poll(std::time::Duration::from_millis(16))?
// && let Event::Key(key) = event::read()?
// {
// app.handle_key_event(key).await?;

if app.should_quit {
break;
}
}
}
// if app.should_quit {
// break;
// }
// }
// }

ratatui::restore();
// ratatui::restore();
Ok(())
}
47 changes: 47 additions & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
pub struct Server {
pub name: String,
// TODO: add in channel category separators, also probably add in cache mechanism for channels as well
pub channels: Vec<Channel>,
}

pub struct Channel {
pub name: String,
// will need to be replaced by a view into a cache mechanism
pub messages: Vec<String>,
// we have a buffer per channel, such that you can write in a channel
// and then switch channels such that when you come back, the in-progress draft is still there
pub buffer: String,
}

// hard coded mock data for development, remove once we've got everything else working
pub static MOCK_SERVERS: std::sync::LazyLock<Vec<Server>> = std::sync::LazyLock::new(|| {
vec![
Server {
name: "Test server".to_string(),
channels: vec![
Channel {
name: "general".to_string(),
messages: vec!["howdy!".to_string()],
buffer: String::new(),
},
Channel {
name: "media".to_string(),
messages: vec![
"look at this cat!".to_string(),
"woahhh so pretty".to_string(),
"yessss I love cats!".to_string(),
],
buffer: String::new(),
},
],
},
Server {
name: "Epic server".to_string(),
channels: vec![Channel {
name: "Now this is epic".to_string(),
messages: vec!["Epic!!!".to_string()],
buffer: String::new(),
}],
},
]
});
126 changes: 123 additions & 3 deletions src/ui.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use crate::app::{App, AppState};
use crate::{
app::{App, AppState},
state::{Channel, Server},
};
use ratatui::{
Frame,
layout::{Constraint, Direction, Layout},
style::{Color, Style},
widgets::{Block, Borders, Paragraph},
prelude::Rect,
style::{Color, Style, Stylize},
text::Span,
widgets::{Block, Borders, List, ListItem, Paragraph, Widget},
};

pub fn render(f: &mut Frame, app: &App) {
Expand Down Expand Up @@ -53,3 +58,118 @@ pub fn render(f: &mut Frame, app: &App) {
}
}
}

pub fn render_state(f: &mut Frame, servers: &[Server]) {
f.render_widget(
AppWidget {
servers,
focused_server: 0,
focused_channel: 1,
},
f.area(),
)
}

struct AppWidget<'state> {
// list of servers to show on the left hand side
servers: &'state [Server],
// of the servers, which one are we focused on for the pannel in the center
focused_server: usize,
// which channel inside the server are we looking at
focused_channel: usize,
}

const SERVER_LIST_WIDTH: u16 = 20;

impl Widget for AppWidget<'_> {
fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer)
where
Self: Sized,
{
let mut server_list_area = area;
server_list_area.width = SERVER_LIST_WIDTH;
let mut server_content_area = area;
server_content_area.x += SERVER_LIST_WIDTH;
server_content_area.width -= SERVER_LIST_WIDTH;

List::new(self.servers.iter().enumerate().map(|(i, s)| {
let item = ListItem::new(s.name.as_str());
if self.focused_server == i {
item.reversed()
} else {
item
}
}))
.block(Block::bordered())
.render(server_list_area, buf);
ServerWidget {
server: &self.servers[self.focused_server],
focused_channel: self.focused_channel,
}
.render(server_content_area, buf);
}
}

struct ServerWidget<'server> {
server: &'server Server,
focused_channel: usize,
}

impl Widget for ServerWidget<'_> {
fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer)
where
Self: Sized,
{
let mut channel_list_area = area;
channel_list_area.width = 20;
let mut channel_area = area;
channel_area.x += 20;
channel_area.width -= 20;

List::new(self.server.channels.iter().enumerate().map(|(i, c)| {
let item = ListItem::new(c.name.as_str());
if i == self.focused_channel {
item.reversed()
} else {
item
}
}))
.block(Block::bordered().title(self.server.name.as_str()))
.render(channel_list_area, buf);
ChannelWidget {
channel: &self.server.channels[self.focused_channel],
}
.render(channel_area, buf);
}
}

struct ChannelWidget<'channel> {
channel: &'channel Channel,
}

impl Widget for ChannelWidget<'_> {
fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer)
where
Self: Sized,
{
let mut messages_area = area;
messages_area.height -= 5;
let mut input_area = area;
input_area.y = messages_area.height;
input_area.height = 5;

List::new(
self.channel
.messages
.iter()
.rev()
.map(|msg| ListItem::new(msg.as_str())),
)
.direction(ratatui::widgets::ListDirection::BottomToTop)
.block(Block::bordered().title(self.channel.name.as_str()))
.render(messages_area, buf);
Paragraph::new(self.channel.buffer.as_str())
.block(Block::bordered())
.render(input_area, buf);
}
}
Loading