Skip to main content

rusticity_term/ecr/
actions.rs

1use crate::app::App;
2use crate::common::{CyclicEnum, InputFocus, PageSize};
3use crate::ui::ecr::{filtered_ecr_images, filtered_ecr_repositories, FILTER_CONTROLS};
4
5// ── Navigation ────────────────────────────────────────────────────────────────
6
7pub fn next_item(app: &mut App) {
8    if app.ecr_state.current_repository.is_some() {
9        let filtered = filtered_ecr_images(app);
10        if !filtered.is_empty() {
11            app.ecr_state.images.next_item(filtered.len());
12        }
13    } else {
14        let filtered = filtered_ecr_repositories(app);
15        if !filtered.is_empty() {
16            app.ecr_state.repositories.selected =
17                (app.ecr_state.repositories.selected + 1).min(filtered.len() - 1);
18            app.ecr_state.repositories.snap_to_page();
19        }
20    }
21}
22
23pub fn prev_item(app: &mut App) {
24    if app.ecr_state.current_repository.is_some() {
25        app.ecr_state.images.prev_item();
26    } else {
27        app.ecr_state.repositories.prev_item();
28    }
29}
30
31pub fn page_down_filter_input(app: &mut App) {
32    if app.ecr_state.input_focus == InputFocus::Filter {
33        let filtered = filtered_ecr_repositories(app);
34        app.ecr_state.repositories.page_down(filtered.len());
35    } else {
36        let page_size = app.ecr_state.repositories.page_size.value();
37        let filtered_count = filtered_ecr_repositories(app).len();
38        app.ecr_state.input_focus.handle_page_down(
39            &mut app.ecr_state.repositories.selected,
40            &mut app.ecr_state.repositories.scroll_offset,
41            page_size,
42            filtered_count,
43        );
44    }
45}
46
47pub fn page_down_normal(app: &mut App) {
48    if app.ecr_state.current_repository.is_some() {
49        let filtered = filtered_ecr_images(app);
50        app.ecr_state.images.page_down(filtered.len());
51    } else {
52        let filtered = filtered_ecr_repositories(app);
53        app.ecr_state.repositories.page_down(filtered.len());
54    }
55}
56
57pub fn page_up_filter_input(app: &mut App) {
58    if app.ecr_state.input_focus == InputFocus::Filter {
59        app.ecr_state.repositories.page_up();
60    } else {
61        let page_size = app.ecr_state.repositories.page_size.value();
62        app.ecr_state.input_focus.handle_page_up(
63            &mut app.ecr_state.repositories.selected,
64            &mut app.ecr_state.repositories.scroll_offset,
65            page_size,
66        );
67    }
68}
69
70pub fn page_up_normal(app: &mut App) {
71    if app.ecr_state.current_repository.is_some() {
72        app.ecr_state.images.page_up();
73    } else {
74        app.ecr_state.repositories.page_up();
75    }
76}
77
78pub fn next_pane(app: &mut App) {
79    if app.ecr_state.current_repository.is_some() {
80        app.ecr_state.images.toggle_expand();
81    } else {
82        app.ecr_state.repositories.toggle_expand();
83    }
84}
85
86pub fn prev_pane(app: &mut App) {
87    if app.ecr_state.current_repository.is_some() {
88        app.ecr_state.images.collapse();
89    } else {
90        app.ecr_state.repositories.collapse();
91    }
92}
93
94pub fn collapse_row(app: &mut App) {
95    if app.ecr_state.current_repository.is_some() {
96        app.ecr_state.images.collapse();
97    } else {
98        app.ecr_state.repositories.collapse();
99    }
100}
101
102pub fn go_to_page(app: &mut App, page: usize) {
103    if app.ecr_state.current_repository.is_some() {
104        let filtered_count = app
105            .ecr_state
106            .images
107            .filtered(|img| {
108                app.ecr_state.images.filter.is_empty()
109                    || img
110                        .tag
111                        .to_lowercase()
112                        .contains(&app.ecr_state.images.filter.to_lowercase())
113                    || img
114                        .digest
115                        .to_lowercase()
116                        .contains(&app.ecr_state.images.filter.to_lowercase())
117            })
118            .len();
119        app.ecr_state.images.goto_page(page, filtered_count);
120    } else {
121        let filtered_count = app
122            .ecr_state
123            .repositories
124            .filtered(|r| {
125                app.ecr_state.repositories.filter.is_empty()
126                    || r.name
127                        .to_lowercase()
128                        .contains(&app.ecr_state.repositories.filter.to_lowercase())
129            })
130            .len();
131        app.ecr_state.repositories.goto_page(page, filtered_count);
132    }
133}
134
135// ── Filter ────────────────────────────────────────────────────────────────────
136
137pub fn apply_filter_reset(app: &mut App) {
138    if app.ecr_state.current_repository.is_some() {
139        app.ecr_state.images.reset();
140    } else {
141        app.ecr_state.repositories.reset();
142    }
143}
144
145pub fn start_filter(app: &mut App) {
146    if app.ecr_state.current_repository.is_none() {
147        app.ecr_state.input_focus = InputFocus::Filter;
148    }
149}
150
151pub fn next_filter_focus(app: &mut App) {
152    app.ecr_state.input_focus = app.ecr_state.input_focus.next(&FILTER_CONTROLS);
153}
154
155pub fn prev_filter_focus(app: &mut App) {
156    app.ecr_state.input_focus = app.ecr_state.input_focus.prev(&FILTER_CONTROLS);
157}
158
159pub fn is_pagination_focused(app: &App) -> bool {
160    app.ecr_state.current_repository.is_none()
161        && app.ecr_state.input_focus == InputFocus::Pagination
162}
163
164// ── Actions ───────────────────────────────────────────────────────────────────
165
166pub fn select_item(app: &mut App) {
167    if app.ecr_state.current_repository.is_none() {
168        let filtered_repos = filtered_ecr_repositories(app);
169        if let Some(repo) = app.ecr_state.repositories.get_selected(&filtered_repos) {
170            let repo_name = repo.name.clone();
171            let repo_uri = repo.uri.clone();
172            app.ecr_state.current_repository = Some(repo_name);
173            app.ecr_state.current_repository_uri = Some(repo_uri);
174            app.ecr_state.images.reset();
175            app.ecr_state.repositories.loading = true;
176        }
177    }
178}
179
180pub fn go_back(app: &mut App) {
181    if app.ecr_state.images.has_expanded_item() {
182        app.ecr_state.images.collapse();
183    } else {
184        app.ecr_state.current_repository = None;
185        app.ecr_state.current_repository_uri = None;
186        app.ecr_state.images.items.clear();
187        app.ecr_state.images.reset();
188    }
189}
190
191pub fn next_detail_tab(app: &mut App) {
192    app.ecr_state.tab = app.ecr_state.tab.next();
193    app.ecr_state.repositories.reset();
194    app.ecr_state.repositories.loading = true;
195}
196
197pub fn prev_detail_tab(app: &mut App) {
198    app.ecr_state.tab = app.ecr_state.tab.prev();
199    app.ecr_state.repositories.reset();
200    app.ecr_state.repositories.loading = true;
201}
202
203pub fn yank(app: &App) {
204    use crate::app::copy_to_clipboard;
205    if app.ecr_state.current_repository.is_some() {
206        let filtered = filtered_ecr_images(app);
207        if let Some(image) = app.ecr_state.images.get_selected(&filtered) {
208            copy_to_clipboard(&image.uri);
209        }
210    } else {
211        let filtered = filtered_ecr_repositories(app);
212        if let Some(repo) = app.ecr_state.repositories.get_selected(&filtered) {
213            copy_to_clipboard(&repo.uri);
214        }
215    }
216}
217
218pub fn scroll_up(app: &mut App) {
219    if app.ecr_state.current_repository.is_some() {
220        app.ecr_state.images.page_up();
221    } else {
222        app.ecr_state.repositories.page_up();
223    }
224}
225
226pub fn scroll_down(app: &mut App) {
227    if app.ecr_state.current_repository.is_some() {
228        let filtered = filtered_ecr_images(app);
229        app.ecr_state.images.page_down(filtered.len());
230    } else {
231        let filtered = filtered_ecr_repositories(app);
232        app.ecr_state.repositories.page_down(filtered.len());
233    }
234}
235
236// ── Column preferences ────────────────────────────────────────────────────────
237
238pub fn toggle_column(app: &mut App) {
239    let idx = app.column_selector_index;
240    if app.ecr_state.current_repository.is_some() {
241        // Images view — idx is 0-based
242        if let Some(col) = app.ecr_image_column_ids.get(idx) {
243            if let Some(pos) = app
244                .ecr_image_visible_column_ids
245                .iter()
246                .position(|c| c == col)
247            {
248                if app.ecr_image_visible_column_ids.len() > 1 {
249                    app.ecr_image_visible_column_ids.remove(pos);
250                }
251            } else {
252                app.ecr_image_visible_column_ids.push(*col);
253            }
254        }
255    } else {
256        // Repositories view — idx is 1-based (0 = header)
257        if idx > 0 && idx <= app.ecr_repo_column_ids.len() {
258            if let Some(col) = app.ecr_repo_column_ids.get(idx - 1) {
259                if let Some(pos) = app
260                    .ecr_repo_visible_column_ids
261                    .iter()
262                    .position(|c| c == col)
263                {
264                    if app.ecr_repo_visible_column_ids.len() > 1 {
265                        app.ecr_repo_visible_column_ids.remove(pos);
266                    }
267                } else {
268                    app.ecr_repo_visible_column_ids.push(*col);
269                }
270            }
271        } else if idx == app.ecr_repo_column_ids.len() + 3 {
272            app.ecr_state.repositories.page_size = PageSize::Ten;
273        } else if idx == app.ecr_repo_column_ids.len() + 4 {
274            app.ecr_state.repositories.page_size = PageSize::TwentyFive;
275        } else if idx == app.ecr_repo_column_ids.len() + 5 {
276            app.ecr_state.repositories.page_size = PageSize::Fifty;
277        } else if idx == app.ecr_repo_column_ids.len() + 6 {
278            app.ecr_state.repositories.page_size = PageSize::OneHundred;
279        }
280    }
281}
282
283pub fn next_preferences(app: &mut App) {
284    // Images view only — repos view handled by generic logic
285    let page_size_idx = app.ecr_image_column_ids.len() + 2;
286    if app.column_selector_index < page_size_idx {
287        app.column_selector_index = page_size_idx;
288    } else {
289        app.column_selector_index = 0;
290    }
291}
292
293pub fn prev_preferences(app: &mut App) {
294    let page_size_idx = app.ecr_image_column_ids.len() + 2;
295    if app.column_selector_index >= page_size_idx {
296        app.column_selector_index = 0;
297    } else {
298        app.column_selector_index = page_size_idx;
299    }
300}
301
302pub fn column_selector_max(app: &App) -> usize {
303    if app.ecr_state.current_repository.is_some() {
304        app.ecr_image_column_ids.len() + 6
305    } else {
306        app.ecr_repo_column_ids.len() + 6
307    }
308}
309
310pub fn column_count(app: &App) -> usize {
311    if app.ecr_state.current_repository.is_some() {
312        app.ecr_image_column_ids.len()
313    } else {
314        app.ecr_repo_column_ids.len()
315    }
316}
317
318// ── Breadcrumb / console URL ──────────────────────────────────────────────────
319
320pub fn breadcrumb(app: &App) -> Vec<String> {
321    let mut parts = vec!["ECR".to_string()];
322    if let Some(repo) = &app.ecr_state.current_repository {
323        parts.push(repo.clone());
324    } else {
325        parts.push("Repositories".to_string());
326    }
327    parts
328}
329
330pub fn console_url(app: &App) -> String {
331    use crate::ecr;
332    if let Some(repo_name) = &app.ecr_state.current_repository {
333        ecr::console_url_private_repository(&app.config.region, &app.config.account_id, repo_name)
334    } else {
335        ecr::console_url_repositories(&app.config.region)
336    }
337}