Create empty dir on send files in local (#9993)
* feat: Add empty dirs on sendfiles * Update connection.rs --------- Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
parent
b64f6271e2
commit
314c93b210
@ -100,6 +100,10 @@ class FileModel {
|
|||||||
fileFetcher.tryCompleteTask(evt['value'], evt['is_local']);
|
fileFetcher.tryCompleteTask(evt['value'], evt['is_local']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void receiveEmptyDirs(Map<String, dynamic> evt) {
|
||||||
|
fileFetcher.tryCompleteEmptyDirsTask(evt['value'], evt['is_local']);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> postOverrideFileConfirm(Map<String, dynamic> evt) async {
|
Future<void> postOverrideFileConfirm(Map<String, dynamic> evt) async {
|
||||||
evtLoop.pushEvent(
|
evtLoop.pushEvent(
|
||||||
_FileDialogEvent(WeakReference(this), FileDialogType.overwrite, evt));
|
_FileDialogEvent(WeakReference(this), FileDialogType.overwrite, evt));
|
||||||
@ -470,7 +474,8 @@ class FileController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// sendFiles from current side (FileController.isLocal) to other side (SelectedItems).
|
/// sendFiles from current side (FileController.isLocal) to other side (SelectedItems).
|
||||||
void sendFiles(SelectedItems items, DirectoryData otherSideData) {
|
Future<void> sendFiles(
|
||||||
|
SelectedItems items, DirectoryData otherSideData) async {
|
||||||
/// ignore wrong items side status
|
/// ignore wrong items side status
|
||||||
if (items.isLocal != isLocal) {
|
if (items.isLocal != isLocal) {
|
||||||
return;
|
return;
|
||||||
@ -496,6 +501,42 @@ class FileController {
|
|||||||
debugPrint(
|
debugPrint(
|
||||||
"path: ${from.path}, toPath: $toPath, to: ${PathUtil.join(toPath, from.name, isWindows)}");
|
"path: ${from.path}, toPath: $toPath, to: ${PathUtil.join(toPath, from.name, isWindows)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isLocal &&
|
||||||
|
versionCmp(rootState.target!.ffiModel.pi.version, '1.3.3') < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Entry> entrys = items.items.toList();
|
||||||
|
var isRemote = isLocal == true ? true : false;
|
||||||
|
|
||||||
|
await Future.forEach(entrys, (Entry item) async {
|
||||||
|
if (!item.isDirectory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<String> paths = [];
|
||||||
|
|
||||||
|
final emptyDirs =
|
||||||
|
await fileFetcher.readEmptyDirs(item.path, isLocal, showHidden);
|
||||||
|
|
||||||
|
if (emptyDirs.isEmpty) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
for (var dir in emptyDirs) {
|
||||||
|
paths.add(dir.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final dirs = paths.map((path) {
|
||||||
|
return PathUtil.getOtherSidePath(directory.value.path, path,
|
||||||
|
options.value.isWindows, toPath, isWindows);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var dir in dirs) {
|
||||||
|
createDirWithRemote(dir, isRemote);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _removeCheckboxRemember = false;
|
bool _removeCheckboxRemember = false;
|
||||||
@ -689,12 +730,16 @@ class FileController {
|
|||||||
sessionId: sessionId, actId: actId, path: path, isRemote: !isLocal);
|
sessionId: sessionId, actId: actId, path: path, isRemote: !isLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createDir(String path) async {
|
Future<void> createDirWithRemote(String path, bool isRemote) async {
|
||||||
bind.sessionCreateDir(
|
bind.sessionCreateDir(
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
actId: JobController.jobID.next(),
|
actId: JobController.jobID.next(),
|
||||||
path: path,
|
path: path,
|
||||||
isRemote: !isLocal);
|
isRemote: isRemote);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> createDir(String path) async {
|
||||||
|
await createDirWithRemote(path, !isLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> renameAction(Entry item, bool isLocal) async {
|
Future<void> renameAction(Entry item, bool isLocal) async {
|
||||||
@ -1064,6 +1109,7 @@ class JobResultListener<T> {
|
|||||||
class FileFetcher {
|
class FileFetcher {
|
||||||
// Map<String,Completer<FileDirectory>> localTasks = {}; // now we only use read local dir sync
|
// Map<String,Completer<FileDirectory>> localTasks = {}; // now we only use read local dir sync
|
||||||
Map<String, Completer<FileDirectory>> remoteTasks = {};
|
Map<String, Completer<FileDirectory>> remoteTasks = {};
|
||||||
|
Map<String, Completer<List<FileDirectory>>> remoteEmptyDirsTasks = {};
|
||||||
Map<int, Completer<FileDirectory>> readRecursiveTasks = {};
|
Map<int, Completer<FileDirectory>> readRecursiveTasks = {};
|
||||||
|
|
||||||
final GetSessionID getSessionID;
|
final GetSessionID getSessionID;
|
||||||
@ -1071,6 +1117,24 @@ class FileFetcher {
|
|||||||
|
|
||||||
FileFetcher(this.getSessionID);
|
FileFetcher(this.getSessionID);
|
||||||
|
|
||||||
|
Future<List<FileDirectory>> registerReadEmptyDirsTask(
|
||||||
|
bool isLocal, String path) {
|
||||||
|
// final jobs = isLocal?localJobs:remoteJobs; // maybe we will use read local dir async later
|
||||||
|
final tasks = remoteEmptyDirsTasks; // bypass now
|
||||||
|
if (tasks.containsKey(path)) {
|
||||||
|
throw "Failed to registerReadEmptyDirsTask, already have same read job";
|
||||||
|
}
|
||||||
|
final c = Completer<List<FileDirectory>>();
|
||||||
|
tasks[path] = c;
|
||||||
|
|
||||||
|
Timer(Duration(seconds: 2), () {
|
||||||
|
tasks.remove(path);
|
||||||
|
if (c.isCompleted) return;
|
||||||
|
c.completeError("Failed to read empty dirs, timeout");
|
||||||
|
});
|
||||||
|
return c.future;
|
||||||
|
}
|
||||||
|
|
||||||
Future<FileDirectory> registerReadTask(bool isLocal, String path) {
|
Future<FileDirectory> registerReadTask(bool isLocal, String path) {
|
||||||
// final jobs = isLocal?localJobs:remoteJobs; // maybe we will use read local dir async later
|
// final jobs = isLocal?localJobs:remoteJobs; // maybe we will use read local dir async later
|
||||||
final tasks = remoteTasks; // bypass now
|
final tasks = remoteTasks; // bypass now
|
||||||
@ -1104,6 +1168,25 @@ class FileFetcher {
|
|||||||
return c.future;
|
return c.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tryCompleteEmptyDirsTask(String? msg, String? isLocalStr) {
|
||||||
|
if (msg == null || isLocalStr == null) return;
|
||||||
|
late final Map<String, Completer<List<FileDirectory>>> tasks;
|
||||||
|
try {
|
||||||
|
final map = jsonDecode(msg);
|
||||||
|
final String path = map["path"];
|
||||||
|
final List<dynamic> fdJsons = map["empty_dirs"];
|
||||||
|
final List<FileDirectory> fds =
|
||||||
|
fdJsons.map((fdJson) => FileDirectory.fromJson(fdJson)).toList();
|
||||||
|
|
||||||
|
tasks = remoteEmptyDirsTasks;
|
||||||
|
final completer = tasks.remove(path);
|
||||||
|
|
||||||
|
completer?.complete(fds);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("tryCompleteJob err: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tryCompleteTask(String? msg, String? isLocalStr) {
|
tryCompleteTask(String? msg, String? isLocalStr) {
|
||||||
if (msg == null || isLocalStr == null) return;
|
if (msg == null || isLocalStr == null) return;
|
||||||
late final Map<Object, Completer<FileDirectory>> tasks;
|
late final Map<Object, Completer<FileDirectory>> tasks;
|
||||||
@ -1127,6 +1210,28 @@ class FileFetcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<FileDirectory>> readEmptyDirs(
|
||||||
|
String path, bool isLocal, bool showHidden) async {
|
||||||
|
try {
|
||||||
|
if (isLocal) {
|
||||||
|
final res = await bind.sessionReadLocalEmptyDirsRecursiveSync(
|
||||||
|
sessionId: sessionId, path: path, includeHidden: showHidden);
|
||||||
|
|
||||||
|
final List<dynamic> fdJsons = jsonDecode(res);
|
||||||
|
|
||||||
|
final List<FileDirectory> fds =
|
||||||
|
fdJsons.map((fdJson) => FileDirectory.fromJson(fdJson)).toList();
|
||||||
|
return fds;
|
||||||
|
} else {
|
||||||
|
await bind.sessionReadRemoteEmptyDirsRecursiveSync(
|
||||||
|
sessionId: sessionId, path: path, includeHidden: showHidden);
|
||||||
|
return registerReadEmptyDirsTask(isLocal, path);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return Future.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<FileDirectory> fetchDirectory(
|
Future<FileDirectory> fetchDirectory(
|
||||||
String path, bool isLocal, bool showHidden) async {
|
String path, bool isLocal, bool showHidden) async {
|
||||||
try {
|
try {
|
||||||
@ -1373,6 +1478,24 @@ class PathUtil {
|
|||||||
static final windowsContext = path.Context(style: path.Style.windows);
|
static final windowsContext = path.Context(style: path.Style.windows);
|
||||||
static final posixContext = path.Context(style: path.Style.posix);
|
static final posixContext = path.Context(style: path.Style.posix);
|
||||||
|
|
||||||
|
static String getOtherSidePath(String mainRootPath, String mainPath,
|
||||||
|
bool isMainWindows, String otherRootPath, bool isOtherWindows) {
|
||||||
|
final mainPathUtil = isMainWindows ? windowsContext : posixContext;
|
||||||
|
final relativePath = mainPathUtil.relative(mainPath, from: mainRootPath);
|
||||||
|
|
||||||
|
final names = mainPathUtil.split(relativePath);
|
||||||
|
|
||||||
|
final otherPathUtil = isOtherWindows ? windowsContext : posixContext;
|
||||||
|
|
||||||
|
String path = otherRootPath;
|
||||||
|
|
||||||
|
for (var name in names) {
|
||||||
|
path = otherPathUtil.join(path, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
static String join(String path1, String path2, bool isWindows) {
|
static String join(String path1, String path2, bool isWindows) {
|
||||||
final pathUtil = isWindows ? windowsContext : posixContext;
|
final pathUtil = isWindows ? windowsContext : posixContext;
|
||||||
return pathUtil.join(path1, path2);
|
return pathUtil.join(path1, path2);
|
||||||
|
@ -309,6 +309,8 @@ class FfiModel with ChangeNotifier {
|
|||||||
.receive(int.parse(evt['id'] as String), evt['text'] ?? '');
|
.receive(int.parse(evt['id'] as String), evt['text'] ?? '');
|
||||||
} else if (name == 'file_dir') {
|
} else if (name == 'file_dir') {
|
||||||
parent.target?.fileModel.receiveFileDir(evt);
|
parent.target?.fileModel.receiveFileDir(evt);
|
||||||
|
} else if (name == 'empty_dirs') {
|
||||||
|
parent.target?.fileModel.receiveEmptyDirs(evt);
|
||||||
} else if (name == 'job_progress') {
|
} else if (name == 'job_progress') {
|
||||||
parent.target?.fileModel.jobController.tryUpdateJobProgress(evt);
|
parent.target?.fileModel.jobController.tryUpdateJobProgress(evt);
|
||||||
} else if (name == 'job_done') {
|
} else if (name == 'job_done') {
|
||||||
|
@ -368,6 +368,16 @@ message ReadDir {
|
|||||||
bool include_hidden = 2;
|
bool include_hidden = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ReadEmptyDirs {
|
||||||
|
string path = 1;
|
||||||
|
bool include_hidden = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ReadEmptyDirsResponse {
|
||||||
|
string path = 1;
|
||||||
|
repeated FileDirectory empty_dirs = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message ReadAllFiles {
|
message ReadAllFiles {
|
||||||
int32 id = 1;
|
int32 id = 1;
|
||||||
string path = 2;
|
string path = 2;
|
||||||
@ -392,6 +402,7 @@ message FileAction {
|
|||||||
FileTransferCancel cancel = 8;
|
FileTransferCancel cancel = 8;
|
||||||
FileTransferSendConfirmRequest send_confirm = 9;
|
FileTransferSendConfirmRequest send_confirm = 9;
|
||||||
FileRename rename = 10;
|
FileRename rename = 10;
|
||||||
|
ReadEmptyDirs read_empty_dirs = 11;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,6 +415,7 @@ message FileResponse {
|
|||||||
FileTransferError error = 3;
|
FileTransferError error = 3;
|
||||||
FileTransferDone done = 4;
|
FileTransferDone done = 4;
|
||||||
FileTransferDigest digest = 5;
|
FileTransferDigest digest = 5;
|
||||||
|
ReadEmptyDirsResponse empty_dirs = 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +185,51 @@ pub fn get_recursive_files(path: &str, include_hidden: bool) -> ResultType<Vec<F
|
|||||||
read_dir_recursive(&get_path(path), &get_path(""), include_hidden)
|
read_dir_recursive(&get_path(path), &get_path(""), include_hidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_empty_dirs_recursive(
|
||||||
|
path: &PathBuf,
|
||||||
|
prefix: &Path,
|
||||||
|
include_hidden: bool,
|
||||||
|
) -> ResultType<Vec<FileDirectory>> {
|
||||||
|
let mut dirs = Vec::new();
|
||||||
|
if path.is_dir() {
|
||||||
|
// to-do: symbol link handling, cp the link rather than the content
|
||||||
|
// to-do: file mode, for unix
|
||||||
|
let fd = read_dir(path, include_hidden)?;
|
||||||
|
if fd.entries.is_empty() {
|
||||||
|
dirs.push(fd);
|
||||||
|
} else {
|
||||||
|
for entry in fd.entries.iter() {
|
||||||
|
match entry.entry_type.enum_value() {
|
||||||
|
Ok(FileType::Dir) => {
|
||||||
|
if let Ok(mut tmp) = read_empty_dirs_recursive(
|
||||||
|
&path.join(&entry.name),
|
||||||
|
&prefix.join(&entry.name),
|
||||||
|
include_hidden,
|
||||||
|
) {
|
||||||
|
for entry in tmp.drain(0..) {
|
||||||
|
dirs.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(dirs)
|
||||||
|
} else if path.is_file() {
|
||||||
|
Ok(dirs)
|
||||||
|
} else {
|
||||||
|
bail!("Not exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_empty_dirs_recursive(
|
||||||
|
path: &str,
|
||||||
|
include_hidden: bool,
|
||||||
|
) -> ResultType<Vec<FileDirectory>> {
|
||||||
|
read_empty_dirs_recursive(&get_path(path), &get_path(""), include_hidden)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_file_exists(file_path: &str) -> bool {
|
pub fn is_file_exists(file_path: &str) -> bool {
|
||||||
return Path::new(file_path).exists();
|
return Path::new(file_path).exists();
|
||||||
|
@ -43,6 +43,18 @@ pub trait FileManager: Interface {
|
|||||||
self.send(Data::CancelJob(id));
|
self.send(Data::CancelJob(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_empty_dirs(&self, path: String, include_hidden: bool) {
|
||||||
|
let mut msg_out = Message::new();
|
||||||
|
let mut file_action = FileAction::new();
|
||||||
|
file_action.set_read_empty_dirs(ReadEmptyDirs {
|
||||||
|
path,
|
||||||
|
include_hidden,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
msg_out.set_file_action(file_action);
|
||||||
|
self.send(Data::Message(msg_out));
|
||||||
|
}
|
||||||
|
|
||||||
fn read_remote_dir(&self, path: String, include_hidden: bool) {
|
fn read_remote_dir(&self, path: String, include_hidden: bool) {
|
||||||
let mut msg_out = Message::new();
|
let mut msg_out = Message::new();
|
||||||
let mut file_action = FileAction::new();
|
let mut file_action = FileAction::new();
|
||||||
|
@ -1299,6 +1299,9 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
}
|
}
|
||||||
Some(message::Union::FileResponse(fr)) => {
|
Some(message::Union::FileResponse(fr)) => {
|
||||||
match fr.union {
|
match fr.union {
|
||||||
|
Some(file_response::Union::EmptyDirs(res)) => {
|
||||||
|
self.handler.update_empty_dirs(res);
|
||||||
|
}
|
||||||
Some(file_response::Union::Dir(fd)) => {
|
Some(file_response::Union::Dir(fd)) => {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let entries = fd.entries.to_vec();
|
let entries = fd.entries.to_vec();
|
||||||
|
@ -5,7 +5,7 @@ use std::{
|
|||||||
task::Poll,
|
task::Poll,
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde_json::Value;
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
@ -1051,6 +1051,11 @@ pub fn get_supported_keyboard_modes(version: i64, peer_platform: &str) -> Vec<Ke
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_fd_to_json(id: i32, path: String, entries: &Vec<FileEntry>) -> String {
|
pub fn make_fd_to_json(id: i32, path: String, entries: &Vec<FileEntry>) -> String {
|
||||||
|
let fd_json = _make_fd_to_json(id, path, entries);
|
||||||
|
serde_json::to_string(&fd_json).unwrap_or("".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _make_fd_to_json(id: i32, path: String, entries: &Vec<FileEntry>) -> Map<String, Value> {
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
let mut fd_json = serde_json::Map::new();
|
let mut fd_json = serde_json::Map::new();
|
||||||
fd_json.insert("id".into(), json!(id));
|
fd_json.insert("id".into(), json!(id));
|
||||||
@ -1066,7 +1071,33 @@ pub fn make_fd_to_json(id: i32, path: String, entries: &Vec<FileEntry>) -> Strin
|
|||||||
entries_out.push(entry_map);
|
entries_out.push(entry_map);
|
||||||
}
|
}
|
||||||
fd_json.insert("entries".into(), json!(entries_out));
|
fd_json.insert("entries".into(), json!(entries_out));
|
||||||
serde_json::to_string(&fd_json).unwrap_or("".into())
|
fd_json
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_vec_fd_to_json(fds: &[FileDirectory]) -> String {
|
||||||
|
let mut fd_jsons = vec![];
|
||||||
|
|
||||||
|
for fd in fds.iter() {
|
||||||
|
let fd_json = _make_fd_to_json(fd.id, fd.path.clone(), &fd.entries);
|
||||||
|
fd_jsons.push(fd_json);
|
||||||
|
}
|
||||||
|
|
||||||
|
serde_json::to_string(&fd_jsons).unwrap_or("".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_empty_dirs_response_to_json(res: &ReadEmptyDirsResponse) -> String {
|
||||||
|
let mut map: Map<String, Value> = serde_json::Map::new();
|
||||||
|
map.insert("path".into(), json!(res.path));
|
||||||
|
|
||||||
|
let mut fd_jsons = vec![];
|
||||||
|
|
||||||
|
for fd in res.empty_dirs.iter() {
|
||||||
|
let fd_json = _make_fd_to_json(fd.id, fd.path.clone(), &fd.entries);
|
||||||
|
fd_jsons.push(fd_json);
|
||||||
|
}
|
||||||
|
map.insert("empty_dirs".into(), fd_jsons.into());
|
||||||
|
|
||||||
|
serde_json::to_string(&map).unwrap_or("".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The function to handle the url scheme sent by the system.
|
/// The function to handle the url scheme sent by the system.
|
||||||
|
@ -726,6 +726,20 @@ impl InvokeUiSession for FlutterHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_empty_dirs(&self, res: ReadEmptyDirsResponse) {
|
||||||
|
self.push_event(
|
||||||
|
"empty_dirs",
|
||||||
|
&[
|
||||||
|
("is_local", "false"),
|
||||||
|
(
|
||||||
|
"value",
|
||||||
|
&crate::common::make_empty_dirs_response_to_json(&res),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// unused in flutter
|
// unused in flutter
|
||||||
fn update_transfer_list(&self) {}
|
fn update_transfer_list(&self) {}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
client::file_trait::FileManager,
|
client::file_trait::FileManager,
|
||||||
common::make_fd_to_json,
|
common::{make_fd_to_json, make_vec_fd_to_json},
|
||||||
flutter::{
|
flutter::{
|
||||||
self, session_add, session_add_existed, session_start_, sessions, try_sync_peer_option,
|
self, session_add, session_add_existed, session_start_, sessions, try_sync_peer_option,
|
||||||
},
|
},
|
||||||
@ -682,6 +682,27 @@ pub fn session_read_local_dir_sync(
|
|||||||
"".to_string()
|
"".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn session_read_local_empty_dirs_recursive_sync(
|
||||||
|
_session_id: SessionID,
|
||||||
|
path: String,
|
||||||
|
include_hidden: bool,
|
||||||
|
) -> String {
|
||||||
|
if let Ok(fds) = fs::get_empty_dirs_recursive(&path, include_hidden) {
|
||||||
|
return make_vec_fd_to_json(&fds);
|
||||||
|
}
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn session_read_remote_empty_dirs_recursive_sync(
|
||||||
|
session_id: SessionID,
|
||||||
|
path: String,
|
||||||
|
include_hidden: bool,
|
||||||
|
) {
|
||||||
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
|
session.read_empty_dirs(path, include_hidden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_get_platform(session_id: SessionID, is_remote: bool) -> String {
|
pub fn session_get_platform(session_id: SessionID, is_remote: bool) -> String {
|
||||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
return session.get_platform(is_remote);
|
return session.get_platform(is_remote);
|
||||||
|
@ -45,6 +45,10 @@ pub static EXIT_RECV_CLOSE: AtomicBool = AtomicBool::new(true);
|
|||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
#[serde(tag = "t", content = "c")]
|
#[serde(tag = "t", content = "c")]
|
||||||
pub enum FS {
|
pub enum FS {
|
||||||
|
ReadEmptyDirs {
|
||||||
|
dir: String,
|
||||||
|
include_hidden: bool,
|
||||||
|
},
|
||||||
ReadDir {
|
ReadDir {
|
||||||
dir: String,
|
dir: String,
|
||||||
include_hidden: bool,
|
include_hidden: bool,
|
||||||
|
@ -2149,6 +2149,9 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
match fa.union {
|
match fa.union {
|
||||||
|
Some(file_action::Union::ReadEmptyDirs(rd)) => {
|
||||||
|
self.read_empty_dirs(&rd.path, rd.include_hidden);
|
||||||
|
}
|
||||||
Some(file_action::Union::ReadDir(rd)) => {
|
Some(file_action::Union::ReadDir(rd)) => {
|
||||||
self.read_dir(&rd.path, rd.include_hidden);
|
self.read_dir(&rd.path, rd.include_hidden);
|
||||||
}
|
}
|
||||||
@ -3145,6 +3148,14 @@ impl Connection {
|
|||||||
raii::AuthedConnID::check_remove_session(self.inner.id(), self.session_key());
|
raii::AuthedConnID::check_remove_session(self.inner.id(), self.session_key());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_empty_dirs(&mut self, dir: &str, include_hidden: bool) {
|
||||||
|
let dir = dir.to_string();
|
||||||
|
self.send_fs(ipc::FS::ReadEmptyDirs {
|
||||||
|
dir,
|
||||||
|
include_hidden,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn read_dir(&mut self, dir: &str, include_hidden: bool) {
|
fn read_dir(&mut self, dir: &str, include_hidden: bool) {
|
||||||
let dir = dir.to_string();
|
let dir = dir.to_string();
|
||||||
self.send_fs(ipc::FS::ReadDir {
|
self.send_fs(ipc::FS::ReadDir {
|
||||||
|
@ -751,6 +751,12 @@ async fn handle_fs(
|
|||||||
use hbb_common::fs::serialize_transfer_job;
|
use hbb_common::fs::serialize_transfer_job;
|
||||||
|
|
||||||
match fs {
|
match fs {
|
||||||
|
ipc::FS::ReadEmptyDirs {
|
||||||
|
dir,
|
||||||
|
include_hidden,
|
||||||
|
} => {
|
||||||
|
read_empty_dirs(&dir, include_hidden, tx).await;
|
||||||
|
}
|
||||||
ipc::FS::ReadDir {
|
ipc::FS::ReadDir {
|
||||||
dir,
|
dir,
|
||||||
include_hidden,
|
include_hidden,
|
||||||
@ -907,6 +913,26 @@ async fn handle_fs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "ios")))]
|
||||||
|
async fn read_empty_dirs(dir: &str, include_hidden: bool, tx: &UnboundedSender<Data>) {
|
||||||
|
let path = dir.to_owned();
|
||||||
|
let path_clone = dir.to_owned();
|
||||||
|
|
||||||
|
if let Ok(Ok(fds)) =
|
||||||
|
spawn_blocking(move || fs::get_empty_dirs_recursive(&path, include_hidden)).await
|
||||||
|
{
|
||||||
|
let mut msg_out = Message::new();
|
||||||
|
let mut file_response = FileResponse::new();
|
||||||
|
file_response.set_empty_dirs(ReadEmptyDirsResponse {
|
||||||
|
path: path_clone,
|
||||||
|
empty_dirs: fds,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
msg_out.set_file_response(file_response);
|
||||||
|
send_raw(msg_out, tx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "ios")))]
|
#[cfg(not(any(target_os = "ios")))]
|
||||||
async fn read_dir(dir: &str, include_hidden: bool, tx: &UnboundedSender<Data>) {
|
async fn read_dir(dir: &str, include_hidden: bool, tx: &UnboundedSender<Data>) {
|
||||||
let path = {
|
let path = {
|
||||||
|
@ -1555,6 +1555,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
|
|||||||
#[cfg(feature = "flutter")]
|
#[cfg(feature = "flutter")]
|
||||||
fn is_multi_ui_session(&self) -> bool;
|
fn is_multi_ui_session(&self) -> bool;
|
||||||
fn update_record_status(&self, start: bool);
|
fn update_record_status(&self, start: bool);
|
||||||
|
fn update_empty_dirs(&self, _res: ReadEmptyDirsResponse) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: InvokeUiSession> Deref for Session<T> {
|
impl<T: InvokeUiSession> Deref for Session<T> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user