mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-06 22:41:28 +03:00
fix: file transfer, auto start on reconnect (#13329)
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -92,6 +92,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
|
|||||||
gFFI.dialogManager.dismissAll();
|
gFFI.dialogManager.dismissAll();
|
||||||
WakelockPlus.disable();
|
WakelockPlus.disable();
|
||||||
});
|
});
|
||||||
|
model.jobController.clear();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1033,30 +1033,54 @@ class JobController {
|
|||||||
await bind.sessionCancelJob(sessionId: sessionId, actId: id);
|
await bind.sessionCancelJob(sessionId: sessionId, actId: id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadLastJob(Map<String, dynamic> evt) {
|
Future<void> loadLastJob(Map<String, dynamic> evt) async {
|
||||||
debugPrint("load last job: $evt");
|
debugPrint("load last job: $evt");
|
||||||
Map<String, dynamic> jobDetail = json.decode(evt['value']);
|
Map<String, dynamic> jobDetail = json.decode(evt['value']);
|
||||||
// int id = int.parse(jobDetail['id']);
|
|
||||||
String remote = jobDetail['remote'];
|
String remote = jobDetail['remote'];
|
||||||
String to = jobDetail['to'];
|
String to = jobDetail['to'];
|
||||||
bool showHidden = jobDetail['show_hidden'];
|
bool showHidden = jobDetail['show_hidden'];
|
||||||
int fileNum = jobDetail['file_num'];
|
int fileNum = jobDetail['file_num'];
|
||||||
bool isRemote = jobDetail['is_remote'];
|
bool isRemote = jobDetail['is_remote'];
|
||||||
final currJobId = JobController.jobID.next();
|
bool isAutoStart = jobDetail['auto_start'] == true;
|
||||||
String fileName = path.basename(isRemote ? remote : to);
|
int currJobId = -1;
|
||||||
var jobProgress = JobProgress()
|
if (isAutoStart) {
|
||||||
..type = JobType.transfer
|
// Ensure jobDetail['id'] exists and is an int
|
||||||
..fileName = fileName
|
if (jobDetail.containsKey('id') &&
|
||||||
..jobName = isRemote ? remote : to
|
jobDetail['id'] != null &&
|
||||||
..id = currJobId
|
jobDetail['id'] is int) {
|
||||||
..isRemoteToLocal = isRemote
|
currJobId = jobDetail['id'];
|
||||||
..fileNum = fileNum
|
}
|
||||||
..remote = remote
|
}
|
||||||
..to = to
|
if (currJobId < 0) {
|
||||||
..showHidden = showHidden
|
// If id is missing or invalid, disable auto-start and assign a new job id
|
||||||
..state = JobState.paused;
|
isAutoStart = false;
|
||||||
jobTable.add(jobProgress);
|
currJobId = JobController.jobID.next();
|
||||||
bind.sessionAddJob(
|
}
|
||||||
|
|
||||||
|
if (!isAutoStart) {
|
||||||
|
if (!(isDesktop || isWebDesktop)) {
|
||||||
|
// Don't add to job table if not auto start on mobile.
|
||||||
|
// Because mobile does not support job list view now.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to job table if not auto start on desktop.
|
||||||
|
String fileName = path.basename(isRemote ? remote : to);
|
||||||
|
final jobProgress = JobProgress()
|
||||||
|
..type = JobType.transfer
|
||||||
|
..fileName = fileName
|
||||||
|
..jobName = isRemote ? remote : to
|
||||||
|
..id = currJobId
|
||||||
|
..isRemoteToLocal = isRemote
|
||||||
|
..fileNum = fileNum
|
||||||
|
..remote = remote
|
||||||
|
..to = to
|
||||||
|
..showHidden = showHidden
|
||||||
|
..state = JobState.paused;
|
||||||
|
jobTable.add(jobProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
await bind.sessionAddJob(
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
isRemote: isRemote,
|
isRemote: isRemote,
|
||||||
includeHidden: showHidden,
|
includeHidden: showHidden,
|
||||||
@@ -1065,6 +1089,11 @@ class JobController {
|
|||||||
to: isRemote ? to : remote,
|
to: isRemote ? to : remote,
|
||||||
fileNum: fileNum,
|
fileNum: fileNum,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (isAutoStart) {
|
||||||
|
await bind.sessionResumeJob(
|
||||||
|
sessionId: sessionId, actId: currJobId, isRemote: isRemote);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void resumeJob(int jobId) {
|
void resumeJob(int jobId) {
|
||||||
@@ -1095,6 +1124,11 @@ class JobController {
|
|||||||
}
|
}
|
||||||
debugPrint("update folder files: $info");
|
debugPrint("update folder files: $info");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
jobTable.clear();
|
||||||
|
jobResultListener.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class JobResultListener<T> {
|
class JobResultListener<T> {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ use std::{
|
|||||||
os::raw::{c_char, c_int, c_void},
|
os::raw::{c_char, c_int, c_void},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||||
Arc, RwLock,
|
Arc, RwLock,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -756,7 +756,7 @@ impl InvokeUiSession for FlutterHandler {
|
|||||||
// unused in flutter
|
// unused in flutter
|
||||||
fn clear_all_jobs(&self) {}
|
fn clear_all_jobs(&self) {}
|
||||||
|
|
||||||
fn load_last_job(&self, _cnt: i32, job_json: &str) {
|
fn load_last_job(&self, _cnt: i32, job_json: &str, _auto_start: bool) {
|
||||||
self.push_event("load_last_job", &[("value", job_json)], &[]);
|
self.push_event("load_last_job", &[("value", job_json)], &[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1328,6 +1328,7 @@ pub fn session_add(
|
|||||||
server_keyboard_enabled: Arc::new(RwLock::new(true)),
|
server_keyboard_enabled: Arc::new(RwLock::new(true)),
|
||||||
server_file_transfer_enabled: Arc::new(RwLock::new(true)),
|
server_file_transfer_enabled: Arc::new(RwLock::new(true)),
|
||||||
server_clipboard_enabled: Arc::new(RwLock::new(true)),
|
server_clipboard_enabled: Arc::new(RwLock::new(true)),
|
||||||
|
reconnect_count: Arc::new(AtomicUsize::new(0)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class JobTable: Reactor.Component {
|
|||||||
self.timer(30ms, function() { self.update(); });
|
self.timer(30ms, function() { self.update(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
function addJob(id, path, to, file_num, show_hidden, is_remote) {
|
function addJob(id, path, to, file_num, show_hidden, is_remote, auto_start) {
|
||||||
var job = { type: "transfer",
|
var job = { type: "transfer",
|
||||||
id: id, path: path, to: to,
|
id: id, path: path, to: to,
|
||||||
include_hidden: show_hidden,
|
include_hidden: show_hidden,
|
||||||
@@ -146,6 +146,10 @@ class JobTable: Reactor.Component {
|
|||||||
this.job_map[id] = this.jobs[this.jobs.length - 1];
|
this.job_map[id] = this.jobs[this.jobs.length - 1];
|
||||||
handler.update_next_job_id(id + 1);
|
handler.update_next_job_id(id + 1);
|
||||||
handler.add_job(id, 0, path, to, file_num, show_hidden, is_remote);
|
handler.add_job(id, 0, path, to, file_num, show_hidden, is_remote);
|
||||||
|
if (auto_start) {
|
||||||
|
this.continueJob(id);
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
stdout.println(JSON.stringify(job));
|
stdout.println(JSON.stringify(job));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,7 +283,8 @@ class JobTable: Reactor.Component {
|
|||||||
if (!err) {
|
if (!err) {
|
||||||
handler.remove_dir(job.id, job.path, job.is_remote);
|
handler.remove_dir(job.id, job.path, job.is_remote);
|
||||||
refreshDir(job.is_remote);
|
refreshDir(job.is_remote);
|
||||||
if (is_remote) file_transfer.remote_folder_view.table.resetCurrent();
|
// Use the job's is_remote; local variable `is_remote` is undefined in this scope.
|
||||||
|
if (job.is_remote) file_transfer.remote_folder_view.table.resetCurrent();
|
||||||
else file_transfer.local_folder_view.table.resetCurrent();
|
else file_transfer.local_folder_view.table.resetCurrent();
|
||||||
}
|
}
|
||||||
} else if (!job.no_confirm) {
|
} else if (!job.no_confirm) {
|
||||||
@@ -697,9 +702,9 @@ handler.clearAllJobs = function() {
|
|||||||
file_transfer.job_table.clearAllJobs();
|
file_transfer.job_table.clearAllJobs();
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.addJob = function (id, path, to, file_num, show_hidden, is_remote) { // load last job
|
handler.addJob = function (id, path, to, file_num, show_hidden, is_remote, auto_start) { // load last job
|
||||||
// stdout.println("restore job: " + is_remote);
|
// stdout.println("restore job: " + is_remote);
|
||||||
file_transfer.job_table.addJob(id,path,to,file_num,show_hidden,is_remote);
|
file_transfer.job_table.addJob(id,path,to,file_num,show_hidden,is_remote,auto_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.updateTransferList = function () {
|
handler.updateTransferList = function () {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
sync::{Arc, Mutex, RwLock},
|
sync::{atomic::AtomicUsize, Arc, Mutex, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
use sciter::{
|
use sciter::{
|
||||||
@@ -199,7 +199,7 @@ impl InvokeUiSession for SciterHandler {
|
|||||||
self.call("clearAllJobs", &make_args!());
|
self.call("clearAllJobs", &make_args!());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_last_job(&self, cnt: i32, job_json: &str) {
|
fn load_last_job(&self, cnt: i32, job_json: &str, auto_start: bool) {
|
||||||
let job: Result<TransferJobMeta, serde_json::Error> = serde_json::from_str(job_json);
|
let job: Result<TransferJobMeta, serde_json::Error> = serde_json::from_str(job_json);
|
||||||
if let Ok(job) = job {
|
if let Ok(job) = job {
|
||||||
let path;
|
let path;
|
||||||
@@ -213,7 +213,15 @@ impl InvokeUiSession for SciterHandler {
|
|||||||
}
|
}
|
||||||
self.call(
|
self.call(
|
||||||
"addJob",
|
"addJob",
|
||||||
&make_args!(cnt, path, to, job.file_num, job.show_hidden, job.is_remote),
|
&make_args!(
|
||||||
|
cnt,
|
||||||
|
path,
|
||||||
|
to,
|
||||||
|
job.file_num,
|
||||||
|
job.show_hidden,
|
||||||
|
job.is_remote,
|
||||||
|
auto_start
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -570,6 +578,7 @@ impl SciterSession {
|
|||||||
server_keyboard_enabled: Arc::new(RwLock::new(true)),
|
server_keyboard_enabled: Arc::new(RwLock::new(true)),
|
||||||
server_file_transfer_enabled: Arc::new(RwLock::new(true)),
|
server_file_transfer_enabled: Arc::new(RwLock::new(true)),
|
||||||
server_clipboard_enabled: Arc::new(RwLock::new(true)),
|
server_clipboard_enabled: Arc::new(RwLock::new(true)),
|
||||||
|
reconnect_count: Arc::new(AtomicUsize::new(0)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ use std::{
|
|||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
sync::{Arc, Mutex, RwLock},
|
sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc, Mutex, RwLock,
|
||||||
|
},
|
||||||
time::SystemTime,
|
time::SystemTime,
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@@ -61,6 +64,9 @@ pub struct Session<T: InvokeUiSession> {
|
|||||||
pub last_change_display: Arc<Mutex<ChangeDisplayRecord>>,
|
pub last_change_display: Arc<Mutex<ChangeDisplayRecord>>,
|
||||||
pub connection_round_state: Arc<Mutex<ConnectionRoundState>>,
|
pub connection_round_state: Arc<Mutex<ConnectionRoundState>>,
|
||||||
pub printer_names: Arc<RwLock<HashMap<i32, String>>>,
|
pub printer_names: Arc<RwLock<HashMap<i32, String>>>,
|
||||||
|
// Indicate whether the session is reconnected.
|
||||||
|
// Used to auto start file transfer after reconnection.
|
||||||
|
pub reconnect_count: Arc<AtomicUsize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -1272,6 +1278,7 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
self.lc.write().unwrap().force_relay = true;
|
self.lc.write().unwrap().force_relay = true;
|
||||||
}
|
}
|
||||||
self.lc.write().unwrap().peer_info = None;
|
self.lc.write().unwrap().peer_info = None;
|
||||||
|
self.reconnect_count.fetch_add(1, Ordering::SeqCst);
|
||||||
let mut lock = self.thread.lock().unwrap();
|
let mut lock = self.thread.lock().unwrap();
|
||||||
// No need to join the previous thread, because it will exit automatically.
|
// No need to join the previous thread, because it will exit automatically.
|
||||||
// And the previous thread will not change important states.
|
// And the previous thread will not change important states.
|
||||||
@@ -1372,6 +1379,24 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
self.send(Data::Close);
|
self.send(Data::Close);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_auto_start_job_str(is_reconnected: bool, job_str: &str) -> Option<String> {
|
||||||
|
if is_reconnected {
|
||||||
|
let job_str = job_str.trim();
|
||||||
|
if let Some(stripped) = job_str.strip_suffix('}') {
|
||||||
|
format!(r#"{},"auto_start": true}}"#, stripped).into()
|
||||||
|
} else {
|
||||||
|
// unreachable in normal cases
|
||||||
|
log::warn!(
|
||||||
|
"The last character is not '}}': {}, auto start is ignored on flutter",
|
||||||
|
job_str
|
||||||
|
);
|
||||||
|
Some(job_str.to_owned())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_last_jobs(&self) {
|
pub fn load_last_jobs(&self) {
|
||||||
self.clear_all_jobs();
|
self.clear_all_jobs();
|
||||||
let pc = self.load_config();
|
let pc = self.load_config();
|
||||||
@@ -1379,18 +1404,32 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
// no last jobs
|
// no last jobs
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let reconnect_count_thr = if cfg!(feature = "flutter") { 0 } else { 1 };
|
||||||
|
let is_reconnected = self.reconnect_count.load(Ordering::SeqCst) > reconnect_count_thr;
|
||||||
// TODO: can add a confirm dialog
|
// TODO: can add a confirm dialog
|
||||||
let mut cnt = 1;
|
let mut cnt = 1;
|
||||||
for job_str in pc.transfer.read_jobs.iter() {
|
for job_str in pc.transfer.read_jobs.iter() {
|
||||||
if !job_str.is_empty() {
|
if !job_str.is_empty() {
|
||||||
self.load_last_job(cnt, job_str);
|
self.load_last_job(
|
||||||
|
cnt,
|
||||||
|
Self::try_auto_start_job_str(is_reconnected, job_str)
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or(job_str),
|
||||||
|
is_reconnected,
|
||||||
|
);
|
||||||
cnt += 1;
|
cnt += 1;
|
||||||
log::info!("restore read_job: {:?}", job_str);
|
log::info!("restore read_job: {:?}", job_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for job_str in pc.transfer.write_jobs.iter() {
|
for job_str in pc.transfer.write_jobs.iter() {
|
||||||
if !job_str.is_empty() {
|
if !job_str.is_empty() {
|
||||||
self.load_last_job(cnt, job_str);
|
self.load_last_job(
|
||||||
|
cnt,
|
||||||
|
Self::try_auto_start_job_str(is_reconnected, job_str)
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or(job_str),
|
||||||
|
is_reconnected,
|
||||||
|
);
|
||||||
cnt += 1;
|
cnt += 1;
|
||||||
log::info!("restore write_job: {:?}", job_str);
|
log::info!("restore write_job: {:?}", job_str);
|
||||||
}
|
}
|
||||||
@@ -1623,7 +1662,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
|
|||||||
fn clear_all_jobs(&self);
|
fn clear_all_jobs(&self);
|
||||||
fn new_message(&self, msg: String);
|
fn new_message(&self, msg: String);
|
||||||
fn update_transfer_list(&self);
|
fn update_transfer_list(&self);
|
||||||
fn load_last_job(&self, cnt: i32, job_json: &str);
|
fn load_last_job(&self, cnt: i32, job_json: &str, auto_start: bool);
|
||||||
fn update_folder_files(
|
fn update_folder_files(
|
||||||
&self,
|
&self,
|
||||||
id: i32,
|
id: i32,
|
||||||
|
|||||||
Reference in New Issue
Block a user