diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 8feb2402f..881cf3b8b 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -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( diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 8dd54fb1a..690c82977 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -879,7 +879,7 @@ class RecentPeerCard extends BasePeerCard { BuildContext context) async { final List> menuItems = [ _connectAction(context), - if (!isWeb) _transferFileAction(context), + _transferFileAction(context), ]; final List favs = (await bind.mainGetFav()).toList(); diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index b75a946c0..5557783b5 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -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 createState() => _FileManagerPageState(); @@ -97,11 +99,14 @@ class _FileManagerPageState extends State 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 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 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 { ], ), ), + if (isWeb) + Obx(() => ElevatedButton.icon( + style: ButtonStyle( + padding: MaterialStateProperty.all( + 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( + 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( @@ -833,19 +889,22 @@ class _FileManagerViewState extends State { : 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 { : Colors.white), ) : Text( - translate('Receive'), + translate(isWeb ? 'Download' : 'Receive'), style: TextStyle( color: selectedItems.items.isEmpty ? Theme.of(context).brightness == diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index a0d5bc0b5..05c79ae86 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -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 onReady() async { await evtLoop.onReady(); - await localController.onReady(); + if (!isWeb) await localController.onReady(); await remoteController.onReady(); } @@ -86,7 +88,7 @@ class FileModel { } Future 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 fetchDirectoryRecursive( + Future 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, diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 8466443ee..2833f3834 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -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'); } diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index adcbadb30..0362490cd 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -52,12 +52,12 @@ class EventToUI_Texture implements EventToUI { class RustdeskImpl { Future stopGlobalEventStream({required String appType, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("stopGlobalEventStream"); } Future hostStopSystemKeyPropagate( {required bool stopped, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("hostStopSystemKeyPropagate"); } int peerGetDefaultSessionsCount({required String id, dynamic hint}) { @@ -88,7 +88,8 @@ class RustdeskImpl { jsonEncode({ 'id': id, 'password': password, - 'is_shared_password': isSharedPassword + 'is_shared_password': isSharedPassword, + 'isFileTransfer': isFileTransfer }) ]); } @@ -107,7 +108,7 @@ class RustdeskImpl { required String id, required Int32List displays, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionStartWithDisplays"); } Future sessionGetRemember( @@ -178,12 +179,12 @@ class RustdeskImpl { required int width, required int height, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionRecordScreen"); } Future sessionRecordStatus( {required UuidValue sessionId, required bool status, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionRecordStatus"); } Future sessionReconnect( @@ -435,7 +436,7 @@ class RustdeskImpl { required int lockModes, required bool downOrUp, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionHandleFlutterRawKeyEvent"); } void sessionEnterOrLeave( @@ -507,7 +508,10 @@ class RustdeskImpl { required String path, required bool includeHidden, dynamic hint}) { - throw UnimplementedError(); + return Future(() => js.context.callMethod('setByName', [ + 'read_remote_dir', + jsonEncode({'path': path, 'include_hidden': includeHidden}) + ])); } Future sessionSendFiles( @@ -518,8 +522,20 @@ class RustdeskImpl { required int fileNum, required bool includeHidden, required bool isRemote, + required bool isDir, dynamic hint}) { - throw UnimplementedError(); + return Future(() => js.context.callMethod('setByName', [ + 'send_files', + jsonEncode({ + 'id': actId, + 'path': path, + 'to': to, + 'file_num': fileNum, + 'include_hidden': includeHidden, + 'is_remote': isRemote, + 'is_dir': isDir, + }) + ])); } Future sessionSetConfirmOverrideFile( @@ -530,7 +546,16 @@ class RustdeskImpl { required bool remember, required bool isUpload, dynamic hint}) { - throw UnimplementedError(); + return Future(() => js.context.callMethod('setByName', [ + 'confirm_override_file', + jsonEncode({ + 'id': actId, + 'file_num': fileNum, + 'need_override': needOverride, + 'remember': remember, + 'is_upload': isUpload + }) + ])); } Future sessionRemoveFile( @@ -540,17 +565,33 @@ class RustdeskImpl { required int fileNum, required bool isRemote, dynamic hint}) { - throw UnimplementedError(); + return Future(() => js.context.callMethod('setByName', [ + 'remove_file', + jsonEncode({ + 'id': actId, + 'path': path, + 'file_num': fileNum, + 'is_remote': isRemote + }) + ])); } - Future sessionReadDirRecursive( + Future sessionReadDirToRemoveRecursive( {required UuidValue sessionId, required int actId, required String path, required bool isRemote, required bool showHidden, dynamic hint}) { - throw UnimplementedError(); + return Future(() => js.context.callMethod('setByName', [ + 'read_dir_to_remove_recursive', + jsonEncode({ + 'id': actId, + 'path': path, + 'is_remote': isRemote, + 'show_hidden': showHidden + }) + ])); } Future sessionRemoveAllEmptyDirs( @@ -559,12 +600,16 @@ class RustdeskImpl { required String path, required bool isRemote, dynamic hint}) { - throw UnimplementedError(); + return Future(() => js.context.callMethod('setByName', [ + 'remove_all_empty_dirs', + jsonEncode({'id': actId, 'path': path, 'is_remote': isRemote}) + ])); } Future sessionCancelJob( {required UuidValue sessionId, required int actId, dynamic hint}) { - throw UnimplementedError(); + return Future( + () => js.context.callMethod('setByName', ['cancel_job', actId])); } Future sessionCreateDir( @@ -573,7 +618,10 @@ class RustdeskImpl { required String path, required bool isRemote, dynamic hint}) { - throw UnimplementedError(); + return Future(() => js.context.callMethod('setByName', [ + 'create_dir', + jsonEncode({'id': actId, 'path': path, 'is_remote': isRemote}) + ])); } Future sessionReadLocalDirSync( @@ -581,17 +629,21 @@ class RustdeskImpl { required String path, required bool showHidden, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionReadLocalDirSync"); } Future sessionGetPlatform( {required UuidValue sessionId, required bool isRemote, dynamic hint}) { - throw UnimplementedError(); + if (isRemote) { + return Future(() => js.context.callMethod('getByName', ['platform'])); + } else { + return Future(() => 'Web'); + } } Future sessionLoadLastTransferJobs( {required UuidValue sessionId, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionLoadLastTransferJobs"); } Future sessionAddJob( @@ -603,7 +655,7 @@ class RustdeskImpl { required bool includeHidden, required bool isRemote, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionAddJob"); } Future sessionResumeJob( @@ -611,7 +663,7 @@ class RustdeskImpl { required int actId, required bool isRemote, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionResumeJob"); } Future sessionElevateDirect( @@ -632,7 +684,7 @@ class RustdeskImpl { Future sessionSwitchSides( {required UuidValue sessionId, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionSwitchSides"); } Future sessionChangeResolution( @@ -642,7 +694,7 @@ class RustdeskImpl { required int height, dynamic hint}) { // note: restore on disconnected - throw UnimplementedError(); + throw UnimplementedError("sessionChangeResolution"); } Future sessionSetSize( @@ -656,15 +708,15 @@ class RustdeskImpl { Future sessionSendSelectedSessionId( {required UuidValue sessionId, required String sid, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionSendSelectedSessionId"); } Future> mainGetSoundInputs({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetSoundInputs"); } Future mainGetDefaultSoundInput({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetDefaultSoundInput"); } String mainGetLoginDeviceInfo({dynamic hint}) { @@ -680,11 +732,11 @@ class RustdeskImpl { } Future mainChangeId({required String newId, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainChangeId"); } Future mainGetAsyncStatus({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetAsyncStatus"); } Future mainGetOption({required String key, dynamic hint}) { @@ -696,11 +748,11 @@ class RustdeskImpl { } Future mainGetError({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetError"); } bool mainShowOption({required String key, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainShowOption"); } Future mainSetOption( @@ -737,11 +789,11 @@ class RustdeskImpl { required String username, required String password, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainSetSocks"); } Future> mainGetSocks({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetSocks"); } Future mainGetAppName({dynamic hint}) { @@ -753,7 +805,7 @@ class RustdeskImpl { } String mainUriPrefixSync({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainUriPrefixSync"); } Future mainGetLicense({dynamic hint}) { @@ -785,11 +837,11 @@ class RustdeskImpl { String mainGetPeerSync({required String id, dynamic hint}) { // TODO: - throw UnimplementedError(); + throw UnimplementedError("mainGetPeerSync"); } Future mainGetLanPeers({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetLanPeers"); } Future mainGetConnectStatus({dynamic hint}) { @@ -798,7 +850,7 @@ class RustdeskImpl { } Future mainCheckConnectStatus({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainCheckConnectStatus"); } Future mainIsUsingPublicServer({dynamic hint}) { @@ -808,7 +860,7 @@ class RustdeskImpl { } Future mainDiscover({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainDiscover"); } Future mainGetApiServer({dynamic hint}) { @@ -820,7 +872,7 @@ class RustdeskImpl { required String body, required String header, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainPostRequest"); } Future mainGetProxyStatus({dynamic hint}) { @@ -834,11 +886,11 @@ class RustdeskImpl { required String header, dynamic hint, }) { - throw UnimplementedError(); + throw UnimplementedError("mainHttpRequest"); } Future mainGetHttpStatus({required String url, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetHttpStatus"); } String mainGetLocalOption({required String key, dynamic hint}) { @@ -846,7 +898,7 @@ class RustdeskImpl { } String mainGetEnv({required String key, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetEnv"); } Future mainSetLocalOption( @@ -940,7 +992,7 @@ class RustdeskImpl { } Future mainGetNewStoredPeers({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetNewStoredPeers"); } Future mainForgetPassword({required String id, dynamic hint}) { @@ -973,7 +1025,7 @@ class RustdeskImpl { Future mainLoadRecentPeersForAb( {required String filter, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainLoadRecentPeersForAb"); } Future mainLoadFavPeers({dynamic hint}) { @@ -981,23 +1033,23 @@ class RustdeskImpl { } Future mainLoadLanPeers({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainLoadLanPeers"); } Future mainRemoveDiscovered({required String id, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainRemoveDiscovered"); } Future mainChangeTheme({required String dark, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainChangeTheme"); } Future mainChangeLanguage({required String lang, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainChangeLanguage"); } String mainVideoSaveDirectory({required bool root, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainVideoSaveDirectory"); } Future mainSetUserDefaultOption( @@ -1026,7 +1078,7 @@ class RustdeskImpl { } String mainGetDisplays({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetDisplays"); } Future sessionAddPortForward( @@ -1035,35 +1087,35 @@ class RustdeskImpl { required String remoteHost, required int remotePort, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionAddPortForward"); } Future sessionRemovePortForward( {required UuidValue sessionId, required int localPort, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionRemovePortForward"); } Future sessionNewRdp({required UuidValue sessionId, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionNewRdp"); } Future sessionRequestVoiceCall( {required UuidValue sessionId, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionRequestVoiceCall"); } Future sessionCloseVoiceCall( {required UuidValue sessionId, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionCloseVoiceCall"); } Future cmHandleIncomingVoiceCall( {required int id, required bool accept, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmHandleIncomingVoiceCall"); } Future cmCloseVoiceCall({required int id, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmCloseVoiceCall"); } Future mainGetLastRemoteId({dynamic hint}) { @@ -1072,7 +1124,7 @@ class RustdeskImpl { } Future mainGetSoftwareUpdateUrl({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetSoftwareUpdateUrl"); } Future mainGetHomeDir({dynamic hint}) { @@ -1096,15 +1148,15 @@ class RustdeskImpl { } Future cmGetClientsState({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmGetClientsState"); } Future cmCheckClientsLength({required int length, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmCheckClientsLength"); } Future cmGetClientsLength({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmCheckClientsLength"); } Future mainInit({required String appDir, dynamic hint}) { @@ -1113,12 +1165,12 @@ class RustdeskImpl { Future mainDeviceId({required String id, dynamic hint}) { // TODO: ? - throw UnimplementedError(); + throw UnimplementedError("mainDeviceId"); } Future mainDeviceName({required String name, dynamic hint}) { // TODO: ? - throw UnimplementedError(); + throw UnimplementedError("mainDeviceName"); } Future mainRemovePeer({required String id, dynamic hint}) { @@ -1127,11 +1179,11 @@ class RustdeskImpl { } bool mainHasHwcodec({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainHasHwcodec"); } bool mainHasVram({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainHasVram"); } String mainSupportedHwdecodings({dynamic hint}) { @@ -1139,15 +1191,15 @@ class RustdeskImpl { } Future mainIsRoot({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainIsRoot"); } int getDoubleClickTime({dynamic hint}) { - throw UnimplementedError(); + return 500; } Future mainStartDbusServer({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainStartDbusServer"); } Future mainSaveAb({required String json, dynamic hint}) { @@ -1177,7 +1229,7 @@ class RustdeskImpl { Future sessionSendPointer( {required UuidValue sessionId, required String msg, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionSendPointer"); } Future sessionSendMouse( @@ -1231,76 +1283,75 @@ class RustdeskImpl { } Future mainSetHomeDir({required String home, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainSetHomeDir"); } String mainGetDataDirIos({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetDataDirIos"); } Future mainStopService({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainStopService"); } Future mainStartService({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainStartService"); } Future mainUpdateTemporaryPassword({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainUpdateTemporaryPassword"); } Future mainSetPermanentPassword( {required String password, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainSetPermanentPassword"); } Future mainCheckSuperUserPermission({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainCheckSuperUserPermission"); } Future mainCheckMouseTime({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainCheckMouseTime"); } Future mainGetMouseTime({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetMouseTime"); } Future mainWol({required String id, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainWol"); } Future mainCreateShortcut({required String id, dynamic hint}) { - // TODO: - throw UnimplementedError(); + throw UnimplementedError("mainCreateShortcut"); } Future cmSendChat( {required int connId, required String msg, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmSendChat"); } Future cmLoginRes( {required int connId, required bool res, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmLoginRes"); } Future cmCloseConnection({required int connId, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmCloseConnection"); } Future cmRemoveDisconnectedConnection( {required int connId, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmRemoveDisconnectedConnection"); } Future cmCheckClickTime({required int connId, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmCheckClickTime"); } Future cmGetClickTime({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmGetClickTime"); } Future cmSwitchPermission( @@ -1308,23 +1359,23 @@ class RustdeskImpl { required String name, required bool enabled, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmSwitchPermission"); } bool cmCanElevate({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmCanElevate"); } Future cmElevatePortable({required int connId, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmElevatePortable"); } Future cmSwitchBack({required int connId, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmSwitchBack"); } Future cmGetConfig({required String name, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmGetConfig"); } Future mainGetBuildDate({dynamic hint}) { @@ -1377,89 +1428,89 @@ class RustdeskImpl { } bool mainIsInstalled({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainIsInstalled"); } void mainInitInputSource({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainIsInstalled"); } bool mainIsInstalledLowerVersion({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainIsInstalledLowerVersion"); } bool mainIsInstalledDaemon({required bool prompt, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainIsInstalledDaemon"); } bool mainIsProcessTrusted({required bool prompt, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainIsProcessTrusted"); } bool mainIsCanScreenRecording({required bool prompt, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainIsCanScreenRecording"); } bool mainIsCanInputMonitoring({required bool prompt, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainIsCanInputMonitoring"); } bool mainIsShareRdp({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainIsShareRdp"); } Future mainSetShareRdp({required bool enable, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainSetShareRdp"); } bool mainGotoInstall({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGotoInstall"); } String mainGetNewVersion({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetNewVersion"); } bool mainUpdateMe({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainUpdateMe"); } Future setCurSessionId({required UuidValue sessionId, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("setCurSessionId"); } bool installShowRunWithoutInstall({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("installShowRunWithoutInstall"); } Future installRunWithoutInstall({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("installRunWithoutInstall"); } Future installInstallMe( {required String options, required String path, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("installInstallMe"); } String installInstallPath({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("installInstallPath"); } Future mainAccountAuth( {required String op, required bool rememberMe, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainAccountAuth"); } Future mainAccountAuthCancel({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainAccountAuthCancel"); } Future mainAccountAuthResult({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainAccountAuthResult"); } Future mainOnMainWindowClose({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainOnMainWindowClose"); } bool mainCurrentIsWayland({dynamic hint}) { @@ -1471,7 +1522,7 @@ class RustdeskImpl { } bool mainHideDock({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainHideDock"); } bool mainHasFileClipboard({dynamic hint}) { @@ -1483,11 +1534,11 @@ class RustdeskImpl { } Future cmInit({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("cmInit"); } Future mainStartIpcUrlServer({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainStartIpcUrlServer"); } Future mainTestWallpaper({required int second, dynamic hint}) { @@ -1537,7 +1588,7 @@ class RustdeskImpl { } Future sendUrlScheme({required String url, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sendUrlScheme"); } Future pluginEvent( @@ -1545,12 +1596,12 @@ class RustdeskImpl { required String peer, required Uint8List event, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginEvent"); } Stream pluginRegisterEventStream( {required String id, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginRegisterEventStream"); } String? pluginGetSessionOption( @@ -1558,7 +1609,7 @@ class RustdeskImpl { required String peer, required String key, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginGetSessionOption"); } Future pluginSetSessionOption( @@ -1567,12 +1618,12 @@ class RustdeskImpl { required String key, required String value, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginSetSessionOption"); } String? pluginGetSharedOption( {required String id, required String key, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginGetSharedOption"); } Future pluginSetSharedOption( @@ -1580,36 +1631,36 @@ class RustdeskImpl { required String key, required String value, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginSetSharedOption"); } Future pluginReload({required String id, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginReload"); } void pluginEnable({required String id, required bool v, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginEnable"); } bool pluginIsEnabled({required String id, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginIsEnabled"); } bool pluginFeatureIsEnabled({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginFeatureIsEnabled"); } Future pluginSyncUi({required String syncTo, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginSyncUi"); } Future pluginListReload({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginListReload"); } Future pluginInstall( {required String id, required bool b, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("pluginInstall"); } bool isSupportMultiUiSession({required String version, dynamic hint}) { @@ -1621,11 +1672,11 @@ class RustdeskImpl { } String mainDefaultPrivacyModeImpl({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainDefaultPrivacyModeImpl"); } String mainSupportedPrivacyModeImpls({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainSupportedPrivacyModeImpls"); } String mainSupportedInputSource({dynamic hint}) { @@ -1636,33 +1687,33 @@ class RustdeskImpl { } Future mainGenerate2Fa({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGenerate2Fa"); } Future mainVerify2Fa({required String code, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainVerify2Fa"); } bool mainHasValid2FaSync({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainHasValid2FaSync"); } String mainGetHardOption({required String key, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetHardOption"); } Future mainCheckHwcodec({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainCheckHwcodec"); } Future sessionRequestNewDisplayInitMsgs( {required UuidValue sessionId, required int display, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("sessionRequestNewDisplayInitMsgs"); } Future mainHandleWaylandScreencastRestoreToken( {required String key, required String value, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainHandleWaylandScreencastRestoreToken"); } bool mainIsOptionFixed({required String key, dynamic hint}) { @@ -1670,23 +1721,23 @@ class RustdeskImpl { } bool mainGetUseTextureRender({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetUseTextureRender"); } bool mainHasValidBotSync({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainHasValidBotSync"); } Future mainVerifyBot({required String token, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainVerifyBot"); } String mainGetUnlockPin({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetUnlockPin"); } String mainSetUnlockPin({required String pin, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainSetUnlockPin"); } bool sessionGetEnableTrustedDevices( @@ -1696,28 +1747,28 @@ class RustdeskImpl { } Future mainGetTrustedDevices({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainGetTrustedDevices"); } Future mainRemoveTrustedDevices({required String json, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainRemoveTrustedDevices"); } Future mainClearTrustedDevices({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainClearTrustedDevices"); } Future getVoiceCallInputDevice({required bool isCm, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("getVoiceCallInputDevice"); } Future setVoiceCallInputDevice( {required bool isCm, required String device, dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("setVoiceCallInputDevice"); } bool isPresetPasswordMobileOnly({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("isPresetPasswordMobileOnly"); } String mainGetBuildinOption({required String key, dynamic hint}) { @@ -1725,21 +1776,34 @@ class RustdeskImpl { } String installInstallOptions({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("installInstallOptions"); } int mainMaxEncryptLen({dynamic hint}) { - throw UnimplementedError(); + throw UnimplementedError("mainMaxEncryptLen"); } - sessionRenameFile( + Future sessionRenameFile( {required UuidValue sessionId, required int actId, required String path, required String newName, required bool isRemote, dynamic hint}) { - throw UnimplementedError(); + return Future(() => js.context.callMethod('setByName', [ + 'rename_file', + jsonEncode({ + 'id': actId, + 'path': path, + 'new_name': newName, + 'is_remote': isRemote + }) + ])); + } + + Future sessionSelectFiles( + {required UuidValue sessionId, dynamic hint}) { + return Future(() => js.context.callMethod('setByName', ['select_files'])); } void dispose() {} diff --git a/flutter/lib/web/dummy.dart b/flutter/lib/web/dummy.dart new file mode 100644 index 000000000..b9e3b80b6 --- /dev/null +++ b/flutter/lib/web/dummy.dart @@ -0,0 +1,14 @@ +Future webselectFiles({required bool is_folder}) async { + throw UnimplementedError("webselectFiles"); +} + +Future 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"); +} diff --git a/flutter/lib/web/web_unique.dart b/flutter/lib/web/web_unique.dart new file mode 100644 index 000000000..14774e668 --- /dev/null +++ b/flutter/lib/web/web_unique.dart @@ -0,0 +1,30 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:js' as js; + +Future webselectFiles({required bool is_folder}) async { + return Future( + () => js.context.callMethod('setByName', ['select_files', is_folder])); +} + +Future 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, + }) + ])); +} diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 020bc98aa..d029de1d2 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -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,