feat(ui): implement interactive add-remote background flow

This commit is contained in:
Black_Raven committed 2026-08-13 18:15:49 +03:00
1 parent 9fb0e7fb19
commit 65939bd501
1 file changed
+136 -1
+136 -1
View File
@@ -1,4 +1,5 @@
use super::*; use super::*;
use crate::operations::remotes::ConfigStep;
use crate::operations::{self, CopyOptions, DeleteOptions, MoveOptions}; use crate::operations::{self, CopyOptions, DeleteOptions, MoveOptions};
use crate::rclone_install::RcloneApp; use crate::rclone_install::RcloneApp;
use eframe::egui; use eframe::egui;
@@ -88,6 +89,13 @@ impl RcloneUI {
local_browser_files: Vec::new(), local_browser_files: Vec::new(),
is_move_mode: false, is_move_mode: false,
settings: AppSettings::default(), settings: AppSettings::default(),
show_add_remote_dialog: false,
new_remote_name: String::new(),
new_remote_type: "drive".to_string(),
add_remote_step: AddRemoteStep::Form,
add_remote_state: None,
add_remote_answer: String::new(),
add_remote_status: String::new(),
active_task_count: 0, active_task_count: 0,
operation_tx: op_tx, operation_tx: op_tx,
operation_rx: op_rx, operation_rx: op_rx,
@@ -173,8 +181,109 @@ impl RcloneUI {
} }
} }
pub fn load_remotes(&mut self) { /// Фоновый цикл создания remote: rclone задает вопросы (--non-interactive),
/// на вопрос config_is_local автоматически отвечаем "true" — открывается
/// браузер для авторизации; остальные вопросы передаются в UI.
fn run_add_remote_loop(
rclone: Arc<RcloneApp>,
name: String,
rtype: String,
initial_state: Option<String>,
initial_answer: Option<String>,
op_id: u32,
tx: Sender<OperationResult>,
) {
std::thread::spawn(move || {
let mut state = initial_state;
let mut answer = initial_answer;
loop {
let feed_tx = tx.clone();
let step = operations::remotes::config_create_step(
&rclone,
&name,
&rtype,
state.as_deref(),
answer.as_deref(),
&mut |line: &str| {
if !line.is_empty() {
let _ = feed_tx.send(OperationResult::ProgressUpdate(
op_id,
0.0,
line.to_string(),
));
}
},
);
match step {
Ok(ConfigStep::Done) => {
log::info!("Remote {} успешно создан", name);
let _ = tx.send(match operations::remotes::list(&rclone) {
Ok(list) => OperationResult::RemoteAdded(op_id, list),
Err(e) => OperationResult::Failure(
op_id,
format!("Облако создано, но список обновить не удалось: {}", e),
),
});
return;
}
Ok(ConfigStep::Question { state: s, question: q }) => {
if q.name == "config_is_local" {
log::info!("Открытие браузера для авторизации {}", rtype);
state = Some(s);
answer = Some("true".to_string());
let _ = tx.send(OperationResult::ProgressUpdate(
op_id,
0.0,
"Открыт браузер для авторизации. Ожидание входа...".into(),
));
continue;
}
log::debug!("rclone задал вопрос: {}", q.name);
let _ = tx.send(OperationResult::ConfigQuestion(op_id, s, q));
return;
}
Err(e) => {
log::error!("Ошибка создания remote {}: {}", name, e);
let _ = tx.send(OperationResult::Failure(op_id, e));
return;
}
}
}
});
}
pub fn start_add_remote(&mut self) {
let name = self.new_remote_name.trim().to_string();
let rtype = self.new_remote_type.trim().to_string();
if name.is_empty() || rtype.is_empty() {
log::warn!("Не удалось создать remote: пустое имя или тип");
return;
}
if let Some(rclone) = self.rclone.clone() { if let Some(rclone) = self.rclone.clone() {
log::info!("Создание нового remote: {} типа {}", name, rtype);
self.active_task_count += 1;
self.add_remote_step = AddRemoteStep::Busy;
self.add_remote_status = "Создание облака...".into();
let op_id = self.add_operation(format!("Создание облака {}", name));
Self::run_add_remote_loop(rclone, name, rtype, None, None, op_id, self.operation_tx.clone());
}
}
pub fn answer_add_remote(&mut self) {
if let (Some(rclone), Some(state)) = (self.rclone.clone(), self.add_remote_state.clone()) {
let answer = self.add_remote_answer.clone();
let name = self.new_remote_name.clone();
let rtype = self.new_remote_type.clone();
log::info!("Ответ на вопрос rclone: {}", answer);
self.active_task_count += 1;
self.add_remote_step = AddRemoteStep::Busy;
self.add_remote_status = "Отправка ответа...".into();
let op_id = self.add_operation(format!("Создание облака {}", name));
Self::run_add_remote_loop(rclone, name, rtype, Some(state), Some(answer), op_id, self.operation_tx.clone());
}
}
pub fn load_remotes(&mut self) { if let Some(rclone) = self.rclone.clone() {
log::info!("Загрузка списка удаленных хранилищ"); log::info!("Загрузка списка удаленных хранилищ");
self.active_task_count += 1; self.active_task_count += 1;
let tx = self.operation_tx.clone(); let tx = self.operation_tx.clone();
@@ -229,6 +338,9 @@ impl RcloneUI {
log::error!("Операция {} завершена с ошибкой: {}", op_id, e); log::error!("Операция {} завершена с ошибкой: {}", op_id, e);
self.active_task_count = self.active_task_count.saturating_sub(1); self.active_task_count = self.active_task_count.saturating_sub(1);
self.active_operations.retain(|op| op.id != op_id); self.active_operations.retain(|op| op.id != op_id);
if matches!(self.add_remote_step, AddRemoteStep::Busy) {
self.add_remote_step = AddRemoteStep::Form;
}
self.error_message = Some(e); self.error_message = Some(e);
} }
OperationResult::FileList(files) => { OperationResult::FileList(files) => {
@@ -243,12 +355,35 @@ impl RcloneUI {
self.remote_list = remotes; self.remote_list = remotes;
self.active_task_count = self.active_task_count.saturating_sub(1); self.active_task_count = self.active_task_count.saturating_sub(1);
} }
OperationResult::RemoteAdded(op_id, remotes) => {
let count = remotes.len();
log::info!("Облако добавлено, список обновлен: {} элементов", count);
self.remote_list = remotes;
self.active_task_count = self.active_task_count.saturating_sub(1);
self.active_operations.retain(|op| op.id != op_id);
self.show_add_remote_dialog = false;
self.add_remote_step = AddRemoteStep::Form;
self.add_remote_state = None;
self.new_remote_name.clear();
self.add_remote_answer.clear();
}
OperationResult::ConfigQuestion(op_id, state, question) => {
log::debug!("Получен вопрос rclone: {}", question.name);
self.active_task_count = self.active_task_count.saturating_sub(1);
self.active_operations.retain(|op| op.id != op_id);
self.add_remote_state = Some(state);
self.add_remote_answer = question.default.clone();
self.add_remote_step = AddRemoteStep::Question(question);
}
OperationResult::ProgressUpdate(op_id, progress, status) => { OperationResult::ProgressUpdate(op_id, progress, status) => {
if let Some(op) = self.active_operations.iter_mut().find(|op| op.id == op_id) { if let Some(op) = self.active_operations.iter_mut().find(|op| op.id == op_id) {
op.progress = progress; op.progress = progress;
op.status = status.clone(); op.status = status.clone();
log::trace!("Операция {}: прогресс {:.1}%, статус: {}", op_id, progress * 100.0, status); log::trace!("Операция {}: прогресс {:.1}%, статус: {}", op_id, progress * 100.0, status);
} }
if matches!(&self.add_remote_step, AddRemoteStep::Busy) {
self.add_remote_status = status;
}
} }
} }
} }