use ratatui::macros::constraints;
use ratzgo::{core::*, event::DefaultContext, widget::row};
use crate::{
ui::{
BookmarkTrack, BookmarksMsg, HelpMsg, LogRelocate, MainState, Message, State,
view::{
bookmarks::{history, tree},
log::LogLayout,
nav::Tab,
},
},
utils::{
jj::LogMode,
tui::{BookmarkTree, LogText},
},
};
pub fn view<'a>(state: &'a mut MainState) -> Element<'a, BookmarksMsg> {
let css = constraints![==1/3, ==2/3];
row! {
css;
[
tree::view(tree::VState {
view: &state.bookmarks,
state: &mut state.bookmarks_state,
mount_point: &state.mount_point,
modal_delete: state.bookmarks_modal_delete.as_ref(),
modal_remotes: state
.bookmarks_modal_remotes
.as_ref()
.map(|v| (v.remotes.as_slice(), &mut state.bookmarks_modal_remotes_state)),
}),
history::view(history::VState {
view: &state.bookmarks_history_view,
state: &mut state.bookmarks_history_state,
})
]
}
.into()
}
pub async fn update(
state: &mut MainState,
msg: BookmarksMsg,
ctx: &mut DefaultContext<Message, State>,
) {
match msg {
BookmarksMsg::UpdateTree(v) => {
state.bookmarks = v;
if state.bookmarks.get().is_empty() {
state.bookmarks_history_view = LogText::default();
state.bookmarks_history_debounce_mut().cancel();
}
let reset = match state.bookmarks_state.selected() {
[bookmark] => state
.bookmarks
.get()
.iter()
.all(|bm| bm.identifier() != bookmark),
[bookmark, remote] => state.bookmarks.get().iter().all(|bm| {
bm.identifier() != bookmark
&& bm.children().iter().all(|rm| rm.identifier() != remote)
}),
[] => true,
_ => unreachable!(),
};
if reset {
state.bookmarks.get().iter().for_each(|v| {
state.bookmarks_state.open(vec![v.identifier().clone()]);
});
let Some(bookmark) = state
.bookmarks
.get()
.first()
.map(|v| v.identifier().clone())
else {
return;
};
state.bookmarks_state.select(vec![bookmark.clone()]);
debounce_history(state, LogMode::Bookmark(bookmark));
}
}
BookmarksMsg::ScrollTree(v) => {
state.bookmarks_state.scroll_vertical(v);
let bookmark = match state.bookmarks_state.selected() {
[bm] => bm.clone(),
[bm, rm] => format!("{bm}{rm}").into(),
_ => {
return;
}
};
debounce_history(state, LogMode::Bookmark(bookmark));
}
BookmarksMsg::ScrollHistory(v) => {
state
.bookmarks_history_state
.scroll_vertical(&state.log_history, v);
}
BookmarksMsg::BookmarkOpen => {
let path = state.bookmarks_state.selected().to_vec();
state.bookmarks_state.open(path);
}
BookmarksMsg::BookmarkClose => {
let path = state.bookmarks_state.selected().to_vec();
state.bookmarks_state.close(&path);
}
BookmarksMsg::UpdateHistory { text, version } => {
if state.bookmarks_history_debounce().version() != version {
let bookmark = match state.bookmarks_state.selected() {
[bm] => bm.clone(),
[bm, rm] => format!("{bm}{rm}").into(),
_ => return,
};
debounce_history(state, LogMode::Bookmark(bookmark));
} else {
state.bookmarks_history_view = text;
state.bookmarks_history_state.reset();
}
}
BookmarksMsg::ViewHistory => {
state.nav_tab = Tab::Log;
let bookmark = match state.bookmarks_state.selected() {
[bm] => bm.clone(),
[bm, rm] => format!("{bm}{rm}").into(),
_ => {
return;
}
};
state.log_mode = LogMode::Bookmark(bookmark);
state.log_history_state.reset();
state.log_reloc = LogRelocate::Index {
index: 0,
file: None,
};
state.log_layout = LogLayout::HISTORY_FILES;
state.log_focus = state.log_layout.into();
ctx.queue().push(Message::Refresh);
}
BookmarksMsg::Track => {
if let [bm] = state.bookmarks_state.selected() {
match bm.split_once('@') {
Some((name, remote)) => {
match state.jj_handle.bookmark_track(name, remote).await {
Ok(_) => {
let name = bm.slice_ref(name);
state.bookmarks_state.select(vec![name]);
}
Err(e) => {
ratzgo::log::error("`bookmark track`", e.into_text());
}
}
}
None => {
let remotes_untrack = match state.jj_handle.remotes_untrack(bm).await {
Ok(v) => v,
Err(e) => {
ratzgo::log::error("remote_untracks", e.into_text());
return;
}
};
if remotes_untrack.is_empty() {
return;
}
state.bookmarks_modal_remotes = Some(BookmarkTrack {
bookmark: bm.clone(),
remotes: remotes_untrack,
});
state.bookmarks_modal_remotes_state.reset();
}
}
}
}
BookmarksMsg::Untrack => {
if let [name, remote] = state.bookmarks_state.selected()
&& let Some(remote) = remote.trim_end_matches('*').strip_prefix('@')
&& remote != "git"
{
match state.jj_handle.bookmark_untrack(name, remote).await {
Ok(_) => {
let bookmark = format!("{name}@{remote}").into();
state.bookmarks_state.select(vec![bookmark]);
}
Err(e) => {
ratzgo::log::error("`bookmark untrack`", e.into_text());
}
}
}
}
BookmarksMsg::Delete => {
if let [name] = state.bookmarks_state.selected()
&& !name.contains('@')
{
state.bookmarks_modal_delete = Some(name.clone());
}
}
BookmarksMsg::DeleteConfirm(yes) => {
if let Some(name) = state.bookmarks_modal_delete.take()
&& yes
&& let Err(e) = state.jj_handle.bookmark_delete(&name).await
{
ratzgo::log::error("`bookmark delete`", e.into_text());
}
}
BookmarksMsg::ScrollRemotes(action) => {
if let Some(v) = &state.bookmarks_modal_remotes {
state
.bookmarks_modal_remotes_state
.scroll_vertical(action, v.remotes.len());
}
}
BookmarksMsg::TrackConfirm(yes) => {
if let Some(v) = state.bookmarks_modal_remotes.take()
&& yes
&& let Some(i) = state.bookmarks_modal_remotes_state.selected()
&& let Some(remote) = v.remotes.get(i)
{
match state.jj_handle.bookmark_track(&v.bookmark, remote).await {
Ok(_) => {
state.bookmarks_state.select(vec![v.bookmark]);
}
Err(e) => {
ratzgo::log::error("`bookmark track`", e.into_text());
}
}
}
}
BookmarksMsg::Help => {
let page = if state.bookmarks_modal_delete.is_some() {
"confirm-modal"
} else if state.bookmarks_modal_remotes.is_some() {
"bookmarks-remotes"
} else {
"bookmarks-tree"
};
ctx.queue().push(HelpMsg::Page(page));
}
}
}
pub fn refresh(state: &mut MainState, ctx: &mut DefaultContext<Message, State>) {
let jj_handle = state.jj_handle.clone();
ctx.queue().spawn_try(async move {
jj_handle
.bookmark_tree()
.await
.map(|v| BookmarksMsg::UpdateTree(BookmarkTree::new(v)))
});
}
fn debounce_history(state: &mut MainState, mode: LogMode) {
let jj = state.jj_handle.clone();
state
.bookmarks_history_debounce_mut()
.spawn_try(|version| async move {
jj.log(&mode).await.map(|v| BookmarksMsg::UpdateHistory {
text: LogText::new(v),
version,
})
});
}