super-stt-app/src/core/app/handlers/models_page/install.rs
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-3.0-only |
2 | | |
3 | | use crate::core::app::AppModel; |
4 | | use crate::ui::messages::{BackendMessage, Message, ModelsPageMessage}; |
5 | | use cosmic::prelude::*; |
6 | | |
7 | | impl AppModel { |
8 | 0 | pub(in crate::core::app) fn handle_models_install_lifecycle( |
9 | 0 | &mut self, |
10 | 0 | message: ModelsPageMessage, |
11 | 0 | ) -> Task<cosmic::Action<Message>> { |
12 | 0 | match message { |
13 | | // Registry / Download-tab messages |
14 | 0 | ModelsPageMessage::InstallBackend(source) => { |
15 | | // Clear a prior "failed to start" so the card reads "Installing…". |
16 | 0 | self.registry.install_errors.remove(&source); |
17 | 0 | let s = source.clone(); |
18 | 0 | Task::perform( |
19 | 0 | async move { crate::daemon::registry::install_by_source(&s).await }, |
20 | 0 | move |res| { |
21 | 0 | cosmic::Action::App(Message::ModelsPage(match res { |
22 | 0 | Ok(a) => ModelsPageMessage::InstallAccepted { |
23 | 0 | source: source.clone(), |
24 | 0 | install_id: a.install_id, |
25 | 0 | }, |
26 | 0 | Err(e) => ModelsPageMessage::InstallFailedToStart { |
27 | 0 | source: source.clone(), |
28 | 0 | error: e.to_string(), |
29 | 0 | }, |
30 | | })) |
31 | 0 | }, |
32 | | ) |
33 | | } |
34 | | |
35 | 0 | ModelsPageMessage::InstallAccepted { source, install_id } => { |
36 | | use crate::state::registry::InstallStatus; |
37 | | use super_stt_shared::registry::events::InstallPhase; |
38 | | // The request was accepted — retire any prior start error. |
39 | 0 | self.registry.install_errors.remove(&source); |
40 | 0 | self.registry.installs.insert( |
41 | 0 | source, |
42 | 0 | InstallStatus { |
43 | 0 | install_id, |
44 | 0 | phase: InstallPhase::Downloading, |
45 | 0 | bytes_done: 0, |
46 | 0 | bytes_total: None, |
47 | 0 | error: None, |
48 | 0 | }, |
49 | | ); |
50 | 0 | Task::none() |
51 | | } |
52 | | |
53 | 0 | ModelsPageMessage::InstallFailedToStart { source, error } => { |
54 | 0 | log::error!("install({source}) failed to start: {error}"); |
55 | 0 | let error = super_stt_shared::daemon::http_client::HttpError::Other(error) |
56 | 0 | .user_message(&super_stt_shared::SUPER_STT); |
57 | | // Drop the pending marker (there is no background install) and |
58 | | // record the reason so the Browse card shows "Failed" + a note |
59 | | // instead of silently snapping back to the Install button. |
60 | 0 | self.registry.installs.remove(&source); |
61 | 0 | self.registry.install_errors.insert(source, error); |
62 | 0 | Task::none() |
63 | | } |
64 | | |
65 | 0 | ModelsPageMessage::UpdateBackend(source) => { |
66 | 0 | self.registry.install_errors.remove(&source); |
67 | 0 | let s = source.clone(); |
68 | 0 | Task::perform( |
69 | 0 | async move { crate::daemon::registry::update(&s).await }, |
70 | 0 | move |res| { |
71 | 0 | cosmic::Action::App(match res { |
72 | 0 | Ok(r) if r.noop => { |
73 | 0 | log::info!("update({source}) noop — already at {}", r.to_version); |
74 | | // Nothing to install, but something to correct: |
75 | | // the only way to reach this is an Update entry |
76 | | // drawn from a stale `installed_version`. Reload |
77 | | // the catalog that decides it, or the entry |
78 | | // stays and every click no-ops again. |
79 | 0 | Message::ModelsPage(ModelsPageMessage::InstallCompleted { source }) |
80 | | } |
81 | 0 | Ok(r) => Message::ModelsPage(ModelsPageMessage::InstallAccepted { |
82 | 0 | source: source.clone(), |
83 | 0 | install_id: r.install_id.unwrap_or_default(), |
84 | 0 | }), |
85 | 0 | Err(e) => { |
86 | 0 | Message::ModelsPage(ModelsPageMessage::InstallFailedToStart { |
87 | 0 | source: source.clone(), |
88 | 0 | error: e.to_string(), |
89 | 0 | }) |
90 | | } |
91 | | }) |
92 | 0 | }, |
93 | | ) |
94 | | } |
95 | | |
96 | 0 | _ => Task::none(), |
97 | | } |
98 | 0 | } |
99 | | |
100 | | /// Uninstall lifecycle: kick off the request and surface its failure. |
101 | 0 | pub(in crate::core::app) fn handle_models_uninstall( |
102 | 0 | &mut self, |
103 | 0 | message: ModelsPageMessage, |
104 | 0 | ) -> Task<cosmic::Action<Message>> { |
105 | 0 | match message { |
106 | 0 | ModelsPageMessage::UninstallBackend(source) => { |
107 | | // Clear any stale failure so the row reads "in progress" on retry. |
108 | 0 | self.registry.uninstall_errors.remove(&source); |
109 | 0 | let s = source.clone(); |
110 | 0 | Task::perform( |
111 | 0 | async move { crate::daemon::registry::uninstall(&s).await }, |
112 | 0 | move |res| { |
113 | 0 | cosmic::Action::App(match res { |
114 | 0 | Ok(_) => Message::Backend(BackendMessage::BackendsReload), |
115 | 0 | Err(error) => Message::ModelsPage(ModelsPageMessage::UninstallFailed { |
116 | 0 | source: source.clone(), |
117 | 0 | error: error.to_string(), |
118 | 0 | }), |
119 | | }) |
120 | 0 | }, |
121 | | ) |
122 | | } |
123 | | |
124 | 0 | ModelsPageMessage::UninstallFailed { source, error } => { |
125 | 0 | log::error!("uninstall({source}) failed: {error}"); |
126 | 0 | self.registry.uninstall_errors.insert(source, error); |
127 | 0 | Task::none() |
128 | | } |
129 | | |
130 | 0 | _ => Task::none(), |
131 | | } |
132 | 0 | } |
133 | | |
134 | 0 | pub(in crate::core::app) fn handle_models_install_progress( |
135 | 0 | &mut self, |
136 | 0 | message: ModelsPageMessage, |
137 | 0 | ) -> Task<cosmic::Action<Message>> { |
138 | 0 | match message { |
139 | | ModelsPageMessage::InstallProgress { |
140 | 0 | install_id, |
141 | 0 | source, |
142 | 0 | phase, |
143 | 0 | bytes_done, |
144 | 0 | bytes_total, |
145 | | } => { |
146 | 0 | if let Some(s) = self.registry.installs.get_mut(&source) |
147 | 0 | && s.install_id == install_id |
148 | | { |
149 | 0 | s.phase = phase; |
150 | 0 | if let Some(d) = bytes_done { |
151 | 0 | s.bytes_done = d; |
152 | 0 | } |
153 | 0 | if let Some(t) = bytes_total { |
154 | 0 | s.bytes_total = Some(t); |
155 | 0 | } |
156 | 0 | } |
157 | 0 | Task::none() |
158 | | } |
159 | | |
160 | 0 | ModelsPageMessage::InstallCompleted { source } => { |
161 | | // The drawer's whole job is done the moment the thing it |
162 | | // previewed is installed: the preview now describes a backend |
163 | | // that is already there, and the Install button under it would |
164 | | // offer to fetch it again. Close it and empty it. |
165 | | // |
166 | | // Only for the install this drawer started — a Browse card's |
167 | | // install reports on the same channel, and it has its own card |
168 | | // to report on. |
169 | 0 | if self.core.window.show_context |
170 | 0 | && self.context_page == crate::state::models::ContextPage::AddBackend |
171 | 0 | && self.registry.add_key() == source |
172 | 0 | { |
173 | 0 | self.core.window.show_context = false; |
174 | 0 | self.registry.clear_add_sheet(); |
175 | 0 | } |
176 | 0 | self.registry.installs.remove(&source); |
177 | 0 | self.registry.install_errors.remove(&source); |
178 | | // Both catalogs, because they carry different halves of what |
179 | | // just changed. The backends list is what the Installed tab |
180 | | // draws; the registry catalog carries `installed_version`, and |
181 | | // that is what decides whether an Update entry is offered. Left |
182 | | // stale, the Update the user just ran stays on the menu and |
183 | | // does nothing when clicked. No index refresh: the annotation |
184 | | // is computed from local install state, so the cached index is |
185 | | // enough and a network round-trip would only slow this down. |
186 | 0 | crate::core::app::handlers::tasks::reload_backend_catalogs() |
187 | | } |
188 | | |
189 | | ModelsPageMessage::InstallFailed { |
190 | 0 | install_id, |
191 | 0 | source, |
192 | 0 | phase, |
193 | 0 | error, |
194 | | } => { |
195 | 0 | if let Some(s) = self.registry.installs.get_mut(&source) |
196 | 0 | && s.install_id == install_id |
197 | 0 | { |
198 | 0 | s.error = Some(error); |
199 | 0 | s.phase = phase; |
200 | 0 | } |
201 | 0 | Task::none() |
202 | | } |
203 | | |
204 | 0 | _ => Task::none(), |
205 | | } |
206 | 0 | } |
207 | | } |