add: file transfer dual logic with bridge

This commit is contained in:
Kingtous 2022-06-27 16:44:34 +08:00
parent d79bdd6afe
commit 2b10da167c
4 changed files with 220 additions and 245 deletions

View File

@ -11,7 +11,6 @@ import 'package:provider/provider.dart';
import 'package:wakelock/wakelock.dart';
import '../../common.dart';
import '../../mobile/widgets/dialog.dart';
import '../../models/model.dart';
class FileManagerPage extends StatefulWidget {
@ -25,7 +24,8 @@ class FileManagerPage extends StatefulWidget {
class _FileManagerPageState extends State<FileManagerPage>
with AutomaticKeepAliveClientMixin {
final _selectedItems = SelectedItems();
final _breadCrumbScroller = ScrollController();
final _breadCrumbLocalScroller = ScrollController();
final _breadCrumbRemoteScroller = ScrollController();
/// FFI with name file_transfer_id
FFI get _ffi => ffi('ft_${widget.id}');
@ -66,38 +66,31 @@ class _FileManagerPageState extends State<FileManagerPage>
onWillPop: () async {
if (model.selectMode) {
model.toggleSelectMode();
} else {
goBack();
}
return false;
},
child: Scaffold(
backgroundColor: MyTheme.grayBg,
appBar: AppBar(
leading: Row(children: [
IconButton(icon: Icon(Icons.close), onPressed: clientClose),
]),
centerTitle: true,
// title: ToggleSwitch(
// initialLabelIndex: model.isLocal ? 0 : 1,
// activeBgColor: [MyTheme.idColor],
// inactiveBgColor: MyTheme.grayBg,
// inactiveFgColor: Colors.black54,
// totalSwitches: 2,
// minWidth: 100,
// fontSize: 15,
// iconSize: 18,
// labels: [translate("Local"), translate("Remote")],
// icons: [Icons.phone_android_sharp, Icons.screen_share],
// onToggle: (index) {
// final current = model.isLocal ? 0 : 1;
// if (index != current) {
// model.togglePage();
// }
// },
// ),
actions: [
PopupMenuButton<String>(
body: Row(
children: [
Flexible(flex: 1, child: body(isLocal: true)),
Flexible(flex: 1, child: body(isLocal: false))
],
),
bottomSheet: bottomSheet(),
));
}));
}
bool needShowCheckBox() {
if (!model.selectMode) {
return false;
}
return !_selectedItems.isOtherPage(model.isLocal);
}
Widget menu({bool isLocal = false}) {
return PopupMenuButton<String>(
icon: Icon(Icons.more_vert),
itemBuilder: (context) {
return [
@ -190,34 +183,16 @@ class _FileManagerPageState extends State<FileManagerPage>
child: Text(translate("OK")))
]));
} else if (v == "hidden") {
model.toggleShowHidden();
model.toggleShowHidden(local: isLocal);
}
}),
],
),
body: Row(
children: [
Flexible(flex: 1, child: body(isLocal: true)),
Flexible(flex: 1, child: body(isLocal: false))
],
),
bottomSheet: bottomSheet(),
));
}));
}
bool needShowCheckBox() {
if (!model.selectMode) {
return false;
}
return !_selectedItems.isOtherPage(model.isLocal);
});
}
Widget body({bool isLocal = false}) {
final fd = isLocal ? model.currentLocalDir : model.currentRemoteDir;
final entries = fd.entries;
return Column(children: [
headTools(),
headTools(isLocal),
Expanded(
child: ListView.builder(
itemCount: entries.length + 1,
@ -301,8 +276,8 @@ class _FileManagerPageState extends State<FileManagerPage>
return;
}
if (entries[index].isDirectory) {
model.openDirectory(entries[index].path);
breadCrumbScrollToEnd();
model.openDirectory(entries[index].path, isLocal: isLocal);
breadCrumbScrollToEnd(isLocal);
} else {
// Perform file-related tasks.
}
@ -322,20 +297,21 @@ class _FileManagerPageState extends State<FileManagerPage>
]);
}
goBack() {
model.goToParentDirectory();
goBack({bool? isLocal}) {
model.goToParentDirectory(isLocal: isLocal);
}
breadCrumbScrollToEnd() {
breadCrumbScrollToEnd(bool isLocal) {
final controller = isLocal ? _breadCrumbLocalScroller : _breadCrumbRemoteScroller;
Future.delayed(Duration(milliseconds: 200), () {
_breadCrumbScroller.animateTo(
_breadCrumbScroller.position.maxScrollExtent,
controller.animateTo(
controller.position.maxScrollExtent,
duration: Duration(milliseconds: 200),
curve: Curves.fastLinearToSlowEaseIn);
});
}
Widget headTools() => Container(
Widget headTools(bool isLocal) => Container(
child: Row(
children: [
Expanded(
@ -353,16 +329,18 @@ class _FileManagerPageState extends State<FileManagerPage>
path = PathUtil.join(path, item, model.currentIsWindows);
}
}
model.openDirectory(path);
}),
model.openDirectory(path, isLocal: isLocal);
}, isLocal),
divider: Icon(Icons.chevron_right),
overflow: ScrollableOverflow(controller: _breadCrumbScroller),
overflow: ScrollableOverflow(controller: isLocal ? _breadCrumbLocalScroller : _breadCrumbRemoteScroller),
)),
Row(
children: [
IconButton(
icon: Icon(Icons.arrow_upward),
onPressed: goBack,
onPressed: () {
goBack(isLocal: isLocal);
},
),
PopupMenuButton<SortBy>(
icon: Icon(Icons.sort),
@ -375,7 +353,10 @@ class _FileManagerPageState extends State<FileManagerPage>
))
.toList();
},
onSelected: model.changeSortStyle),
onSelected: (sort) {
model.changeSortStyle(sort, isLocal: isLocal);
}),
menu(isLocal: isLocal)
],
)
],
@ -486,8 +467,8 @@ class _FileManagerPageState extends State<FileManagerPage>
}
List<BreadCrumbItem> getPathBreadCrumbItems(
void Function() onHome, void Function(List<String>) onPressed) {
final path = model.currentShortPath;
void Function() onHome, void Function(List<String>) onPressed, bool isLocal) {
final path = model.shortPath(isLocal);
final list = PathUtil.split(path, model.currentIsWindows);
final breadCrumbList = [
BreadCrumbItem(

View File

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hbb/desktop/pages/file_manager_page.dart';
import 'package:flutter_hbb/desktop/widgets/titlebar_widget.dart';
import 'package:flutter_hbb/utils/multi_window_manager.dart';
import 'package:get/get.dart';
/// File Transfer for multi tabs
class FileManagerTabPage extends StatefulWidget {
@ -21,7 +22,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage>
// refactor List<int> when using multi-tab
// this singleton is only for test
List<String> connectionIds = List.empty(growable: true);
var initialIndex = 0;
var initialIndex = 0.obs;
_FileManagerTabPageState(Map<String, dynamic> params) {
if (params['id'] != null) {
@ -37,21 +38,15 @@ class _FileManagerTabPageState extends State<FileManagerTabPage>
"call ${call.method} with args ${call.arguments} from window ${fromWindowId}");
// for simplify, just replace connectionId
if (call.method == "new_file_transfer") {
setState(() {
final args = jsonDecode(call.arguments);
final id = args['id'];
final indexOf = connectionIds.indexOf(id);
if (indexOf >= 0) {
setState(() {
initialIndex = indexOf;
});
initialIndex.value = indexOf;
} else {
connectionIds.add(id);
setState(() {
initialIndex = connectionIds.length - 1;
});
initialIndex.value = connectionIds.length - 1;
}
});
}
});
}
@ -59,8 +54,9 @@ class _FileManagerTabPageState extends State<FileManagerTabPage>
@override
Widget build(BuildContext context) {
return Scaffold(
body: DefaultTabController(
initialIndex: initialIndex,
body: Obx(
()=> DefaultTabController(
initialIndex: initialIndex.value,
length: connectionIds.length,
animationDuration: Duration.zero,
child: Column(
@ -106,6 +102,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage>
],
),
),
),
);
}
@ -114,9 +111,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage>
if (indexOf == -1) {
return;
}
setState(() {
connectionIds.removeAt(indexOf);
initialIndex = max(0, initialIndex - 1);
});
initialIndex.value = max(0, initialIndex.value - 1);
}
}

View File

@ -60,6 +60,21 @@ class FileModel extends ChangeNotifier {
}
}
String shortPath(bool isLocal) {
final dir = isLocal ? currentLocalDir : currentRemoteDir;
if (dir.path.startsWith(currentHome)) {
var path = dir.path.replaceFirst(currentHome, "");
if (path.length == 0) return "";
if (path[0] == "/" || path[0] == "\\") {
// remove more '/' or '\'
path = path.replaceFirst(path[0], "");
}
return path;
} else {
return dir.path.replaceFirst(currentHome, "");
}
}
bool get currentShowHidden =>
_isLocal ? _localOption.showHidden : _remoteOption.showHidden;
@ -265,9 +280,9 @@ class FileModel extends ChangeNotifier {
openDirectory(currentHome);
}
goToParentDirectory() {
goToParentDirectory({bool? isLocal}) {
final parent = PathUtil.dirname(currentDir.path, currentIsWindows);
openDirectory(parent);
openDirectory(parent, isLocal: isLocal);
}
sendFiles(SelectedItems items) {
@ -282,17 +297,10 @@ class FileModel extends ChangeNotifier {
items.isLocal! ? _localOption.isWindows : _remoteOption.isWindows;
final showHidden =
items.isLocal! ? _localOption.showHidden : _remoteOption.showHidden;
items.items.forEach((from) {
items.items.forEach((from) async {
_jobId++;
final msg = {
"id": _jobId.toString(),
"path": from.path,
"to": PathUtil.join(toPath, from.name, isWindows),
"file_num": "0",
"show_hidden": showHidden.toString(),
"is_remote": (!(items.isLocal!)).toString()
};
_ffi.target?.setByName("send_files", jsonEncode(msg));
await _ffi.target?.bind.sessionSendFiles(id: '${_ffi.target?.getId()}', actId: _jobId, path: from.path, to: PathUtil.join(toPath, from.name, isWindows)
,fileNum: 0, includeHidden: showHidden, isRemote: !(items.isLocal!));
});
}
@ -485,43 +493,34 @@ class FileModel extends ChangeNotifier {
}
sendRemoveFile(String path, int fileNum, bool isLocal) {
final msg = {
"id": _jobId.toString(),
"path": path,
"file_num": fileNum.toString(),
"is_remote": (!(isLocal)).toString()
};
_ffi.target?.setByName("remove_file", jsonEncode(msg));
_ffi.target?.bind.sessionRemoveFile(id: '${_ffi.target?.getId()}', actId: _jobId, path: path, isRemote: !isLocal, fileNum: fileNum);
}
sendRemoveEmptyDir(String path, int fileNum, bool isLocal) {
final msg = {
"id": _jobId.toString(),
"path": path,
"is_remote": (!isLocal).toString()
};
_ffi.target?.setByName("remove_all_empty_dirs", jsonEncode(msg));
_ffi.target?.bind.sessionRemoveAllEmptyDirs(id: '${_ffi.target?.getId()}', actId: _jobId, path: path, isRemote: !isLocal);
}
createDir(String path) {
createDir(String path) async {
_jobId++;
final msg = {
"id": _jobId.toString(),
"path": path,
"is_remote": (!isLocal).toString()
};
_ffi.target?.setByName("create_dir", jsonEncode(msg));
_ffi.target?.bind.sessionCreateDir(id: '${_ffi.target?.getId()}', actId: _jobId, path: path, isRemote: !isLocal);
}
cancelJob(int id) {
_ffi.target?.setByName("cancel_job", id.toString());
cancelJob(int id) async {
_ffi.target?.bind.sessionCancelJob(id: '${_ffi.target?.getId()}', actId: id);
jobReset();
}
changeSortStyle(SortBy sort) {
changeSortStyle(SortBy sort, {bool? isLocal}) {
_sortStyle = sort;
if (isLocal == null) {
// compatible for mobile logic
_currentLocalDir.changeSortStyle(sort);
_currentRemoteDir.changeSortStyle(sort);
} else if (isLocal) {
_currentLocalDir.changeSortStyle(sort);
} else {
_currentRemoteDir.changeSortStyle(sort);
}
notifyListeners();
}

View File

@ -101,7 +101,7 @@ class RustDeskMultiWindowManager {
case WindowType.RemoteDesktop:
return _remoteDesktopWindowId;
case WindowType.FileTransfer:
break;
return _fileTransferWindowId;
case WindowType.PortForward:
break;
case WindowType.Unknown: