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::InstallBackendFromRepoUrl(url) => { |
36 | 0 | self.registry.install_errors.remove(&url); |
37 | 0 | let u = url.clone(); |
38 | 0 | Task::perform( |
39 | 0 | async move { crate::daemon::registry::install_by_repo_url(&u).await }, |
40 | 0 | move |res| { |
41 | 0 | cosmic::Action::App(Message::ModelsPage(match res { |
42 | 0 | Ok(a) => ModelsPageMessage::InstallAccepted { |
43 | 0 | source: url.clone(), |
44 | 0 | install_id: a.install_id, |
45 | 0 | }, |
46 | 0 | Err(e) => ModelsPageMessage::InstallFailedToStart { |
47 | 0 | source: url.clone(), |
48 | 0 | error: e.to_string(), |
49 | 0 | }, |
50 | | })) |
51 | 0 | }, |
52 | | ) |
53 | | } |
54 | | |
55 | 0 | ModelsPageMessage::InstallAccepted { source, install_id } => { |
56 | | use crate::state::registry::InstallStatus; |
57 | | use super_stt_shared::registry::events::InstallPhase; |
58 | | // The request was accepted — retire any prior start error. |
59 | 0 | self.registry.install_errors.remove(&source); |
60 | 0 | self.registry.installs.insert( |
61 | 0 | source, |
62 | 0 | InstallStatus { |
63 | 0 | install_id, |
64 | 0 | phase: InstallPhase::Downloading, |
65 | 0 | bytes_done: 0, |
66 | 0 | bytes_total: None, |
67 | 0 | error: None, |
68 | 0 | }, |
69 | | ); |
70 | 0 | Task::none() |
71 | | } |
72 | | |
73 | 0 | ModelsPageMessage::InstallFailedToStart { source, error } => { |
74 | 0 | log::error!("install({source}) failed to start: {error}"); |
75 | | // Drop the pending marker (there is no background install) and |
76 | | // record the reason so the Browse card shows "Failed" + a note |
77 | | // instead of silently snapping back to the Install button. |
78 | 0 | self.registry.installs.remove(&source); |
79 | 0 | self.registry.install_errors.insert(source, error); |
80 | 0 | Task::none() |
81 | | } |
82 | | |
83 | 0 | ModelsPageMessage::UpdateBackend(source) => { |
84 | 0 | self.registry.install_errors.remove(&source); |
85 | 0 | let s = source.clone(); |
86 | 0 | Task::perform( |
87 | 0 | async move { crate::daemon::registry::update(&s).await }, |
88 | 0 | move |res| { |
89 | 0 | cosmic::Action::App(match res { |
90 | 0 | Ok(r) if r.noop => { |
91 | 0 | log::info!("update({source}) noop — already at {}", r.to_version); |
92 | | // Nothing to install, but something to correct: |
93 | | // the only way to reach this is an Update entry |
94 | | // drawn from a stale `installed_version`. Reload |
95 | | // the catalog that decides it, or the entry |
96 | | // stays and every click no-ops again. |
97 | 0 | Message::ModelsPage(ModelsPageMessage::InstallCompleted { source }) |
98 | | } |
99 | 0 | Ok(r) => Message::ModelsPage(ModelsPageMessage::InstallAccepted { |
100 | 0 | source: source.clone(), |
101 | 0 | install_id: r.install_id.unwrap_or_default(), |
102 | 0 | }), |
103 | 0 | Err(e) => { |
104 | 0 | Message::ModelsPage(ModelsPageMessage::InstallFailedToStart { |
105 | 0 | source: source.clone(), |
106 | 0 | error: e.to_string(), |
107 | 0 | }) |
108 | | } |
109 | | }) |
110 | 0 | }, |
111 | | ) |
112 | | } |
113 | | |
114 | 0 | _ => Task::none(), |
115 | | } |
116 | 0 | } |
117 | | |
118 | | /// Uninstall lifecycle: kick off the request and surface its failure. |
119 | 0 | pub(in crate::core::app) fn handle_models_uninstall( |
120 | 0 | &mut self, |
121 | 0 | message: ModelsPageMessage, |
122 | 0 | ) -> Task<cosmic::Action<Message>> { |
123 | 0 | match message { |
124 | 0 | ModelsPageMessage::UninstallBackend(source) => { |
125 | | // Clear any stale failure so the row reads "in progress" on retry. |
126 | 0 | self.registry.uninstall_errors.remove(&source); |
127 | 0 | let s = source.clone(); |
128 | 0 | Task::perform( |
129 | 0 | async move { crate::daemon::registry::uninstall(&s).await }, |
130 | 0 | move |res| { |
131 | 0 | cosmic::Action::App(match res { |
132 | 0 | Ok(_) => Message::Backend(BackendMessage::BackendsReload), |
133 | 0 | Err(error) => Message::ModelsPage(ModelsPageMessage::UninstallFailed { |
134 | 0 | source: source.clone(), |
135 | 0 | error: error.to_string(), |
136 | 0 | }), |
137 | | }) |
138 | 0 | }, |
139 | | ) |
140 | | } |
141 | | |
142 | 0 | ModelsPageMessage::UninstallFailed { source, error } => { |
143 | 0 | log::error!("uninstall({source}) failed: {error}"); |
144 | 0 | self.registry.uninstall_errors.insert(source, error); |
145 | 0 | Task::none() |
146 | | } |
147 | | |
148 | 0 | _ => Task::none(), |
149 | | } |
150 | 0 | } |
151 | | |
152 | 0 | pub(in crate::core::app) fn handle_models_install_progress( |
153 | 0 | &mut self, |
154 | 0 | message: ModelsPageMessage, |
155 | 0 | ) -> Task<cosmic::Action<Message>> { |
156 | 0 | match message { |
157 | | ModelsPageMessage::InstallProgress { |
158 | 0 | install_id, |
159 | 0 | source, |
160 | 0 | phase, |
161 | 0 | bytes_done, |
162 | 0 | bytes_total, |
163 | | } => { |
164 | 0 | if let Some(s) = self.registry.installs.get_mut(&source) |
165 | 0 | && s.install_id == install_id |
166 | | { |
167 | 0 | s.phase = phase; |
168 | 0 | if let Some(d) = bytes_done { |
169 | 0 | s.bytes_done = d; |
170 | 0 | } |
171 | 0 | if let Some(t) = bytes_total { |
172 | 0 | s.bytes_total = Some(t); |
173 | 0 | } |
174 | 0 | } |
175 | 0 | Task::none() |
176 | | } |
177 | | |
178 | 0 | ModelsPageMessage::InstallCompleted { source } => { |
179 | 0 | self.registry.installs.remove(&source); |
180 | 0 | self.registry.install_errors.remove(&source); |
181 | | // Both catalogs, because they carry different halves of what |
182 | | // just changed. The backends list is what the Installed tab |
183 | | // draws; the registry catalog carries `installed_version`, and |
184 | | // that is what decides whether an Update entry is offered. Left |
185 | | // stale, the Update the user just ran stays on the menu and |
186 | | // does nothing when clicked. No index refresh: the annotation |
187 | | // is computed from local install state, so the cached index is |
188 | | // enough and a network round-trip would only slow this down. |
189 | 0 | crate::core::app::handlers::tasks::reload_backend_catalogs() |
190 | | } |
191 | | |
192 | | ModelsPageMessage::InstallFailed { |
193 | 0 | install_id, |
194 | 0 | source, |
195 | 0 | phase, |
196 | 0 | error, |
197 | | } => { |
198 | 0 | if let Some(s) = self.registry.installs.get_mut(&source) |
199 | 0 | && s.install_id == install_id |
200 | 0 | { |
201 | 0 | s.error = Some(error); |
202 | 0 | s.phase = phase; |
203 | 0 | } |
204 | 0 | Task::none() |
205 | | } |
206 | | |
207 | 0 | _ => Task::none(), |
208 | | } |
209 | 0 | } |
210 | | } |