add: sortby, address link, platform, last jobs[1/2]
This commit is contained in:
parent
79217ca1d9
commit
5aded67597
@ -1,4 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
@ -26,8 +25,6 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
final _localSelectedItems = SelectedItems();
|
final _localSelectedItems = SelectedItems();
|
||||||
final _remoteSelectedItems = SelectedItems();
|
final _remoteSelectedItems = SelectedItems();
|
||||||
final _breadCrumbLocalScroller = ScrollController();
|
|
||||||
final _breadCrumbRemoteScroller = ScrollController();
|
|
||||||
|
|
||||||
/// FFI with name file_transfer_id
|
/// FFI with name file_transfer_id
|
||||||
FFI get _ffi => ffi('ft_${widget.id}');
|
FFI get _ffi => ffi('ft_${widget.id}');
|
||||||
@ -94,41 +91,11 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
icon: Icon(Icons.more_vert),
|
icon: Icon(Icons.more_vert),
|
||||||
itemBuilder: (context) {
|
itemBuilder: (context) {
|
||||||
return [
|
return [
|
||||||
PopupMenuItem(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(Icons.refresh, color: Colors.black),
|
|
||||||
SizedBox(width: 5),
|
|
||||||
Text(translate("Refresh File"))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
value: "refresh",
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(Icons.check, color: Colors.black),
|
|
||||||
SizedBox(width: 5),
|
|
||||||
Text(translate("Multi Select"))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
value: "select",
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(Icons.folder_outlined, color: Colors.black),
|
|
||||||
SizedBox(width: 5),
|
|
||||||
Text(translate("Create Folder"))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
value: "folder",
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
model.currentShowHidden
|
model.getCurrentShowHidden(isLocal)
|
||||||
? Icons.check_box_outlined
|
? Icons.check_box_outlined
|
||||||
: Icons.check_box_outline_blank,
|
: Icons.check_box_outline_blank,
|
||||||
color: Colors.black),
|
color: Colors.black),
|
||||||
@ -141,46 +108,7 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
];
|
];
|
||||||
},
|
},
|
||||||
onSelected: (v) {
|
onSelected: (v) {
|
||||||
if (v == "refresh") {
|
if (v == "hidden") {
|
||||||
model.refresh();
|
|
||||||
} else if (v == "select") {
|
|
||||||
_localSelectedItems.clear();
|
|
||||||
model.toggleSelectMode();
|
|
||||||
} else if (v == "folder") {
|
|
||||||
final name = TextEditingController();
|
|
||||||
DialogManager.show((setState, close) => CustomAlertDialog(
|
|
||||||
title: Text(translate("Create Folder")),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
TextFormField(
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText:
|
|
||||||
translate("Please enter the folder name"),
|
|
||||||
),
|
|
||||||
controller: name,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
style: flatButtonStyle,
|
|
||||||
onPressed: () => close(false),
|
|
||||||
child: Text(translate("Cancel"))),
|
|
||||||
ElevatedButton(
|
|
||||||
style: flatButtonStyle,
|
|
||||||
onPressed: () {
|
|
||||||
if (name.value.text.isNotEmpty) {
|
|
||||||
model.createDir(PathUtil.join(
|
|
||||||
model.currentDir.path,
|
|
||||||
name.value.text,
|
|
||||||
model.currentIsWindows));
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(translate("OK")))
|
|
||||||
]));
|
|
||||||
} else if (v == "hidden") {
|
|
||||||
model.toggleShowHidden(local: isLocal);
|
model.toggleShowHidden(local: isLocal);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -189,9 +117,23 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
Widget body({bool isLocal = false}) {
|
Widget body({bool isLocal = false}) {
|
||||||
final fd = isLocal ? model.currentLocalDir : model.currentRemoteDir;
|
final fd = isLocal ? model.currentLocalDir : model.currentRemoteDir;
|
||||||
final entries = fd.entries;
|
final entries = fd.entries;
|
||||||
|
final sortIndex = (SortBy style) {
|
||||||
|
switch (style) {
|
||||||
|
case SortBy.Name:
|
||||||
|
return 1;
|
||||||
|
case SortBy.Type:
|
||||||
|
return 0;
|
||||||
|
case SortBy.Modified:
|
||||||
|
return 2;
|
||||||
|
case SortBy.Size:
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}(model.getSortStyle(isLocal));
|
||||||
|
final sortAscending =
|
||||||
|
isLocal ? model.localSortAscending : model.remoteSortAscending;
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white70, border: Border.all(color: Colors.grey)),
|
color: Colors.white54, border: Border.all(color: Colors.black26)),
|
||||||
margin: const EdgeInsets.all(16.0),
|
margin: const EdgeInsets.all(16.0),
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
@ -204,16 +146,36 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: DataTable(
|
child: DataTable(
|
||||||
showCheckboxColumn: true,
|
showCheckboxColumn: true,
|
||||||
dataRowHeight: 30,
|
dataRowHeight: 25,
|
||||||
|
headingRowHeight: 30,
|
||||||
columnSpacing: 8,
|
columnSpacing: 8,
|
||||||
|
showBottomBorder: true,
|
||||||
|
sortColumnIndex: sortIndex,
|
||||||
|
sortAscending: sortAscending,
|
||||||
columns: [
|
columns: [
|
||||||
DataColumn(label: Text(translate(" "))), // icon
|
DataColumn(label: Text(translate(" "))), // icon
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text(
|
label: Text(
|
||||||
translate("Name"),
|
translate("Name"),
|
||||||
)),
|
),
|
||||||
DataColumn(label: Text(translate("Modified"))),
|
onSort: (columnIndex, ascending) {
|
||||||
DataColumn(label: Text(translate("Size"))),
|
model.changeSortStyle(SortBy.Name,
|
||||||
|
isLocal: isLocal, ascending: ascending);
|
||||||
|
}),
|
||||||
|
DataColumn(
|
||||||
|
label: Text(
|
||||||
|
translate("Modified"),
|
||||||
|
),
|
||||||
|
onSort: (columnIndex, ascending) {
|
||||||
|
model.changeSortStyle(SortBy.Modified,
|
||||||
|
isLocal: isLocal, ascending: ascending);
|
||||||
|
}),
|
||||||
|
DataColumn(
|
||||||
|
label: Text(translate("Size")),
|
||||||
|
onSort: (columnIndex, ascending) {
|
||||||
|
model.changeSortStyle(SortBy.Size,
|
||||||
|
isLocal: isLocal, ascending: ascending);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
rows: entries.map((entry) {
|
rows: entries.map((entry) {
|
||||||
final sizeStr = entry.isFile
|
final sizeStr = entry.isFile
|
||||||
@ -228,23 +190,29 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
} else {
|
} else {
|
||||||
getSelectedItem(isLocal).remove(entry);
|
getSelectedItem(isLocal).remove(entry);
|
||||||
}
|
}
|
||||||
setState((){});
|
setState(() {});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selected: getSelectedItem(isLocal).contains(entry),
|
selected: getSelectedItem(isLocal).contains(entry),
|
||||||
cells: [
|
cells: [
|
||||||
// TODO: icon
|
|
||||||
DataCell(Icon(
|
DataCell(Icon(
|
||||||
entry.isFile ? Icons.feed_outlined : Icons.folder,
|
entry.isFile ? Icons.feed_outlined : Icons.folder,
|
||||||
size: 25)),
|
size: 25)),
|
||||||
DataCell(
|
DataCell(
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: BoxConstraints(maxWidth: 100),
|
constraints: BoxConstraints(maxWidth: 100),
|
||||||
child: Text(entry.name,
|
child: Tooltip(
|
||||||
overflow: TextOverflow.ellipsis)),
|
message: entry.name,
|
||||||
onTap: () {
|
child: Text(entry.name,
|
||||||
|
overflow: TextOverflow.ellipsis),
|
||||||
|
)), onTap: () {
|
||||||
if (entry.isDirectory) {
|
if (entry.isDirectory) {
|
||||||
model.openDirectory(entry.path, isLocal: isLocal);
|
model.openDirectory(entry.path, isLocal: isLocal);
|
||||||
|
if (isLocal) {
|
||||||
|
_localSelectedItems.clear();
|
||||||
|
} else {
|
||||||
|
_remoteSelectedItems.clear();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Perform file-related tasks.
|
// Perform file-related tasks.
|
||||||
final _selectedItems = getSelectedItem(isLocal);
|
final _selectedItems = getSelectedItem(isLocal);
|
||||||
@ -253,7 +221,7 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
} else {
|
} else {
|
||||||
_selectedItems.add(isLocal, entry);
|
_selectedItems.add(isLocal, entry);
|
||||||
}
|
}
|
||||||
setState((){});
|
setState(() {});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
DataCell(Text(
|
DataCell(Text(
|
||||||
@ -277,7 +245,7 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
Center(child: listTail(isLocal: isLocal)),
|
// Center(child: listTail(isLocal: isLocal)),
|
||||||
// Expanded(
|
// Expanded(
|
||||||
// child: ListView.builder(
|
// child: ListView.builder(
|
||||||
// itemCount: entries.length + 1,
|
// itemCount: entries.length + 1,
|
||||||
@ -388,9 +356,10 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
Widget statusList() {
|
Widget statusList() {
|
||||||
return PreferredSize(
|
return PreferredSize(
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.only(top: 16.0,bottom: 16.0, right: 16.0),
|
margin: const EdgeInsets.only(top: 16.0, bottom: 16.0, right: 16.0),
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
decoration: BoxDecoration(color: Colors.white70,border: Border.all(color: Colors.grey)),
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white70, border: Border.all(color: Colors.grey)),
|
||||||
child: Obx(
|
child: Obx(
|
||||||
() => ListView.builder(
|
() => ListView.builder(
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
@ -404,7 +373,9 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
Transform.rotate(
|
Transform.rotate(
|
||||||
angle: item.isRemote ? pi : 0,
|
angle: item.isRemote ? pi : 0,
|
||||||
child: Icon(Icons.send)),
|
child: Icon(Icons.send)),
|
||||||
SizedBox(width: 16.0,),
|
SizedBox(
|
||||||
|
width: 16.0,
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -412,15 +383,28 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
children: [
|
children: [
|
||||||
Tooltip(
|
Tooltip(
|
||||||
message: item.jobName,
|
message: item.jobName,
|
||||||
child: Text('${item.jobName}',
|
child: Text(
|
||||||
|
'${item.jobName}',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(color: Colors.black45), overflow: TextOverflow.ellipsis,)),
|
style: TextStyle(color: Colors.black45),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
)),
|
||||||
Wrap(
|
Wrap(
|
||||||
children: [
|
children: [
|
||||||
Text('${item.state.display()} ${max(0, item.fileNum)}/${item.fileCount} '),
|
Text(
|
||||||
Text('${translate("files")} ${readableFileSize(item.totalSize.toDouble())} '),
|
'${item.state.display()} ${max(0, item.fileNum)}/${item.fileCount} '),
|
||||||
Offstage(offstage: item.state != JobState.inProgress, child: Text('${readableFileSize(item.speed) + "/s"} ')),
|
Text(
|
||||||
Text('${(item.finishedSize.toDouble() * 100 / item.totalSize.toDouble()).toStringAsFixed(2)}%'),
|
'${translate("files")} ${readableFileSize(item.totalSize.toDouble())} '),
|
||||||
|
Offstage(
|
||||||
|
offstage:
|
||||||
|
item.state != JobState.inProgress,
|
||||||
|
child: Text(
|
||||||
|
'${readableFileSize(item.speed) + "/s"} ')),
|
||||||
|
Offstage(
|
||||||
|
offstage: item.totalSize <= 0,
|
||||||
|
child: Text(
|
||||||
|
'${(item.finishedSize.toDouble() * 100 / item.totalSize.toDouble()).toStringAsFixed(2)}%'),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -429,19 +413,26 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
IconButton(icon: Icon(Icons.delete), onPressed: () {
|
IconButton(
|
||||||
model.jobTable.removeAt(index);
|
icon: Icon(Icons.delete),
|
||||||
model.cancelJob(item.id);
|
onPressed: () {
|
||||||
},),
|
model.jobTable.removeAt(index);
|
||||||
|
model.cancelJob(item.id);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 8.0,),
|
SizedBox(
|
||||||
Divider(height: 2.0, )
|
height: 8.0,
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 2.0,
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemCount: model.jobTable.length,
|
itemCount: model.jobTable.length,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -453,100 +444,175 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
model.goToParentDirectory(isLocal: isLocal);
|
model.goToParentDirectory(isLocal: isLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
breadCrumbScrollToEnd(bool isLocal) {
|
|
||||||
final controller =
|
|
||||||
isLocal ? _breadCrumbLocalScroller : _breadCrumbRemoteScroller;
|
|
||||||
Future.delayed(Duration(milliseconds: 200), () {
|
|
||||||
controller.animateTo(controller.position.maxScrollExtent,
|
|
||||||
duration: Duration(milliseconds: 200),
|
|
||||||
curve: Curves.fastLinearToSlowEaseIn);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget headTools(bool isLocal) => Container(
|
Widget headTools(bool isLocal) => Container(
|
||||||
child: Row(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Offstage(
|
// symbols
|
||||||
offstage: isLocal,
|
PreferredSize(
|
||||||
child: TextButton.icon(
|
child: Row(
|
||||||
onPressed: (){
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
final items = getSelectedItem(isLocal);
|
children: [
|
||||||
model.sendFiles(items, isRemote: true);
|
Container(
|
||||||
}, icon: Transform.rotate(
|
width: 50,
|
||||||
angle: isLocal ? 0 : pi,
|
height: 50,
|
||||||
child: Icon(
|
decoration: BoxDecoration(color: Colors.blue),
|
||||||
Icons.send
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: FutureBuilder<String>(
|
||||||
|
future: _ffi.bind.sessionGetPlatform(
|
||||||
|
id: _ffi.id, isRemote: !isLocal),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
|
||||||
|
return getPlatformImage('${snapshot.data}');
|
||||||
|
} else {
|
||||||
|
return CircularProgressIndicator(color: Colors.white,);
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
Text(isLocal
|
||||||
|
? translate("Local Computer")
|
||||||
|
: translate("Remote Computer"))
|
||||||
|
.marginOnly(left: 8.0)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
), label: Text(translate('Receive'))),
|
preferredSize: Size(double.infinity, 70)),
|
||||||
),
|
// buttons
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(color: Colors.black12)
|
|
||||||
),
|
|
||||||
child: BreadCrumb(
|
|
||||||
items: getPathBreadCrumbItems(() => model.goHome(isLocal: isLocal), (list) {
|
|
||||||
var path = "";
|
|
||||||
final currentHome = model.getCurrentHome(isLocal);
|
|
||||||
final currentIsWindows = model.getCurrentIsWindows(isLocal);
|
|
||||||
if (currentHome.startsWith(list[0])) {
|
|
||||||
// absolute path
|
|
||||||
for (var item in list) {
|
|
||||||
path = PathUtil.join(path, item, currentIsWindows);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
path += currentHome;
|
|
||||||
for (var item in list) {
|
|
||||||
path = PathUtil.join(path, item, currentIsWindows);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
model.openDirectory(path, isLocal: isLocal);
|
|
||||||
}, isLocal),
|
|
||||||
divider: Icon(Icons.chevron_right),
|
|
||||||
overflow: ScrollableOverflow(
|
|
||||||
controller: isLocal
|
|
||||||
? _breadCrumbLocalScroller
|
|
||||||
: _breadCrumbRemoteScroller),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
Row(
|
||||||
icon: Icon(Icons.arrow_upward),
|
children: [
|
||||||
onPressed: () {
|
IconButton(
|
||||||
goBack(isLocal: isLocal);
|
onPressed: () {
|
||||||
},
|
model.goHome(isLocal: isLocal);
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.home_outlined)),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.arrow_upward),
|
||||||
|
onPressed: () {
|
||||||
|
goBack(isLocal: isLocal);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
menu(isLocal: isLocal),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
PopupMenuButton<SortBy>(
|
Expanded(
|
||||||
icon: Icon(Icons.sort),
|
child: Container(
|
||||||
itemBuilder: (context) {
|
decoration: BoxDecoration(
|
||||||
return SortBy.values
|
border: Border.all(color: Colors.black12)),
|
||||||
.map((e) => PopupMenuItem(
|
child: TextField(
|
||||||
child:
|
decoration: InputDecoration(
|
||||||
Text(translate(e.toString().split(".").last)),
|
border: InputBorder.none,
|
||||||
value: e,
|
isDense: true,
|
||||||
))
|
prefix: Padding(padding: EdgeInsets.only(left: 4.0)),
|
||||||
.toList();
|
suffix: DropdownButton<String>(
|
||||||
|
isDense: true,
|
||||||
|
underline: Offstage(),
|
||||||
|
items: [
|
||||||
|
// TODO: favourite
|
||||||
|
DropdownMenuItem(child: Text('/'), value: '/',)
|
||||||
|
], onChanged: (path) {
|
||||||
|
if (path is String && path.isNotEmpty){
|
||||||
|
model.openDirectory(path, isLocal: isLocal);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
),
|
||||||
|
controller: TextEditingController(
|
||||||
|
text: isLocal
|
||||||
|
? model.currentLocalDir.path
|
||||||
|
: model.currentRemoteDir.path),
|
||||||
|
onSubmitted: (path) {
|
||||||
|
model.openDirectory(path, isLocal: isLocal);
|
||||||
|
},
|
||||||
|
))),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
model.refresh(isLocal: isLocal);
|
||||||
},
|
},
|
||||||
onSelected: (sort) {
|
icon: Icon(Icons.refresh))
|
||||||
model.changeSortStyle(sort, isLocal: isLocal);
|
|
||||||
}),
|
|
||||||
menu(isLocal: isLocal),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Offstage(
|
Row(
|
||||||
offstage: !isLocal,
|
textDirection: isLocal ? TextDirection.ltr : TextDirection.rtl,
|
||||||
child: TextButton.icon(
|
children: [
|
||||||
onPressed: (){
|
Expanded(
|
||||||
final items = getSelectedItem(isLocal);
|
child: Row(
|
||||||
model.sendFiles(items, isRemote: !isLocal);
|
mainAxisAlignment:
|
||||||
}, icon: Transform.rotate(
|
isLocal ? MainAxisAlignment.start : MainAxisAlignment.end,
|
||||||
angle: isLocal ? 0 : pi,
|
children: [
|
||||||
child: Icon(
|
IconButton(
|
||||||
Icons.send
|
onPressed: () {
|
||||||
|
final name = TextEditingController();
|
||||||
|
DialogManager.show((setState, close) =>
|
||||||
|
CustomAlertDialog(
|
||||||
|
title: Text(translate("Create Folder")),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: translate(
|
||||||
|
"Please enter the folder name"),
|
||||||
|
),
|
||||||
|
controller: name,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
style: flatButtonStyle,
|
||||||
|
onPressed: () => close(false),
|
||||||
|
child: Text(translate("Cancel"))),
|
||||||
|
ElevatedButton(
|
||||||
|
style: flatButtonStyle,
|
||||||
|
onPressed: () {
|
||||||
|
if (name.value.text.isNotEmpty) {
|
||||||
|
model.createDir(
|
||||||
|
PathUtil.join(
|
||||||
|
model
|
||||||
|
.getCurrentDir(isLocal)
|
||||||
|
.path,
|
||||||
|
name.value.text,
|
||||||
|
model.getCurrentIsWindows(
|
||||||
|
isLocal)),
|
||||||
|
isLocal: isLocal);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(translate("OK")))
|
||||||
|
]));
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.create_new_folder_outlined)),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final items = isLocal
|
||||||
|
? _localSelectedItems
|
||||||
|
: _remoteSelectedItems;
|
||||||
|
debugPrint("remove items: ${items.items}");
|
||||||
|
await (model.removeAction(items));
|
||||||
|
items.clear();
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.delete_forever_outlined)),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
), label: Text(translate('Send'))),
|
TextButton.icon(
|
||||||
)
|
onPressed: () {
|
||||||
|
final items = getSelectedItem(isLocal);
|
||||||
|
model.sendFiles(items, isRemote: !isLocal);
|
||||||
|
},
|
||||||
|
icon: Transform.rotate(
|
||||||
|
angle: isLocal ? 0 : pi,
|
||||||
|
child: Icon(
|
||||||
|
Icons.send,
|
||||||
|
color: Colors.black54,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
isLocal ? translate('Send') : translate('Receive'),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black54,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
).marginOnly(top: 8.0)
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -578,7 +644,8 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
Widget? bottomSheet() {
|
Widget? bottomSheet() {
|
||||||
final state = model.jobState;
|
final state = model.jobState;
|
||||||
final isOtherPage = _localSelectedItems.isOtherPage(model.isLocal);
|
final isOtherPage = _localSelectedItems.isOtherPage(model.isLocal);
|
||||||
final selectedItemsLen = "${_localSelectedItems.length} ${translate("items")}";
|
final selectedItemsLen =
|
||||||
|
"${_localSelectedItems.length} ${translate("items")}";
|
||||||
final local = _localSelectedItems.isLocal == null
|
final local = _localSelectedItems.isLocal == null
|
||||||
? ""
|
? ""
|
||||||
: " [${_localSelectedItems.isLocal! ? translate("Local") : translate("Remote")}]";
|
: " [${_localSelectedItems.isLocal! ? translate("Local") : translate("Remote")}]";
|
||||||
@ -677,6 +744,15 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool get wantKeepAlive => true;
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
|
/// Get the image for the current [platform].
|
||||||
|
Widget getPlatformImage(String platform) {
|
||||||
|
platform = platform.toLowerCase();
|
||||||
|
if (platform == 'mac os')
|
||||||
|
platform = 'mac';
|
||||||
|
else if (platform != 'linux' && platform != 'android') platform = 'win';
|
||||||
|
return Image.asset('assets/$platform.png', width: 25, height: 25);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BottomSheetBody extends StatelessWidget {
|
class BottomSheetBody extends StatelessWidget {
|
||||||
|
@ -40,6 +40,20 @@ class FileModel extends ChangeNotifier {
|
|||||||
|
|
||||||
SortBy get sortStyle => _sortStyle;
|
SortBy get sortStyle => _sortStyle;
|
||||||
|
|
||||||
|
SortBy _localSortStyle = SortBy.Name;
|
||||||
|
|
||||||
|
bool _localSortAscending = true;
|
||||||
|
|
||||||
|
bool _remoteSortAscending = true;
|
||||||
|
|
||||||
|
SortBy _remoteSortStyle = SortBy.Name;
|
||||||
|
|
||||||
|
bool get localSortAscending => _localSortAscending;
|
||||||
|
|
||||||
|
SortBy getSortStyle(bool isLocal){
|
||||||
|
return isLocal ? _localSortStyle : _remoteSortStyle;
|
||||||
|
}
|
||||||
|
|
||||||
FileDirectory _currentLocalDir = FileDirectory();
|
FileDirectory _currentLocalDir = FileDirectory();
|
||||||
|
|
||||||
FileDirectory get currentLocalDir => _currentLocalDir;
|
FileDirectory get currentLocalDir => _currentLocalDir;
|
||||||
@ -50,6 +64,10 @@ class FileModel extends ChangeNotifier {
|
|||||||
|
|
||||||
FileDirectory get currentDir => _isLocal ? currentLocalDir : currentRemoteDir;
|
FileDirectory get currentDir => _isLocal ? currentLocalDir : currentRemoteDir;
|
||||||
|
|
||||||
|
FileDirectory getCurrentDir(bool isLocal) {
|
||||||
|
return isLocal ? currentLocalDir : currentRemoteDir;
|
||||||
|
}
|
||||||
|
|
||||||
String get currentHome => _isLocal ? _localOption.home : _remoteOption.home;
|
String get currentHome => _isLocal ? _localOption.home : _remoteOption.home;
|
||||||
|
|
||||||
String getCurrentHome(bool isLocal) {
|
String getCurrentHome(bool isLocal) {
|
||||||
@ -92,6 +110,10 @@ class FileModel extends ChangeNotifier {
|
|||||||
bool get currentShowHidden =>
|
bool get currentShowHidden =>
|
||||||
_isLocal ? _localOption.showHidden : _remoteOption.showHidden;
|
_isLocal ? _localOption.showHidden : _remoteOption.showHidden;
|
||||||
|
|
||||||
|
bool getCurrentShowHidden(bool isLocal) {
|
||||||
|
return isLocal ? _localOption.showHidden : _remoteOption.showHidden;
|
||||||
|
}
|
||||||
|
|
||||||
bool get currentIsWindows =>
|
bool get currentIsWindows =>
|
||||||
_isLocal ? _localOption.isWindows : _remoteOption.isWindows;
|
_isLocal ? _localOption.isWindows : _remoteOption.isWindows;
|
||||||
|
|
||||||
@ -163,13 +185,15 @@ class FileModel extends ChangeNotifier {
|
|||||||
try {
|
try {
|
||||||
final fd = FileDirectory.fromJson(jsonDecode(evt['value']));
|
final fd = FileDirectory.fromJson(jsonDecode(evt['value']));
|
||||||
fd.format(_remoteOption.isWindows, sort: _sortStyle);
|
fd.format(_remoteOption.isWindows, sort: _sortStyle);
|
||||||
if (fd.id > 0){
|
if (fd.id > 0) {
|
||||||
final jobIndex = getJob(fd.id);
|
final jobIndex = getJob(fd.id);
|
||||||
if (jobIndex != -1){
|
if (jobIndex != -1) {
|
||||||
final job = jobTable[jobIndex];
|
final job = jobTable[jobIndex];
|
||||||
var totalSize = 0;
|
var totalSize = 0;
|
||||||
var fileCount = fd.entries.length;
|
var fileCount = fd.entries.length;
|
||||||
fd.entries.forEach((element) {totalSize += element.size;});
|
fd.entries.forEach((element) {
|
||||||
|
totalSize += element.size;
|
||||||
|
});
|
||||||
job.totalSize = totalSize;
|
job.totalSize = totalSize;
|
||||||
job.fileCount = fileCount;
|
job.fileCount = fileCount;
|
||||||
debugPrint("update receive details:${fd.path}");
|
debugPrint("update receive details:${fd.path}");
|
||||||
@ -179,11 +203,11 @@ class FileModel extends ChangeNotifier {
|
|||||||
debugPrint("init remote home:${fd.path}");
|
debugPrint("init remote home:${fd.path}");
|
||||||
_currentRemoteDir = fd;
|
_currentRemoteDir = fd;
|
||||||
}
|
}
|
||||||
notifyListeners();
|
}
|
||||||
return;
|
finally {}
|
||||||
} finally {}
|
|
||||||
}
|
}
|
||||||
_fileFetcher.tryCompleteTask(evt['value'], evt['is_local']);
|
_fileFetcher.tryCompleteTask(evt['value'], evt['is_local']);
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
jobDone(Map<String, dynamic> evt) {
|
jobDone(Map<String, dynamic> evt) {
|
||||||
@ -307,10 +331,10 @@ class FileModel extends ChangeNotifier {
|
|||||||
_remoteOption.clear();
|
_remoteOption.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
refresh({bool? isLocal}) {
|
||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
openDirectory(currentRemoteDir.path);
|
isLocal = isLocal ?? _isLocal;
|
||||||
openDirectory(currentLocalDir.path);
|
isLocal ? openDirectory(currentLocalDir.path) : openDirectory(currentRemoteDir.path);
|
||||||
} else {
|
} else {
|
||||||
openDirectory(currentDir.path);
|
openDirectory(currentDir.path);
|
||||||
}
|
}
|
||||||
@ -344,7 +368,8 @@ class FileModel extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
goHome({bool? isLocal}) {
|
goHome({bool? isLocal}) {
|
||||||
openDirectory(currentHome, isLocal: isLocal);
|
isLocal = isLocal ?? _isLocal;
|
||||||
|
openDirectory(getCurrentHome(isLocal), isLocal: isLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
goToParentDirectory({bool? isLocal}) {
|
goToParentDirectory({bool? isLocal}) {
|
||||||
@ -598,7 +623,8 @@ class FileModel extends ChangeNotifier {
|
|||||||
_ffi.target?.bind.sessionRemoveAllEmptyDirs(id: '${_ffi.target?.id}', actId: _jobId, path: path, isRemote: !isLocal);
|
_ffi.target?.bind.sessionRemoveAllEmptyDirs(id: '${_ffi.target?.id}', actId: _jobId, path: path, isRemote: !isLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
createDir(String path) async {
|
createDir(String path, {bool? isLocal}) async {
|
||||||
|
isLocal = isLocal ?? this.isLocal;
|
||||||
_jobId++;
|
_jobId++;
|
||||||
_ffi.target?.bind.sessionCreateDir(id: '${_ffi.target?.id}', actId: _jobId, path: path, isRemote: !isLocal);
|
_ffi.target?.bind.sessionCreateDir(id: '${_ffi.target?.id}', actId: _jobId, path: path, isRemote: !isLocal);
|
||||||
}
|
}
|
||||||
@ -608,16 +634,20 @@ class FileModel extends ChangeNotifier {
|
|||||||
jobReset();
|
jobReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
changeSortStyle(SortBy sort, {bool? isLocal}) {
|
changeSortStyle(SortBy sort, {bool? isLocal, bool ascending = true}) {
|
||||||
_sortStyle = sort;
|
_sortStyle = sort;
|
||||||
if (isLocal == null) {
|
if (isLocal == null) {
|
||||||
// compatible for mobile logic
|
// compatible for mobile logic
|
||||||
_currentLocalDir.changeSortStyle(sort);
|
_currentLocalDir.changeSortStyle(sort, ascending: ascending);
|
||||||
_currentRemoteDir.changeSortStyle(sort);
|
_currentRemoteDir.changeSortStyle(sort, ascending: ascending);
|
||||||
|
_localSortStyle = sort; _localSortAscending = ascending;
|
||||||
|
_remoteSortStyle = sort; _remoteSortAscending = ascending;
|
||||||
} else if (isLocal) {
|
} else if (isLocal) {
|
||||||
_currentLocalDir.changeSortStyle(sort);
|
_currentLocalDir.changeSortStyle(sort, ascending: ascending);
|
||||||
|
_localSortStyle = sort; _localSortAscending = ascending;
|
||||||
} else {
|
} else {
|
||||||
_currentRemoteDir.changeSortStyle(sort);
|
_currentRemoteDir.changeSortStyle(sort, ascending: ascending);
|
||||||
|
_remoteSortStyle = sort; _remoteSortAscending = ascending;
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
@ -640,6 +670,8 @@ class FileModel extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
debugPrint("update folder files: ${info}");
|
debugPrint("update folder files: ${info}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get remoteSortAscending => _remoteSortAscending;
|
||||||
}
|
}
|
||||||
|
|
||||||
class JobResultListener<T> {
|
class JobResultListener<T> {
|
||||||
@ -809,8 +841,8 @@ class FileDirectory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changeSortStyle(SortBy sort) {
|
changeSortStyle(SortBy sort, {bool ascending = true}) {
|
||||||
entries = _sortList(entries, sort);
|
entries = _sortList(entries, sort, ascending);
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
@ -929,7 +961,7 @@ class DirectoryOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// code from file_manager pkg after edit
|
// code from file_manager pkg after edit
|
||||||
List<Entry> _sortList(List<Entry> list, SortBy sortType) {
|
List<Entry> _sortList(List<Entry> list, SortBy sortType, bool ascending) {
|
||||||
if (sortType == SortBy.Name) {
|
if (sortType == SortBy.Name) {
|
||||||
// making list of only folders.
|
// making list of only folders.
|
||||||
final dirs = list.where((element) => element.isDirectory).toList();
|
final dirs = list.where((element) => element.isDirectory).toList();
|
||||||
@ -942,7 +974,7 @@ List<Entry> _sortList(List<Entry> list, SortBy sortType) {
|
|||||||
files.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
|
files.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
|
||||||
|
|
||||||
// first folders will go to list (if available) then files will go to list.
|
// first folders will go to list (if available) then files will go to list.
|
||||||
return [...dirs, ...files];
|
return ascending ? [...dirs, ...files] : [...dirs.reversed.toList(), ...files.reversed.toList()];
|
||||||
} else if (sortType == SortBy.Modified) {
|
} else if (sortType == SortBy.Modified) {
|
||||||
// making the list of Path & DateTime
|
// making the list of Path & DateTime
|
||||||
List<_PathStat> _pathStat = [];
|
List<_PathStat> _pathStat = [];
|
||||||
@ -957,7 +989,7 @@ List<Entry> _sortList(List<Entry> list, SortBy sortType) {
|
|||||||
list.sort((a, b) => _pathStat
|
list.sort((a, b) => _pathStat
|
||||||
.indexWhere((element) => element.path == a.name)
|
.indexWhere((element) => element.path == a.name)
|
||||||
.compareTo(_pathStat.indexWhere((element) => element.path == b.name)));
|
.compareTo(_pathStat.indexWhere((element) => element.path == b.name)));
|
||||||
return list;
|
return ascending ? list : list.reversed.toList();
|
||||||
} else if (sortType == SortBy.Type) {
|
} else if (sortType == SortBy.Type) {
|
||||||
// making list of only folders.
|
// making list of only folders.
|
||||||
final dirs = list.where((element) => element.isDirectory).toList();
|
final dirs = list.where((element) => element.isDirectory).toList();
|
||||||
@ -974,7 +1006,7 @@ List<Entry> _sortList(List<Entry> list, SortBy sortType) {
|
|||||||
.split('.')
|
.split('.')
|
||||||
.last
|
.last
|
||||||
.compareTo(b.name.toLowerCase().split('.').last));
|
.compareTo(b.name.toLowerCase().split('.').last));
|
||||||
return [...dirs, ...files];
|
return ascending ? [...dirs, ...files]: [...dirs.reversed.toList(), ...files.reversed.toList()];
|
||||||
} else if (sortType == SortBy.Size) {
|
} else if (sortType == SortBy.Size) {
|
||||||
// create list of path and size
|
// create list of path and size
|
||||||
Map<String, int> _sizeMap = {};
|
Map<String, int> _sizeMap = {};
|
||||||
@ -999,7 +1031,7 @@ List<Entry> _sortList(List<Entry> list, SortBy sortType) {
|
|||||||
.indexWhere((element) => element.key == a.name)
|
.indexWhere((element) => element.key == a.name)
|
||||||
.compareTo(
|
.compareTo(
|
||||||
_sizeMapList.indexWhere((element) => element.key == b.name)));
|
_sizeMapList.indexWhere((element) => element.key == b.name)));
|
||||||
return [...dirs, ...files];
|
return ascending ? [...dirs, ...files]: [...dirs.reversed.toList(), ...files.reversed.toList()];
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,10 @@ void wire_session_peer_option(int64_t port_,
|
|||||||
struct wire_uint_8_list *name,
|
struct wire_uint_8_list *name,
|
||||||
struct wire_uint_8_list *value);
|
struct wire_uint_8_list *value);
|
||||||
|
|
||||||
|
void wire_session_get_peer_option(int64_t port_,
|
||||||
|
struct wire_uint_8_list *id,
|
||||||
|
struct wire_uint_8_list *name);
|
||||||
|
|
||||||
void wire_session_input_os_password(int64_t port_,
|
void wire_session_input_os_password(int64_t port_,
|
||||||
struct wire_uint_8_list *id,
|
struct wire_uint_8_list *id,
|
||||||
struct wire_uint_8_list *value);
|
struct wire_uint_8_list *value);
|
||||||
@ -139,7 +143,8 @@ void wire_session_read_dir_recursive(int64_t port_,
|
|||||||
struct wire_uint_8_list *id,
|
struct wire_uint_8_list *id,
|
||||||
int32_t act_id,
|
int32_t act_id,
|
||||||
struct wire_uint_8_list *path,
|
struct wire_uint_8_list *path,
|
||||||
bool is_remote);
|
bool is_remote,
|
||||||
|
bool show_hidden);
|
||||||
|
|
||||||
void wire_session_remove_all_empty_dirs(int64_t port_,
|
void wire_session_remove_all_empty_dirs(int64_t port_,
|
||||||
struct wire_uint_8_list *id,
|
struct wire_uint_8_list *id,
|
||||||
@ -197,6 +202,7 @@ static int64_t dummy_method_to_enforce_bundling(void) {
|
|||||||
dummy_var ^= ((int64_t) (void*) wire_session_send_chat);
|
dummy_var ^= ((int64_t) (void*) wire_session_send_chat);
|
||||||
dummy_var ^= ((int64_t) (void*) wire_session_send_mouse);
|
dummy_var ^= ((int64_t) (void*) wire_session_send_mouse);
|
||||||
dummy_var ^= ((int64_t) (void*) wire_session_peer_option);
|
dummy_var ^= ((int64_t) (void*) wire_session_peer_option);
|
||||||
|
dummy_var ^= ((int64_t) (void*) wire_session_get_peer_option);
|
||||||
dummy_var ^= ((int64_t) (void*) wire_session_input_os_password);
|
dummy_var ^= ((int64_t) (void*) wire_session_input_os_password);
|
||||||
dummy_var ^= ((int64_t) (void*) wire_session_read_remote_dir);
|
dummy_var ^= ((int64_t) (void*) wire_session_read_remote_dir);
|
||||||
dummy_var ^= ((int64_t) (void*) wire_session_send_files);
|
dummy_var ^= ((int64_t) (void*) wire_session_send_files);
|
||||||
|
@ -5,6 +5,8 @@ use std::{
|
|||||||
|
|
||||||
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
|
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
|
||||||
|
|
||||||
|
use hbb_common::config::PeerConfig;
|
||||||
|
use hbb_common::fs::TransferJobMeta;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
compress::decompress,
|
compress::decompress,
|
||||||
@ -464,6 +466,41 @@ impl Session {
|
|||||||
log::debug!("{:?}", msg_out);
|
log::debug!("{:?}", msg_out);
|
||||||
self.send_msg(msg_out);
|
self.send_msg(msg_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_config(&self) -> PeerConfig {
|
||||||
|
load_config(&self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_platform(&self, is_remote: bool) -> String {
|
||||||
|
if is_remote {
|
||||||
|
self.lc.read().unwrap().info.platform.clone()
|
||||||
|
} else {
|
||||||
|
whoami::platform().to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_last_jobs(&self) {
|
||||||
|
let pc = self.load_config();
|
||||||
|
if pc.transfer.write_jobs.is_empty() && pc.transfer.read_jobs.is_empty() {
|
||||||
|
// no last jobs
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut cnt = 1;
|
||||||
|
for job_str in pc.transfer.read_jobs.iter() {
|
||||||
|
if !job_str.is_empty() {
|
||||||
|
self.push_event("addJob", vec![("value", job_str)]);
|
||||||
|
cnt += 1;
|
||||||
|
println!("restore read_job: {:?}", job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for job_str in pc.transfer.write_jobs.iter() {
|
||||||
|
if !job_str.is_empty() {
|
||||||
|
self.push_event("addJob", vec![("value", job_str)]);
|
||||||
|
cnt += 1;
|
||||||
|
println!("restore write_job: {:?}", job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileManager for Session {}
|
impl FileManager for Session {}
|
||||||
|
@ -339,6 +339,19 @@ pub fn session_read_local_dir_sync(id: String, path: String, show_hidden: bool)
|
|||||||
"".to_string()
|
"".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn session_get_platform(id: String, is_remote: bool) -> String {
|
||||||
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
|
return session.get_platform(is_remote);
|
||||||
|
}
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn session_load_last_transfer_jobs(id: String) {
|
||||||
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
|
return session.load_last_jobs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// FFI for **get** commands which are idempotent.
|
/// FFI for **get** commands which are idempotent.
|
||||||
/// Return result in c string.
|
/// Return result in c string.
|
||||||
///
|
///
|
||||||
|
Loading…
x
Reference in New Issue
Block a user