Add welcome page

This commit is contained in:
Victor Fuentes 2022-09-12 17:59:27 -04:00
parent 2037e64a5d
commit 19b78dddcf
No known key found for this signature in database
GPG Key ID: 0A88B68D6A9ACAE0
6 changed files with 316 additions and 79 deletions

View File

@ -1,6 +1,12 @@
use std::{error::Error, env, path::Path, fs::{self, File}, io::Write};
use std::{
env,
error::Error,
fs::{self, File},
io::Write,
path::Path,
};
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
pub struct NscConfig {
@ -8,20 +14,18 @@ pub struct NscConfig {
pub flake: Option<String>,
}
pub fn getconfig() -> NscConfig {
pub fn getconfig() -> Option<NscConfig> {
if let Ok(c) = getconfigval() {
c
Some(c)
} else {
NscConfig {
systemconfig: String::from("/etc/nixos/configuration.nix"),
flake: None,
}
None
}
}
fn getconfigval() -> Result<NscConfig, Box<dyn Error>> {
let configfile = checkconfig()?;
let config: NscConfig = serde_json::from_reader(File::open(format!("{}/config.json", configfile))?)?;
let config: NscConfig =
serde_json::from_reader(File::open(format!("{}/config.json", configfile))?)?;
Ok(config)
}
@ -29,8 +33,10 @@ fn checkconfig() -> Result<String, Box<dyn Error>> {
let cfgdir = format!("{}/.config/nix-software-center", env::var("HOME")?);
if !Path::is_file(Path::new(&format!("{}/config.json", &cfgdir))) {
if !Path::is_file(Path::new("/etc/nix-software-center/config.json")) {
createdefaultconfig()?;
Ok(cfgdir)
Err(Box::new(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"No config file found",
)))
} else {
Ok("/etc/nix-software-center/".to_string())
}
@ -39,19 +45,6 @@ fn checkconfig() -> Result<String, Box<dyn Error>> {
}
}
// fn configexists() -> Result<bool, Box<dyn Error>> {
// let cfgdir = format!("{}/.config/nix-software-center", env::var("HOME")?);
// if !Path::is_file(Path::new(&format!("{}/config.json", &cfgdir))) {
// if !Path::is_file(Path::new("/etc/nix-software-center/config.json")) {
// Ok(false)
// } else {
// Ok(true)
// }
// } else {
// Ok(true)
// }
// }
pub fn editconfig(config: NscConfig) -> Result<(), Box<dyn Error>> {
let cfgdir = format!("{}/.config/nix-software-center", env::var("HOME")?);
fs::create_dir_all(&cfgdir)?;
@ -59,40 +52,4 @@ pub fn editconfig(config: NscConfig) -> Result<(), Box<dyn Error>> {
let mut file = File::create(format!("{}/config.json", cfgdir))?;
file.write_all(json.as_bytes())?;
Ok(())
}
fn createdefaultconfig() -> Result<(), Box<dyn Error>> {
let cfgdir = format!("{}/.config/nix-software-center", env::var("HOME")?);
fs::create_dir_all(&cfgdir)?;
let config = NscConfig {
systemconfig: "/etc/nixos/configuration.nix".to_string(),
flake: None,
};
let json = serde_json::to_string_pretty(&config)?;
let mut file = File::create(format!("{}/config.json", cfgdir))?;
file.write_all(json.as_bytes())?;
Ok(())
}
// pub fn readconfig(cfg: String) -> Result<(String, Option<String>), Box<dyn Error>> {
// let file = fs::read_to_string(cfg)?;
// let config: NscConfig = match serde_json::from_str(&file) {
// Ok(x) => x,
// Err(_) => {
// createdefaultconfig()?;
// return Ok((
// "/etc/nixos/configuration.nix".to_string(),
// None,
// ));
// }
// };
// if Path::is_file(Path::new(&config.systemconfig)) {
// Ok((config.systemconfig, config.flake))
// } else {
// Ok((
// "/etc/nixos/configuration.nix".to_string(),
// None,
// ))
// }
// }
}

View File

@ -14,3 +14,4 @@ pub mod about;
pub mod preferencespage;
pub mod categorypage;
pub mod categorytile;
pub mod welcome;

View File

@ -23,7 +23,6 @@ use std::{
use log::*;
use crate::parse::config::NscConfig;
use crate::parse::config::getconfig;
use crate::parse::packages::PkgMaintainer;
use crate::parse::packages::StrOrVec;
use crate::ui::installworker::InstallAsyncHandlerMsg;
@ -169,14 +168,15 @@ pub enum PkgAsyncMsg {
}
#[derive(Debug)]
pub struct PkgPageTypes {
pub struct PkgPageInit {
pub syspkgs: SystemPkgs,
pub userpkgs: UserPkgs
pub userpkgs: UserPkgs,
pub config: NscConfig,
}
#[relm4::component(pub)]
impl Component for PkgModel {
type Init = PkgPageTypes;
type Init = PkgPageInit;
type Input = PkgMsg;
type Output = AppMsg;
type Widgets = PkgWidgets;
@ -932,14 +932,14 @@ impl Component for PkgModel {
}
fn init(
pkgtypes: Self::Init,
initparams: Self::Init,
root: &Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let installworker = InstallAsyncHandler::builder()
.detach_worker(InstallAsyncHandlerInit { syspkgs: pkgtypes.syspkgs.clone(), userpkgs: pkgtypes.userpkgs.clone() })
.detach_worker(InstallAsyncHandlerInit { syspkgs: initparams.syspkgs.clone(), userpkgs: initparams.userpkgs.clone() })
.forward(sender.input_sender(), identity);
let config = getconfig();
let config = initparams.config;
installworker.emit(InstallAsyncHandlerMsg::SetConfig(config.clone()));
let model = PkgModel {
config,
@ -962,8 +962,8 @@ impl Component for PkgModel {
// installinguserpkgs: HashSet::new(),
// installingsystempkgs: HashSet::new(),
// removinguserpkgs: HashSet::new(),
syspkgtype: pkgtypes.syspkgs,
userpkgtype: pkgtypes.userpkgs,
syspkgtype: initparams.syspkgs,
userpkgtype: initparams.userpkgs,
workqueue: HashSet::new(),
launchable: None,
tracker: 0,

View File

@ -1,4 +1,4 @@
use crate::{parse::{cache::channelver, config::{getconfig, NscConfig}}, APPINFO};
use crate::{parse::{cache::channelver, config::NscConfig}, APPINFO};
use super::{pkgpage::InstallType, window::*, updatedialog::{UpdateDialogModel, UpdateDialogMsg}, updateworker::{UpdateAsyncHandler, UpdateAsyncHandlerMsg, UpdateAsyncHandlerInit}};
use adw::prelude::*;
@ -44,6 +44,7 @@ pub struct UpdatePageInit {
pub window: gtk::Window,
pub systype: SystemPkgs,
pub usertype: UserPkgs,
pub config: NscConfig,
}
#[relm4::component(pub)]
@ -286,7 +287,7 @@ impl SimpleComponent for UpdatePageModel {
.detach_worker(UpdateAsyncHandlerInit { syspkgs: initparams.systype.clone(), userpkgs: initparams.usertype.clone() })
.forward(sender.input_sender(), identity);
let config = getconfig();
let config = initparams.config;
updateworker.emit(UpdateAsyncHandlerMsg::UpdateConfig(config.clone()));
let model = UpdatePageModel {

250
src/ui/welcome.rs Normal file
View File

@ -0,0 +1,250 @@
use std::path::{PathBuf, Path};
use adw::prelude::*;
use log::info;
use relm4::*;
use relm4_components::open_dialog::*;
use crate::parse::config::NscConfig;
use super::window::AppMsg;
#[tracker::track]
pub struct WelcomeModel {
hidden: bool,
confpath: Option<PathBuf>,
flake: bool,
flakepath: Option<PathBuf>,
#[tracker::no_eq]
conf_dialog: Controller<OpenDialog>,
#[tracker::no_eq]
flake_dialog: Controller<OpenDialog>,
}
#[derive(Debug)]
pub enum WelcomeMsg {
Show,
Close,
UpdateConfPath(PathBuf),
UpdateFlakePath(PathBuf),
ClearFlakePath,
OpenConf,
OpenFlake,
Ignore,
}
#[relm4::component(pub)]
impl SimpleComponent for WelcomeModel {
type InitParams = gtk::Window;
type Input = WelcomeMsg;
type Output = AppMsg;
type Widgets = WelcomeWidgets;
view! {
window = adw::Window {
set_transient_for: Some(&parent_window),
set_modal: true,
#[watch]
set_visible: !model.hidden,
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
gtk::Box {
set_valign: gtk::Align::Center,
set_vexpand: true,
set_orientation: gtk::Orientation::Vertical,
set_spacing: 20,
set_margin_all: 20,
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 10,
gtk::Label {
add_css_class: "title-1",
set_text: "Welcome the Nix Software Center!",
set_justify: gtk::Justification::Center,
},
gtk::Label {
add_css_class: "dim-label",
set_text: "If your configuration file is not in the default location, you can change it here.",
},
},
gtk::ListBox {
add_css_class: "boxed-list",
set_halign: gtk::Align::Fill,
set_selection_mode: gtk::SelectionMode::None,
adw::ActionRow {
set_title: "Configuration file",
add_suffix = &gtk::Button {
set_halign: gtk::Align::Center,
set_valign: gtk::Align::Center,
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 5,
gtk::Image {
set_icon_name: Some("document-open-symbolic"),
},
gtk::Label {
#[watch]
set_label: {
if let Some(path) = &model.confpath {
let x = path.file_name().unwrap_or_default().to_str().unwrap_or_default();
if x.is_empty() {
"(None)"
} else {
x
}
} else {
"(None)"
}
}
}
},
connect_clicked[sender] => move |_| {
sender.input(WelcomeMsg::OpenConf);
}
},
},
},
gtk::ListBox {
add_css_class: "boxed-list",
set_halign: gtk::Align::Fill,
set_selection_mode: gtk::SelectionMode::None,
adw::ActionRow {
set_title: "Flake file",
set_subtitle: "If you are using flakes, you can specify the path to your flake.nix file here.",
add_suffix = &gtk::Button {
set_halign: gtk::Align::Center,
set_valign: gtk::Align::Center,
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 5,
gtk::Image {
set_icon_name: Some("document-open-symbolic"),
},
gtk::Label {
#[watch]
set_label: {
if let Some(path) = &model.flakepath {
let x = path.file_name().unwrap_or_default().to_str().unwrap_or_default();
if x.is_empty() {
"(None)"
} else {
x
}
} else {
"(None)"
}
}
}
},
connect_clicked[sender] => move |_| {
sender.input(WelcomeMsg::OpenFlake);
}
},
add_suffix = &gtk::Button {
set_halign: gtk::Align::Center,
set_valign: gtk::Align::Center,
set_icon_name: "user-trash-symbolic",
connect_clicked[sender] => move |_| {
sender.input(WelcomeMsg::ClearFlakePath);
}
}
},
},
#[name(btn)]
gtk::Button {
#[watch]
set_sensitive: model.confpath.is_some(),
add_css_class: "pill",
add_css_class: "suggested-action",
set_label: "Continue",
set_hexpand: false,
set_halign: gtk::Align::Center,
connect_clicked[sender] => move |_| {
sender.input(WelcomeMsg::Close);
},
}
}
}
}
}
fn init(
parent_window: Self::InitParams,
root: &Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let conf_dialog = OpenDialog::builder()
.transient_for_native(root)
.launch(OpenDialogSettings::default())
.forward(&sender.input, |response| match response {
OpenDialogResponse::Accept(path) => WelcomeMsg::UpdateConfPath(path),
OpenDialogResponse::Cancel => WelcomeMsg::Ignore,
});
let flake_dialog = OpenDialog::builder()
.transient_for_native(root)
.launch(OpenDialogSettings::default())
.forward(&sender.input, |response| match response {
OpenDialogResponse::Accept(path) => WelcomeMsg::UpdateFlakePath(path),
OpenDialogResponse::Cancel => WelcomeMsg::Ignore,
});
let model = WelcomeModel {
hidden: true,
confpath: if Path::new("/etc/nixos/configuration.nix").exists() { Some(PathBuf::from("/etc/nixos/configuration.nix")) } else { None }, // parent_window.configpath.to_string(),
flake: false,
flakepath: None,
conf_dialog,
flake_dialog,
tracker: 0,
};
let widgets = view_output!();
widgets.btn.grab_focus();
ComponentParts { model, widgets }
}
fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>) {
self.reset();
match msg {
WelcomeMsg::Show => {
self.hidden = false;
}
WelcomeMsg::Close => {
if let Some(confpath) = &self.confpath {
let config = NscConfig {
systemconfig: confpath.to_string_lossy().to_string(),
flake: self.flakepath.as_ref().map(|x| x.to_string_lossy().to_string()),
};
sender.output(AppMsg::LoadConfig(config));
self.hidden = true;
}
}
WelcomeMsg::UpdateConfPath(s) => {
info!("Set configuration path to {}", s.to_string_lossy());
self.set_confpath(Some(s));
}
WelcomeMsg::UpdateFlakePath(s) => {
info!("Set flake path to {}", s.to_string_lossy());
self.set_flakepath(Some(s));
}
WelcomeMsg::ClearFlakePath => {
info!("Clear flake path");
self.set_flakepath(None);
}
WelcomeMsg::OpenConf => {
self.conf_dialog.emit(OpenDialogMsg::Open)
}
WelcomeMsg::OpenFlake => {
self.flake_dialog.emit(OpenDialogMsg::Open)
}
WelcomeMsg::Ignore => {}
}
}
}

View File

@ -5,14 +5,14 @@ use adw::prelude::*;
use edit_distance;
use serde_json::Value;
use spdx::Expression;
use crate::{parse::{packages::{Package, LicenseEnum, Platform}, cache::{uptodatelegacy, uptodateflake}, config::{NscConfig, getconfig, editconfig}}, ui::{installedpage::InstalledItem, pkgpage::PkgPageTypes}, APPINFO, config};
use crate::{parse::{packages::{Package, LicenseEnum, Platform}, cache::{uptodatelegacy, uptodateflake}, config::{NscConfig, getconfig, editconfig}}, ui::{installedpage::InstalledItem, pkgpage::PkgPageInit, welcome::WelcomeMsg}, APPINFO, config};
use log::*;
use super::{
categories::{PkgGroup, PkgCategory},
pkgtile::PkgTile,
pkgpage::{PkgModel, PkgMsg, PkgInitModel, self, InstallType, WorkPkg},
windowloading::{LoadErrorModel, LoadErrorMsg, WindowAsyncHandler, WindowAsyncHandlerMsg, CacheReturn}, searchpage::{SearchPageModel, SearchPageMsg, SearchItem}, installedpage::{InstalledPageModel, InstalledPageMsg}, updatepage::{UpdatePageModel, UpdatePageMsg, UpdateItem, UpdatePageInit}, about::{AboutPageModel, AboutPageMsg}, preferencespage::{PreferencesPageModel, PreferencesPageMsg}, categorypage::{CategoryPageModel, CategoryPageMsg}, categorytile::CategoryTile,
windowloading::{LoadErrorModel, LoadErrorMsg, WindowAsyncHandler, WindowAsyncHandlerMsg, CacheReturn}, searchpage::{SearchPageModel, SearchPageMsg, SearchItem}, installedpage::{InstalledPageModel, InstalledPageMsg}, updatepage::{UpdatePageModel, UpdatePageMsg, UpdateItem, UpdatePageInit}, about::{AboutPageModel, AboutPageMsg}, preferencespage::{PreferencesPageModel, PreferencesPageMsg}, categorypage::{CategoryPageModel, CategoryPageMsg}, categorytile::CategoryTile, welcome::WelcomeModel,
};
#[derive(PartialEq)]
@ -91,6 +91,7 @@ pub enum AppMsg {
UpdateFlake(Option<String>),
TryLoad,
ReloadUpdate,
LoadConfig(NscConfig),
Close,
LoadError(String, String),
Initialize(HashMap<String, Package>, Vec<String>, HashMap<String, String>, HashMap<PkgCategory, Vec<String>>, HashMap<PkgCategory, Vec<String>>, Option<HashMap<String, String>> /* profile pkgs */),
@ -375,6 +376,18 @@ impl Component for AppModel {
debug!("userpkgtype: {:?}", userpkgtype);
debug!("syspkgtype: {:?}", syspkgtype);
let (config, welcome) = if let Some(config) = getconfig() {
debug!("Got config: {:?}", config);
(config, false)
} else {
// Show welcome page
debug!("No config found");
(NscConfig {
systemconfig: String::from("/etc/nixos/configuration.nix"),
flake: None,
}, true)
};
let windowloading = WindowAsyncHandler::builder()
.detach_worker(())
.forward(sender.input_sender(), identity);
@ -382,9 +395,10 @@ impl Component for AppModel {
.launch(root.clone().upcast())
.forward(sender.input_sender(), identity);
let pkgpage = PkgModel::builder()
.launch(PkgPageTypes {
.launch(PkgPageInit {
userpkgs: userpkgtype.clone(),
syspkgs: syspkgtype.clone(),
config: config.clone(),
})
.forward(sender.input_sender(), identity);
let searchpage = SearchPageModel::builder()
@ -397,12 +411,10 @@ impl Component for AppModel {
.launch(userpkgtype.clone())
.forward(sender.input_sender(), identity);
let updatepage = UpdatePageModel::builder()
.launch(UpdatePageInit { window: root.clone().upcast(), systype: syspkgtype.clone(), usertype: userpkgtype.clone() })
.launch(UpdatePageInit { window: root.clone().upcast(), systype: syspkgtype.clone(), usertype: userpkgtype.clone(), config: config.clone() })
.forward(sender.input_sender(), identity);
let viewstack = adw::ViewStack::new();
let config = getconfig();
let model = AppModel {
application,
mainwindow: root.clone(),
@ -438,7 +450,14 @@ impl Component for AppModel {
tracker: 0,
};
model.windowloading.emit(WindowAsyncHandlerMsg::CheckCache(CacheReturn::Init, model.syspkgtype.clone(), model.userpkgtype.clone(), model.config.clone()));
if welcome {
let welcomepage = WelcomeModel::builder()
.launch(root.clone().upcast())
.forward(sender.input_sender(), identity);
welcomepage.emit(WelcomeMsg::Show);
} else {
model.windowloading.emit(WindowAsyncHandlerMsg::CheckCache(CacheReturn::Init, model.syspkgtype.clone(), model.userpkgtype.clone(), model.config.clone()));
}
let recbox = model.recommendedapps.widget();
let categorybox = model.categories.widget();
let viewstack = &model.viewstack;
@ -496,6 +515,15 @@ impl Component for AppModel {
AppMsg::ReloadUpdate => {
self.windowloading.emit(WindowAsyncHandlerMsg::CheckCache(CacheReturn::Update, self.syspkgtype.clone(), self.userpkgtype.clone(), self.config.clone()));
}
AppMsg::LoadConfig(config) => {
self.config = config;
if let Err(e) = editconfig(self.config.clone()) {
warn!("Error editing config: {}", e);
}
self.pkgpage.emit(PkgMsg::UpdateConfig(self.config.clone()));
self.updatepage.emit(UpdatePageMsg::UpdateConfig(self.config.clone()));
self.windowloading.emit(WindowAsyncHandlerMsg::CheckCache(CacheReturn::Init, self.syspkgtype.clone(), self.userpkgtype.clone(), self.config.clone()));
}
AppMsg::Close => {
self.application.quit();
}