Add rebuild output window

This commit is contained in:
Victor Fuentes 2022-11-01 14:27:22 -04:00
parent f355d40336
commit 0e9d1105f7
No known key found for this signature in database
GPG Key ID: 0A88B68D6A9ACAE0
10 changed files with 535 additions and 341 deletions

65
Cargo.lock generated
View File

@ -198,9 +198,9 @@ checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.12.1" version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" checksum = "5aec14f5d4e6e3f927cd0c81f72e5710d95ee9019fbeb4b3021193867491bfd8"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -250,9 +250,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.73" version = "1.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574"
[[package]] [[package]]
name = "cfg-expr" name = "cfg-expr"
@ -1230,9 +1230,9 @@ dependencies = [
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.14.20" version = "0.14.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
@ -1617,7 +1617,7 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]] [[package]]
name = "nix-data" name = "nix-data"
version = "0.0.2" version = "0.0.2"
source = "git+https://github.com/snowflakelinux/nix-data?rev=a5168c768e8cf8bd3e1afe1772f8e05e5b03ed95#a5168c768e8cf8bd3e1afe1772f8e05e5b03ed95" source = "git+https://github.com/snowflakelinux/nix-data#33479e595f14142f15fb6ec29cc585352c2c7703"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"csv", "csv",
@ -1669,6 +1669,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"sha256", "sha256",
"sourceview5",
"spdx", "spdx",
"sqlx", "sqlx",
"tokio", "tokio",
@ -1751,9 +1752,9 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.15.0" version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]] [[package]]
name = "opaque-debug" name = "opaque-debug"
@ -1967,14 +1968,14 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]] [[package]]
name = "png" name = "png"
version = "0.17.6" version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f0e7f4c94ec26ff209cee506314212639d6c91b80afb82984819fafce9df01c" checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crc32fast", "crc32fast",
"flate2", "flate2",
"miniz_oxide 0.5.4", "miniz_oxide 0.6.2",
] ]
[[package]] [[package]]
@ -2500,6 +2501,42 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "sourceview5"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "922cc28db6bec169868319262dd932f6403e5ce95dad0d2bb6fcc9ac03be7f10"
dependencies = [
"bitflags",
"futures-channel",
"futures-core",
"gdk-pixbuf",
"gdk4",
"gio",
"glib",
"gtk4",
"libc",
"pango",
"sourceview5-sys",
]
[[package]]
name = "sourceview5-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73277b2a53923aeecd212a89379dce7e6c687fe35fe6dd41d9b0d7b3d4c2eb0b"
dependencies = [
"gdk-pixbuf-sys",
"gdk4-sys",
"gio-sys",
"glib-sys",
"gobject-sys",
"gtk4-sys",
"libc",
"pango-sys",
"system-deps",
]
[[package]] [[package]]
name = "spdx" name = "spdx"
version = "0.9.0" version = "0.9.0"
@ -2777,9 +2814,9 @@ dependencies = [
[[package]] [[package]]
name = "tiff" name = "tiff"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7259662e32d1e219321eb309d5f9d898b779769d81b76e762c07c8e5d38fcb65" checksum = "9f71e422515e83e3ab8a03d4781d05ebf864fc61f4546e6ecffa58cbd34181a0"
dependencies = [ dependencies = [
"flate2", "flate2",
"jpeg-decoder", "jpeg-decoder",

View File

@ -9,6 +9,7 @@ relm4 = { version = "0.5.0-beta.4", features = ["all"] }
relm4-components = { package = "relm4-components", version = "0.5.0-beta.4"} relm4-components = { package = "relm4-components", version = "0.5.0-beta.4"}
adw = { package = "libadwaita", version = "0.2", features = ["v1_2", "gtk_v4_6"] } adw = { package = "libadwaita", version = "0.2", features = ["v1_2", "gtk_v4_6"] }
gtk = { package = "gtk4", version = "0.5", features = ["v4_6"] } gtk = { package = "gtk4", version = "0.5", features = ["v4_6"] }
sourceview5 = { version = "0.5", features = ["v5_4"] }
tokio = { version = "1.21", features = ["rt", "macros", "time", "rt-multi-thread", "sync", "process"] } tokio = { version = "1.21", features = ["rt", "macros", "time", "rt-multi-thread", "sync", "process"] }
tracker = "0.1" tracker = "0.1"
@ -17,7 +18,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9" serde_yaml = "0.9"
nix-editor = "0.3.0-beta.1" nix-editor = "0.3.0-beta.1"
nix-data = { git = "https://github.com/snowflakelinux/nix-data", rev = "a5168c768e8cf8bd3e1afe1772f8e05e5b03ed95" } nix-data = { git = "https://github.com/snowflakelinux/nix-data" }
sqlx = { version = "0.6", features = [ "runtime-tokio-native-tls" , "sqlite" ] } sqlx = { version = "0.6", features = [ "runtime-tokio-native-tls" , "sqlite" ] }

View File

@ -57,6 +57,7 @@
pango pango
pkg-config pkg-config
polkit polkit
sqlite
wrapGAppsHook4 wrapGAppsHook4
nixos-appstream-data nixos-appstream-data
]; ];

View File

@ -1,12 +1,13 @@
use super::pkgpage::{InstallType, PkgAction, PkgMsg, WorkPkg}; use super::pkgpage::{InstallType, PkgAction, PkgMsg, WorkPkg};
use super::window::{SystemPkgs, UserPkgs}; use super::rebuild::RebuildMsg;
use super::window::{SystemPkgs, UserPkgs, REBUILD_BROKER};
use log::*; use log::*;
use nix_data::config::configfile::NixDataConfig; use nix_data::config::configfile::NixDataConfig;
use relm4::*; use relm4::*;
use std::error::Error; use anyhow::{Result, anyhow};
use std::path::Path; use std::path::Path;
use std::process::Stdio; use std::process::Stdio;
use std::{fs, io}; use std::fs;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
#[tracker::track] #[tracker::track]
@ -265,6 +266,7 @@ impl Worker for InstallAsyncHandler {
} }
}, },
InstallType::System => { InstallType::System => {
REBUILD_BROKER.send(RebuildMsg::Show);
if let Some(systemconfig) = systemconfig { if let Some(systemconfig) = systemconfig {
match work.action { match work.action {
PkgAction::Install => { PkgAction::Install => {
@ -281,12 +283,15 @@ impl Worker for InstallAsyncHandler {
{ {
Ok(b) => { Ok(b) => {
if b { if b {
REBUILD_BROKER.send(RebuildMsg::FinishSuccess);
sender.output(PkgMsg::FinishedProcess(work)) sender.output(PkgMsg::FinishedProcess(work))
} else { } else {
REBUILD_BROKER.send(RebuildMsg::FinishError(None));
sender.output(PkgMsg::FailedProcess(work)) sender.output(PkgMsg::FailedProcess(work))
} }
} }
Err(e) => { Err(e) => {
REBUILD_BROKER.send(RebuildMsg::FinishError(None));
sender.output(PkgMsg::FailedProcess(work)); sender.output(PkgMsg::FailedProcess(work));
warn!("Error installing system package: {}", e); warn!("Error installing system package: {}", e);
} }
@ -307,12 +312,15 @@ impl Worker for InstallAsyncHandler {
{ {
Ok(b) => { Ok(b) => {
if b { if b {
REBUILD_BROKER.send(RebuildMsg::FinishSuccess);
sender.output(PkgMsg::FinishedProcess(work)) sender.output(PkgMsg::FinishedProcess(work))
} else { } else {
REBUILD_BROKER.send(RebuildMsg::FinishError(None));
sender.output(PkgMsg::FailedProcess(work)) sender.output(PkgMsg::FailedProcess(work))
} }
} }
Err(e) => { Err(e) => {
REBUILD_BROKER.send(RebuildMsg::FinishError(None));
sender.output(PkgMsg::FailedProcess(work)); sender.output(PkgMsg::FailedProcess(work));
warn!("Error removing system package: {}", e); warn!("Error removing system package: {}", e);
} }
@ -344,7 +352,7 @@ async fn installsys(
systemconfig: String, systemconfig: String,
flakeargs: Option<String>, flakeargs: Option<String>,
_sender: ComponentSender<InstallAsyncHandler>, _sender: ComponentSender<InstallAsyncHandler>,
) -> Result<bool, Box<dyn Error>> { ) -> Result<bool> {
let mut p = pkg; let mut p = pkg;
let f = fs::read_to_string(&systemconfig)?; let f = fs::read_to_string(&systemconfig)?;
if let Ok(s) = nix_editor::read::getwithvalue(&f, "environment.systemPackages") { if let Ok(s) = nix_editor::read::getwithvalue(&f, "environment.systemPackages") {
@ -352,10 +360,7 @@ async fn installsys(
p = format!("pkgs.{}", p); p = format!("pkgs.{}", p);
} }
} else { } else {
return Err(Box::new(io::Error::new( return Err(anyhow!("Failed to write configuration.nix"));
io::ErrorKind::InvalidData,
"Failed to write configuration.nix",
)));
} }
let out = match action { let out = match action {
@ -363,10 +368,7 @@ async fn installsys(
match nix_editor::write::addtoarr(&f, "environment.systemPackages", vec![p]) { match nix_editor::write::addtoarr(&f, "environment.systemPackages", vec![p]) {
Ok(x) => x, Ok(x) => x,
Err(_) => { Err(_) => {
return Err(Box::new(io::Error::new( return Err(anyhow!("Failed to write configuration.nix"));
io::ErrorKind::InvalidData,
"Failed to write configuration.nix",
)))
} }
} }
} }
@ -374,10 +376,7 @@ async fn installsys(
match nix_editor::write::rmarr(&f, "environment.systemPackages", vec![p]) { match nix_editor::write::rmarr(&f, "environment.systemPackages", vec![p]) {
Ok(x) => x, Ok(x) => x,
Err(_) => { Err(_) => {
return Err(Box::new(io::Error::new( return Err(anyhow!("Failed to write configuration.nix"));
io::ErrorKind::InvalidData,
"Failed to write configuration.nix",
)))
} }
} }
} }
@ -419,13 +418,12 @@ async fn installsys(
.arg(&systemconfig) .arg(&systemconfig)
.arg("--") .arg("--")
.arg("switch") .arg("switch")
.arg("--impure")
.args(&rebuildargs) .args(&rebuildargs)
.stderr(Stdio::piped()) .stderr(Stdio::piped())
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.spawn()?; .spawn()?;
// sender.input(InstallAsyncHandlerMsg::SetPid(cmd.id()));
cmd.stdin.take().unwrap().write_all(out.as_bytes()).await?; cmd.stdin.take().unwrap().write_all(out.as_bytes()).await?;
let stderr = cmd.stderr.take().unwrap(); let stderr = cmd.stderr.take().unwrap();
let reader = tokio::io::BufReader::new(stderr); let reader = tokio::io::BufReader::new(stderr);
@ -433,6 +431,7 @@ async fn installsys(
let mut lines = reader.lines(); let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await { while let Ok(Some(line)) = lines.next_line().await {
trace!("CAUGHT LINE: {}", line); trace!("CAUGHT LINE: {}", line);
REBUILD_BROKER.send(RebuildMsg::UpdateText(line));
} }
if cmd.wait().await?.success() { if cmd.wait().await?.success() {
Ok(true) Ok(true)

View File

@ -1,17 +1,17 @@
pub mod window;
pub mod windowloading;
pub mod pkgtile;
pub mod categories;
pub mod pkgpage;
pub mod screenshotfactory;
pub mod installworker;
pub mod searchpage;
pub mod installedpage;
pub mod updatepage;
pub mod updatedialog;
pub mod updateworker;
pub mod about; pub mod about;
pub mod preferencespage; pub mod categories;
pub mod categorypage; pub mod categorypage;
pub mod categorytile; pub mod categorytile;
pub mod installedpage;
pub mod installworker;
pub mod pkgpage;
pub mod pkgtile;
pub mod preferencespage;
pub mod rebuild;
pub mod screenshotfactory;
pub mod searchpage;
pub mod updatepage;
pub mod updateworker;
pub mod welcome; pub mod welcome;
pub mod window;
pub mod windowloading;

244
src/ui/rebuild.rs Normal file
View File

@ -0,0 +1,244 @@
use super::window::AppMsg;
use adw::prelude::*;
use log::{info, trace};
use relm4::*;
use sourceview5::prelude::*;
#[tracker::track]
pub struct RebuildModel {
hidden: bool,
text: String,
status: RebuildStatus,
config: String,
path: String,
flake: Option<String>,
scheme: Option<sourceview5::StyleScheme>,
}
#[derive(Debug)]
pub enum RebuildMsg {
Show,
FinishSuccess,
FinishError(Option<String>),
UpdateText(String),
Close,
SetScheme(String),
Quit,
}
#[derive(PartialEq)]
enum RebuildStatus {
Building,
Success,
Error,
}
#[relm4::component(pub)]
impl SimpleComponent for RebuildModel {
type Init = gtk::Window;
type Input = RebuildMsg;
type Output = AppMsg;
type Widgets = RebuildWidgets;
view! {
dialog = adw::Window {
set_transient_for: Some(&parent_window),
set_modal: true,
#[track(model.changed(RebuildModel::hidden()))]
set_default_width: 500,
#[track(model.changed(RebuildModel::hidden()))]
set_default_height: 200,//295),
set_resizable: true,
#[watch]
set_visible: !model.hidden,
add_css_class: "dialog",
add_css_class: "message",
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
#[name(statusstack)]
gtk::Stack {
set_margin_top: 20,
set_transition_type: gtk::StackTransitionType::Crossfade,
set_vhomogeneous: false,
#[name(building)]
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 10,
gtk::Spinner {
#[watch]
set_spinning: true,
set_height_request: 60,
},
gtk::Label {
set_label: "Building...",
add_css_class: "title-1",
},
},
#[name(success)]
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 10,
gtk::Image {
add_css_class: "success",
set_icon_name: Some("object-select-symbolic"),
set_pixel_size: 128,
},
gtk::Label {
set_label: "Done!",
add_css_class: "title-1",
},
gtk::Label {
set_label: "Rebuild successful!",
add_css_class: "dim-label",
}
},
#[name(error)]
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 10,
gtk::Image {
add_css_class: "error",
set_icon_name: Some("dialog-error-symbolic"),
set_pixel_size: 128,
},
gtk::Label {
set_label: "Error!",
add_css_class: "title-1",
},
gtk::Label {
set_label: "Rebuild failed! See below for error message.",
add_css_class: "dim-label",
}
}
},
gtk::Frame {
set_margin_all: 20,
#[name(scrollwindow)]
gtk::ScrolledWindow {
set_max_content_height: 500,
set_min_content_height: 100,
#[name(outview)]
sourceview5::View {
set_editable: false,
set_cursor_visible: false,
set_monospace: true,
set_top_margin: 5,
set_bottom_margin: 5,
set_left_margin: 5,
set_vexpand: true,
set_hexpand: true,
set_vscroll_policy: gtk::ScrollablePolicy::Minimum,
#[wrap(Some)]
set_buffer: outbuf = &sourceview5::Buffer {
#[track(model.changed(RebuildModel::scheme()))]
set_style_scheme: model.scheme.as_ref(),
#[track(model.changed(RebuildModel::text()))]
set_text: &model.text,
}
}
}
},
gtk::Box {
add_css_class: "dialog-action-area",
set_orientation: gtk::Orientation::Horizontal,
set_homogeneous: true,
#[track(model.changed(RebuildModel::status()))]
set_visible: model.status != RebuildStatus::Building,
gtk::Button {
set_label: "Close",
#[track(model.changed(RebuildModel::status()))]
set_visible: model.status != RebuildStatus::Building,
connect_clicked[sender] => move |_| {
sender.input(RebuildMsg::Close)
}
}
}
}
}
}
fn pre_view() {
match model.status {
RebuildStatus::Building => {
statusstack.set_visible_child(building);
}
RebuildStatus::Success => statusstack.set_visible_child(success),
RebuildStatus::Error => statusstack.set_visible_child(error),
}
}
fn post_view() {
let adj = scrollwindow.vadjustment();
if model.status == RebuildStatus::Building {
adj.set_upper(adj.upper() + 20.0);
}
adj.set_value(adj.upper());
if model.status != RebuildStatus::Building {
outview.scroll_to_mark(&outview.buffer().get_insert(), 0.0, true, 0.0, 0.0);
scrollwindow.hadjustment().set_value(0.0);
}
}
fn init(
parent_window: Self::Init,
root: &Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let model = RebuildModel {
hidden: true,
text: String::new(),
status: RebuildStatus::Building,
config: String::new(),
path: String::new(),
flake: None,
scheme: None,
tracker: 0,
};
let widgets = view_output!();
ComponentParts { model, widgets }
}
fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>) {
self.reset();
match msg {
RebuildMsg::Show => {
self.update_hidden(|x| *x = false);
self.update_text(|x| x.clear());
self.set_status(RebuildStatus::Building);
}
RebuildMsg::UpdateText(s) => {
info!("RebuildMsg::UpdateText({})", s);
let newtext = if self.text.is_empty() {
s
} else {
format!("{}\n{}", self.text, s)
};
self.set_text(newtext);
trace!("NEWTEXT: {}", self.text);
}
RebuildMsg::FinishSuccess => {
self.set_status(RebuildStatus::Success);
}
RebuildMsg::FinishError(msg) => {
if let Some(s) = msg {
self.set_text(s)
}
self.update_hidden(|x| *x = false);
self.set_status(RebuildStatus::Error);
}
RebuildMsg::Close => {
self.update_hidden(|x| *x = true);
self.update_text(|x| x.clear());
}
RebuildMsg::SetScheme(scheme) => {
self.set_scheme(sourceview5::StyleSchemeManager::default().scheme(&scheme));
}
RebuildMsg::Quit => {
sender.output(AppMsg::Close);
}
}
}
}

View File

@ -1,137 +0,0 @@
use adw::prelude::*;
use relm4::*;
use super::updatepage::UpdatePageMsg;
#[derive(Debug)]
pub struct UpdateDialogModel {
hidden: bool,
message: String,
done: bool,
failed: bool,
}
#[derive(Debug)]
pub enum UpdateDialogMsg {
Show(String),
Close,
Done,
Failed,
}
#[relm4::component(pub)]
impl SimpleComponent for UpdateDialogModel {
type Init = gtk::Window;
type Input = UpdateDialogMsg;
type Output = UpdatePageMsg;
type Widgets = UpdateDialogWidgets;
view! {
dialog = adw::Window {
#[watch]
set_visible: !model.hidden,
set_transient_for: Some(&parent_window),
set_modal: true,
set_resizable: false,
set_default_width: 500,
set_default_height: 200,
add_css_class: "dialog",
add_css_class: "message",
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_halign: gtk::Align::Fill,
set_valign: gtk::Align::Fill,
set_hexpand: true,
set_vexpand: true,
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_halign: gtk::Align::Center,
set_valign: gtk::Align::Center,
set_hexpand: true,
set_vexpand: true,
set_margin_all: 15,
set_spacing: 10,
gtk::Label {
#[watch]
set_visible: !model.message.is_empty(),
add_css_class: "title-1",
#[watch]
set_label: &model.message,
},
if model.done {
gtk::Image {
add_css_class: "success",
set_icon_name: Some("emblem-ok-symbolic"),
set_pixel_size: 128,
}
} else if model.failed {
gtk::Image {
add_css_class: "error",
set_icon_name: Some("dialog-error-symbolic"),
set_pixel_size: 128,
}
} else {
gtk::Spinner {
#[watch]
set_visible: !model.done,
#[watch]
set_spinning: !model.done,
}
}
},
gtk::Box {
#[watch]
set_visible: model.done || model.failed,
add_css_class: "dialog-action-area",
set_valign: gtk::Align::End,
set_vexpand: true,
set_orientation: gtk::Orientation::Horizontal,
set_homogeneous: true,
gtk::Button {
set_label: "Close",
connect_clicked[sender] => move |_| {
sender.input(UpdateDialogMsg::Close);
}
}
},
}
}
}
fn init(
parent_window: Self::Init,
root: &Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let model = UpdateDialogModel {
hidden: true,
done: false,
failed: false,
message: String::default(),
};
let widgets = view_output!();
ComponentParts { model, widgets }
}
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
match msg {
UpdateDialogMsg::Show(desc) => {
self.message = desc;
self.hidden = false;
self.done = false;
self.failed = false;
}
UpdateDialogMsg::Close => {
self.hidden = true;
}
UpdateDialogMsg::Done => {
self.done = true;
}
UpdateDialogMsg::Failed => {
self.failed = true;
}
}
}
}

View File

@ -1,6 +1,6 @@
use crate::APPINFO; use crate::APPINFO;
use super::{pkgpage::InstallType, window::*, updatedialog::{UpdateDialogModel, UpdateDialogMsg}, updateworker::{UpdateAsyncHandler, UpdateAsyncHandlerMsg, UpdateAsyncHandlerInit}}; use super::{pkgpage::InstallType, window::*, updateworker::{UpdateAsyncHandler, UpdateAsyncHandlerMsg, UpdateAsyncHandlerInit}, rebuild::RebuildMsg};
use adw::prelude::*; use adw::prelude::*;
use nix_data::config::configfile::NixDataConfig; use nix_data::config::configfile::NixDataConfig;
use relm4::{factory::*, gtk::pango, *}; use relm4::{factory::*, gtk::pango, *};
@ -16,8 +16,6 @@ pub struct UpdatePageModel {
updatesystemlist: FactoryVecDeque<UpdateItemModel>, updatesystemlist: FactoryVecDeque<UpdateItemModel>,
channelupdate: Option<(String, String)>, channelupdate: Option<(String, String)>,
#[tracker::no_eq] #[tracker::no_eq]
updatedialog: Controller<UpdateDialogModel>,
#[tracker::no_eq]
updateworker: WorkerController<UpdateAsyncHandler>, updateworker: WorkerController<UpdateAsyncHandler>,
config: NixDataConfig, config: NixDataConfig,
systype: SystemPkgs, systype: SystemPkgs,
@ -34,11 +32,10 @@ pub enum UpdatePageMsg {
UpdateSystem, UpdateSystem,
UpdateAllUser, UpdateAllUser,
UpdateUser(String), UpdateUser(String),
UpdateChannels, // UpdateChannels,
UpdateSystemAndChannels, // UpdateSystemAndChannels,
UpdateAll, UpdateAll,
DoneWorking, DoneWorking,
DoneLoading,
FailedWorking, FailedWorking,
} }
@ -87,101 +84,101 @@ impl SimpleComponent for UpdatePageModel {
} }
} }
}, },
gtk::Box { // gtk::Box {
set_orientation: gtk::Orientation::Horizontal, // set_orientation: gtk::Orientation::Horizontal,
set_hexpand: true, // set_hexpand: true,
#[watch] // #[watch]
set_visible: model.channelupdate.is_some(), // set_visible: model.channelupdate.is_some(),
gtk::Label { // gtk::Label {
set_halign: gtk::Align::Start, // set_halign: gtk::Align::Start,
add_css_class: "title-4", // add_css_class: "title-4",
set_label: "Channels", // set_label: "Channels",
}, // },
}, // },
gtk::ListBox { // gtk::ListBox {
set_valign: gtk::Align::Start, // set_valign: gtk::Align::Start,
add_css_class: "boxed-list", // add_css_class: "boxed-list",
set_selection_mode: gtk::SelectionMode::None, // set_selection_mode: gtk::SelectionMode::None,
#[watch] // #[watch]
set_visible: model.channelupdate.is_some(), // set_visible: model.channelupdate.is_some(),
adw::PreferencesRow { // adw::PreferencesRow {
set_activatable: false, // set_activatable: false,
set_can_focus: false, // set_can_focus: false,
#[wrap(Some)] // #[wrap(Some)]
set_child = &gtk::Box { // set_child = &gtk::Box {
set_orientation: gtk::Orientation::Horizontal, // set_orientation: gtk::Orientation::Horizontal,
set_hexpand: true, // set_hexpand: true,
set_spacing: 10, // set_spacing: 10,
set_margin_all: 10, // set_margin_all: 10,
adw::Bin { // adw::Bin {
set_valign: gtk::Align::Center, // set_valign: gtk::Align::Center,
gtk::Image { // gtk::Image {
add_css_class: "icon-dropshadow", // add_css_class: "icon-dropshadow",
set_halign: gtk::Align::Start, // set_halign: gtk::Align::Start,
set_icon_name: Some("application-x-addon"), // set_icon_name: Some("application-x-addon"),
set_pixel_size: 64, // set_pixel_size: 64,
} // }
}, // },
gtk::Box { // gtk::Box {
set_orientation: gtk::Orientation::Vertical, // set_orientation: gtk::Orientation::Vertical,
set_halign: gtk::Align::Fill, // set_halign: gtk::Align::Fill,
set_valign: gtk::Align::Center, // set_valign: gtk::Align::Center,
set_hexpand: true, // set_hexpand: true,
set_spacing: 2, // set_spacing: 2,
gtk::Label { // gtk::Label {
set_halign: gtk::Align::Start, // set_halign: gtk::Align::Start,
set_label: "nixos", // set_label: "nixos",
set_ellipsize: pango::EllipsizeMode::End, // set_ellipsize: pango::EllipsizeMode::End,
set_lines: 1, // set_lines: 1,
set_wrap: true, // set_wrap: true,
set_max_width_chars: 0, // set_max_width_chars: 0,
}, // },
gtk::Label { // gtk::Label {
set_halign: gtk::Align::Start, // set_halign: gtk::Align::Start,
add_css_class: "dim-label", // add_css_class: "dim-label",
add_css_class: "caption", // add_css_class: "caption",
set_label: { // set_label: {
&(if let Some((old, new)) = &model.channelupdate { // &(if let Some((old, new)) = &model.channelupdate {
format!("{}{}", old, new) // format!("{} → {}", old, new)
} else { // } else {
String::default() // String::default()
}) // })
}, // },
set_visible: model.channelupdate.is_some(), // set_visible: model.channelupdate.is_some(),
set_ellipsize: pango::EllipsizeMode::End, // set_ellipsize: pango::EllipsizeMode::End,
set_lines: 1, // set_lines: 1,
set_wrap: true, // set_wrap: true,
set_max_width_chars: 0, // set_max_width_chars: 0,
}, // },
}, // },
gtk::Box { // gtk::Box {
set_orientation: gtk::Orientation::Vertical, // set_orientation: gtk::Orientation::Vertical,
set_spacing: 5, // set_spacing: 5,
set_halign: gtk::Align::End, // set_halign: gtk::Align::End,
set_valign: gtk::Align::Center, // set_valign: gtk::Align::Center,
gtk::Button { // gtk::Button {
add_css_class: "suggested-action", // add_css_class: "suggested-action",
set_valign: gtk::Align::Center, // set_valign: gtk::Align::Center,
set_halign: gtk::Align::End, // set_halign: gtk::Align::End,
set_label: "Update channel and system", // set_label: "Update channel and system",
set_can_focus: false, // set_can_focus: false,
connect_clicked[sender] => move |_| { // connect_clicked[sender] => move |_| {
sender.input(UpdatePageMsg::UpdateSystemAndChannels); // sender.input(UpdatePageMsg::UpdateSystemAndChannels);
} // }
}, // },
gtk::Button { // gtk::Button {
set_valign: gtk::Align::Center, // set_valign: gtk::Align::Center,
set_halign: gtk::Align::End, // set_halign: gtk::Align::End,
set_label: "Update channel only", // set_label: "Update channel only",
set_can_focus: false, // set_can_focus: false,
connect_clicked[sender] => move |_| { // connect_clicked[sender] => move |_| {
sender.input(UpdatePageMsg::UpdateChannels); // sender.input(UpdatePageMsg::UpdateChannels);
} // }
}, // },
} // }
} // }
} // }
}, // },
gtk::Box { gtk::Box {
set_orientation: gtk::Orientation::Horizontal, set_orientation: gtk::Orientation::Horizontal,
set_hexpand: true, set_hexpand: true,
@ -234,7 +231,7 @@ impl SimpleComponent for UpdatePageModel {
set_halign: gtk::Align::End, set_halign: gtk::Align::End,
set_hexpand: true, set_hexpand: true,
set_valign: gtk::Align::Center, set_valign: gtk::Align::Center,
set_label: "Update All", set_label: "Update",
connect_clicked[sender] => move |_|{ connect_clicked[sender] => move |_|{
sender.input(UpdatePageMsg::UpdateSystem); sender.input(UpdatePageMsg::UpdateSystem);
}, },
@ -282,9 +279,6 @@ impl SimpleComponent for UpdatePageModel {
root: &Self::Root, root: &Self::Root,
sender: ComponentSender<Self>, sender: ComponentSender<Self>,
) -> ComponentParts<Self> { ) -> ComponentParts<Self> {
let updatedialog = UpdateDialogModel::builder()
.launch(initparams.window.upcast())
.forward(sender.input_sender(), identity);
let updateworker = UpdateAsyncHandler::builder() let updateworker = UpdateAsyncHandler::builder()
.detach_worker(UpdateAsyncHandlerInit { syspkgs: initparams.systype.clone(), userpkgs: initparams.usertype.clone() }) .detach_worker(UpdateAsyncHandlerInit { syspkgs: initparams.systype.clone(), userpkgs: initparams.usertype.clone() })
.forward(sender.input_sender(), identity); .forward(sender.input_sender(), identity);
@ -297,7 +291,6 @@ impl SimpleComponent for UpdatePageModel {
updatesystemlist: FactoryVecDeque::new(gtk::ListBox::new(), sender.input_sender()), updatesystemlist: FactoryVecDeque::new(gtk::ListBox::new(), sender.input_sender()),
channelupdate: None, channelupdate: None,
updatetracker: 0, updatetracker: 0,
updatedialog,
updateworker, updateworker,
config, config,
systype: initparams.systype, systype: initparams.systype,
@ -329,13 +322,13 @@ impl SimpleComponent for UpdatePageModel {
info!("UpdatePageMsg::Update"); info!("UpdatePageMsg::Update");
debug!("UPDATEUSERLIST: {:?}", updateuserlist); debug!("UPDATEUSERLIST: {:?}", updateuserlist);
debug!("UPDATESYSTEMLIST: {:?}", updatesystemlist); debug!("UPDATESYSTEMLIST: {:?}", updatesystemlist);
self.channelupdate = match nix_data::cache::channel::uptodate() { // self.channelupdate = match nix_data::cache::channel::uptodate() {
Ok(x) => { // Ok(x) => {
x // x
}, // },
Err(_) => None, // Err(_) => None,
}; // };
debug!("CHANNELUPDATE: {:?}", self.channelupdate); // debug!("CHANNELUPDATE: {:?}", self.channelupdate);
self.update_updatetracker(|_| ()); self.update_updatetracker(|_| ());
let mut updateuserlist_guard = self.updateuserlist.guard(); let mut updateuserlist_guard = self.updateuserlist.guard();
updateuserlist_guard.clear(); updateuserlist_guard.clear();
@ -347,6 +340,16 @@ impl SimpleComponent for UpdatePageModel {
for updatesystem in updatesystemlist { for updatesystem in updatesystemlist {
updatesystemlist_guard.push_back(updatesystem); updatesystemlist_guard.push_back(updatesystem);
} }
updatesystemlist_guard.push_back(UpdateItem {
pkg: None,
name: "NixOS".to_string(),
pname: "nixos".to_string(),
summary: None,
icon: None,
pkgtype: InstallType::System,
verfrom: None,
verto: None,
});
} }
UpdatePageMsg::OpenRow(row, pkgtype) => match pkgtype { UpdatePageMsg::OpenRow(row, pkgtype) => match pkgtype {
InstallType::User => { InstallType::User => {
@ -366,38 +369,36 @@ impl SimpleComponent for UpdatePageModel {
} }
} }
}, },
UpdatePageMsg::UpdateChannels => { // UpdatePageMsg::UpdateChannels => {
self.updatedialog.emit(UpdateDialogMsg::Show(String::from("Updating channels..."))); // self.updatedialog.emit(UpdateDialogMsg::Show(String::from("Updating channels...")));
self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateChannels); // self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateChannels);
} // }
UpdatePageMsg::UpdateSystemAndChannels => { // UpdatePageMsg::UpdateSystemAndChannels => {
self.updatedialog.emit(UpdateDialogMsg::Show(String::from("Updating system and channels..."))); // self.updatedialog.emit(UpdateDialogMsg::Show(String::from("Updating system and channels...")));
self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateChannelsAndSystem); // self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateChannelsAndSystem);
} // }
UpdatePageMsg::UpdateSystem => { UpdatePageMsg::UpdateSystem => {
self.updatedialog.emit(UpdateDialogMsg::Show(String::from("Updating system..."))); REBUILD_BROKER.send(RebuildMsg::Show);
self.updateworker.emit(UpdateAsyncHandlerMsg::RebuildSystem); self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateSystem);
} }
UpdatePageMsg::UpdateUser(pkg) => { UpdatePageMsg::UpdateUser(pkg) => {
info!("UPDATE USER PKG: {}", pkg); info!("UPDATE USER PKG: {}", pkg);
warn!("unimplemented"); warn!("unimplemented");
} }
UpdatePageMsg::UpdateAllUser => { UpdatePageMsg::UpdateAllUser => {
self.updatedialog.emit(UpdateDialogMsg::Show(String::from("Updating all user packages..."))); REBUILD_BROKER.send(RebuildMsg::Show);
self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateUserPkgs); self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateUserPkgs);
} }
UpdatePageMsg::UpdateAll => { UpdatePageMsg::UpdateAll => {
self.updatedialog.emit(UpdateDialogMsg::Show(String::from("Updating everything..."))); REBUILD_BROKER.send(RebuildMsg::Show);
self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateAll); self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateAll);
} }
UpdatePageMsg::DoneWorking => { UpdatePageMsg::DoneWorking => {
REBUILD_BROKER.send(RebuildMsg::FinishSuccess);
sender.output(AppMsg::UpdateInstalledPkgs); sender.output(AppMsg::UpdateInstalledPkgs);
} }
UpdatePageMsg::DoneLoading => {
self.updatedialog.emit(UpdateDialogMsg::Done);
}
UpdatePageMsg::FailedWorking => { UpdatePageMsg::FailedWorking => {
self.updatedialog.emit(UpdateDialogMsg::Failed); REBUILD_BROKER.send(RebuildMsg::FinishError(None));
} }
} }
} }

View File

@ -1,9 +1,12 @@
use nix_data::config::configfile::NixDataConfig; use nix_data::config::configfile::NixDataConfig;
use relm4::*; use relm4::*;
use std::{error::Error, path::Path, process::Stdio}; use std::{path::Path, process::Stdio};
use tokio::io::AsyncBufReadExt; use tokio::io::AsyncBufReadExt;
use anyhow::Result;
use log::*; use log::*;
use crate::ui::{rebuild::RebuildMsg, window::REBUILD_BROKER};
use super::{ use super::{
updatepage::UpdatePageMsg, updatepage::UpdatePageMsg,
window::{SystemPkgs, UserPkgs}, window::{SystemPkgs, UserPkgs},
@ -25,8 +28,10 @@ pub enum UpdateAsyncHandlerMsg {
UpdateConfig(NixDataConfig), UpdateConfig(NixDataConfig),
UpdatePkgTypes(SystemPkgs, UserPkgs), UpdatePkgTypes(SystemPkgs, UserPkgs),
UpdateChannels, // UpdateChannels,
UpdateChannelsAndSystem, // UpdateChannelsAndSystem,
UpdateSystem,
RebuildSystem, RebuildSystem,
UpdateUserPkgs, UpdateUserPkgs,
@ -79,35 +84,52 @@ impl Worker for UpdateAsyncHandler {
self.syspkgs = syspkgs; self.syspkgs = syspkgs;
self.userpkgs = userpkgs; self.userpkgs = userpkgs;
} }
UpdateAsyncHandlerMsg::UpdateChannels => { // UpdateAsyncHandlerMsg::UpdateChannels => {
// let systemconfig = self.systemconfig.clone();
// let flakeargs = self.flakeargs.clone();
// let syspkgs = self.syspkgs.clone();
// relm4::spawn(async move {
// let result = runcmd(NscCmd::Channel, systemconfig, flakeargs, syspkgs).await;
// match result {
// Ok(true) => {
// sender.output(UpdatePageMsg::DoneWorking);
// }
// _ => {
// warn!("UPDATE CHANNEL FAILED");
// sender.output(UpdatePageMsg::FailedWorking);
// }
// }
// });
// }
// UpdateAsyncHandlerMsg::UpdateChannelsAndSystem => {
// let systenconfig = self.systemconfig.clone();
// let flakeargs = self.flakeargs.clone();
// let syspkgs = self.syspkgs.clone();
// relm4::spawn(async move {
// let result = runcmd(NscCmd::All, systenconfig, flakeargs, syspkgs).await;
// match result {
// Ok(true) => {
// sender.output(UpdatePageMsg::DoneWorking);
// }
// _ => {
// warn!("UPDATE CHANNEL AND SYSTEM FAILED");
// sender.output(UpdatePageMsg::FailedWorking);
// }
// }
// });
// }
UpdateAsyncHandlerMsg::UpdateSystem => {
let systemconfig = self.systemconfig.clone(); let systemconfig = self.systemconfig.clone();
let flakeargs = self.flakeargs.clone(); let flakeargs = self.flakeargs.clone();
let syspkgs = self.syspkgs.clone(); let syspkgs = self.syspkgs.clone();
relm4::spawn(async move { relm4::spawn(async move {
let result = runcmd(NscCmd::Channel, systemconfig, flakeargs, syspkgs).await; let result = runcmd(NscCmd::All, systemconfig, flakeargs, syspkgs).await;
match result { match result {
Ok(true) => { Ok(true) => {
sender.output(UpdatePageMsg::DoneWorking); sender.output(UpdatePageMsg::DoneWorking);
} }
_ => { _ => {
warn!("UPDATE CHANNEL FAILED"); warn!("UPDATE SYSTEM FAILED");
sender.output(UpdatePageMsg::FailedWorking);
}
}
});
}
UpdateAsyncHandlerMsg::UpdateChannelsAndSystem => {
let systenconfig = self.systemconfig.clone();
let flakeargs = self.flakeargs.clone();
let syspkgs = self.syspkgs.clone();
relm4::spawn(async move {
let result = runcmd(NscCmd::All, systenconfig, flakeargs, syspkgs).await;
match result {
Ok(true) => {
sender.output(UpdatePageMsg::DoneWorking);
}
_ => {
warn!("UPDATE CHANNEL AND SYSTEM FAILED");
sender.output(UpdatePageMsg::FailedWorking); sender.output(UpdatePageMsg::FailedWorking);
} }
} }
@ -190,7 +212,7 @@ async fn runcmd(
_systemconfig: Option<String>, _systemconfig: Option<String>,
flakeargs: Option<String>, flakeargs: Option<String>,
syspkgs: SystemPkgs, syspkgs: SystemPkgs,
) -> Result<bool, Box<dyn Error + Send + Sync>> { ) -> Result<bool> {
let exe = match std::env::current_exe() { let exe = match std::env::current_exe() {
Ok(mut e) => { Ok(mut e) => {
e.pop(); // root/bin e.pop(); // root/bin
@ -255,6 +277,7 @@ async fn runcmd(
.arg(flakepath) .arg(flakepath)
.arg("--") .arg("--")
.arg("switch") .arg("switch")
.arg("--impure")
.args(&rebuildargs) .args(&rebuildargs)
.stderr(Stdio::piped()) .stderr(Stdio::piped())
.spawn()?, .spawn()?,
@ -267,6 +290,7 @@ async fn runcmd(
let mut lines = reader.lines(); let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await { while let Ok(Some(line)) = lines.next_line().await {
REBUILD_BROKER.send(RebuildMsg::UpdateText(line.to_string()));
trace!("CAUGHT REBUILD LINE: {}", line); trace!("CAUGHT REBUILD LINE: {}", line);
} }
if cmd.wait().await?.success() { if cmd.wait().await?.success() {
@ -276,7 +300,7 @@ async fn runcmd(
} }
} }
async fn updateenv() -> Result<bool, Box<dyn Error + Send + Sync>> { async fn updateenv() -> Result<bool> {
let mut cmd = tokio::process::Command::new("nix-env") let mut cmd = tokio::process::Command::new("nix-env")
.arg("-u") .arg("-u")
.stderr(Stdio::piped()) .stderr(Stdio::piped())
@ -287,6 +311,7 @@ async fn updateenv() -> Result<bool, Box<dyn Error + Send + Sync>> {
let mut lines = reader.lines(); let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await { while let Ok(Some(line)) = lines.next_line().await {
REBUILD_BROKER.send(RebuildMsg::UpdateText(line.to_string()));
trace!("CAUGHT NIXENV LINE: {}", line); trace!("CAUGHT NIXENV LINE: {}", line);
} }
if cmd.wait().await?.success() { if cmd.wait().await?.success() {
@ -296,7 +321,7 @@ async fn updateenv() -> Result<bool, Box<dyn Error + Send + Sync>> {
} }
} }
async fn updateprofile() -> Result<bool, Box<dyn Error + Send + Sync>> { async fn updateprofile() -> Result<bool> {
let mut cmd = tokio::process::Command::new("nix") let mut cmd = tokio::process::Command::new("nix")
.arg("profile") .arg("profile")
.arg("upgrade") .arg("upgrade")
@ -311,6 +336,7 @@ async fn updateprofile() -> Result<bool, Box<dyn Error + Send + Sync>> {
let mut lines = reader.lines(); let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await { while let Ok(Some(line)) = lines.next_line().await {
REBUILD_BROKER.send(RebuildMsg::UpdateText(line.to_string()));
trace!("CAUGHT NIX PROFILE LINE: {}", line); trace!("CAUGHT NIX PROFILE LINE: {}", line);
} }
if cmd.wait().await?.success() { if cmd.wait().await?.success() {

View File

@ -4,7 +4,7 @@ use crate::{
config::{editconfig, getconfig}, config::{editconfig, getconfig},
packages::{AppData, LicenseEnum, PkgMaintainer, Platform}, packages::{AppData, LicenseEnum, PkgMaintainer, Platform},
}, },
ui::{installedpage::InstalledItem, pkgpage::PkgPageInit, welcome::WelcomeMsg}, ui::{installedpage::InstalledItem, pkgpage::PkgPageInit, welcome::WelcomeMsg, rebuild::RebuildMsg},
APPINFO, APPINFO,
}; };
use adw::prelude::*; use adw::prelude::*;
@ -32,9 +32,11 @@ use super::{
searchpage::{SearchItem, SearchPageModel, SearchPageMsg}, searchpage::{SearchItem, SearchPageModel, SearchPageMsg},
updatepage::{UpdateItem, UpdatePageInit, UpdatePageModel, UpdatePageMsg}, updatepage::{UpdateItem, UpdatePageInit, UpdatePageModel, UpdatePageMsg},
welcome::WelcomeModel, welcome::WelcomeModel,
windowloading::{LoadErrorModel, LoadErrorMsg, WindowAsyncHandler, WindowAsyncHandlerMsg}, windowloading::{LoadErrorModel, LoadErrorMsg, WindowAsyncHandler, WindowAsyncHandlerMsg}, rebuild::RebuildModel,
}; };
pub static REBUILD_BROKER: MessageBroker<RebuildModel> = MessageBroker::new();
#[derive(PartialEq)] #[derive(PartialEq)]
enum Page { enum Page {
FrontPage, FrontPage,
@ -109,6 +111,8 @@ pub struct AppModel {
updatepage: Controller<UpdatePageModel>, updatepage: Controller<UpdatePageModel>,
viewstack: adw::ViewStack, viewstack: adw::ViewStack,
installedpagebusy: Vec<(String, InstallType)>, installedpagebusy: Vec<(String, InstallType)>,
#[tracker::no_eq]
rebuild: Controller<RebuildModel>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -146,8 +150,8 @@ pub enum AppMsg {
RemoveInstalledBusy(WorkPkg), RemoveInstalledBusy(WorkPkg),
OpenCategoryPage(PkgCategory), OpenCategoryPage(PkgCategory),
LoadCategory(PkgCategory), LoadCategory(PkgCategory),
UpdateRecPkgs(Vec<String>), UpdateRecPkgs(Vec<String>),
SetDarkMode(bool),
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -490,6 +494,9 @@ impl Component for AppModel {
config: config.clone(), config: config.clone(),
}) })
.forward(sender.input_sender(), identity); .forward(sender.input_sender(), identity);
let rebuild = RebuildModel::builder()
.launch_with_broker(root.clone().upcast(), &REBUILD_BROKER)
.forward(sender.input_sender(), identity);
let viewstack = adw::ViewStack::new(); let viewstack = adw::ViewStack::new();
let model = AppModel { let model = AppModel {
@ -523,9 +530,18 @@ impl Component for AppModel {
updatepage, updatepage,
viewstack, viewstack,
installedpagebusy: vec![], installedpagebusy: vec![],
rebuild,
tracker: 0, tracker: 0,
}; };
{
let sender = sender.clone();
adw::StyleManager::default()
.connect_dark_notify(move |x| sender.input(AppMsg::SetDarkMode(x.is_dark())));
}
sender.input(AppMsg::SetDarkMode(adw::StyleManager::default().is_dark()));
if welcome { if welcome {
let welcomepage = WelcomeModel::builder() let welcomepage = WelcomeModel::builder()
.launch(root.clone().upcast()) .launch(root.clone().upcast())
@ -582,6 +598,7 @@ impl Component for AppModel {
frontvs.set_icon_name(Some("nsc-home-symbolic")); frontvs.set_icon_name(Some("nsc-home-symbolic"));
installedvs.set_icon_name(Some("nsc-installed-symbolic")); installedvs.set_icon_name(Some("nsc-installed-symbolic"));
updatesvs.set_icon_name(Some("nsc-update-symbolic")); updatesvs.set_icon_name(Some("nsc-update-symbolic"));
ComponentParts { model, widgets } ComponentParts { model, widgets }
} }
@ -1803,6 +1820,11 @@ FROM pkgs JOIN meta ON (pkgs.attribute = meta.attribute) WHERE pkgs.attribute =
AppAsyncMsg::LoadCategory(category, catrec, catall) AppAsyncMsg::LoadCategory(category, catrec, catall)
}); });
} }
AppMsg::SetDarkMode(dark) => {
info!("AppMsg::SetDarkMode({})", dark);
let scheme = if dark { "Adwaita-dark" } else { "Adwaita" };
self.rebuild.emit(RebuildMsg::SetScheme(scheme.to_string()));
}
} }
} }