mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-27 14:59:02 +08:00
parent
579e0fac36
commit
ed18e3c786
@ -262,6 +262,7 @@ class _FileManagerPageState extends State<FileManagerPage>
|
||||
Offstage(
|
||||
offstage: item.state != JobState.paused,
|
||||
child: MenuButton(
|
||||
tooltip: translate("Resume"),
|
||||
onPressed: () {
|
||||
jobController.resumeJob(item.id);
|
||||
},
|
||||
@ -274,6 +275,7 @@ class _FileManagerPageState extends State<FileManagerPage>
|
||||
),
|
||||
),
|
||||
MenuButton(
|
||||
tooltip: translate("Delete"),
|
||||
padding: EdgeInsets.only(right: 15),
|
||||
child: SvgPicture.asset(
|
||||
"assets/close.svg",
|
||||
@ -521,6 +523,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
Row(
|
||||
children: [
|
||||
MenuButton(
|
||||
tooltip: translate('Back'),
|
||||
padding: EdgeInsets.only(
|
||||
right: 3,
|
||||
),
|
||||
@ -540,6 +543,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
},
|
||||
),
|
||||
MenuButton(
|
||||
tooltip: translate('Parent directory'),
|
||||
child: RotatedBox(
|
||||
quarterTurns: 3,
|
||||
child: SvgPicture.asset(
|
||||
@ -604,6 +608,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
switch (_locationStatus.value) {
|
||||
case LocationStatus.bread:
|
||||
return MenuButton(
|
||||
tooltip: translate('Search'),
|
||||
onPressed: () {
|
||||
_locationStatus.value = LocationStatus.fileSearchBar;
|
||||
Future.delayed(
|
||||
@ -630,6 +635,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
);
|
||||
case LocationStatus.fileSearchBar:
|
||||
return MenuButton(
|
||||
tooltip: translate('Clear'),
|
||||
onPressed: () {
|
||||
onSearchText("", isLocal);
|
||||
_locationStatus.value = LocationStatus.bread;
|
||||
@ -645,6 +651,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
}
|
||||
}),
|
||||
MenuButton(
|
||||
tooltip: translate('Refresh File'),
|
||||
padding: EdgeInsets.only(
|
||||
left: 3,
|
||||
),
|
||||
@ -670,6 +677,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
isLocal ? MainAxisAlignment.start : MainAxisAlignment.end,
|
||||
children: [
|
||||
MenuButton(
|
||||
tooltip: translate('Home'),
|
||||
padding: EdgeInsets.only(
|
||||
right: 3,
|
||||
),
|
||||
@ -685,11 +693,27 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
hoverColor: Theme.of(context).hoverColor,
|
||||
),
|
||||
MenuButton(
|
||||
tooltip: translate('Create Folder'),
|
||||
onPressed: () {
|
||||
final name = TextEditingController();
|
||||
String? errorText;
|
||||
_ffi.dialogManager.show((setState, close, context) {
|
||||
name.addListener(() {
|
||||
if (errorText != null) {
|
||||
setState(() {
|
||||
errorText = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
submit() {
|
||||
if (name.value.text.isNotEmpty) {
|
||||
if (!PathUtil.validName(name.value.text,
|
||||
controller.options.value.isWindows)) {
|
||||
setState(() {
|
||||
errorText = translate("Invalid folder name");
|
||||
});
|
||||
return;
|
||||
}
|
||||
controller.createDir(PathUtil.join(
|
||||
controller.directory.value.path,
|
||||
name.value.text,
|
||||
@ -721,6 +745,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
labelText: translate(
|
||||
"Please enter the folder name",
|
||||
),
|
||||
errorText: errorText,
|
||||
),
|
||||
controller: name,
|
||||
autofocus: true,
|
||||
@ -754,6 +779,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
hoverColor: Theme.of(context).hoverColor,
|
||||
),
|
||||
Obx(() => MenuButton(
|
||||
tooltip: translate('Delete'),
|
||||
onPressed: SelectedItems.valid(selectedItems.items)
|
||||
? () async {
|
||||
await (controller
|
||||
@ -885,6 +911,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
||||
},
|
||||
child: MenuButton(
|
||||
tooltip: translate('More'),
|
||||
onPressed: () => mod_menu.showMenu(
|
||||
context: context,
|
||||
position: menuPos,
|
||||
@ -974,6 +1001,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
final lastModifiedStr = entry.isDrive
|
||||
? " "
|
||||
: "${entry.lastModified().toString().replaceAll(".000", "")} ";
|
||||
var secondaryPosition = RelativeRect.fromLTRB(0, 0, 0, 0);
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 1),
|
||||
child: Obx(() => Container(
|
||||
@ -1038,6 +1066,35 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
_onSelectedChanged(
|
||||
items, filteredEntries, entry, isLocal);
|
||||
},
|
||||
onSecondaryTap: () {
|
||||
final items = [
|
||||
if (!entry.isDrive &&
|
||||
versionCmp(_ffi.ffiModel.pi.version,
|
||||
"1.3.0") >=
|
||||
0)
|
||||
mod_menu.PopupMenuItem(
|
||||
child: Text("Rename"),
|
||||
height: CustomPopupMenuTheme.height,
|
||||
onTap: () {
|
||||
controller.renameAction(entry, isLocal);
|
||||
},
|
||||
)
|
||||
];
|
||||
if (items.isNotEmpty) {
|
||||
mod_menu.showMenu(
|
||||
context: context,
|
||||
position: secondaryPosition,
|
||||
items: items,
|
||||
);
|
||||
}
|
||||
},
|
||||
onSecondaryTapDown: (details) {
|
||||
secondaryPosition = RelativeRect.fromLTRB(
|
||||
details.globalPosition.dx,
|
||||
details.globalPosition.dy,
|
||||
details.globalPosition.dx,
|
||||
details.globalPosition.dy);
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
width: 2.0,
|
||||
|
@ -1157,6 +1157,16 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> {
|
||||
Text(translate('Create Folder'))
|
||||
],
|
||||
);
|
||||
case CmFileAction.rename:
|
||||
return Column(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.drive_file_move_outlined,
|
||||
color: Theme.of(context).tabBarTheme.labelColor,
|
||||
),
|
||||
Text(translate('Rename'))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ class _MenuButtonState extends State<MenuButton> {
|
||||
return Padding(
|
||||
padding: widget.padding,
|
||||
child: Tooltip(
|
||||
waitDuration: Duration(milliseconds: 300),
|
||||
message: widget.tooltip,
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
|
@ -204,36 +204,54 @@ class _FileManagerPageState extends State<FileManagerPage> {
|
||||
setState(() {});
|
||||
} else if (v == "folder") {
|
||||
final name = TextEditingController();
|
||||
gFFI.dialogManager
|
||||
.show((setState, close, context) => CustomAlertDialog(
|
||||
title: Text(translate("Create Folder")),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
labelText: translate(
|
||||
"Please enter the folder name"),
|
||||
),
|
||||
controller: name,
|
||||
),
|
||||
],
|
||||
String? errorText;
|
||||
gFFI.dialogManager.show((setState, close, context) {
|
||||
name.addListener(() {
|
||||
if (errorText != null) {
|
||||
setState(() {
|
||||
errorText = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
return CustomAlertDialog(
|
||||
title: Text(translate("Create Folder")),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
labelText:
|
||||
translate("Please enter the folder name"),
|
||||
errorText: errorText,
|
||||
),
|
||||
actions: [
|
||||
dialogButton("Cancel",
|
||||
onPressed: () => close(false),
|
||||
isOutline: true),
|
||||
dialogButton("OK", onPressed: () {
|
||||
if (name.value.text.isNotEmpty) {
|
||||
currentFileController.createDir(
|
||||
PathUtil.join(
|
||||
currentDir.path,
|
||||
name.value.text,
|
||||
currentOptions.isWindows));
|
||||
close();
|
||||
}
|
||||
})
|
||||
]));
|
||||
controller: name,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
dialogButton("Cancel",
|
||||
onPressed: () => close(false), isOutline: true),
|
||||
dialogButton("OK", onPressed: () {
|
||||
if (name.value.text.isNotEmpty) {
|
||||
if (!PathUtil.validName(
|
||||
name.value.text,
|
||||
currentFileController
|
||||
.options.value.isWindows)) {
|
||||
setState(() {
|
||||
errorText =
|
||||
translate("Invalid folder name");
|
||||
});
|
||||
return;
|
||||
}
|
||||
currentFileController.createDir(PathUtil.join(
|
||||
currentDir.path,
|
||||
name.value.text,
|
||||
currentOptions.isWindows));
|
||||
close();
|
||||
}
|
||||
})
|
||||
]);
|
||||
});
|
||||
} else if (v == "hidden") {
|
||||
currentFileController.toggleShowHidden();
|
||||
}
|
||||
@ -497,7 +515,15 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
child: Text(translate("Properties")),
|
||||
value: "properties",
|
||||
enabled: false,
|
||||
)
|
||||
),
|
||||
if (!entries[index].isDrive &&
|
||||
versionCmp(gFFI.ffiModel.pi.version,
|
||||
"1.3.0") >=
|
||||
0)
|
||||
PopupMenuItem(
|
||||
child: Text(translate("Rename")),
|
||||
value: "rename",
|
||||
)
|
||||
];
|
||||
},
|
||||
onSelected: (v) {
|
||||
@ -509,6 +535,9 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
_selectedItems.clear();
|
||||
widget.selectMode.toggle(isLocal);
|
||||
setState(() {});
|
||||
} else if (v == "rename") {
|
||||
controller.renameAction(
|
||||
entries[index], isLocal);
|
||||
}
|
||||
}),
|
||||
onTap: () {
|
||||
|
@ -291,7 +291,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(translate('Enable trusted devices')),
|
||||
Text(translate('enable-trusted-devices-tip'),
|
||||
Text('* ${translate('enable-trusted-devices-tip')}',
|
||||
style: Theme.of(context).textTheme.bodySmall),
|
||||
],
|
||||
),
|
||||
|
@ -33,6 +33,8 @@ class CmFileModel {
|
||||
_onFileRemove(evt['remove']);
|
||||
} else if (evt['create_dir'] != null) {
|
||||
_onDirCreate(evt['create_dir']);
|
||||
} else if (evt['rename'] != null) {
|
||||
_onRename(evt['rename']);
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,8 +61,6 @@ class CmFileModel {
|
||||
|
||||
_dealOneJob(dynamic l, bool calcSpeed) {
|
||||
final data = TransferJobSerdeData.fromJson(l);
|
||||
Client? client =
|
||||
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == data.connId);
|
||||
var jobTable = _jobTables[data.connId];
|
||||
if (jobTable == null) {
|
||||
debugPrint("jobTable should not be null");
|
||||
@ -70,12 +70,7 @@ class CmFileModel {
|
||||
if (job == null) {
|
||||
job = CmFileLog();
|
||||
jobTable.add(job);
|
||||
final currentSelectedTab =
|
||||
gFFI.serverModel.tabController.state.value.selectedTabInfo;
|
||||
if (!(gFFI.chatModel.isShowCMSidePage &&
|
||||
currentSelectedTab.key == data.connId.toString())) {
|
||||
client?.unreadChatMessageCount.value += 1;
|
||||
}
|
||||
_addUnread(data.connId);
|
||||
}
|
||||
job.id = data.id;
|
||||
job.action =
|
||||
@ -167,8 +162,6 @@ class CmFileModel {
|
||||
try {
|
||||
dynamic d = jsonDecode(log);
|
||||
FileActionLog data = FileActionLog.fromJson(d);
|
||||
Client? client =
|
||||
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == data.connId);
|
||||
var jobTable = _jobTables[data.connId];
|
||||
if (jobTable == null) {
|
||||
debugPrint("jobTable should not be null");
|
||||
@ -179,17 +172,45 @@ class CmFileModel {
|
||||
..fileName = data.path
|
||||
..action = CmFileAction.createDir
|
||||
..state = JobState.done);
|
||||
final currentSelectedTab =
|
||||
gFFI.serverModel.tabController.state.value.selectedTabInfo;
|
||||
if (!(gFFI.chatModel.isShowCMSidePage &&
|
||||
currentSelectedTab.key == data.connId.toString())) {
|
||||
client?.unreadChatMessageCount.value += 1;
|
||||
}
|
||||
_addUnread(data.connId);
|
||||
jobTable.refresh();
|
||||
} catch (e) {
|
||||
debugPrint('$e');
|
||||
}
|
||||
}
|
||||
|
||||
_onRename(dynamic log) {
|
||||
try {
|
||||
dynamic d = jsonDecode(log);
|
||||
FileRenamenLog data = FileRenamenLog.fromJson(d);
|
||||
var jobTable = _jobTables[data.connId];
|
||||
if (jobTable == null) {
|
||||
debugPrint("jobTable should not be null");
|
||||
return;
|
||||
}
|
||||
final fileName = '${data.path} -> ${data.newName}';
|
||||
jobTable.add(CmFileLog()
|
||||
..id = 0
|
||||
..fileName = fileName
|
||||
..action = CmFileAction.rename
|
||||
..state = JobState.done);
|
||||
_addUnread(data.connId);
|
||||
jobTable.refresh();
|
||||
} catch (e) {
|
||||
debugPrint('$e');
|
||||
}
|
||||
}
|
||||
|
||||
_addUnread(int connId) {
|
||||
Client? client =
|
||||
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == connId);
|
||||
final currentSelectedTab =
|
||||
gFFI.serverModel.tabController.state.value.selectedTabInfo;
|
||||
if (!(gFFI.chatModel.isShowCMSidePage &&
|
||||
currentSelectedTab.key == connId.toString())) {
|
||||
client?.unreadChatMessageCount.value += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CmFileAction {
|
||||
@ -198,6 +219,7 @@ enum CmFileAction {
|
||||
localToRemote,
|
||||
remove,
|
||||
createDir,
|
||||
rename,
|
||||
}
|
||||
|
||||
class CmFileLog {
|
||||
@ -285,3 +307,22 @@ class FileActionLog {
|
||||
dir: d['dir'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
class FileRenamenLog {
|
||||
int connId = 0;
|
||||
String path = '';
|
||||
String newName = '';
|
||||
|
||||
FileRenamenLog({
|
||||
required this.connId,
|
||||
required this.path,
|
||||
required this.newName,
|
||||
});
|
||||
|
||||
FileRenamenLog.fromJson(dynamic d)
|
||||
: this(
|
||||
connId: d['connId'] ?? 0,
|
||||
path: d['path'] ?? '',
|
||||
newName: d['newName'] ?? '',
|
||||
);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/common/widgets/dialog.dart';
|
||||
import 'package:flutter_hbb/utils/event_loop.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
@ -642,6 +643,77 @@ class FileController {
|
||||
path: path,
|
||||
isRemote: !isLocal);
|
||||
}
|
||||
|
||||
Future<void> renameAction(Entry item, bool isLocal) async {
|
||||
final textEditingController = TextEditingController(text: item.name);
|
||||
String? errorText;
|
||||
dialogManager?.show((setState, close, context) {
|
||||
textEditingController.addListener(() {
|
||||
if (errorText != null) {
|
||||
setState(() {
|
||||
errorText = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
submit() async {
|
||||
final newName = textEditingController.text;
|
||||
if (newName.isEmpty || newName == item.name) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
if (directory.value.entries.any((e) => e.name == newName)) {
|
||||
setState(() {
|
||||
errorText = translate("Already exists");
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!PathUtil.validName(newName, options.value.isWindows)) {
|
||||
setState(() {
|
||||
if (item.isDirectory) {
|
||||
errorText = translate("Invalid folder name");
|
||||
} else {
|
||||
errorText = translate("Invalid file name");
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
await bind.sessionRenameFile(
|
||||
sessionId: sessionId,
|
||||
actId: JobController.jobID.next(),
|
||||
path: item.path,
|
||||
newName: newName,
|
||||
isRemote: !isLocal);
|
||||
close();
|
||||
}
|
||||
|
||||
return CustomAlertDialog(
|
||||
content: Column(
|
||||
children: [
|
||||
DialogTextField(
|
||||
title: '${translate('Rename')} ${item.name}',
|
||||
controller: textEditingController,
|
||||
errorText: errorText,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
dialogButton(
|
||||
"Cancel",
|
||||
icon: Icon(Icons.close_rounded),
|
||||
onPressed: close,
|
||||
isOutline: true,
|
||||
),
|
||||
dialogButton(
|
||||
"OK",
|
||||
icon: Icon(Icons.done_rounded),
|
||||
onPressed: submit,
|
||||
),
|
||||
],
|
||||
onSubmit: submit,
|
||||
onCancel: close,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class JobController {
|
||||
@ -1083,6 +1155,13 @@ class PathUtil {
|
||||
final pathUtil = isWindows ? windowsContext : posixContext;
|
||||
return pathUtil.dirname(path);
|
||||
}
|
||||
|
||||
static bool validName(String name, bool isWindows) {
|
||||
final unixFileNamePattern = RegExp(r'^[^/\0]+$');
|
||||
final windowsFileNamePattern = RegExp(r'^[^<>:"/\\|?*]+$');
|
||||
final reg = isWindows ? windowsFileNamePattern : unixFileNamePattern;
|
||||
return reg.hasMatch(name);
|
||||
}
|
||||
}
|
||||
|
||||
class DirectoryOptions {
|
||||
|
@ -371,6 +371,12 @@ message ReadAllFiles {
|
||||
bool include_hidden = 3;
|
||||
}
|
||||
|
||||
message FileRename {
|
||||
int32 id = 1;
|
||||
string path = 2;
|
||||
string new_name = 3;
|
||||
}
|
||||
|
||||
message FileAction {
|
||||
oneof union {
|
||||
ReadDir read_dir = 1;
|
||||
@ -382,6 +388,7 @@ message FileAction {
|
||||
ReadAllFiles all_files = 7;
|
||||
FileTransferCancel cancel = 8;
|
||||
FileTransferSendConfirmRequest send_confirm = 9;
|
||||
FileRename rename = 10;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -838,6 +838,21 @@ pub fn create_dir(dir: &str) -> ResultType<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rename_file(path: &str, new_name: &str) -> ResultType<()> {
|
||||
let path = std::path::Path::new(&path);
|
||||
if path.exists() {
|
||||
let dir = path
|
||||
.parent()
|
||||
.ok_or(anyhow!("Parent directoy of {path:?} not exists"))?;
|
||||
let new_path = dir.join(&new_name);
|
||||
std::fs::rename(&path, &new_path)?;
|
||||
Ok(())
|
||||
} else {
|
||||
bail!("{path:?} not exists");
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn transform_windows_path(entries: &mut Vec<FileEntry>) {
|
||||
for entry in entries {
|
||||
|
@ -3180,6 +3180,7 @@ pub enum Data {
|
||||
NewVoiceCall,
|
||||
CloseVoiceCall,
|
||||
ResetDecoder(Option<usize>),
|
||||
RenameFile((i32, String, String, bool)),
|
||||
}
|
||||
|
||||
/// Keycode for key events.
|
||||
|
@ -1,4 +1,4 @@
|
||||
use hbb_common::{fs, message_proto::*, log};
|
||||
use hbb_common::{fs, log, message_proto::*};
|
||||
|
||||
use super::{Data, Interface};
|
||||
|
||||
@ -7,7 +7,12 @@ pub trait FileManager: Interface {
|
||||
fs::get_home_as_string()
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli", feature = "flutter")))]
|
||||
#[cfg(not(any(
|
||||
target_os = "android",
|
||||
target_os = "ios",
|
||||
feature = "cli",
|
||||
feature = "flutter"
|
||||
)))]
|
||||
fn read_dir(&self, path: String, include_hidden: bool) -> sciter::Value {
|
||||
match fs::read_dir(&fs::get_path(&path), include_hidden) {
|
||||
Err(_) => sciter::Value::null(),
|
||||
@ -20,7 +25,12 @@ pub trait FileManager: Interface {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "ios", feature = "cli", feature = "flutter"))]
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "ios",
|
||||
feature = "cli",
|
||||
feature = "flutter"
|
||||
))]
|
||||
fn read_dir(&self, path: &str, include_hidden: bool) -> String {
|
||||
use crate::common::make_fd_to_json;
|
||||
match fs::read_dir(&fs::get_path(path), include_hidden) {
|
||||
@ -136,4 +146,8 @@ pub trait FileManager: Interface {
|
||||
is_upload,
|
||||
)));
|
||||
}
|
||||
|
||||
fn rename_file(&self, act_id: i32, path: String, new_name: String, is_remote: bool) {
|
||||
self.send(Data::RenameFile((act_id, path, new_name, is_remote)));
|
||||
}
|
||||
}
|
||||
|
@ -817,6 +817,25 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Data::RenameFile((id, path, new_name, is_remote)) => {
|
||||
if is_remote {
|
||||
let mut msg_out = Message::new();
|
||||
let mut file_action = FileAction::new();
|
||||
file_action.set_rename(FileRename {
|
||||
id,
|
||||
path,
|
||||
new_name,
|
||||
..Default::default()
|
||||
});
|
||||
msg_out.set_file_action(file_action);
|
||||
allow_err!(peer.send(&msg_out).await);
|
||||
} else {
|
||||
let err = fs::rename_file(&path, &new_name)
|
||||
.err()
|
||||
.map(|e| e.to_string());
|
||||
self.handle_job_status(id, -1, err);
|
||||
}
|
||||
}
|
||||
Data::RecordScreen(start, display, w, h, id) => {
|
||||
let _ = self
|
||||
.video_sender
|
||||
|
@ -710,6 +710,18 @@ pub fn session_resume_job(session_id: SessionID, act_id: i32, is_remote: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_rename_file(
|
||||
session_id: SessionID,
|
||||
act_id: i32,
|
||||
path: String,
|
||||
new_name: String,
|
||||
is_remote: bool,
|
||||
) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.rename_file(act_id, path, new_name, is_remote);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_elevate_direct(session_id: SessionID) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.elevate_direct();
|
||||
|
@ -102,6 +102,11 @@ pub enum FS {
|
||||
last_modified: u64,
|
||||
is_upload: bool,
|
||||
},
|
||||
Rename {
|
||||
id: i32,
|
||||
path: String,
|
||||
new_name: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", "平台"),
|
||||
("Days remaining", "剩余天数"),
|
||||
("enable-trusted-devices-tip", "允许受信任的设备跳过 2FA 验证"),
|
||||
("Parent directory", "父目录"),
|
||||
("Resume", "继续"),
|
||||
("Invalid file name", "无效文件名"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", "Platforma"),
|
||||
("Days remaining", "Zbývajících dnů"),
|
||||
("enable-trusted-devices-tip", "Přeskočte 2FA ověření na důvěryhodných zařízeních"),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", "Plattform"),
|
||||
("Days remaining", "Verbleibende Tage"),
|
||||
("enable-trusted-devices-tip", "2FA-Verifizierung auf vertrauenswürdigen Geräten überspringen"),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", "Plataforma"),
|
||||
("Days remaining", "Días restantes"),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", "Piattaforma"),
|
||||
("Days remaining", "Giorni rimanenti"),
|
||||
("enable-trusted-devices-tip", "Salta verifica 2FA nei dispositivi attendibili"),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", "플랫폼"),
|
||||
("Days remaining", "일 남음"),
|
||||
("enable-trusted-devices-tip", "신뢰할 수 있는 기기에서 2FA 검증 건너뛰기"),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -641,5 +641,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Platform", ""),
|
||||
("Days remaining", ""),
|
||||
("enable-trusted-devices-tip", ""),
|
||||
("Parent directory", ""),
|
||||
("Resume", ""),
|
||||
("Invalid file name", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -2274,6 +2274,22 @@ impl Connection {
|
||||
job.confirm(&r);
|
||||
}
|
||||
}
|
||||
Some(file_action::Union::Rename(r)) => {
|
||||
self.send_fs(ipc::FS::Rename {
|
||||
id: r.id,
|
||||
path: r.path.clone(),
|
||||
new_name: r.new_name.clone(),
|
||||
});
|
||||
self.send_to_cm(ipc::Data::FileTransferLog((
|
||||
"rename".to_string(),
|
||||
serde_json::to_string(&FileRenameLog {
|
||||
conn_id: self.inner.id(),
|
||||
path: r.path,
|
||||
new_name: r.new_name,
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
)));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -3451,6 +3467,14 @@ struct FileActionLog {
|
||||
dir: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct FileRenameLog {
|
||||
conn_id: i32,
|
||||
path: String,
|
||||
new_name: String,
|
||||
}
|
||||
|
||||
struct FileRemoveLogControl {
|
||||
conn_id: i32,
|
||||
instant: Instant,
|
||||
|
@ -881,6 +881,9 @@ async fn handle_fs(
|
||||
}
|
||||
}
|
||||
}
|
||||
ipc::FS::Rename { id, path, new_name } => {
|
||||
rename_file(path, new_name, id, tx).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -945,6 +948,17 @@ async fn create_dir(path: String, id: i32, tx: &UnboundedSender<Data>) {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "ios")))]
|
||||
async fn rename_file(path: String, new_name: String, id: i32, tx: &UnboundedSender<Data>) {
|
||||
handle_result(
|
||||
spawn_blocking(move || fs::rename_file(&path, &new_name)).await,
|
||||
id,
|
||||
0,
|
||||
tx,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "ios")))]
|
||||
async fn remove_dir(path: String, id: i32, recursive: bool, tx: &UnboundedSender<Data>) {
|
||||
let path = fs::get_path(&path);
|
||||
|
Loading…
Reference in New Issue
Block a user