From c106744882ea824e9384b62df736e63d420dacb8 Mon Sep 17 00:00:00 2001 From: Piotr <114903054+elevenhsoft@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:01:54 +0200 Subject: [PATCH 1/9] some code cleanup, reorganization and fixes --- src/browser.rs | 123 ++++++ src/common.rs | 767 +++++--------------------------------- src/favicon.rs | 45 +++ src/launcher.rs | 444 ++++++++++++++++++++++ src/main.rs | 3 + src/pages/creator.rs | 3 +- src/pages/home_screen.rs | 3 +- src/pages/mod.rs | 65 ++-- src/supported_browsers.rs | 2 +- 9 files changed, 738 insertions(+), 717 deletions(-) create mode 100644 src/browser.rs create mode 100644 src/favicon.rs create mode 100644 src/launcher.rs diff --git a/src/browser.rs b/src/browser.rs new file mode 100644 index 0000000..ff3bff3 --- /dev/null +++ b/src/browser.rs @@ -0,0 +1,123 @@ +use std::path::PathBuf; + +use crate::{common, fl, supported_browsers}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BrowserType { + NoBrowser, + Firefox, + FirefoxFlatpak, + ZenFlatpak, + Chromium, + ChromiumFlatpak, + Falkon, + FalkonFlatpak, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Browser { + pub _type: BrowserType, + pub name: String, + pub exec: String, + pub test: PathBuf, + pub profile_path: PathBuf, +} + +impl AsRef for Browser { + fn as_ref(&self) -> &str { + &self.name + } +} + +impl Browser { + pub fn new( + _type: BrowserType, + name: &str, + exec: &str, + test_path: &str, + profile_path: &str, + ) -> Self { + let name = name.to_string(); + + let mut test = PathBuf::new(); + let mut exe_path = PathBuf::new(); + + let base = common::home_dir(); + let data_home = base.join(".local/share"); + + if exec.starts_with(".local/share/") { + let flatpak_path: Vec<&str> = exec.split(".local/share/").collect(); + let path = data_home.join(flatpak_path[1]); + exe_path.push(path); + } else { + exe_path.push(exec) + } + + if test_path.starts_with(".local/share") { + let flatpak_path: Vec<&str> = test_path.split(".local/share/").collect(); + let path = data_home.join(flatpak_path[1]); + test.push(path); + } else { + test.push(test_path) + } + + let exec = exe_path.to_str().unwrap().to_string(); + + let profile_path = base.join(profile_path); + + Self { + _type, + name, + exec, + test, + profile_path, + } + } + + pub fn web_browser(name: String) -> Option { + let supported = get_supported_browsers(); + supported.into_iter().find(|b| b.name == name) + } + + pub fn is_installed(&self) -> bool { + !matches!(self._type, BrowserType::NoBrowser) + } +} + +pub fn get_supported_browsers() -> Vec { + let mut test_browsers: Vec = Vec::new(); + + let native_browsers: Vec = supported_browsers::native_browsers(); + let flatpak_browsers: Vec = supported_browsers::flatpak_browsers(); + let nix_browsers: Vec = supported_browsers::nix_browsers(); + + test_browsers.extend(native_browsers); + test_browsers.extend(flatpak_browsers); + test_browsers.extend(nix_browsers); + + let mut browsers = Vec::new(); + + for browser in test_browsers { + let exists = browser.test.as_path().try_exists(); + + match exists { + Ok(found) => match found { + true => browsers.push(browser), + false => continue, + }, + Err(_) => continue, + } + } + + if browsers.is_empty() { + browsers.push(Browser::new( + BrowserType::NoBrowser, + &fl!("select-browser"), + "", + "", + "", + )); + } + + browsers +} diff --git a/src/common.rs b/src/common.rs index bc89d15..b3e3010 100644 --- a/src/common.rs +++ b/src/common.rs @@ -2,32 +2,26 @@ use std::{ ffi::OsStr, - fs::{self, copy, create_dir_all, remove_dir_all, remove_file, File}, - io::{self, BufRead, Cursor, Read, Write}, + fs::{copy, create_dir_all, File}, + io::{Cursor, Read}, path::PathBuf, str::FromStr, sync::Mutex, }; -use anyhow::{anyhow, Error, Result}; use base64::prelude::*; use bytes::Bytes; use cosmic::widget; use image::ImageReader; use image::{load_from_memory, GenericImageView}; -use rand::{thread_rng, Rng}; use reqwest::Client; use svg::node::element::Image; use svg::Document; +use tokio::io::AsyncReadExt; use url::Url; use walkdir::WalkDir; -use crate::{ - fl, - icon_cache::IconCache, - pages, - supported_browsers::{flatpak_browsers, native_browsers, nix_browsers}, -}; +use crate::{favicon, icon_cache::IconCache, pages::iconpicker}; lazy_static::lazy_static! { static ref ICON_CACHE: Mutex = Mutex::new(IconCache::new()); @@ -43,12 +37,23 @@ pub fn url_valid(url: &str) -> bool { } pub fn is_svg(path: &str) -> bool { - !url_valid(path) && PathBuf::from_str(path).unwrap().extension() == Some(OsStr::new("svg")) + if !url_valid(path) { + if let Ok(pb) = PathBuf::from_str(path) { + if pb.extension() == Some(OsStr::new("svg")) { + return true; + } + } + } + false } pub fn home_dir() -> PathBuf { let home = std::env::var("HOME"); + if let Some(path) = dirs::home_dir() { + return path; + } + if let Ok(path) = home { return PathBuf::from_str(&path).unwrap(); } @@ -66,7 +71,7 @@ pub fn icons_location() -> PathBuf { home_dir().join(".local/share/icons") } -pub fn system_fonts() -> PathBuf { +pub fn system_icons() -> PathBuf { if let Ok(path) = PathBuf::from_str("/usr/share/icons") { path } else { @@ -74,562 +79,10 @@ pub fn system_fonts() -> PathBuf { } } -pub fn my_icons_location() -> PathBuf { +pub fn qwa_icons_location() -> PathBuf { icons_location().join("QuickWebApps") } -pub fn webapplauncher_is_valid( - webbrowser: &Browser, - icon: &str, - codename: &str, - name: &str, - url: &str, -) -> bool { - let installed = get_webapps(); - - for app in installed.iter().flatten() { - if !url_valid(url) - || !webbrowser.is_installed() - || (name.is_empty() || app.name == name) - || icon.is_empty() - || (codename.is_empty() || app.codename == codename) - || url.is_empty() - { - return false; - } - } - - true -} - -#[derive(Debug, Clone)] -pub struct WebAppLauncher { - pub path: PathBuf, - pub codename: String, - pub web_browser: Browser, - pub name: String, - pub icon: String, - pub is_valid: bool, - pub exec: String, - // pub args: Vec, - pub category: String, - pub url: String, - pub custom_parameters: String, - pub isolate_profile: bool, - pub navbar: bool, - pub is_incognito: bool, -} - -impl WebAppLauncher { - pub fn new( - name: String, - codename: Option, - url: String, - icon: String, - category: String, - browser: Browser, - custom_parameters: String, - isolated: bool, - navbar: bool, - privatewindow: bool, - ) -> Self { - let codename = if let Some(codename) = codename { - codename - } else { - let random_code: u16 = thread_rng().gen_range(1000..10000); - format!("{}{}", name.replace(' ', ""), random_code) - }; - let filename = format!("webapp-{}.desktop", codename); - let path = desktop_filepath(&filename); - let web_browser = browser; - let exec = web_browser.exec.clone(); - // let args = Vec::new(); - let isolate_profile = isolated; - let is_incognito = privatewindow; - - let is_valid = webapplauncher_is_valid(&web_browser, &icon, &codename, &name, &url); - - Self { - path, - codename, - web_browser, - name, - icon, - is_valid, - exec, - // args, - category, - url, - custom_parameters, - isolate_profile, - navbar, - is_incognito, - } - } - - pub fn read(path: PathBuf, codename: String) -> Result { - let file = File::open(&path)?; - let mut browser_name = String::new(); - let mut name = String::new(); - let mut icon = String::new(); - let mut is_valid = false; - let mut exec = String::new(); - let mut args = Vec::new(); - let mut category = String::new(); - let mut url = String::new(); - let mut custom_parameters = String::new(); - let mut isolate_profile = false; - let mut navbar = false; - let mut is_incognito = false; - - let reader = io::BufReader::new(file); - - let mut is_webapp = false; - - for line_result in reader.lines() { - match line_result { - Ok(line) => { - if line.contains("StartupWMClass=WebApp") - || line.contains("StartupWMClass=Chromium") - || line.contains("StartupWMClass=ICE-SSB") - { - is_webapp = true; - }; - - if line.contains("Name=") { - name = line.replace("Name=", ""); - }; - - if line.contains("Icon=") { - icon = line.replace("Icon=", ""); - }; - - if line.contains("Exec=") { - exec = line.replace("Exec=", ""); - }; - - if line.contains("Categories=") { - category = line - .replace("Categories=", "") - .replace("GTK;", "") - .replace(';', ""); - }; - - if line.contains("X-WebApp-Browser=") { - browser_name = line.replace("X-WebApp-Browser=", ""); - }; - - if line.contains("X-WebApp-URL=") { - url = line.replace("X-WebApp-URL=", ""); - }; - - if line.contains("X-WebApp-CustomParameters=") { - custom_parameters = line.replace("X-WebApp-CustomParameters=", ""); - }; - - if line.contains("X-WebApp-Isolated=") { - isolate_profile = line.replace("X-WebApp-Isolated=", "") == "true" - }; - - if line.contains("X-WebApp-Navbar=") { - navbar = line.replace("X-WebApp-Navbar=", "") == "true" - }; - - if line.contains("X-WebApp-PrivateWindow=") { - is_incognito = line.replace("X-WebApp-PrivateWindow=", "") == "true" - }; - } - Err(e) => eprintln!("Error reading line: {}", e), - } - } - - if is_webapp && !name.is_empty() && !icon.is_empty() { - is_valid = true - } - - let web_browser = Browser::web_browser(browser_name); - - match web_browser { - Some(web_browser) => { - exec.split(' ').enumerate().for_each(|(n, arg)| { - if n > 0 && !arg.is_empty() { - args.push(arg.to_string()) - } - }); - - Ok(WebAppLauncher { - path, - codename, - web_browser, - name, - icon, - is_valid, - exec, - // args, - category, - url, - custom_parameters, - isolate_profile, - navbar, - is_incognito, - }) - } - None => Err(anyhow!("Cannot read web app launcher.")), - } - } - - fn create_firefox_userjs(&self, is_zen_browser: bool, path: PathBuf) -> bool { - let content = match is_zen_browser { - true => include_bytes!("../data/runtime/zen-browser/profile/user.js"), - false => include_bytes!("../data/runtime/firefox/profile/user.js"), - }; - - let mut file = File::create(&path) - .unwrap_or_else(|_| panic!("failed to create user.js in {:?}", path)); - - file.write_all(content).is_ok() - } - - fn create_user_chrome_css( - &self, - is_zen_browser: bool, - path: PathBuf, - create_navbar: bool, - ) -> bool { - let mut file = File::create(&path) - .unwrap_or_else(|_| panic!("cant create userChrome.css in {:?}", path)); - - if create_navbar { - file.write_all(b"").is_ok() - } else { - match is_zen_browser { - true => file - .write_all(include_bytes!( - "../data/runtime/zen-browser/profile/chrome/userChrome.css" - )) - .is_ok(), - false => file - .write_all(include_bytes!( - "../data/runtime/firefox/profile/chrome/userChrome.css" - )) - .is_ok(), - } - } - } - - fn exec_firefox(&self, is_zen_browser: bool) -> String { - let profile_path = self.web_browser.profile_path.join(&self.codename); - let user_js_path = profile_path.join("user.js"); - let mut user_chrome_css = profile_path.join("chrome"); - - tracing::info!("Creating profile directory in: {:?}", &profile_path); - create_dir_all(&profile_path) - .unwrap_or_else(|_| panic!("cant create profile dir in {:?}", &profile_path)); - create_dir_all(&user_chrome_css) - .unwrap_or_else(|_| panic!("cant create chrome dir in {:?}", &user_chrome_css)); - - user_chrome_css = user_chrome_css.join("userChrome.css"); - - self.create_firefox_userjs(is_zen_browser, user_js_path); - self.create_user_chrome_css(is_zen_browser, user_chrome_css, self.navbar); - - let profile_path = profile_path.to_str().unwrap(); - - let mut exec_string = format!( - "{} --class WebApp-{} --name WebApp-{} --profile {} --no-remote ", - self.exec, self.codename, self.codename, profile_path - ); - - if self.is_incognito { - exec_string.push_str("--private-window "); - } - - if !self.custom_parameters.is_empty() { - exec_string.push_str(&format!("{} ", self.custom_parameters)); - } - - exec_string.push_str(&self.url); - - exec_string - } - - fn exec_chromium(&self) -> String { - let mut exec_string = format!( - "{} --app={} --class=WebApp-{} --name=WebApp-{} ", - self.exec, self.url, self.codename, self.codename - ); - - if self.isolate_profile { - let profile_dir = self.web_browser.profile_path.join(&self.codename); - - tracing::info!("Creating profile directory in: {:?}", &profile_dir); - let _ = create_dir_all(&profile_dir); - let profile_path = profile_dir.to_str().unwrap(); - exec_string.push_str(&format!("--user-data-dir={} ", profile_path)); - } - - if self.is_incognito { - if self.web_browser.name.starts_with("Microsoft Edge") { - exec_string.push_str("--inprivate "); - } else { - exec_string.push_str("--incognito "); - } - } - - if !self.custom_parameters.is_empty() { - exec_string.push_str(&format!("{} ", self.custom_parameters)); - } - - exec_string - } - - fn exec_falkon(&self) -> String { - let mut exec_string = String::new(); - - if self.isolate_profile { - let profile_dir = self.web_browser.profile_path.join(&self.codename); - tracing::info!("Creating profile directory in: {:?}", &profile_dir); - let _ = create_dir_all(&profile_dir); - - let profile_path = profile_dir.to_str().unwrap(); - - exec_string = format!( - "{} --portable --wmclass WebApp-{} --profile {} ", - self.exec, self.codename, profile_path - ); - } - - if self.is_incognito { - exec_string.push_str("--private-browsing "); - } - - if !self.custom_parameters.is_empty() { - exec_string.push_str(&format!("{} ", self.custom_parameters)); - } - - exec_string.push_str(&format!("--no-remote --current-tab {}", self.url)); - - exec_string - } - - fn exec_string(&self) -> String { - match self.web_browser._type { - BrowserType::Firefox => self.exec_firefox(false), - BrowserType::FirefoxFlatpak => self.exec_firefox(false), - BrowserType::ZenFlatpak => self.exec_firefox(true), - BrowserType::Chromium => self.exec_chromium(), - BrowserType::ChromiumFlatpak => self.exec_chromium(), - BrowserType::Falkon => self.exec_falkon(), - BrowserType::FalkonFlatpak => self.exec_falkon(), - BrowserType::NoBrowser => String::new(), - } - } - - pub fn create(&self) -> Result<()> { - let mut output = File::create(&self.path)?; - - writeln!(output, "[Desktop Entry]")?; - writeln!(output, "Version=1.0")?; - writeln!(output, "Name={}", self.name)?; - writeln!(output, "Comment=Web App")?; - writeln!(output, "Exec={}", self.exec_string())?; - writeln!(output, "Terminal=false")?; - writeln!(output, "Type=Application")?; - writeln!(output, "Icon={}", self.icon)?; - writeln!(output, "Categories=GTK;{};", self.category)?; - writeln!(output, "MimeType=text/html;text/xml;application/xhtml_xml;")?; - writeln!(output, "StartupWMClass=WebApp-{}", self.codename)?; - writeln!(output, "StartupNotify=true")?; - writeln!(output, "X-MultipleArgs=false")?; - writeln!(output, "X-WebApp-Browser={}", self.web_browser.name)?; - writeln!(output, "X-WebApp-URL={}", self.url)?; - writeln!(output, "X-WebApp-Navbar={}", self.navbar)?; - writeln!(output, "X-WebApp-PrivateWindow={}", self.is_incognito)?; - writeln!(output, "X-WebApp-Isolated={}", self.isolate_profile)?; - writeln!( - output, - "X-WebApp-CustomParameters={}", - self.custom_parameters - )?; - - Ok(()) - } - - pub fn delete(&self) -> Result<()> { - let exist = self.path.as_path().exists(); - - match exist { - true => { - remove_file(&self.path)?; - } - false => { - tracing::error!("file not found"); - } - } - - let profile_path = self.web_browser.profile_path.join(&self.codename); - - if remove_dir_all(&profile_path).is_ok() { - tracing::info!( - "Removed profile directory, from: {}", - profile_path.to_str().unwrap() - ); - }; - - Ok(()) - } -} - -pub fn get_webapps() -> Vec> { - let mut webapps = Vec::new(); - - match fs::read_dir(desktop_filepath("")) { - Ok(entries) => { - for entry in entries { - match entry { - Ok(entry) => { - let entry_fn = entry.file_name(); - let filename = entry_fn.to_str().unwrap(); - - if filename.starts_with("webapp-") && filename.ends_with(".desktop") { - let codename = filename.replace("webapp-", "").replace(".desktop", ""); - - let launcher = WebAppLauncher::read(entry.path(), codename); - webapps.push(launcher); - } - } - Err(e) => tracing::error!("Error reading directory: {}", e), - } - } - } - Err(_) => { - create_dir_all(desktop_filepath("")).expect("Cannot create local applications dir"); - } - } - - webapps -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum BrowserType { - NoBrowser, - Firefox, - FirefoxFlatpak, - ZenFlatpak, - Chromium, - ChromiumFlatpak, - Falkon, - FalkonFlatpak, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Browser { - pub _type: BrowserType, - pub name: String, - pub exec: String, - pub test: PathBuf, - pub profile_path: PathBuf, -} - -impl AsRef for Browser { - fn as_ref(&self) -> &str { - &self.name - } -} - -impl Browser { - pub fn new( - _type: BrowserType, - name: &str, - exec: &str, - test_path: &str, - profile_path: &str, - ) -> Self { - let name = name.to_string(); - - let mut test = PathBuf::new(); - let mut exe_path = PathBuf::new(); - - let base = home_dir(); - let data_home = base.join(".local/share"); - - if exec.starts_with(".local/share/") { - let flatpak_path: Vec<&str> = exec.split(".local/share/").collect(); - let path = data_home.join(flatpak_path[1]); - exe_path.push(path); - } else { - exe_path.push(exec) - } - - if test_path.starts_with(".local/share") { - let flatpak_path: Vec<&str> = test_path.split(".local/share/").collect(); - let path = data_home.join(flatpak_path[1]); - test.push(path); - } else { - test.push(test_path) - } - - let exec = exe_path.to_str().unwrap().to_string(); - - let profile_path = base.join(profile_path); - - Self { - _type, - name, - exec, - test, - profile_path, - } - } - - pub fn web_browser(name: String) -> Option { - let supported = get_supported_browsers(); - supported.into_iter().find(|b| b.name == name) - } - - pub fn is_installed(&self) -> bool { - !matches!(self._type, BrowserType::NoBrowser) - } -} - -pub fn get_supported_browsers() -> Vec { - let mut test_browsers: Vec = Vec::new(); - - let native_browsers = native_browsers(); - let flatpak_browsers = flatpak_browsers(); - let nix_browsers: Vec = nix_browsers(); - - test_browsers.extend(native_browsers); - test_browsers.extend(flatpak_browsers); - test_browsers.extend(nix_browsers); - - let mut browsers = Vec::new(); - - for browser in test_browsers { - let exists = browser.test.as_path().try_exists(); - - match exists { - Ok(found) => match found { - true => browsers.push(browser), - false => continue, - }, - Err(_) => continue, - } - } - - if browsers.is_empty() { - browsers.push(Browser::new( - BrowserType::NoBrowser, - &fl!("select-browser"), - "", - "", - "", - )); - } - - browsers -} - pub fn get_icon_name_from_url(url: &str) -> String { match Url::parse(url) { Ok(url) => match url.host_str() { @@ -665,14 +118,14 @@ pub async fn find_icon(path: PathBuf, icon_name: String) -> Vec { } } } else if let Some(path) = entry.path().to_str() { - let image = ImageReader::open(path).unwrap().decode(); - - if let Ok(img) = image { - if img.width() >= 64 - && img.height() >= 64 - && !icons.contains(&path.to_string()) - { - icons.push(path.to_string()) + if let Ok(image) = ImageReader::open(path) { + if let Ok(img) = image.decode() { + if img.width() >= 64 + && img.height() >= 64 + && !icons.contains(&path.to_string()) + { + icons.push(path.to_string()) + } } } } @@ -687,10 +140,10 @@ pub async fn find_icons(icon_name: String, url: String) -> Vec { let mut result: Vec = Vec::new(); result.extend(find_icon(icons_location(), icon_name.clone()).await); - result.extend(find_icon(system_fonts(), icon_name).await); + result.extend(find_icon(system_icons(), icon_name).await); if url_valid(&url) { - if let Ok(data) = download_favicon(&url).await { + if let Ok(data) = favicon::download_favicon(&url).await { result.extend(data) } }; @@ -698,50 +151,9 @@ pub async fn find_icons(icon_name: String, url: String) -> Vec { result } -#[derive(Debug, serde::Serialize, serde::Deserialize)] -pub struct FaviconResponse { - pub url: String, - pub host: String, - pub status: u16, - #[serde(rename = "statusText")] - pub status_text: String, - pub icons: Vec, -} - -#[derive(Debug, serde::Serialize, serde::Deserialize)] -pub struct FaviconIcon { - pub sizes: String, - pub href: String, -} - -pub async fn download_favicon(url: &str) -> Result> { - let mut favicons = Vec::new(); - - let url = Url::parse(url)?; - - if let Some(domain) = url.domain() { - let request = Client::new() - .get(format!( - "https://www.faviconextractor.com/api/favicon/{}", - domain - )) - .send() - .await?; - - let response: FaviconResponse = request.json().await?; +pub fn convert_raster_to_svg_format(img_slice: Bytes, icon_name: &str) -> String { + let save_path = icon_save_path(icon_name, "svg"); - if response.status == 200 { - response - .icons - .iter() - .for_each(|icon| favicons.push(icon.href.clone())); - } - } - - Ok(favicons) -} - -pub fn convert_raster_to_svg_format(img_slice: Bytes, icon_name: &str) -> Option { if let Ok(data) = load_from_memory(&img_slice) { let (width, height) = data.dimensions(); let mut image_buffer = Vec::new(); @@ -766,27 +178,23 @@ pub fn convert_raster_to_svg_format(img_slice: Bytes, icon_name: &str) -> Option .add(image_element); // Save the SVG document - if let Some(save_path) = icon_save_path(icon_name, "svg") { - svg::save(&save_path, &document).unwrap(); - return Some(save_path); - } + let _ = svg::save(&save_path, &document).is_ok(); } - None + save_path } -fn icon_save_path(icon_name: &str, format: &str) -> Option { - let ret = my_icons_location() +fn icon_save_path(icon_name: &str, format: &str) -> String { + qwa_icons_location() .join(format!("{}.{}", icon_name, format)) - .to_str()? - .to_string(); - - Some(ret) + .to_str() + .unwrap() + .to_string() } -pub fn move_icon(path: String, output_name: String) -> Option { - create_dir_all(my_icons_location()).expect("cant create folder for your icons"); +pub fn move_icon(path: String, output_name: String) -> String { + create_dir_all(qwa_icons_location()).expect("cant create folder for your icons"); let icon_name = output_name.replace(' ', ""); @@ -800,73 +208,74 @@ pub fn move_icon(path: String, output_name: String) -> Option { } } else if !path.contains(&icon_name) { if !is_svg(&path) { - let file = File::open(&path); - if let Ok(mut opened) = file { + if let Ok(mut file) = File::open(&path) { let mut buffer = Vec::new(); - opened.read_to_end(&mut buffer).unwrap(); - + file.read_to_end(&mut buffer).unwrap(); let content = Bytes::from(buffer); return convert_raster_to_svg_format(content, &icon_name); } } else { - let ret = icon_save_path(&icon_name, "svg")?; - copy(&path, &ret).unwrap(); + let save_path = icon_save_path(&icon_name, "svg"); + copy(&path, &save_path).unwrap(); - return Some(ret); + return save_path; } } - Some(path) + path } -pub async fn image_handle(path: String) -> Option { - let mut data: Vec<_> = Vec::new(); - let pathbuf = PathBuf::from_str(&path).unwrap(); - +pub async fn image_handle(path: String) -> Option { if url_valid(&path) { - data.extend( - Client::new() - .get(&path) - .send() - .await - .unwrap() - .bytes() - .await - .unwrap() - .to_vec(), - ); - } else if let Ok(mut file) = File::open(&pathbuf) { - let mut buffer = Vec::new(); - - if pathbuf.is_file() { - file.read_to_end(&mut buffer).unwrap(); - }; + let mut data: Vec<_> = Vec::new(); - data.extend(buffer); - }; + if let Ok(response) = Client::new().get(&path).send().await { + if let Ok(bytes) = response.bytes().await { + data.extend(bytes); + } + } - if is_svg(&path) { - let handle = widget::svg::Handle::from_memory(data); + let handle = widget::image::Handle::from_memory(data); - return Some(pages::iconpicker::Icon::new( - pages::iconpicker::IconType::Svg(handle), + return Some(iconpicker::Icon::new( + iconpicker::IconType::Raster(handle), path, )); - } else if let Ok(image) = ImageReader::new(Cursor::new(&data)) - .with_guessed_format() - .unwrap() - .decode() - { - if image.width() >= 96 && image.height() >= 96 { - let handle = widget::image::Handle::from_memory(data); - - return Some(pages::iconpicker::Icon::new( - pages::iconpicker::IconType::Raster(handle), - path, - )); - } }; + if let Ok(result_path) = PathBuf::from_str(&path) { + if result_path.is_file() { + if is_svg(&path) { + let handle = widget::svg::Handle::from_path(&result_path); + + return Some(iconpicker::Icon::new( + iconpicker::IconType::Svg(handle), + path, + )); + } else { + let mut data: Vec<_> = Vec::new(); + + if let Ok(mut file) = tokio::fs::File::open(&result_path).await { + let _ = file.read_to_end(&mut data).await; + } + + if let Ok(image_reader) = ImageReader::new(Cursor::new(&data)).with_guessed_format() + { + if let Ok(image) = image_reader.decode() { + if image.width() >= 96 && image.height() >= 96 { + let handle = widget::image::Handle::from_memory(data); + + return Some(iconpicker::Icon::new( + iconpicker::IconType::Raster(handle), + path, + )); + } + }; + } + } + }; + } + None } diff --git a/src/favicon.rs b/src/favicon.rs new file mode 100644 index 0000000..83be92a --- /dev/null +++ b/src/favicon.rs @@ -0,0 +1,45 @@ +use reqwest::Client; +use url::Url; + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct FaviconResponse { + pub url: String, + pub host: String, + pub status: u16, + #[serde(rename = "statusText")] + pub status_text: String, + pub icons: Vec, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct FaviconIcon { + pub sizes: String, + pub href: String, +} + +pub async fn download_favicon(url: &str) -> anyhow::Result> { + let mut favicons = Vec::new(); + + let url = Url::parse(url)?; + + if let Some(domain) = url.domain() { + let request = Client::new() + .get(format!( + "https://www.faviconextractor.com/api/favicon/{}", + domain + )) + .send() + .await?; + + let response: FaviconResponse = request.json().await?; + + if response.status == 200 { + response + .icons + .iter() + .for_each(|icon| favicons.push(icon.href.clone())); + } + } + + Ok(favicons) +} diff --git a/src/launcher.rs b/src/launcher.rs new file mode 100644 index 0000000..b5cb109 --- /dev/null +++ b/src/launcher.rs @@ -0,0 +1,444 @@ +use std::{ + fs::{self, create_dir_all, remove_dir_all, remove_file, File}, + io::{self, BufRead, Write}, + path::PathBuf, +}; + +use anyhow::{anyhow, Error}; +use rand::{thread_rng, Rng}; + +use crate::{browser, common, launcher}; + +pub fn webapplauncher_is_valid( + webbrowser: &browser::Browser, + icon: &str, + codename: &str, + name: &str, + url: &str, +) -> bool { + let installed = get_webapps(); + + for app in installed.iter().flatten() { + if !common::url_valid(url) + || !webbrowser.is_installed() + || (name.is_empty() || app.name == name) + || icon.is_empty() + || (codename.is_empty() || app.codename == codename) + || url.is_empty() + { + return false; + } + } + + true +} + +pub fn get_webapps() -> Vec> { + let mut webapps = Vec::new(); + + match fs::read_dir(common::desktop_filepath("")) { + Ok(entries) => { + for entry in entries { + match entry { + Ok(entry) => { + let entry_fn = entry.file_name(); + let filename = entry_fn.to_str().unwrap(); + + if filename.starts_with("webapp-") && filename.ends_with(".desktop") { + let codename = filename.replace("webapp-", "").replace(".desktop", ""); + + let launcher = WebAppLauncher::read(entry.path(), codename); + webapps.push(launcher); + } + } + Err(e) => tracing::error!("Error reading directory: {}", e), + } + } + } + Err(_) => { + create_dir_all(common::desktop_filepath("")) + .expect("Cannot create local applications dir"); + } + } + + webapps +} + +#[derive(Debug, Clone)] +pub struct WebAppLauncher { + pub path: PathBuf, + pub codename: String, + pub web_browser: browser::Browser, + pub name: String, + pub icon: String, + pub is_valid: bool, + pub exec: String, + // pub args: Vec, + pub category: String, + pub url: String, + pub custom_parameters: String, + pub isolate_profile: bool, + pub navbar: bool, + pub is_incognito: bool, +} + +#[allow(clippy::too_many_arguments)] +impl WebAppLauncher { + pub fn new( + name: String, + codename: Option, + url: String, + icon: String, + category: String, + browser: browser::Browser, + custom_parameters: String, + isolated: bool, + navbar: bool, + privatewindow: bool, + ) -> Self { + let codename = if let Some(codename) = codename { + codename + } else { + let random_code: u16 = thread_rng().gen_range(1000..10000); + format!("{}{}", name.replace(' ', ""), random_code) + }; + let filename = format!("webapp-{}.desktop", codename); + let path = common::desktop_filepath(&filename); + let web_browser = browser; + let exec = web_browser.exec.clone(); + // let args = Vec::new(); + let isolate_profile = isolated; + let is_incognito = privatewindow; + + let is_valid = webapplauncher_is_valid(&web_browser, &icon, &codename, &name, &url); + + Self { + path, + codename, + web_browser, + name, + icon, + is_valid, + exec, + // args, + category, + url, + custom_parameters, + isolate_profile, + navbar, + is_incognito, + } + } + + pub fn read(path: PathBuf, codename: String) -> Result { + let file = File::open(&path)?; + let mut browser_name = String::new(); + let mut name = String::new(); + let mut icon = String::new(); + let mut is_valid = false; + let mut exec = String::new(); + let mut args = Vec::new(); + let mut category = String::new(); + let mut url = String::new(); + let mut custom_parameters = String::new(); + let mut isolate_profile = false; + let mut navbar = false; + let mut is_incognito = false; + + let reader = io::BufReader::new(file); + + let mut is_webapp = false; + + for line_result in reader.lines() { + match line_result { + Ok(line) => { + if line.contains("StartupWMClass=WebApp") + || line.contains("StartupWMClass=Chromium") + || line.contains("StartupWMClass=ICE-SSB") + { + is_webapp = true; + }; + + if line.contains("Name=") { + name = line.replace("Name=", ""); + }; + + if line.contains("Icon=") { + icon = line.replace("Icon=", ""); + }; + + if line.contains("Exec=") { + exec = line.replace("Exec=", ""); + }; + + if line.contains("Categories=") { + category = line + .replace("Categories=", "") + .replace("GTK;", "") + .replace(';', ""); + }; + + if line.contains("X-WebApp-Browser=") { + browser_name = line.replace("X-WebApp-Browser=", ""); + }; + + if line.contains("X-WebApp-URL=") { + url = line.replace("X-WebApp-URL=", ""); + }; + + if line.contains("X-WebApp-CustomParameters=") { + custom_parameters = line.replace("X-WebApp-CustomParameters=", ""); + }; + + if line.contains("X-WebApp-Isolated=") { + isolate_profile = line.replace("X-WebApp-Isolated=", "") == "true" + }; + + if line.contains("X-WebApp-Navbar=") { + navbar = line.replace("X-WebApp-Navbar=", "") == "true" + }; + + if line.contains("X-WebApp-PrivateWindow=") { + is_incognito = line.replace("X-WebApp-PrivateWindow=", "") == "true" + }; + } + Err(e) => eprintln!("Error reading line: {}", e), + } + } + + if is_webapp && !name.is_empty() && !icon.is_empty() { + is_valid = true + } + + let web_browser = browser::Browser::web_browser(browser_name); + + match web_browser { + Some(web_browser) => { + exec.split(' ').enumerate().for_each(|(n, arg)| { + if n > 0 && !arg.is_empty() { + args.push(arg.to_string()) + } + }); + + Ok(WebAppLauncher { + path, + codename, + web_browser, + name, + icon, + is_valid, + exec, + // args, + category, + url, + custom_parameters, + isolate_profile, + navbar, + is_incognito, + }) + } + None => Err(anyhow!("Cannot read web app launcher.")), + } + } + + fn create_firefox_userjs(&self, is_zen_browser: bool, path: PathBuf) -> bool { + let content = match is_zen_browser { + true => include_bytes!("../data/runtime/zen-browser/profile/user.js"), + false => include_bytes!("../data/runtime/firefox/profile/user.js"), + }; + + let mut file = File::create(&path) + .unwrap_or_else(|_| panic!("failed to create user.js in {:?}", path)); + + file.write_all(content).is_ok() + } + + fn create_user_chrome_css( + &self, + is_zen_browser: bool, + path: PathBuf, + create_navbar: bool, + ) -> bool { + let mut file = File::create(&path) + .unwrap_or_else(|_| panic!("cant create userChrome.css in {:?}", path)); + + if create_navbar { + file.write_all(b"").is_ok() + } else { + match is_zen_browser { + true => file + .write_all(include_bytes!( + "../data/runtime/zen-browser/profile/chrome/userChrome.css" + )) + .is_ok(), + false => file + .write_all(include_bytes!( + "../data/runtime/firefox/profile/chrome/userChrome.css" + )) + .is_ok(), + } + } + } + + fn exec_firefox(&self, is_zen_browser: bool) -> String { + let profile_path = self.web_browser.profile_path.join(&self.codename); + let user_js_path = profile_path.join("user.js"); + let mut user_chrome_css = profile_path.join("chrome"); + + tracing::info!("Creating profile directory in: {:?}", &profile_path); + create_dir_all(&profile_path) + .unwrap_or_else(|_| panic!("cant create profile dir in {:?}", &profile_path)); + create_dir_all(&user_chrome_css) + .unwrap_or_else(|_| panic!("cant create chrome dir in {:?}", &user_chrome_css)); + + user_chrome_css = user_chrome_css.join("userChrome.css"); + + self.create_firefox_userjs(is_zen_browser, user_js_path); + self.create_user_chrome_css(is_zen_browser, user_chrome_css, self.navbar); + + let profile_path = profile_path.to_str().unwrap(); + + let mut exec_string = format!( + "{} --class WebApp-{} --name WebApp-{} --profile {} --no-remote ", + self.exec, self.codename, self.codename, profile_path + ); + + if self.is_incognito { + exec_string.push_str("--private-window "); + } + + if !self.custom_parameters.is_empty() { + exec_string.push_str(&format!("{} ", self.custom_parameters)); + } + + exec_string.push_str(&self.url); + + exec_string + } + + fn exec_chromium(&self) -> String { + let mut exec_string = format!( + "{} --app={} --class=WebApp-{} --name=WebApp-{} ", + self.exec, self.url, self.codename, self.codename + ); + + if self.isolate_profile { + let profile_dir = self.web_browser.profile_path.join(&self.codename); + + tracing::info!("Creating profile directory in: {:?}", &profile_dir); + let _ = create_dir_all(&profile_dir); + let profile_path = profile_dir.to_str().unwrap(); + exec_string.push_str(&format!("--user-data-dir={} ", profile_path)); + } + + if self.is_incognito { + if self.web_browser.name.starts_with("Microsoft Edge") { + exec_string.push_str("--inprivate "); + } else { + exec_string.push_str("--incognito "); + } + } + + if !self.custom_parameters.is_empty() { + exec_string.push_str(&format!("{} ", self.custom_parameters)); + } + + exec_string + } + + fn exec_falkon(&self) -> String { + let mut exec_string = String::new(); + + if self.isolate_profile { + let profile_dir = self.web_browser.profile_path.join(&self.codename); + tracing::info!("Creating profile directory in: {:?}", &profile_dir); + let _ = create_dir_all(&profile_dir); + + let profile_path = profile_dir.to_str().unwrap(); + + exec_string = format!( + "{} --portable --wmclass WebApp-{} --profile {} ", + self.exec, self.codename, profile_path + ); + } + + if self.is_incognito { + exec_string.push_str("--private-browsing "); + } + + if !self.custom_parameters.is_empty() { + exec_string.push_str(&format!("{} ", self.custom_parameters)); + } + + exec_string.push_str(&format!("--no-remote --current-tab {}", self.url)); + + exec_string + } + + fn exec_string(&self) -> String { + match self.web_browser._type { + browser::BrowserType::Firefox => self.exec_firefox(false), + browser::BrowserType::FirefoxFlatpak => self.exec_firefox(false), + browser::BrowserType::ZenFlatpak => self.exec_firefox(true), + browser::BrowserType::Chromium => self.exec_chromium(), + browser::BrowserType::ChromiumFlatpak => self.exec_chromium(), + browser::BrowserType::Falkon => self.exec_falkon(), + browser::BrowserType::FalkonFlatpak => self.exec_falkon(), + browser::BrowserType::NoBrowser => String::new(), + } + } + + pub fn create(&self) -> anyhow::Result<()> { + let mut output = File::create(&self.path)?; + + writeln!(output, "[Desktop Entry]")?; + writeln!(output, "Version=1.0")?; + writeln!(output, "Name={}", self.name)?; + writeln!(output, "Comment=Web App")?; + writeln!(output, "Exec={}", self.exec_string())?; + writeln!(output, "Terminal=false")?; + writeln!(output, "Type=Application")?; + writeln!(output, "Icon={}", self.icon)?; + writeln!(output, "Categories=GTK;{};", self.category)?; + writeln!(output, "MimeType=text/html;text/xml;application/xhtml_xml;")?; + writeln!(output, "StartupWMClass=WebApp-{}", self.codename)?; + writeln!(output, "StartupNotify=true")?; + writeln!(output, "X-MultipleArgs=false")?; + writeln!(output, "X-WebApp-Browser={}", self.web_browser.name)?; + writeln!(output, "X-WebApp-URL={}", self.url)?; + writeln!(output, "X-WebApp-Navbar={}", self.navbar)?; + writeln!(output, "X-WebApp-PrivateWindow={}", self.is_incognito)?; + writeln!(output, "X-WebApp-Isolated={}", self.isolate_profile)?; + writeln!( + output, + "X-WebApp-CustomParameters={}", + self.custom_parameters + )?; + + Ok(()) + } + + pub fn delete(&self) -> anyhow::Result<()> { + let exist = self.path.as_path().exists(); + + match exist { + true => { + remove_file(&self.path)?; + } + false => { + tracing::error!("file not found"); + } + } + + let profile_path = self.web_browser.profile_path.join(&self.codename); + + if remove_dir_all(&profile_path).is_ok() { + tracing::info!( + "Removed profile directory from: {}", + profile_path.to_str().unwrap() + ); + }; + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index 6a9ebe8..dc91847 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,8 @@ +mod browser; mod common; +mod favicon; mod icon_cache; +mod launcher; mod localize; mod pages; mod supported_browsers; diff --git a/src/pages/creator.rs b/src/pages/creator.rs index 8f302c2..e2a6be9 100644 --- a/src/pages/creator.rs +++ b/src/pages/creator.rs @@ -7,7 +7,8 @@ use cosmic::{ }; use crate::{ - common::{get_supported_browsers, icon_cache_get, url_valid, Browser, BrowserType}, + browser::{get_supported_browsers, Browser, BrowserType}, + common::{icon_cache_get, url_valid}, fl, pages::{self, iconpicker::IconType}, warning::{WarnAction, WarnMessages}, diff --git a/src/pages/home_screen.rs b/src/pages/home_screen.rs index 3a14a98..2ca5c41 100644 --- a/src/pages/home_screen.rs +++ b/src/pages/home_screen.rs @@ -1,6 +1,7 @@ use crate::{ - common::{get_webapps, icon_cache_get, WebAppLauncher}, + common::icon_cache_get, fl, + launcher::{get_webapps, WebAppLauncher}, pages::{Buttons, Message}, }; diff --git a/src/pages/mod.rs b/src/pages/mod.rs index bd2a16b..0b91378 100644 --- a/src/pages/mod.rs +++ b/src/pages/mod.rs @@ -27,9 +27,8 @@ use cosmic::{ use crate::{ add_icon_packs_install_script, common::{ - self, find_icon, find_icons, get_icon_name_from_url, get_supported_browsers, - icon_cache_get, image_handle, move_icon, my_icons_location, Browser, BrowserType, - WebAppLauncher, + self, find_icon, find_icons, get_icon_name_from_url, icon_cache_get, image_handle, + move_icon, qwa_icons_location, }, execute_script, fl, icon_pack_installed, pages::home_screen::Home, @@ -38,15 +37,16 @@ use crate::{ warning::WarnMessages, warning::{WarnAction, Warning}, }; +use crate::{browser, launcher}; #[derive(Debug, Clone)] pub enum Buttons { SearchFavicon, - Edit(WebAppLauncher), - Delete(WebAppLauncher), + Edit(launcher::WebAppLauncher), + Delete(launcher::WebAppLauncher), DoneEdit((Option, Option)), DoneCreate, - AppNameSubmit(WebAppLauncher), + AppNameSubmit(launcher::WebAppLauncher), } #[allow(clippy::large_enum_variant)] @@ -234,11 +234,10 @@ impl Application for Window { if let Ok(buf) = PathBuf::from_str(&path) { let icon_name = buf.file_stem(); if let Some(file_stem) = icon_name { - move_icon(path.to_string(), file_stem.to_str().unwrap().to_string()) - .unwrap(); + move_icon(path.to_string(), file_stem.to_str().unwrap().to_string()); return Command::perform( - find_icon(my_icons_location(), String::new()), + find_icon(qwa_icons_location(), String::new()), |result| app(Message::FoundIcons(result)), ); } @@ -260,7 +259,7 @@ impl Application for Window { } Message::Clicked(buttons) => match buttons { Buttons::DoneCreate => { - let new_entry = WebAppLauncher::new( + let new_entry = launcher::WebAppLauncher::new( self.creator_window.app_title.clone(), None, self.creator_window.app_url.clone(), @@ -284,7 +283,7 @@ impl Application for Window { Buttons::DoneEdit((new_name, old_icon)) => { if let Some(launcher) = self.main_window.launcher.to_owned() { let _deleted = launcher.delete(); - let mut edited_entry = WebAppLauncher::new( + let mut edited_entry = launcher::WebAppLauncher::new( self.creator_window.app_title.clone(), Some(launcher.codename), self.creator_window.app_url.clone(), @@ -330,7 +329,7 @@ impl Application for Window { }) } Buttons::Edit(launcher) => { - let selected_browser = get_supported_browsers() + let selected_browser = browser::get_supported_browsers() .iter() .position(|b| b.name == launcher.web_browser.name); @@ -344,7 +343,8 @@ impl Application for Window { self.creator_window.app_parameters = launcher.custom_parameters; self.creator_window.app_category = launcher.category; self.creator_window.app_browser = - Browser::web_browser(launcher.web_browser.name).expect("browser not found"); + browser::Browser::web_browser(launcher.web_browser.name) + .expect("browser not found"); self.creator_window.selected_browser = selected_browser; self.creator_window.app_navbar = launcher.navbar; self.creator_window.app_incognito = launcher.is_incognito; @@ -379,7 +379,7 @@ impl Application for Window { self.icon_selector.loading = true; - Command::perform(find_icon(my_icons_location(), icon_name), |result| { + Command::perform(find_icon(qwa_icons_location(), icon_name), |result| { app(Message::FoundIcons(result)) }) } @@ -449,9 +449,8 @@ impl Application for Window { if !self.icon_selector.icons.is_empty() { let path = self.icon_selector.icons[0].path.clone(); - if let Some(saved) = move_icon(path, self.creator_window.app_title.clone()) { - self.creator_window.app_icon = saved; - }; + self.creator_window.app_icon = + move_icon(path, self.creator_window.app_title.clone()); self.creator_window.selected_icon = Some(self.icon_selector.icons[0].clone()); } @@ -462,9 +461,8 @@ impl Application for Window { let path = icon.path.clone(); self.creator_window.selected_icon = Some(icon.clone()); - if let Some(saved) = move_icon(path, self.creator_window.app_title.clone()) { - self.creator_window.app_icon = saved; - }; + self.creator_window.app_icon = + move_icon(path, self.creator_window.app_title.clone()); if self.creator_window.selected_icon.is_some() && !self.creator_window.app_icon.is_empty() @@ -484,20 +482,17 @@ impl Application for Window { Message::SetIcon(icon) => { let path = icon.path; - if let Some(saved) = move_icon(path, self.creator_window.app_title.clone()) { - self.current_page = Pages::AppCreator; - self.creator_window.app_icon.clone_from(&saved); + let saved = move_icon(path, self.creator_window.app_title.clone()); + self.current_page = Pages::AppCreator; + self.creator_window.app_icon.clone_from(&saved); - Command::perform(image_handle(saved), |result| { - if let Some(res) = result { - app(Message::SelectIcon(res)) - } else { - message::none() - } - }) - } else { - Command::none() - } + Command::perform(image_handle(saved), |result| { + if let Some(res) = result { + app(Message::SelectIcon(res)) + } else { + message::none() + } + }) } Message::SelectIcon(ico) => { self.creator_window.selected_icon = Some(ico.clone()); @@ -582,7 +577,7 @@ impl Window { self.set_window_title(self.match_title()) } - fn create_valid_launcher(&mut self, entry: WebAppLauncher) -> anyhow::Result<()> { + fn create_valid_launcher(&mut self, entry: launcher::WebAppLauncher) -> anyhow::Result<()> { if entry.is_valid && self.warning.is_empty() { let _ = entry.create().is_ok(); self.creator_window.edit_mode = false; @@ -604,7 +599,7 @@ impl Window { if self.creator_window.app_icon.is_empty() { self.warning.push_warn(WarnMessages::AppIcon) } - if self.creator_window.app_browser._type == BrowserType::NoBrowser { + if self.creator_window.app_browser._type == browser::BrowserType::NoBrowser { self.warning.push_warn(WarnMessages::AppBrowser) } } diff --git a/src/supported_browsers.rs b/src/supported_browsers.rs index 34570b9..0dd40da 100644 --- a/src/supported_browsers.rs +++ b/src/supported_browsers.rs @@ -1,4 +1,4 @@ -use crate::common::{Browser, BrowserType}; +use crate::browser::{Browser, BrowserType}; #[allow(dead_code)] pub fn native_browsers() -> Vec { From b02b80d75b4264da900eed4108f442b1ba59db46 Mon Sep 17 00:00:00 2001 From: Piotr <114903054+elevenhsoft@users.noreply.github.com> Date: Sat, 28 Sep 2024 16:36:35 +0200 Subject: [PATCH 2/9] reinitialize creator window when done --- src/pages/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/mod.rs b/src/pages/mod.rs index 0b91378..19ecc25 100644 --- a/src/pages/mod.rs +++ b/src/pages/mod.rs @@ -277,7 +277,7 @@ impl Application for Window { } else { self.warning.push_warn(WarnMessages::Duplicate); } - self.creator_window.edit_mode = false; + self.creator_window = creator::AppCreator::new(); Command::none() } Buttons::DoneEdit((new_name, old_icon)) => { @@ -310,7 +310,7 @@ impl Application for Window { self.warning.push_warn(WarnMessages::Duplicate); } } - self.creator_window.edit_mode = false; + self.creator_window = creator::AppCreator::new(); Command::none() } Buttons::AppNameSubmit(mut launcher) => { From caf2fc0679e0a26a5cb8ed25de695811996b9e94 Mon Sep 17 00:00:00 2001 From: Piotr <114903054+elevenhsoft@users.noreply.github.com> Date: Sat, 28 Sep 2024 16:44:27 +0200 Subject: [PATCH 3/9] move Icon struct to common mod --- src/common.rs | 37 +++++++++++++++++++++++-------------- src/pages/creator.rs | 8 ++++---- src/pages/iconpicker.rs | 35 +++++------------------------------ src/pages/mod.rs | 14 ++++---------- 4 files changed, 36 insertions(+), 58 deletions(-) diff --git a/src/common.rs b/src/common.rs index b3e3010..fdf7e55 100644 --- a/src/common.rs +++ b/src/common.rs @@ -21,7 +21,7 @@ use tokio::io::AsyncReadExt; use url::Url; use walkdir::WalkDir; -use crate::{favicon, icon_cache::IconCache, pages::iconpicker}; +use crate::{favicon, icon_cache::IconCache}; lazy_static::lazy_static! { static ref ICON_CACHE: Mutex = Mutex::new(IconCache::new()); @@ -226,7 +226,7 @@ pub fn move_icon(path: String, output_name: String) -> String { path } -pub async fn image_handle(path: String) -> Option { +pub async fn image_handle(path: String) -> Option { if url_valid(&path) { let mut data: Vec<_> = Vec::new(); @@ -238,10 +238,7 @@ pub async fn image_handle(path: String) -> Option { let handle = widget::image::Handle::from_memory(data); - return Some(iconpicker::Icon::new( - iconpicker::IconType::Raster(handle), - path, - )); + return Some(Icon::new(IconType::Raster(handle), path)); }; if let Ok(result_path) = PathBuf::from_str(&path) { @@ -249,10 +246,7 @@ pub async fn image_handle(path: String) -> Option { if is_svg(&path) { let handle = widget::svg::Handle::from_path(&result_path); - return Some(iconpicker::Icon::new( - iconpicker::IconType::Svg(handle), - path, - )); + return Some(Icon::new(IconType::Svg(handle), path)); } else { let mut data: Vec<_> = Vec::new(); @@ -266,10 +260,7 @@ pub async fn image_handle(path: String) -> Option { if image.width() >= 96 && image.height() >= 96 { let handle = widget::image::Handle::from_memory(data); - return Some(iconpicker::Icon::new( - iconpicker::IconType::Raster(handle), - path, - )); + return Some(Icon::new(IconType::Raster(handle), path)); } }; } @@ -279,3 +270,21 @@ pub async fn image_handle(path: String) -> Option { None } + +#[derive(Debug, Clone, PartialEq)] +pub enum IconType { + Raster(widget::image::Handle), + Svg(widget::svg::Handle), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Icon { + pub icon: IconType, + pub path: String, +} + +impl Icon { + pub fn new(icon: IconType, path: String) -> Self { + Self { icon, path } + } +} diff --git a/src/pages/creator.rs b/src/pages/creator.rs index e2a6be9..01aa811 100644 --- a/src/pages/creator.rs +++ b/src/pages/creator.rs @@ -8,9 +8,9 @@ use cosmic::{ use crate::{ browser::{get_supported_browsers, Browser, BrowserType}, - common::{icon_cache_get, url_valid}, + common::{self, icon_cache_get, url_valid, IconType}, fl, - pages::{self, iconpicker::IconType}, + pages::{self}, warning::{WarnAction, WarnMessages}, }; @@ -29,7 +29,7 @@ pub struct AppCreator { pub app_navbar: bool, pub app_incognito: bool, pub app_isolated: bool, - pub selected_icon: Option, + pub selected_icon: Option, pub app_browsers: Vec, pub selected_browser: Option, pub edit_mode: bool, @@ -181,7 +181,7 @@ impl AppCreator { } } - fn icon_picker_icon(&self, icon: Option) -> Element { + fn icon_picker_icon(&self, icon: Option) -> Element { let ico = if let Some(ico) = icon { match ico.icon { IconType::Raster(data) => widget::button::custom(widget::image(data)) diff --git a/src/pages/iconpicker.rs b/src/pages/iconpicker.rs index 8847c44..1867d99 100644 --- a/src/pages/iconpicker.rs +++ b/src/pages/iconpicker.rs @@ -6,13 +6,12 @@ use cosmic::{ Element, }; -use crate::{fl, icon_pack_installed, pages::Message}; +use crate::{common, fl, icon_pack_installed, pages::Message}; #[derive(Debug, Clone, Default)] pub struct IconPicker { pub icon_searching: String, - pub icons: Vec, - pub loading: bool, + pub icons: Vec, } impl IconPicker { @@ -22,13 +21,7 @@ impl IconPicker { .on_submit(Message::PerformIconSearch) .width(Length::FillPortion(3)); - let loading_state_text = if !self.loading { - text(fl!("my-icons")) - } else { - text(fl!("loading")) - }; - - let my_icons_btn = widget::button::custom(loading_state_text) + let my_icons_btn = widget::button::custom(text(fl!("my-icons"))) .on_press(Message::MyIcons) .padding(8) .width(Length::FillPortion(1)); @@ -57,12 +50,12 @@ impl IconPicker { for ico in self.icons.iter() { let btn = match ico.clone().icon { - IconType::Raster(icon) => widget::button::custom(widget::image(icon)) + common::IconType::Raster(icon) => widget::button::custom(widget::image(icon)) .width(Length::Fixed(64.)) .height(Length::Fixed(64.)) .on_press(Message::ChangeIcon(ico.clone())) .style(theme::Button::Icon), - IconType::Svg(icon) => widget::button::custom(widget::svg(icon)) + common::IconType::Svg(icon) => widget::button::custom(widget::svg(icon)) .width(Length::Fixed(64.)) .height(Length::Fixed(64.)) .on_press(Message::ChangeIcon(ico.clone())) @@ -87,21 +80,3 @@ impl IconPicker { .into() } } - -#[derive(Debug, Clone, PartialEq)] -pub enum IconType { - Raster(widget::image::Handle), - Svg(widget::svg::Handle), -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Icon { - pub icon: IconType, - pub path: String, -} - -impl Icon { - pub fn new(icon: IconType, path: String) -> Self { - Self { icon, path } - } -} diff --git a/src/pages/mod.rs b/src/pages/mod.rs index 19ecc25..6518afc 100644 --- a/src/pages/mod.rs +++ b/src/pages/mod.rs @@ -66,13 +66,13 @@ pub enum Message { Clicked(Buttons), // icons CustomIconsSearch(String), - ChangeIcon(iconpicker::Icon), + ChangeIcon(common::Icon), MyIcons, PerformIconSearch, FoundIcons(Vec), - PushIcon(Option), - SetIcon(iconpicker::Icon), - SelectIcon(iconpicker::Icon), + PushIcon(Option), + SetIcon(common::Icon), + SelectIcon(common::Icon), Warning((WarnAction, WarnMessages)), @@ -377,15 +377,12 @@ impl Application for Window { Message::MyIcons => { let icon_name = self.icon_selector.icon_searching.clone(); - self.icon_selector.loading = true; - Command::perform(find_icon(qwa_icons_location(), icon_name), |result| { app(Message::FoundIcons(result)) }) } Message::PerformIconSearch => { self.icon_selector.icons.clear(); - self.icon_selector.loading = true; let name = if self.icon_selector.icon_searching.is_empty() && !self.creator_window.app_url.is_empty() @@ -407,7 +404,6 @@ impl Application for Window { } Message::CustomIconsSearch(input) => { self.icon_selector.icon_searching = input; - self.icon_selector.loading = false; Command::none() } @@ -445,8 +441,6 @@ impl Application for Window { Command::batch(vec![cmd, done]) } Message::LoadingDone => { - self.icon_selector.loading = false; - if !self.icon_selector.icons.is_empty() { let path = self.icon_selector.icons[0].path.clone(); self.creator_window.app_icon = From 9d24c94ccba9a41bddcd138e8b6e43789f7567f7 Mon Sep 17 00:00:00 2001 From: Piotr <114903054+elevenhsoft@users.noreply.github.com> Date: Sat, 28 Sep 2024 17:19:28 +0200 Subject: [PATCH 4/9] improv: reorganization update method --- src/pages/creator.rs | 39 +++++---- src/pages/mod.rs | 188 ++++++++++++++++++------------------------- 2 files changed, 97 insertions(+), 130 deletions(-) diff --git a/src/pages/creator.rs b/src/pages/creator.rs index 01aa811..143aebf 100644 --- a/src/pages/creator.rs +++ b/src/pages/creator.rs @@ -1,5 +1,6 @@ use cosmic::{ app::{message::app, Message as CosmicMessage}, + command, iced::{id, Length}, style, widget::{self}, @@ -92,93 +93,89 @@ impl AppCreator { } pub fn update(&mut self, message: Message) -> Command> { + let mut commands: Vec>> = Vec::new(); + match message { Message::Title(title) => { self.app_title = title; if self.app_title.len() >= 3 { - Command::perform(async {}, |_| { + commands.push(command::future(async { app(pages::Message::Warning(( WarnAction::Remove, WarnMessages::AppName, ))) - }) + })) } else { - Command::perform(async {}, |_| { + commands.push(command::future(async { app(pages::Message::Warning(( WarnAction::Add, WarnMessages::AppName, ))) - }) + })) } } Message::Url(url) => { self.app_url = url; if url_valid(&self.app_url) { - Command::perform(async {}, |_| { + commands.push(command::future(async { app(pages::Message::Warning(( WarnAction::Remove, WarnMessages::AppUrl, ))) - }) + })) } else { - Command::perform(async {}, |_| { + commands.push(command::future(async { app(pages::Message::Warning(( WarnAction::Add, WarnMessages::AppUrl, ))) - }) + })) } } Message::Arguments(args) => { self.app_parameters = args; - Command::none() } Message::Browser(idx) => { let browser = &self.app_browsers[idx]; self.selected_browser = Some(idx); self.app_browser = browser.clone(); - match browser._type { - BrowserType::NoBrowser => Command::perform(async {}, |_| { + commands.push(match browser._type { + BrowserType::NoBrowser => command::future(async { app(pages::Message::Warning(( WarnAction::Add, WarnMessages::AppBrowser, ))) }), - _ => Command::perform(async {}, |_| { + _ => command::future(async { app(pages::Message::Warning(( WarnAction::Remove, WarnMessages::AppBrowser, ))) }), - } + }) } Message::Category(idx) => { self.app_category.clone_from(&self.app_categories[idx]); self.selected_category = idx; - Command::none() } Message::Clicked(buttons) => match buttons { Buttons::Navbar(selected) => { self.app_navbar = selected; - - Command::none() } Buttons::IsolatedProfile(selected) => { self.app_isolated = selected; - - Command::none() } Buttons::Incognito(selected) => { self.app_incognito = selected; - - Command::none() } }, - } + }; + + Command::batch(commands) } fn icon_picker_icon(&self, icon: Option) -> Element { diff --git a/src/pages/mod.rs b/src/pages/mod.rs index 6518afc..f526f2f 100644 --- a/src/pages/mod.rs +++ b/src/pages/mod.rs @@ -13,7 +13,7 @@ use cosmic::app::command::set_theme; use cosmic::iced::alignment::Horizontal; use cosmic::iced::Length; use cosmic::widget::Container; -use cosmic::{app, Theme}; +use cosmic::{app, command, Theme}; use cosmic::{ app::{ message::{self, app}, @@ -165,69 +165,63 @@ impl Application for Window { } fn update(&mut self, message: Self::Message) -> Command> { + let mut commands: Vec>> = Vec::new(); + match message { Message::OpenHome => { self.current_page = Pages::MainWindow; - - self.update_title() + commands.push(self.update_title()); } Message::OpenCreator => { self.current_page = Pages::AppCreator; self.init_warning_box(); - - self.update_title() + commands.push(self.update_title()); } Message::CloseCreator => { self.current_page = Pages::MainWindow; self.creator_window.edit_mode = false; - - self.update_title() + commands.push(self.update_title()); } Message::Creator(message) => { - let command = self.creator_window.update(message); - - command.map(|mess| mess) + commands.push(self.creator_window.update(message).map(|mess| mess)); } Message::Warning((action, message)) => { match action { WarnAction::Add => self.warning.push_warn(message), WarnAction::Remove => self.warning.remove_warn(message), }; - Command::none() } Message::OpenIconPicker => { self.current_page = Pages::IconPicker; - - self.update_title() + commands.push(self.update_title()) } Message::OpenIconPickerDialog => { - return Command::perform( - async move { - let result = SelectedFiles::open_file() - .title("Open multiple images") - .accept_label("Attach") - .modal(true) - .multiple(true) - .filter(FileFilter::new("PNG Image").glob("*.png")) - .filter(FileFilter::new("SVG Images").glob("*.svg")) - .send() - .await - .unwrap() - .response(); - - if let Ok(result) = result { - result - .uris() - .iter() - .map(|file| file.path().to_string()) - .collect::>() - } else { - Vec::new() - } - }, - |files| cosmic::app::message::app(Message::OpenFileResult(files)), - ); + commands.push(command::future(async move { + let result = SelectedFiles::open_file() + .title("Open multiple images") + .accept_label("Attach") + .modal(true) + .multiple(true) + .filter(FileFilter::new("PNG Image").glob("*.png")) + .filter(FileFilter::new("SVG Images").glob("*.svg")) + .send() + .await + .unwrap() + .response(); + + if let Ok(result) = result { + let files = result + .uris() + .iter() + .map(|file| file.path().to_string()) + .collect::>(); + + cosmic::app::message::app(Message::OpenFileResult(files)) + } else { + cosmic::app::message::none() + } + })); } Message::OpenFileResult(result) => { for path in result { @@ -236,14 +230,14 @@ impl Application for Window { if let Some(file_stem) = icon_name { move_icon(path.to_string(), file_stem.to_str().unwrap().to_string()); - return Command::perform( - find_icon(qwa_icons_location(), String::new()), - |result| app(Message::FoundIcons(result)), - ); - } + commands.push(command::future(async { + app(Message::FoundIcons( + find_icon(qwa_icons_location(), String::new()).await, + )) + })); + }; } } - Command::none() } Message::EditAppName(flag) => { if !flag { @@ -251,11 +245,9 @@ impl Application for Window { } self.main_window.edit_appname = flag; - Command::none() } Message::AppNameInput(new_name) => { self.main_window.new_app_name = new_name; - Command::none() } Message::Clicked(buttons) => match buttons { Buttons::DoneCreate => { @@ -278,7 +270,6 @@ impl Application for Window { self.warning.push_warn(WarnMessages::Duplicate); } self.creator_window = creator::AppCreator::new(); - Command::none() } Buttons::DoneEdit((new_name, old_icon)) => { if let Some(launcher) = self.main_window.launcher.to_owned() { @@ -311,7 +302,6 @@ impl Application for Window { } } self.creator_window = creator::AppCreator::new(); - Command::none() } Buttons::AppNameSubmit(mut launcher) => { launcher.name.clone_from(&self.main_window.new_app_name); @@ -321,12 +311,12 @@ impl Application for Window { self.main_window.new_app_name.clear(); - Command::perform(async {}, |_| { + commands.push(command::future(async { app(Message::Clicked(Buttons::DoneEdit(( Some(launcher.name), Some(launcher.icon), )))) - }) + })); } Buttons::Edit(launcher) => { let selected_browser = browser::get_supported_browsers() @@ -350,36 +340,36 @@ impl Application for Window { self.creator_window.app_incognito = launcher.is_incognito; self.creator_window.edit_mode = true; - Command::perform(image_handle(launcher.icon), |result| { - if let Some(res) = result { + commands.push(command::future(async { + if let Some(res) = image_handle(launcher.icon).await { return app(Message::SetIcon(res)); } app::Message::None - }) + })); } Buttons::Delete(launcher) => { let _ = launcher.delete(); - - Command::none() } Buttons::SearchFavicon => { if common::url_valid(&self.creator_window.app_url) { self.icon_selector.icons.clear(); + let url = self.creator_window.app_url.clone(); let name = get_icon_name_from_url(&self.creator_window.app_url); - let icons = find_icons(name, self.creator_window.app_url.clone()); - Command::perform(icons, |icons| app(Message::FoundIcons(icons))) - } else { - Command::none() + commands.push(command::future(async { + app(Message::FoundIcons(find_icons(name, url).await)) + })) } } }, Message::MyIcons => { let icon_name = self.icon_selector.icon_searching.clone(); - Command::perform(find_icon(qwa_icons_location(), icon_name), |result| { - app(Message::FoundIcons(result)) - }) + commands.push(command::future(async { + app(Message::FoundIcons( + find_icon(qwa_icons_location(), icon_name).await, + )) + })) } Message::PerformIconSearch => { self.icon_selector.icons.clear(); @@ -399,35 +389,26 @@ impl Application for Window { { return Command::perform(icons, |icons| app(Message::FoundIcons(icons))); } - - Command::none() } Message::CustomIconsSearch(input) => { self.icon_selector.icon_searching = input; - - Command::none() } Message::FoundIcons(result) => { self.icon_selector.icons.clear(); - let mut commands: Vec>> = Vec::new(); - result.into_iter().for_each(|path| { - commands.push(Command::perform(image_handle(path), |result| { - app(Message::PushIcon(result)) + commands.push(command::future(async { + app(Message::PushIcon(image_handle(path).await)) })); }); - - Command::batch(commands) } Message::PushIcon(icon) => { - let mut cmd = Command::none(); if icon.is_some() { - cmd = Command::perform(async {}, |_| { + commands.push(command::future(async { app(Message::Warning(( WarnAction::Remove, WarnMessages::AppIcon, ))) - }) + })); }; if let Some(ico) = icon { @@ -436,9 +417,7 @@ impl Application for Window { } }; - let done = Command::perform(async {}, |_| app(Message::LoadingDone)); - - Command::batch(vec![cmd, done]) + commands.push(command::future(async { app(Message::LoadingDone) })); } Message::LoadingDone => { if !self.icon_selector.icons.is_empty() { @@ -447,8 +426,6 @@ impl Application for Window { move_icon(path, self.creator_window.app_title.clone()); self.creator_window.selected_icon = Some(self.icon_selector.icons[0].clone()); } - - Command::none() } Message::ChangeIcon(icon) => { self.current_page = Pages::AppCreator; @@ -461,16 +438,16 @@ impl Application for Window { if self.creator_window.selected_icon.is_some() && !self.creator_window.app_icon.is_empty() { - Command::perform(async {}, |_| { + commands.push(command::future(async { app(Message::Warning(( WarnAction::Remove, WarnMessages::AppIcon, ))) - }) + })); } else { - Command::perform(async {}, |_| { + commands.push(command::future(async { app(Message::Warning((WarnAction::Add, WarnMessages::AppIcon))) - }) + })); } } Message::SetIcon(icon) => { @@ -480,58 +457,51 @@ impl Application for Window { self.current_page = Pages::AppCreator; self.creator_window.app_icon.clone_from(&saved); - Command::perform(image_handle(saved), |result| { - if let Some(res) = result { + commands.push(command::future(async { + if let Some(res) = image_handle(saved).await { app(Message::SelectIcon(res)) } else { message::none() } - }) + })); } Message::SelectIcon(ico) => { self.creator_window.selected_icon = Some(ico.clone()); self.creator_window.app_icon = ico.path; - - Command::none() } Message::DownloadIconsPack => { let installator = Installator::new(); self.current_page = Pages::IconInstallator(installator); - - let update_title = self.update_title(); - - Command::batch(vec![ - update_title, - Command::perform(add_icon_packs_install_script(), |file| { - app(Message::InstallScript(file)) - }), - ]) + commands.push(self.update_title()); + commands.push(command::future(async { + app(Message::InstallScript( + add_icon_packs_install_script().await, + )) + })); } Message::InstallScript(script) => { if !icon_pack_installed() { - return Command::perform(execute_script(script), |status| { - app(Message::InstallCommand(status)) - }); + commands.push(command::future(async { + app(Message::InstallCommand(execute_script(script).await)) + })); } - Command::none() } Message::InstallCommand(exit_status) => { if ExitStatus::success(&exit_status) { self.current_page = Pages::MainWindow; } - - self.update_title() + commands.push(self.update_title()) } Message::SystemTheme => { if std::env::var("XDG_CURRENT_DESKTOP") != Ok("COSMIC".to_string()) { - set_theme(Theme::custom(Arc::new( + commands.push(set_theme(Theme::custom(Arc::new( cosmic_theme::Theme::preferred_theme(), - ))) - } else { - Command::none() + )))) } } } + + Command::batch(commands) } fn view(&self) -> Element { From 6b523d33d01100cb8549c0e285af1ce6c96b37cc Mon Sep 17 00:00:00 2001 From: Piotr <114903054+elevenhsoft@users.noreply.github.com> Date: Sat, 28 Sep 2024 17:21:59 +0200 Subject: [PATCH 5/9] reinitialize creator page after closing it --- src/pages/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/mod.rs b/src/pages/mod.rs index f526f2f..256314d 100644 --- a/src/pages/mod.rs +++ b/src/pages/mod.rs @@ -180,7 +180,7 @@ impl Application for Window { } Message::CloseCreator => { self.current_page = Pages::MainWindow; - self.creator_window.edit_mode = false; + self.creator_window = creator::AppCreator::new(); commands.push(self.update_title()); } Message::Creator(message) => { From 115406fb935216473d7a8057dc41068c89f180b4 Mon Sep 17 00:00:00 2001 From: Piotr <114903054+elevenhsoft@users.noreply.github.com> Date: Sat, 28 Sep 2024 17:42:22 +0200 Subject: [PATCH 6/9] fix: moving icon --- src/common.rs | 24 +++++++++++------------- src/pages/mod.rs | 27 +++++++++++++++------------ 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/common.rs b/src/common.rs index fdf7e55..e85644e 100644 --- a/src/common.rs +++ b/src/common.rs @@ -206,21 +206,19 @@ pub fn move_icon(path: String, output_name: String) -> String { return convert_raster_to_svg_format(content, &icon_name); } - } else if !path.contains(&icon_name) { - if !is_svg(&path) { - if let Ok(mut file) = File::open(&path) { - let mut buffer = Vec::new(); - file.read_to_end(&mut buffer).unwrap(); - let content = Bytes::from(buffer); - - return convert_raster_to_svg_format(content, &icon_name); - } - } else { - let save_path = icon_save_path(&icon_name, "svg"); - copy(&path, &save_path).unwrap(); + } else if !is_svg(&path) { + if let Ok(mut file) = File::open(&path) { + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer).unwrap(); + let content = Bytes::from(buffer); - return save_path; + return convert_raster_to_svg_format(content, &icon_name); } + } else { + let save_path = icon_save_path(&icon_name, "svg"); + copy(&path, &save_path).unwrap(); + + return save_path; } path diff --git a/src/pages/mod.rs b/src/pages/mod.rs index 256314d..e08f126 100644 --- a/src/pages/mod.rs +++ b/src/pages/mod.rs @@ -224,20 +224,23 @@ impl Application for Window { })); } Message::OpenFileResult(result) => { - for path in result { - if let Ok(buf) = PathBuf::from_str(&path) { - let icon_name = buf.file_stem(); - if let Some(file_stem) = icon_name { - move_icon(path.to_string(), file_stem.to_str().unwrap().to_string()); - - commands.push(command::future(async { - app(Message::FoundIcons( - find_icon(qwa_icons_location(), String::new()).await, - )) - })); + commands.push(command::future(async { + for path in result { + if let Ok(buf) = PathBuf::from_str(&path) { + let icon_name = buf.file_stem(); + if let Some(file_stem) = icon_name { + move_icon( + path.to_string(), + file_stem.to_str().unwrap().to_string(), + ); + }; }; } - } + + app(Message::FoundIcons( + find_icon(qwa_icons_location(), String::new()).await, + )) + })); } Message::EditAppName(flag) => { if !flag { From 5a3c5ff56471f735af7b9c30d6f97ac38c48d1dd Mon Sep 17 00:00:00 2001 From: Piotr <114903054+elevenhsoft@users.noreply.github.com> Date: Sat, 28 Sep 2024 17:45:11 +0200 Subject: [PATCH 7/9] simplify move_icon function --- src/common.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/common.rs b/src/common.rs index e85644e..7cc5614 100644 --- a/src/common.rs +++ b/src/common.rs @@ -206,7 +206,9 @@ pub fn move_icon(path: String, output_name: String) -> String { return convert_raster_to_svg_format(content, &icon_name); } - } else if !is_svg(&path) { + }; + + if !is_svg(&path) { if let Ok(mut file) = File::open(&path) { let mut buffer = Vec::new(); file.read_to_end(&mut buffer).unwrap(); @@ -214,14 +216,12 @@ pub fn move_icon(path: String, output_name: String) -> String { return convert_raster_to_svg_format(content, &icon_name); } - } else { - let save_path = icon_save_path(&icon_name, "svg"); - copy(&path, &save_path).unwrap(); + }; - return save_path; - } + let save_path = icon_save_path(&icon_name, "svg"); + copy(&path, &save_path).unwrap(); - path + save_path } pub async fn image_handle(path: String) -> Option { From e2b4e55488b3192cd21074009d78e8798874573a Mon Sep 17 00:00:00 2001 From: Piotr <114903054+elevenhsoft@users.noreply.github.com> Date: Sat, 28 Sep 2024 18:35:45 +0200 Subject: [PATCH 8/9] remove unnecessary argument --- src/common.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common.rs b/src/common.rs index 7cc5614..52e809d 100644 --- a/src/common.rs +++ b/src/common.rs @@ -152,7 +152,7 @@ pub async fn find_icons(icon_name: String, url: String) -> Vec { } pub fn convert_raster_to_svg_format(img_slice: Bytes, icon_name: &str) -> String { - let save_path = icon_save_path(icon_name, "svg"); + let save_path = icon_save_path(icon_name); if let Ok(data) = load_from_memory(&img_slice) { let (width, height) = data.dimensions(); @@ -185,9 +185,9 @@ pub fn convert_raster_to_svg_format(img_slice: Bytes, icon_name: &str) -> String save_path } -fn icon_save_path(icon_name: &str, format: &str) -> String { +fn icon_save_path(icon_name: &str) -> String { qwa_icons_location() - .join(format!("{}.{}", icon_name, format)) + .join(format!("{}.svg", icon_name)) .to_str() .unwrap() .to_string() @@ -218,7 +218,7 @@ pub fn move_icon(path: String, output_name: String) -> String { } }; - let save_path = icon_save_path(&icon_name, "svg"); + let save_path = icon_save_path(&icon_name); copy(&path, &save_path).unwrap(); save_path From 631f1004015af2de4524c59a870f1b1afc521bd0 Mon Sep 17 00:00:00 2001 From: Piotr <114903054+elevenhsoft@users.noreply.github.com> Date: Sat, 28 Sep 2024 18:40:17 +0200 Subject: [PATCH 9/9] remove clippy allow hint --- src/common.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/common.rs b/src/common.rs index 52e809d..57e9288 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,5 +1,3 @@ -#![allow(clippy::too_many_arguments)] - use std::{ ffi::OsStr, fs::{copy, create_dir_all, File},