Skip to main content

gitkraft_gui/features/commits/
update.rs

1//! Update logic for commit-related messages.
2
3use iced::Task;
4
5use crate::message::Message;
6use crate::state::GitKraft;
7
8use super::commands;
9
10/// Handle all commit-related messages, returning a [`Task`] for any follow-up
11/// async work.
12pub fn update(state: &mut GitKraft, message: Message) -> Task<Message> {
13    match message {
14        Message::SelectCommit(index) => {
15            // Read repo_path and commit info before taking a mutable borrow.
16            let repo_path = state.active_tab().repo_path.clone();
17            let commit_info = state
18                .active_tab()
19                .commits
20                .get(index)
21                .map(|c| (c.oid.clone(), c.short_oid.clone()));
22
23            let tab = state.active_tab_mut();
24            tab.selected_commit = Some(index);
25            tab.show_commit_detail = true;
26            // Clear previous diff state immediately for snappy feedback.
27            tab.commit_files.clear();
28            tab.selected_diff = None;
29            tab.selected_file_index = None;
30            tab.diff_scroll_offset = 0.0;
31
32            // Load just the file list (instant — no line parsing).
33            if let (Some(path), Some((oid, short_oid))) = (repo_path, commit_info) {
34                let tab = state.active_tab_mut();
35                tab.status_message = Some(format!("Loading files for {short_oid}…"));
36                tab.selected_commit_oid = Some(oid.clone());
37                commands::load_commit_file_list(path, oid)
38            } else {
39                Task::none()
40            }
41        }
42
43        Message::CommitFileListLoaded(result) => {
44            match result {
45                Ok(files) => {
46                    let file_count = files.len();
47                    let tab = state.active_tab_mut();
48                    tab.commit_files = files;
49                    tab.status_message = Some(format!("{file_count} file(s) changed."));
50
51                    // Auto-select the first file and load its diff.
52                    if file_count > 0 {
53                        let first_file = &tab.commit_files[0];
54                        let file_path = first_file.display_path().to_string();
55                        tab.selected_file_index = Some(0);
56                        tab.is_loading_file_diff = true;
57
58                        if let (Some(repo_path), Some(oid)) =
59                            (tab.repo_path.clone(), tab.selected_commit_oid.clone())
60                        {
61                            return commands::load_single_file_diff(repo_path, oid, file_path);
62                        }
63                    }
64                }
65                Err(e) => {
66                    let tab = state.active_tab_mut();
67                    tab.commit_files.clear();
68                    tab.error_message = Some(format!("Failed to load commit files: {e}"));
69                    tab.status_message = None;
70                }
71            }
72            Task::none()
73        }
74
75        Message::SingleFileDiffLoaded(result) => {
76            let tab = state.active_tab_mut();
77            tab.is_loading_file_diff = false;
78            match result {
79                Ok(diff) => {
80                    tab.selected_diff = Some(diff);
81                    tab.diff_scroll_offset = 0.0;
82                }
83                Err(e) => {
84                    tab.selected_diff = None;
85                    tab.error_message = Some(format!("Failed to load file diff: {e}"));
86                }
87            }
88            Task::none()
89        }
90
91        Message::CommitMessageChanged(msg) => {
92            state.active_tab_mut().commit_message = msg;
93            Task::none()
94        }
95
96        Message::CreateCommit => {
97            let msg;
98            let staged_empty;
99            {
100                let tab = state.active_tab();
101                msg = tab.commit_message.trim().to_string();
102                staged_empty = tab.staged_changes.is_empty();
103            }
104            if msg.is_empty() || staged_empty {
105                return Task::none();
106            }
107            with_repo!(state, loading, "Creating commit…".into(), |repo_path| {
108                commands::create_commit(repo_path, msg)
109            })
110        }
111
112        Message::CommitCreated(result) => {
113            state.active_tab_mut().is_loading = false;
114            match result {
115                Ok(()) => {
116                    {
117                        let tab = state.active_tab_mut();
118                        tab.commit_message.clear();
119                        tab.status_message = Some("Commit created.".into());
120                    }
121                    state.refresh_active_tab()
122                }
123                Err(e) => {
124                    let tab = state.active_tab_mut();
125                    tab.error_message = Some(format!("Commit failed: {e}"));
126                    tab.status_message = None;
127                    Task::none()
128                }
129            }
130        }
131
132        _ => Task::none(),
133    }
134}