mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-23 19:49:05 +08:00
parent
29b01e9cef
commit
eb1ef0969c
@ -30,6 +30,7 @@ import 'common/widgets/overlay.dart';
|
||||
import 'mobile/pages/file_manager_page.dart';
|
||||
import 'mobile/pages/remote_page.dart';
|
||||
import 'desktop/pages/remote_page.dart' as desktop_remote;
|
||||
import 'desktop/pages/file_manager_page.dart' as desktop_file_manager;
|
||||
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
|
||||
import 'models/model.dart';
|
||||
import 'models/platform_model.dart';
|
||||
@ -2370,18 +2371,33 @@ connect(BuildContext context, String id,
|
||||
}
|
||||
} else {
|
||||
if (isFileTransfer) {
|
||||
if (!await AndroidPermissionManager.check(kManageExternalStorage)) {
|
||||
if (!await AndroidPermissionManager.request(kManageExternalStorage)) {
|
||||
return;
|
||||
if (isAndroid) {
|
||||
if (!await AndroidPermissionManager.check(kManageExternalStorage)) {
|
||||
if (!await AndroidPermissionManager.request(kManageExternalStorage)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) => FileManagerPage(
|
||||
id: id, password: password, isSharedPassword: isSharedPassword),
|
||||
),
|
||||
);
|
||||
if (isWeb) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) =>
|
||||
desktop_file_manager.FileManagerPage(
|
||||
id: id,
|
||||
password: password,
|
||||
isSharedPassword: isSharedPassword),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) => FileManagerPage(
|
||||
id: id, password: password, isSharedPassword: isSharedPassword),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (isWeb) {
|
||||
Navigator.push(
|
||||
|
@ -879,7 +879,7 @@ class RecentPeerCard extends BasePeerCard {
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
if (!isWeb) _transferFileAction(context),
|
||||
_transferFileAction(context),
|
||||
];
|
||||
|
||||
final List favs = (await bind.mainGetFav()).toList();
|
||||
|
@ -17,6 +17,8 @@ import 'package:flutter_hbb/models/file_model.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
import 'package:flutter_hbb/web/dummy.dart'
|
||||
if (dart.library.html) 'package:flutter_hbb/web/web_unique.dart';
|
||||
|
||||
import '../../consts.dart';
|
||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||
@ -55,14 +57,14 @@ class FileManagerPage extends StatefulWidget {
|
||||
required this.id,
|
||||
required this.password,
|
||||
required this.isSharedPassword,
|
||||
required this.tabController,
|
||||
this.tabController,
|
||||
this.forceRelay})
|
||||
: super(key: key);
|
||||
final String id;
|
||||
final String? password;
|
||||
final bool? isSharedPassword;
|
||||
final bool? forceRelay;
|
||||
final DesktopTabController tabController;
|
||||
final DesktopTabController? tabController;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _FileManagerPageState();
|
||||
@ -97,11 +99,14 @@ class _FileManagerPageState extends State<FileManagerPage>
|
||||
if (!isLinux) {
|
||||
WakelockPlus.enable();
|
||||
}
|
||||
if (isWeb) {
|
||||
_ffi.ffiModel.updateEventListener(_ffi.sessionId, widget.id);
|
||||
}
|
||||
debugPrint("File manager page init success with id ${widget.id}");
|
||||
_ffi.dialogManager.setOverlayState(_overlayKeyState);
|
||||
// Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState.
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
widget.tabController.onSelected?.call(widget.id);
|
||||
widget.tabController?.onSelected?.call(widget.id);
|
||||
});
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
@ -140,10 +145,11 @@ class _FileManagerPageState extends State<FileManagerPage>
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
body: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 3,
|
||||
child: dropArea(FileManagerView(
|
||||
model.localController, _ffi, _mouseFocusScope))),
|
||||
if (!isWeb)
|
||||
Flexible(
|
||||
flex: 3,
|
||||
child: dropArea(FileManagerView(
|
||||
model.localController, _ffi, _mouseFocusScope))),
|
||||
Flexible(
|
||||
flex: 3,
|
||||
child: dropArea(FileManagerView(
|
||||
@ -192,7 +198,13 @@ class _FileManagerPageState extends State<FileManagerPage>
|
||||
return Icon(Icons.delete_outline, color: color);
|
||||
default:
|
||||
return Transform.rotate(
|
||||
angle: job.isRemoteToLocal ? pi : 0,
|
||||
angle: isWeb
|
||||
? job.isRemoteToLocal
|
||||
? pi / 2
|
||||
: pi / 2 * 3
|
||||
: job.isRemoteToLocal
|
||||
? pi
|
||||
: 0,
|
||||
child: Icon(Icons.arrow_forward_ios, color: color),
|
||||
);
|
||||
}
|
||||
@ -800,6 +812,50 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isWeb)
|
||||
Obx(() => ElevatedButton.icon(
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
|
||||
isLocal
|
||||
? EdgeInsets.only(left: 10)
|
||||
: EdgeInsets.only(right: 10)),
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
selectedItems.items.isEmpty
|
||||
? MyTheme.accent80
|
||||
: MyTheme.accent,
|
||||
),
|
||||
),
|
||||
onPressed: () => {webselectFiles(is_folder: true)},
|
||||
icon: Offstage(),
|
||||
label: Text(
|
||||
translate('Upload folder'),
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
),
|
||||
))).marginOnly(left: 16),
|
||||
if (isWeb)
|
||||
Obx(() => ElevatedButton.icon(
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
|
||||
isLocal
|
||||
? EdgeInsets.only(left: 10)
|
||||
: EdgeInsets.only(right: 10)),
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
selectedItems.items.isEmpty
|
||||
? MyTheme.accent80
|
||||
: MyTheme.accent,
|
||||
),
|
||||
),
|
||||
onPressed: () => {webselectFiles(is_folder: false)},
|
||||
icon: Offstage(),
|
||||
label: Text(
|
||||
translate('Upload files'),
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
),
|
||||
))).marginOnly(left: 16),
|
||||
Obx(() => ElevatedButton.icon(
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
|
||||
@ -833,19 +889,22 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
: Colors.white,
|
||||
),
|
||||
)
|
||||
: RotatedBox(
|
||||
quarterTurns: 2,
|
||||
child: SvgPicture.asset(
|
||||
"assets/arrow.svg",
|
||||
colorFilter: svgColor(selectedItems.items.isEmpty
|
||||
? Theme.of(context).brightness ==
|
||||
Brightness.light
|
||||
? MyTheme.grayBg
|
||||
: MyTheme.darkGray
|
||||
: Colors.white),
|
||||
alignment: Alignment.bottomRight,
|
||||
),
|
||||
),
|
||||
: isWeb
|
||||
? Offstage()
|
||||
: RotatedBox(
|
||||
quarterTurns: 2,
|
||||
child: SvgPicture.asset(
|
||||
"assets/arrow.svg",
|
||||
colorFilter: svgColor(
|
||||
selectedItems.items.isEmpty
|
||||
? Theme.of(context).brightness ==
|
||||
Brightness.light
|
||||
? MyTheme.grayBg
|
||||
: MyTheme.darkGray
|
||||
: Colors.white),
|
||||
alignment: Alignment.bottomRight,
|
||||
),
|
||||
),
|
||||
label: isLocal
|
||||
? SvgPicture.asset(
|
||||
"assets/arrow.svg",
|
||||
@ -857,7 +916,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
||||
: Colors.white),
|
||||
)
|
||||
: Text(
|
||||
translate('Receive'),
|
||||
translate(isWeb ? 'Download' : 'Receive'),
|
||||
style: TextStyle(
|
||||
color: selectedItems.items.isEmpty
|
||||
? Theme.of(context).brightness ==
|
||||
|
@ -7,6 +7,8 @@ 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;
|
||||
import 'package:flutter_hbb/web/dummy.dart'
|
||||
if (dart.library.html) 'package:flutter_hbb/web/web_unique.dart';
|
||||
|
||||
import '../consts.dart';
|
||||
import 'model.dart';
|
||||
@ -74,7 +76,7 @@ class FileModel {
|
||||
|
||||
Future<void> onReady() async {
|
||||
await evtLoop.onReady();
|
||||
await localController.onReady();
|
||||
if (!isWeb) await localController.onReady();
|
||||
await remoteController.onReady();
|
||||
}
|
||||
|
||||
@ -86,7 +88,7 @@ class FileModel {
|
||||
}
|
||||
|
||||
Future<void> refreshAll() async {
|
||||
await localController.refresh();
|
||||
if (!isWeb) await localController.refresh();
|
||||
await remoteController.refresh();
|
||||
}
|
||||
|
||||
@ -228,6 +230,33 @@ class FileModel {
|
||||
);
|
||||
}, useAnimation: false);
|
||||
}
|
||||
|
||||
void onSelectedFiles(dynamic obj) {
|
||||
localController.selectedItems.clear();
|
||||
|
||||
try {
|
||||
int handleIndex = int.parse(obj['handleIndex']);
|
||||
final file = jsonDecode(obj['file']);
|
||||
var entry = Entry.fromJson(file);
|
||||
entry.path = entry.name;
|
||||
final otherSideData = remoteController.directoryData();
|
||||
final toPath = otherSideData.directory.path;
|
||||
final isWindows = otherSideData.options.isWindows;
|
||||
final showHidden = otherSideData.options.showHidden;
|
||||
final jobID = jobController.addTransferJob(entry, false);
|
||||
webSendLocalFiles(
|
||||
handleIndex: handleIndex,
|
||||
actId: jobID,
|
||||
path: entry.path,
|
||||
to: PathUtil.join(toPath, entry.name, isWindows),
|
||||
fileNum: 0,
|
||||
includeHidden: showHidden,
|
||||
isRemote: false,
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint("Failed to decode onSelectedFiles: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DirectoryData {
|
||||
@ -462,7 +491,8 @@ class FileController {
|
||||
to: PathUtil.join(toPath, from.name, isWindows),
|
||||
fileNum: 0,
|
||||
includeHidden: showHidden,
|
||||
isRemote: isRemoteToLocal);
|
||||
isRemote: isRemoteToLocal,
|
||||
isDir: from.isDirectory);
|
||||
debugPrint(
|
||||
"path: ${from.path}, toPath: $toPath, to: ${PathUtil.join(toPath, from.name, isWindows)}");
|
||||
}
|
||||
@ -489,7 +519,7 @@ class FileController {
|
||||
} else if (item.isDirectory) {
|
||||
title = translate("Not an empty directory");
|
||||
dialogManager?.showLoading(translate("Waiting"));
|
||||
final fd = await fileFetcher.fetchDirectoryRecursive(
|
||||
final fd = await fileFetcher.fetchDirectoryRecursiveToRemove(
|
||||
jobID, item.path, items.isLocal, true);
|
||||
if (fd.path.isEmpty) {
|
||||
fd.path = item.path;
|
||||
@ -809,7 +839,6 @@ class JobController {
|
||||
job.speed = double.parse(evt['speed']);
|
||||
job.finishedSize = int.parse(evt['finished_size']);
|
||||
job.recvJobRes = true;
|
||||
debugPrint("update job $id with $evt");
|
||||
jobTable.refresh();
|
||||
}
|
||||
} catch (e) {
|
||||
@ -1116,11 +1145,11 @@ class FileFetcher {
|
||||
}
|
||||
}
|
||||
|
||||
Future<FileDirectory> fetchDirectoryRecursive(
|
||||
Future<FileDirectory> fetchDirectoryRecursiveToRemove(
|
||||
int actID, String path, bool isLocal, bool showHidden) async {
|
||||
// TODO test Recursive is show hidden default?
|
||||
try {
|
||||
await bind.sessionReadDirRecursive(
|
||||
await bind.sessionReadDirToRemoveRecursive(
|
||||
sessionId: sessionId,
|
||||
actId: actID,
|
||||
path: path,
|
||||
|
@ -390,6 +390,10 @@ class FfiModel with ChangeNotifier {
|
||||
handleFollowCurrentDisplay(evt, sessionId, peerId);
|
||||
} else if (name == 'use_texture_render') {
|
||||
_handleUseTextureRender(evt, sessionId, peerId);
|
||||
} else if (name == "selected_files") {
|
||||
if (isWeb) {
|
||||
parent.target?.fileModel.onSelectedFiles(evt);
|
||||
}
|
||||
} else {
|
||||
debugPrint('Event is not handled in the fixed branch: $name');
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
14
flutter/lib/web/dummy.dart
Normal file
14
flutter/lib/web/dummy.dart
Normal file
@ -0,0 +1,14 @@
|
||||
Future<void> webselectFiles({required bool is_folder}) async {
|
||||
throw UnimplementedError("webselectFiles");
|
||||
}
|
||||
|
||||
Future<void> webSendLocalFiles(
|
||||
{required int handleIndex,
|
||||
required int actId,
|
||||
required String path,
|
||||
required String to,
|
||||
required int fileNum,
|
||||
required bool includeHidden,
|
||||
required bool isRemote}) {
|
||||
throw UnimplementedError("webSendLocalFiles");
|
||||
}
|
30
flutter/lib/web/web_unique.dart
Normal file
30
flutter/lib/web/web_unique.dart
Normal file
@ -0,0 +1,30 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:js' as js;
|
||||
|
||||
Future<void> webselectFiles({required bool is_folder}) async {
|
||||
return Future(
|
||||
() => js.context.callMethod('setByName', ['select_files', is_folder]));
|
||||
}
|
||||
|
||||
Future<void> webSendLocalFiles(
|
||||
{required int handleIndex,
|
||||
required int actId,
|
||||
required String path,
|
||||
required String to,
|
||||
required int fileNum,
|
||||
required bool includeHidden,
|
||||
required bool isRemote}) {
|
||||
return Future(() => js.context.callMethod('setByName', [
|
||||
'send_local_files',
|
||||
jsonEncode({
|
||||
'id': actId,
|
||||
'handle_index': handleIndex,
|
||||
'path': path,
|
||||
'to': to,
|
||||
'file_num': fileNum,
|
||||
'include_hidden': includeHidden,
|
||||
'is_remote': isRemote,
|
||||
})
|
||||
]));
|
||||
}
|
@ -602,6 +602,7 @@ pub fn session_send_files(
|
||||
file_num: i32,
|
||||
include_hidden: bool,
|
||||
is_remote: bool,
|
||||
_is_dir: bool,
|
||||
) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.send_files(act_id, path, to, file_num, include_hidden, is_remote);
|
||||
@ -633,7 +634,7 @@ pub fn session_remove_file(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_read_dir_recursive(
|
||||
pub fn session_read_dir_to_remove_recursive(
|
||||
session_id: SessionID,
|
||||
act_id: i32,
|
||||
path: String,
|
||||
|
Loading…
Reference in New Issue
Block a user