Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(backup): re-add export selection to text file #435

Merged
merged 1 commit into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/core/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::{fmt, fs};
/// Global environment variable to keep
/// track of the current device serial.
pub const ANDROID_SERIAL: &str = "ANDROID_SERIAL";
pub const EXPORT_FILE_NAME: &str = "selection_export.txt";

pub fn fetch_packages(uad_lists: &PackageHashMap, user_id: Option<&User>) -> Vec<PackageRow> {
let all_system_packages = list_all_system_packages(user_id); // installed and uninstalled packages
Expand Down Expand Up @@ -109,6 +110,20 @@ pub fn format_diff_time_from_now(date: DateTime<Utc>) -> String {
}
}

pub async fn export_selection(packages: Vec<PackageRow>) -> Result<bool, String> {
let selected = packages
.iter()
.filter(|p| p.selected)
.map(|p| p.name.clone())
.collect::<Vec<String>>()
.join("\n");

match fs::write(EXPORT_FILE_NAME, selected) {
Ok(_) => Ok(true),
Err(err) => Err(err.to_string()),
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DisplayablePath {
pub path: PathBuf,
Expand Down
83 changes: 76 additions & 7 deletions src/gui/views/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use crate::core::theme::Theme;
use crate::core::uad_lists::{
load_debloat_lists, Opposite, PackageHashMap, PackageState, Removal, UadList, UadListState,
};
use crate::core::utils::fetch_packages;
use crate::core::utils::open_url;
use crate::core::utils::ANDROID_SERIAL;
use crate::core::utils::{
export_selection, fetch_packages, open_url, ANDROID_SERIAL, EXPORT_FILE_NAME,
};
use crate::gui::style;
use crate::gui::widgets::navigation_menu::ICONS;
use std::env;
Expand Down Expand Up @@ -58,6 +58,7 @@ pub struct List {
description: String,
selection_modal: bool,
error_modal: Option<String>,
export_modal: bool,
current_package_index: usize,
is_adb_satisfied: bool,
}
Expand Down Expand Up @@ -85,6 +86,8 @@ pub enum Message {
ADBSatisfied(bool),
UpdateFailed,
GoToUrl(PathBuf),
ExportSelection,
SelectionExported(Result<bool, String>),
}

pub struct SummaryEntry {
Expand Down Expand Up @@ -116,6 +119,7 @@ impl List {
Message::ModalHide => {
self.selection_modal = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we have so many modals, we might be better off with creating some sort of enum like self.focused_modal = Modal::Selection and then match upon that. Anyways, you don't have to implement it here. Let's do that in a separate commit / PR.

self.error_modal = None;
self.export_modal = false;
Command::none()
}
Message::ModalValidate => {
Expand Down Expand Up @@ -324,6 +328,17 @@ impl List {
open_url(url);
Command::none()
}
Message::ExportSelection => Command::perform(
export_selection(self.phone_packages[i_user].clone()),
Message::SelectionExported,
),
Message::SelectionExported(export) => {
match export {
Ok(_) => self.export_modal = true,
Err(err) => error!("Failed to export current selection: {:?}", err),
};
Command::none()
}
Message::Nothing => Command::none(),
}
}
Expand Down Expand Up @@ -493,10 +508,30 @@ impl List {
.padding([5, 10])
};

let action_row = row![Space::new(Length::Fill, Length::Shrink), review_selection]
.width(Length::Fill)
.spacing(10)
.align_items(Alignment::Center);
let export_selection = if !self.selected_packages.is_empty() {
button(text(format!(
"Export current selection ({})",
self.selected_packages.len()
)))
.on_press(Message::ExportSelection)
.padding([5, 10])
.style(style::Button::Primary)
} else {
button(text(format!(
"Export current selection ({})",
self.selected_packages.len()
)))
.padding([5, 10])
};

let action_row = row![
export_selection,
Space::new(Length::Fill, Length::Shrink),
review_selection
]
.width(Length::Fill)
.spacing(10)
.align_items(Alignment::Center);

let unavailable = container(
column![
Expand Down Expand Up @@ -545,6 +580,40 @@ impl List {
.on_blur(Message::ModalHide)
.into();
}

if self.export_modal {
let title = container(row![text("Success").size(24)].align_items(Alignment::Center))
.width(Length::Fill)
.style(style::Container::Frame)
.padding([10, 0, 10, 0])
.center_y()
.center_x();

let text_box = row![
text("Exported current selection into file.\nFile is exported in same directory where UAD-ng is located.").width(Length::Fill),
].padding(20);

let file_row = row![text(EXPORT_FILE_NAME).style(style::Text::Commentary)].padding(20);

let modal_btn_row = row![
Space::new(Length::Fill, Length::Shrink),
button(text("Close").width(Length::Shrink))
.width(Length::Shrink)
.on_press(Message::ModalHide),
Space::new(Length::Fill, Length::Shrink),
];

let ctn = container(column![title, text_box, file_row, modal_btn_row])
.height(Length::Shrink)
.width(500)
.padding(10)
.style(style::Container::Frame);

return Modal::new(content.padding(10), ctn)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might not need the explicit return here. Just a nit though.

.on_blur(Message::ModalHide)
.into();
}

if let Some(err) = &self.error_modal {
error_view(err, content).into()
} else {
Expand Down