use std::time::Duration;
use futures_util::StreamExt;
use ratatui::{crossterm::event::KeyCode, macros::constraints};
use ratzgo::{
core::*,
event::DefaultContext,
widget::{column, stack},
};
use crate::{
config::Config,
ui::{
LogMsg, Message, NavMsg, NotifyMsg, State,
view::{
bookmarks, help, hint, log,
nav::{self, Tab},
notification, operations, tags,
},
},
utils::jj::NotifyGitChange,
};
pub async fn init(state: &mut State, ctx: &mut DefaultContext<Message, State>) {
let log_stream = ratzgo::log::init();
match Config::load_or_default() {
Ok(v) => {
state.config = v;
}
Err(e) => {
ratzgo::log::error("load-config", format!("{e:?}"));
}
}
let debounce_duration = Duration::from_millis(50);
state
.main
.log_show_debounce
.set(ctx.make_debounce(debounce_duration))
.expect("log show debounce must only be initialized once");
state
.main
.log_diff_debounce
.set(ctx.make_debounce(debounce_duration))
.expect("log diff debounce must only be initialized once");
state
.main
.bookmarks_history_debounce
.set(ctx.make_debounce(debounce_duration))
.expect("bookmarks history debounce must only be initialized once");
state
.main
.tags_history_debounce
.set(ctx.make_debounce(debounce_duration))
.expect("tags history debounce must only be initialized once");
match NotifyGitChange::new(state.main.jj_handle.clone()) {
Ok(notify) => {
ctx.select_mut()
.source(notify.into_stream().map(|_| Message::Refresh));
}
Err(e) => {
tracing::error!(error = %e, "watch .jj/working_copy failed");
ctx.exit(true);
return;
}
}
ctx.select_mut()
.source(log_stream.map(|ev| Message::Notify(NotifyMsg::LogEvent(ev))));
if let Err(e) = state.main.jj_handle.workspace_update_stale().await {
ratzgo::log::error("workspace update-stale", e.into_text());
}
if state.config.launch_fetch {
ctx.queue().push(LogMsg::Fetch);
}
ctx.queue().push(Message::Refresh);
}
pub fn view(state: &mut State) -> Element<'_, Message> {
let hint_vstate = hint::State {
fetching: state.main.log_fetching.clone(),
pushing: state.main.log_pushing.clone(),
};
let main_mp = state.main.mount_point.view();
stack![
column! [
constraints![==3, ==100%, ==3];
[
nav::view(state.main.nav_tab).into().map(Into::into),
match state.main.nav_tab {
Tab::Log => log::view(&mut state.main).map(Into::into),
Tab::Bookmarks => bookmarks::view(&mut state.main).map(Into::into),
Tab::Tags => tags::view(&mut state.main).map(Into::into),
Tab::Operations => operations::view(&mut state.main).map(Into::into),
},
hint::view(hint_vstate)
]
]
.on_key(
|k| k.code == KeyCode::Char('1'),
NavMsg::TabSelect(Tab::Log).into(),
)
.on_key(
|k| k.code == KeyCode::Char('2'),
NavMsg::TabSelect(Tab::Bookmarks).into(),
)
.on_key(
|k| k.code == KeyCode::Char('3'),
NavMsg::TabSelect(Tab::Tags).into(),
)
.on_key(
|k| k.code == KeyCode::Char('4'),
NavMsg::TabSelect(Tab::Operations).into(),
)
.on_key(|k| k.code == KeyCode::Char('H'), NavMsg::TabPrev.into())
.on_key(|k| k.code == KeyCode::Char('L'), NavMsg::TabNext.into())
.on_key(|k| k.code == KeyCode::Char('q'), Message::Exit)
.active(true),
main_mp,
help::view(&mut state.help),
notification::view(&state.notify),
]
.into()
}
pub async fn update(state: &mut State, msg: Message, ctx: &mut DefaultContext<Message, State>) {
match msg {
Message::Nav(msg) => {
nav::update(&mut state.main, msg, ctx);
}
Message::Log(msg) => {
log::update(&mut state.main, msg, ctx).await;
}
Message::Bookmarks(msg) => {
bookmarks::update(&mut state.main, msg, ctx).await;
}
Message::Tags(msg) => {
tags::update(&mut state.main, msg, ctx).await;
}
Message::Op(msg) => {
operations::update(&mut state.main, msg, ctx);
}
Message::Notify(msg) => notification::update(&mut state.notify, msg),
Message::Help(msg) => help::update(&mut state.help, msg),
Message::Refresh => match state.main.nav_tab {
Tab::Log => log::refresh(&mut state.main, ctx),
Tab::Bookmarks => bookmarks::refresh(&mut state.main, ctx),
Tab::Tags => tags::refresh(&mut state.main, ctx),
Tab::Operations => operations::refresh(&mut state.main, ctx),
},
Message::Exit => ctx.exit(true),
}
}