| 
									
										
										
										
											2022-09-15 17:41:10 +08:00
										 |  |  | // original cm window in Sciter version.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 00:34:04 +08:00
										 |  |  | import 'dart:async'; | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  | import 'dart:math'; | 
					
						
							| 
									
										
										
										
											2022-08-18 00:34:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-11 18:59:26 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-06 11:18:12 +08:00
										 |  |  | import 'package:flutter_hbb/consts.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-22 20:18:31 +08:00
										 |  |  | import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; | 
					
						
							|  |  |  | import 'package:flutter_hbb/models/chat_model.dart'; | 
					
						
							| 
									
										
										
										
											2023-11-05 15:55:09 +08:00
										 |  |  | import 'package:flutter_hbb/models/cm_file_model.dart'; | 
					
						
							| 
									
										
										
										
											2023-02-10 21:18:55 +08:00
										 |  |  | import 'package:flutter_hbb/utils/platform_channel.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  | import 'package:get/get.dart'; | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  | import 'package:percent_indicator/linear_percent_indicator.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-11 18:59:26 +08:00
										 |  |  | import 'package:provider/provider.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-18 11:07:53 +08:00
										 |  |  | import 'package:window_manager/window_manager.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-27 18:34:05 +08:00
										 |  |  | import 'package:flutter_svg/flutter_svg.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-11 18:59:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import '../../common.dart'; | 
					
						
							| 
									
										
										
										
											2022-10-25 21:36:01 +09:00
										 |  |  | import '../../common/widgets/chat_page.dart'; | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  | import '../../models/file_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-11 18:59:26 +08:00
										 |  |  | import '../../models/platform_model.dart'; | 
					
						
							|  |  |  | import '../../models/server_model.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 19:49:41 +08:00
										 |  |  | class DesktopServerPage extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  |   const DesktopServerPage({Key? key}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-11 18:59:26 +08:00
										 |  |  |   @override | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  |   State<DesktopServerPage> createState() => _DesktopServerPageState(); | 
					
						
							| 
									
										
										
										
											2022-08-11 18:59:26 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-17 21:46:56 +08:00
										 |  |  | class _DesktopServerPageState extends State<DesktopServerPage> | 
					
						
							| 
									
										
										
										
											2022-08-23 21:28:44 +08:00
										 |  |  |     with WindowListener, AutomaticKeepAliveClientMixin { | 
					
						
							| 
									
										
										
										
											2022-09-01 21:18:53 +08:00
										 |  |  |   final tabController = gFFI.serverModel.tabController; | 
					
						
							| 
									
										
										
										
											2022-08-11 18:59:26 +08:00
										 |  |  |   @override | 
					
						
							| 
									
										
										
										
											2022-08-18 19:49:41 +08:00
										 |  |  |   void initState() { | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     gFFI.ffiModel.updateEventListener(gFFI.sessionId, ""); | 
					
						
							| 
									
										
										
										
											2022-08-23 21:28:44 +08:00
										 |  |  |     windowManager.addListener(this); | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |     Get.put(tabController); | 
					
						
							| 
									
										
										
										
											2023-01-23 22:07:50 +08:00
										 |  |  |     tabController.onRemoved = (_, id) { | 
					
						
							|  |  |  |       onRemoveId(id); | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2022-08-18 19:49:41 +08:00
										 |  |  |     super.initState(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-23 21:28:44 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							|  |  |  |     windowManager.removeListener(this); | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void onWindowClose() { | 
					
						
							| 
									
										
										
										
											2023-02-23 16:49:31 +01:00
										 |  |  |     Future.wait([gFFI.serverModel.closeAll(), gFFI.close()]).then((_) { | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |       if (isMacOS) { | 
					
						
							| 
									
										
										
										
											2023-02-10 21:18:55 +08:00
										 |  |  |         RdPlatformChannel.instance.terminate(); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         windowManager.setPreventClose(false); | 
					
						
							|  |  |  |         windowManager.close(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-08-23 21:28:44 +08:00
										 |  |  |     super.onWindowClose(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-01 21:18:53 +08:00
										 |  |  |   void onRemoveId(String id) { | 
					
						
							|  |  |  |     if (tabController.state.value.tabs.isEmpty) { | 
					
						
							|  |  |  |       windowManager.close(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2022-08-11 18:59:26 +08:00
										 |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-08-17 21:46:56 +08:00
										 |  |  |     super.build(context); | 
					
						
							| 
									
										
										
										
											2022-08-22 20:18:31 +08:00
										 |  |  |     return MultiProvider( | 
					
						
							| 
									
										
										
										
											2023-05-18 22:10:49 +02:00
										 |  |  |       providers: [ | 
					
						
							|  |  |  |         ChangeNotifierProvider.value(value: gFFI.serverModel), | 
					
						
							|  |  |  |         ChangeNotifierProvider.value(value: gFFI.chatModel), | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       child: Consumer<ServerModel>( | 
					
						
							|  |  |  |         builder: (context, serverModel, child) => Container( | 
					
						
							|  |  |  |           decoration: BoxDecoration( | 
					
						
							|  |  |  |               border: Border.all(color: MyTheme.color(context).border!)), | 
					
						
							|  |  |  |           child: Scaffold( | 
					
						
							|  |  |  |             backgroundColor: Theme.of(context).scaffoldBackgroundColor, | 
					
						
							| 
									
										
										
										
											2023-05-21 10:20:40 +02:00
										 |  |  |             body: ConnectionManager(), | 
					
						
							| 
									
										
										
										
											2023-05-18 22:10:49 +02:00
										 |  |  |           ), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-08-11 18:59:26 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-17 21:46:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   bool get wantKeepAlive => true; | 
					
						
							| 
									
										
										
										
											2022-08-11 18:59:26 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 20:18:31 +08:00
										 |  |  | class ConnectionManager extends StatefulWidget { | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<StatefulWidget> createState() => ConnectionManagerState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ConnectionManagerState extends State<ConnectionManager> { | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     gFFI.serverModel.updateClientState(); | 
					
						
							| 
									
										
										
										
											2023-06-07 20:31:54 +08:00
										 |  |  |     gFFI.serverModel.tabController.onSelected = (client_id_str) { | 
					
						
							|  |  |  |       final client_id = int.tryParse(client_id_str); | 
					
						
							|  |  |  |       if (client_id != null) { | 
					
						
							| 
									
										
										
										
											2023-07-05 10:56:54 +08:00
										 |  |  |         final client = | 
					
						
							|  |  |  |             gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == client_id); | 
					
						
							|  |  |  |         if (client != null) { | 
					
						
							| 
									
										
										
										
											2023-07-10 16:02:47 +08:00
										 |  |  |           gFFI.chatModel.changeCurrentKey(MessageKey(client.peerId, client.id)); | 
					
						
							| 
									
										
										
										
											2023-07-08 11:09:50 +08:00
										 |  |  |           if (client.unreadChatMessageCount.value > 0) { | 
					
						
							|  |  |  |             Future.delayed(Duration.zero, () { | 
					
						
							|  |  |  |               client.unreadChatMessageCount.value = 0; | 
					
						
							| 
									
										
										
										
											2023-07-09 19:14:57 +08:00
										 |  |  |               gFFI.chatModel.showChatPage(MessageKey(client.peerId, client.id)); | 
					
						
							| 
									
										
										
										
											2023-07-08 11:09:50 +08:00
										 |  |  |             }); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-07-05 10:56:54 +08:00
										 |  |  |           windowManager.setTitle(getWindowNameWithId(client.peerId)); | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |           gFFI.cmFileModel.updateCurrentClientId(client.id); | 
					
						
							| 
									
										
										
										
											2023-07-05 10:56:54 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-06-07 20:31:54 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2022-11-04 21:43:51 +08:00
										 |  |  |     gFFI.chatModel.isConnManager = true; | 
					
						
							| 
									
										
										
										
											2022-08-22 20:18:31 +08:00
										 |  |  |     super.initState(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-11 18:59:26 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     final serverModel = Provider.of<ServerModel>(context); | 
					
						
							| 
									
										
										
										
											2022-11-23 14:23:57 +08:00
										 |  |  |     pointerHandler(PointerEvent e) { | 
					
						
							|  |  |  |       if (serverModel.cmHiddenTimer != null) { | 
					
						
							|  |  |  |         serverModel.cmHiddenTimer!.cancel(); | 
					
						
							|  |  |  |         serverModel.cmHiddenTimer = null; | 
					
						
							|  |  |  |         debugPrint("CM hidden timer has been canceled"); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-17 21:46:56 +08:00
										 |  |  |     return serverModel.clients.isEmpty | 
					
						
							| 
									
										
										
										
											2022-08-18 11:07:53 +08:00
										 |  |  |         ? Column( | 
					
						
							|  |  |  |             children: [ | 
					
						
							| 
									
										
										
										
											2022-09-06 11:18:12 +08:00
										 |  |  |               buildTitleBar(), | 
					
						
							| 
									
										
										
										
											2022-08-18 11:07:53 +08:00
										 |  |  |               Expanded( | 
					
						
							|  |  |  |                 child: Center( | 
					
						
							|  |  |  |                   child: Text(translate("Waiting")), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |             ], | 
					
						
							| 
									
										
										
										
											2022-08-17 21:46:56 +08:00
										 |  |  |           ) | 
					
						
							| 
									
										
										
										
											2022-10-26 21:39:28 +09:00
										 |  |  |         : Listener( | 
					
						
							|  |  |  |             onPointerDown: pointerHandler, | 
					
						
							|  |  |  |             onPointerMove: pointerHandler, | 
					
						
							|  |  |  |             child: DesktopTab( | 
					
						
							| 
									
										
										
										
											2023-05-19 23:21:13 +02:00
										 |  |  |               showTitle: false, | 
					
						
							|  |  |  |               showMaximize: false, | 
					
						
							|  |  |  |               showMinimize: true, | 
					
						
							|  |  |  |               showClose: true, | 
					
						
							|  |  |  |               onWindowCloseButton: handleWindowCloseButton, | 
					
						
							|  |  |  |               controller: serverModel.tabController, | 
					
						
							| 
									
										
										
										
											2023-07-08 17:55:55 +08:00
										 |  |  |               selectedBorderColor: MyTheme.accent, | 
					
						
							| 
									
										
										
										
											2023-05-19 23:21:13 +02:00
										 |  |  |               maxLabelWidth: 100, | 
					
						
							|  |  |  |               tail: buildScrollJumper(), | 
					
						
							|  |  |  |               selectedTabBackgroundColor: | 
					
						
							| 
									
										
										
										
											2023-07-08 17:55:55 +08:00
										 |  |  |                   Theme.of(context).hintColor.withOpacity(0), | 
					
						
							| 
									
										
										
										
											2023-05-19 23:21:13 +02:00
										 |  |  |               tabBuilder: (key, icon, label, themeConf) { | 
					
						
							|  |  |  |                 final client = serverModel.clients | 
					
						
							|  |  |  |                     .firstWhereOrNull((client) => client.id.toString() == key); | 
					
						
							|  |  |  |                 return Row( | 
					
						
							|  |  |  |                   mainAxisAlignment: MainAxisAlignment.center, | 
					
						
							|  |  |  |                   children: [ | 
					
						
							|  |  |  |                     Tooltip( | 
					
						
							|  |  |  |                         message: key, | 
					
						
							|  |  |  |                         waitDuration: Duration(seconds: 1), | 
					
						
							|  |  |  |                         child: label), | 
					
						
							| 
									
										
										
										
											2023-07-10 21:03:35 +08:00
										 |  |  |                     unreadMessageCountBuilder(client?.unreadChatMessageCount) | 
					
						
							|  |  |  |                         .marginOnly(left: 4), | 
					
						
							| 
									
										
										
										
											2023-05-19 23:21:13 +02:00
										 |  |  |                   ], | 
					
						
							|  |  |  |                 ); | 
					
						
							|  |  |  |               }, | 
					
						
							| 
									
										
										
										
											2023-09-29 15:09:58 +08:00
										 |  |  |               pageViewBuilder: (pageView) => LayoutBuilder( | 
					
						
							|  |  |  |                 builder: (context, constrains) { | 
					
						
							|  |  |  |                   var borderWidth = 0.0; | 
					
						
							|  |  |  |                   if (constrains.maxWidth > | 
					
						
							|  |  |  |                       kConnectionManagerWindowSizeClosedChat.width) { | 
					
						
							|  |  |  |                     borderWidth = kConnectionManagerWindowSizeOpenChat.width - | 
					
						
							|  |  |  |                         constrains.maxWidth; | 
					
						
							|  |  |  |                   } else { | 
					
						
							|  |  |  |                     borderWidth = kConnectionManagerWindowSizeClosedChat.width - | 
					
						
							|  |  |  |                         constrains.maxWidth; | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                   if (borderWidth < 0 || borderWidth > 50) { | 
					
						
							|  |  |  |                     borderWidth = 0; | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                   final realClosedWidth = | 
					
						
							|  |  |  |                       kConnectionManagerWindowSizeClosedChat.width - | 
					
						
							|  |  |  |                           borderWidth; | 
					
						
							|  |  |  |                   final realChatPageWidth = | 
					
						
							|  |  |  |                       constrains.maxWidth - realClosedWidth; | 
					
						
							|  |  |  |                   return Row(children: [ | 
					
						
							|  |  |  |                     if (constrains.maxWidth > | 
					
						
							|  |  |  |                         kConnectionManagerWindowSizeClosedChat.width) | 
					
						
							|  |  |  |                       Consumer<ChatModel>( | 
					
						
							|  |  |  |                           builder: (_, model, child) => SizedBox( | 
					
						
							|  |  |  |                                 width: realChatPageWidth, | 
					
						
							|  |  |  |                                 child: buildRemoteBlock( | 
					
						
							|  |  |  |                                   child: Container( | 
					
						
							|  |  |  |                                       decoration: BoxDecoration( | 
					
						
							|  |  |  |                                           border: Border( | 
					
						
							|  |  |  |                                               right: BorderSide( | 
					
						
							|  |  |  |                                                   color: Theme.of(context) | 
					
						
							|  |  |  |                                                       .dividerColor))), | 
					
						
							|  |  |  |                                       child: buildSidePage()), | 
					
						
							|  |  |  |                                 ), | 
					
						
							|  |  |  |                               )), | 
					
						
							|  |  |  |                     SizedBox( | 
					
						
							|  |  |  |                         width: realClosedWidth, | 
					
						
							|  |  |  |                         child: | 
					
						
							|  |  |  |                             SizedBox(width: realClosedWidth, child: pageView)), | 
					
						
							|  |  |  |                   ]); | 
					
						
							|  |  |  |                 }, | 
					
						
							| 
									
										
										
										
											2023-05-19 23:21:13 +02:00
										 |  |  |               ), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2022-08-18 11:07:53 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |   Widget buildSidePage() { | 
					
						
							|  |  |  |     final selected = gFFI.serverModel.tabController.state.value.selected; | 
					
						
							|  |  |  |     if (selected < 0 || selected >= gFFI.serverModel.clients.length) { | 
					
						
							|  |  |  |       return Offstage(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     final clientType = gFFI.serverModel.clients[selected].type_(); | 
					
						
							|  |  |  |     if (clientType == ClientType.file) { | 
					
						
							|  |  |  |       return _FileTransferLogPage(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       return ChatPage(type: ChatPageType.desktopCM); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-06 11:18:12 +08:00
										 |  |  |   Widget buildTitleBar() { | 
					
						
							|  |  |  |     return SizedBox( | 
					
						
							|  |  |  |       height: kDesktopRemoteTabBarHeight, | 
					
						
							| 
									
										
										
										
											2022-08-18 11:07:53 +08:00
										 |  |  |       child: Row( | 
					
						
							|  |  |  |         crossAxisAlignment: CrossAxisAlignment.center, | 
					
						
							|  |  |  |         children: [ | 
					
						
							| 
									
										
										
										
											2022-09-06 11:18:12 +08:00
										 |  |  |           const _AppIcon(), | 
					
						
							|  |  |  |           Expanded( | 
					
						
							|  |  |  |             child: GestureDetector( | 
					
						
							|  |  |  |               onPanStart: (d) { | 
					
						
							|  |  |  |                 windowManager.startDragging(); | 
					
						
							|  |  |  |               }, | 
					
						
							|  |  |  |               child: Container( | 
					
						
							| 
									
										
										
										
											2023-02-23 16:49:31 +01:00
										 |  |  |                 color: Theme.of(context).colorScheme.background, | 
					
						
							| 
									
										
										
										
											2022-09-06 11:18:12 +08:00
										 |  |  |               ), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2022-08-18 11:07:53 +08:00
										 |  |  |           const SizedBox( | 
					
						
							|  |  |  |             width: 4.0, | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |           ), | 
					
						
							| 
									
										
										
										
											2022-09-06 11:18:12 +08:00
										 |  |  |           const _CloseButton() | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |         ], | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-10-20 23:22:02 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |   Widget buildScrollJumper() { | 
					
						
							|  |  |  |     final offstage = gFFI.serverModel.clients.length < 2; | 
					
						
							|  |  |  |     final sc = gFFI.serverModel.tabController.state.value.scrollController; | 
					
						
							|  |  |  |     return Offstage( | 
					
						
							|  |  |  |         offstage: offstage, | 
					
						
							|  |  |  |         child: Row( | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             ActionIcon( | 
					
						
							|  |  |  |                 icon: Icons.arrow_left, iconSize: 22, onTap: sc.backward), | 
					
						
							|  |  |  |             ActionIcon( | 
					
						
							|  |  |  |                 icon: Icons.arrow_right, iconSize: 22, onTap: sc.forward), | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         )); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-11-06 21:21:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   Future<bool> handleWindowCloseButton() async { | 
					
						
							|  |  |  |     var tabController = gFFI.serverModel.tabController; | 
					
						
							|  |  |  |     final connLength = tabController.length; | 
					
						
							|  |  |  |     if (connLength <= 1) { | 
					
						
							|  |  |  |       windowManager.close(); | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       final opt = "enable-confirm-closing-tabs"; | 
					
						
							|  |  |  |       final bool res; | 
					
						
							| 
									
										
										
										
											2023-08-06 16:42:13 +08:00
										 |  |  |       if (!option2bool(opt, bind.mainGetLocalOption(key: opt))) { | 
					
						
							| 
									
										
										
										
											2022-11-06 21:21:57 +08:00
										 |  |  |         res = true; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         res = await closeConfirmDialog(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (res) { | 
					
						
							|  |  |  |         windowManager.close(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-24 21:52:21 +08:00
										 |  |  | Widget buildConnectionCard(Client client) { | 
					
						
							| 
									
										
										
										
											2022-10-08 20:15:02 +08:00
										 |  |  |   return Consumer<ServerModel>( | 
					
						
							| 
									
										
										
										
											2023-05-21 10:20:40 +02:00
										 |  |  |     builder: (context, value, child) => Column( | 
					
						
							|  |  |  |       mainAxisAlignment: MainAxisAlignment.start, | 
					
						
							|  |  |  |       crossAxisAlignment: CrossAxisAlignment.start, | 
					
						
							|  |  |  |       key: ValueKey(client.id), | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         _CmHeader(client: client), | 
					
						
							|  |  |  |         client.type_() != ClientType.remote || client.disconnected | 
					
						
							|  |  |  |             ? Offstage() | 
					
						
							|  |  |  |             : _PrivilegeBoard(client: client), | 
					
						
							|  |  |  |         Expanded( | 
					
						
							|  |  |  |           child: Align( | 
					
						
							|  |  |  |             alignment: Alignment.bottomCenter, | 
					
						
							|  |  |  |             child: _CmControlPanel(client: client), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |     ).paddingSymmetric(vertical: 4.0, horizontal: 8.0), | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2022-08-24 21:52:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 11:07:53 +08:00
										 |  |  | class _AppIcon extends StatelessWidget { | 
					
						
							|  |  |  |   const _AppIcon({Key? key}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return Container( | 
					
						
							|  |  |  |       margin: EdgeInsets.symmetric(horizontal: 4.0), | 
					
						
							| 
									
										
											  
											
												Fix/custom client styles (#7373)
* Fix. qs styles
Signed-off-by: fufesou <shuanglongchen@yeah.net>
* custom client, options
Signed-off-by: fufesou <shuanglongchen@yeah.net>
* Move logo.svg to icon.svg
Signed-off-by: fufesou <shuanglongchen@yeah.net>
* Refact. Custom client, connection status ui.
Signed-off-by: fufesou <shuanglongchen@yeah.net>
* Custom client ui. Disable settings, hide "Change password"
Signed-off-by: fufesou <shuanglongchen@yeah.net>
* Custom client, logo align center
Signed-off-by: fufesou <shuanglongchen@yeah.net>
* Custom client, refact, outgoing ui
Signed-off-by: fufesou <shuanglongchen@yeah.net>
* Custom client, outgoing, settings icon
Signed-off-by: fufesou <shuanglongchen@yeah.net>
* Custom client, powered by RustDesk
Signed-off-by: fufesou <shuanglongchen@yeah.net>
* Custom client, remove unused SizeBox
Signed-off-by: fufesou <shuanglongchen@yeah.net>
* Update config.rs
* Update flutter_ffi.rs
---------
Signed-off-by: fufesou <shuanglongchen@yeah.net>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
											
										 
											2024-03-14 11:36:14 +08:00
										 |  |  |       child: loadIcon(30), | 
					
						
							| 
									
										
										
										
											2022-08-18 11:07:53 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _CloseButton extends StatelessWidget { | 
					
						
							|  |  |  |   const _CloseButton({Key? key}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-09-06 11:18:12 +08:00
										 |  |  |     return IconButton( | 
					
						
							|  |  |  |       onPressed: () { | 
					
						
							|  |  |  |         windowManager.close(); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       icon: const Icon( | 
					
						
							|  |  |  |         IconFont.close, | 
					
						
							|  |  |  |         size: 18, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       splashColor: Colors.transparent, | 
					
						
							|  |  |  |       hoverColor: Colors.transparent, | 
					
						
							| 
									
										
										
										
											2022-08-18 11:07:53 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 00:34:04 +08:00
										 |  |  | class _CmHeader extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |   final Client client; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const _CmHeader({Key? key, required this.client}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 00:34:04 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   State<_CmHeader> createState() => _CmHeaderState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _CmHeaderState extends State<_CmHeader> | 
					
						
							|  |  |  |     with AutomaticKeepAliveClientMixin { | 
					
						
							|  |  |  |   Client get client => widget.client; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-04 18:20:23 +08:00
										 |  |  |   final _time = 0.obs; | 
					
						
							| 
									
										
										
										
											2022-08-18 00:34:04 +08:00
										 |  |  |   Timer? _timer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							|  |  |  |     _timer = Timer.periodic(Duration(seconds: 1), (_) { | 
					
						
							| 
									
										
										
										
											2022-11-21 18:45:36 +08:00
										 |  |  |       if (client.authorized && !client.disconnected) { | 
					
						
							|  |  |  |         _time.value = _time.value + 1; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-08-18 00:34:04 +08:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2023-06-07 20:31:54 +08:00
										 |  |  |     gFFI.serverModel.tabController.onSelected?.call(client.id.toString()); | 
					
						
							| 
									
										
										
										
											2022-08-18 00:34:04 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							|  |  |  |     _timer?.cancel(); | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-08-18 00:34:04 +08:00
										 |  |  |     super.build(context); | 
					
						
							| 
									
										
										
										
											2023-05-18 22:59:07 +02:00
										 |  |  |     return Container( | 
					
						
							|  |  |  |       decoration: BoxDecoration( | 
					
						
							|  |  |  |         borderRadius: BorderRadius.circular(10.0), | 
					
						
							|  |  |  |         gradient: LinearGradient( | 
					
						
							|  |  |  |           begin: Alignment.topRight, | 
					
						
							|  |  |  |           end: Alignment.bottomLeft, | 
					
						
							|  |  |  |           colors: [ | 
					
						
							|  |  |  |             Color(0xff00bfe1), | 
					
						
							|  |  |  |             Color(0xff0071ff), | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2023-05-19 23:21:13 +02:00
										 |  |  |       margin: EdgeInsets.symmetric(horizontal: 5.0, vertical: 10.0), | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |       padding: EdgeInsets.only( | 
					
						
							|  |  |  |         top: 10.0, | 
					
						
							|  |  |  |         bottom: 10.0, | 
					
						
							|  |  |  |         left: 10.0, | 
					
						
							|  |  |  |         right: 5.0, | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2023-05-18 22:59:07 +02:00
										 |  |  |       child: Row( | 
					
						
							|  |  |  |         crossAxisAlignment: CrossAxisAlignment.start, | 
					
						
							|  |  |  |         children: [ | 
					
						
							|  |  |  |           Container( | 
					
						
							|  |  |  |             width: 70, | 
					
						
							|  |  |  |             height: 70, | 
					
						
							|  |  |  |             alignment: Alignment.center, | 
					
						
							|  |  |  |             decoration: BoxDecoration( | 
					
						
							|  |  |  |               color: str2color(client.name), | 
					
						
							|  |  |  |               borderRadius: BorderRadius.circular(15.0), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             child: Text( | 
					
						
							|  |  |  |               client.name[0], | 
					
						
							|  |  |  |               style: TextStyle( | 
					
						
							|  |  |  |                 fontWeight: FontWeight.bold, | 
					
						
							|  |  |  |                 color: Colors.white, | 
					
						
							|  |  |  |                 fontSize: 55, | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |               ), | 
					
						
							| 
									
										
										
										
											2023-05-18 22:59:07 +02:00
										 |  |  |             ), | 
					
						
							|  |  |  |           ).marginOnly(right: 10.0), | 
					
						
							|  |  |  |           Expanded( | 
					
						
							|  |  |  |             child: Column( | 
					
						
							|  |  |  |               mainAxisAlignment: MainAxisAlignment.start, | 
					
						
							|  |  |  |               crossAxisAlignment: CrossAxisAlignment.start, | 
					
						
							|  |  |  |               children: [ | 
					
						
							|  |  |  |                 FittedBox( | 
					
						
							|  |  |  |                     child: Text( | 
					
						
							|  |  |  |                   client.name, | 
					
						
							|  |  |  |                   style: TextStyle( | 
					
						
							|  |  |  |                     color: Colors.white, | 
					
						
							|  |  |  |                     fontWeight: FontWeight.bold, | 
					
						
							|  |  |  |                     fontSize: 20, | 
					
						
							|  |  |  |                     overflow: TextOverflow.ellipsis, | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                   maxLines: 1, | 
					
						
							|  |  |  |                 )), | 
					
						
							|  |  |  |                 FittedBox( | 
					
						
							|  |  |  |                   child: Text( | 
					
						
							|  |  |  |                     "(${client.peerId})", | 
					
						
							|  |  |  |                     style: TextStyle(color: Colors.white, fontSize: 14), | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                 ).marginOnly(bottom: 10.0), | 
					
						
							|  |  |  |                 FittedBox( | 
					
						
							|  |  |  |                     child: Row( | 
					
						
							|  |  |  |                   children: [ | 
					
						
							|  |  |  |                     Text( | 
					
						
							|  |  |  |                       client.authorized | 
					
						
							| 
									
										
										
										
											2022-11-21 18:45:36 +08:00
										 |  |  |                           ? client.disconnected | 
					
						
							|  |  |  |                               ? translate("Disconnected") | 
					
						
							|  |  |  |                               : translate("Connected") | 
					
						
							| 
									
										
										
										
											2023-05-18 22:59:07 +02:00
										 |  |  |                           : "${translate("Request access to your device")}...", | 
					
						
							|  |  |  |                       style: TextStyle(color: Colors.white), | 
					
						
							|  |  |  |                     ).marginOnly(right: 8.0), | 
					
						
							|  |  |  |                     if (client.authorized) | 
					
						
							|  |  |  |                       Obx( | 
					
						
							|  |  |  |                         () => Text( | 
					
						
							|  |  |  |                           formatDurationToTime( | 
					
						
							|  |  |  |                             Duration(seconds: _time.value), | 
					
						
							|  |  |  |                           ), | 
					
						
							|  |  |  |                           style: TextStyle(color: Colors.white), | 
					
						
							|  |  |  |                         ), | 
					
						
							|  |  |  |                       ) | 
					
						
							|  |  |  |                   ], | 
					
						
							|  |  |  |                 )) | 
					
						
							|  |  |  |               ], | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |           ), | 
					
						
							| 
									
										
										
										
											2023-05-18 22:59:07 +02:00
										 |  |  |           Offstage( | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |             offstage: !client.authorized || | 
					
						
							|  |  |  |                 (client.type_() != ClientType.remote && | 
					
						
							|  |  |  |                     client.type_() != ClientType.file), | 
					
						
							| 
									
										
										
										
											2023-05-18 22:59:07 +02:00
										 |  |  |             child: IconButton( | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |               onPressed: () => checkClickTime(client.id, () { | 
					
						
							| 
									
										
										
										
											2023-11-05 15:55:09 +08:00
										 |  |  |                 if (client.type_() == ClientType.file) { | 
					
						
							|  |  |  |                   gFFI.chatModel.toggleCMFilePage(); | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |                 } else { | 
					
						
							|  |  |  |                   gFFI.chatModel | 
					
						
							|  |  |  |                       .toggleCMChatPage(MessageKey(client.peerId, client.id)); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               }), | 
					
						
							|  |  |  |               icon: SvgPicture.asset(client.type_() == ClientType.file | 
					
						
							|  |  |  |                   ? 'assets/file_transfer.svg' | 
					
						
							|  |  |  |                   : 'assets/chat2.svg'), | 
					
						
							| 
									
										
										
										
											2023-05-18 22:59:07 +02:00
										 |  |  |               splashRadius: kDesktopIconButtonSplashRadius, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 00:34:04 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   bool get wantKeepAlive => true; | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 19:49:41 +08:00
										 |  |  | class _PrivilegeBoard extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |   final Client client; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const _PrivilegeBoard({Key? key, required this.client}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 19:49:41 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   State<StatefulWidget> createState() => _PrivilegeBoardState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _PrivilegeBoardState extends State<_PrivilegeBoard> { | 
					
						
							|  |  |  |   late final client = widget.client; | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |   Widget buildPermissionIcon(bool enabled, IconData iconData, | 
					
						
							| 
									
										
										
										
											2023-05-20 18:33:40 +02:00
										 |  |  |       Function(bool)? onTap, String tooltipText) { | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |     return Tooltip( | 
					
						
							| 
									
										
										
										
											2023-06-03 22:19:39 +08:00
										 |  |  |       message: "$tooltipText: ${enabled ? "ON" : "OFF"}", | 
					
						
							| 
									
										
										
										
											2023-11-06 16:55:19 +08:00
										 |  |  |       waitDuration: Duration.zero, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |       child: Container( | 
					
						
							|  |  |  |         decoration: BoxDecoration( | 
					
						
							| 
									
										
										
										
											2023-05-20 18:30:13 +02:00
										 |  |  |           color: enabled ? MyTheme.accent : Colors.grey[700], | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |           borderRadius: BorderRadius.circular(10.0), | 
					
						
							|  |  |  |         ), | 
					
						
							| 
									
										
										
										
											2023-05-21 14:24:51 +02:00
										 |  |  |         padding: EdgeInsets.all(8.0), | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |         child: InkWell( | 
					
						
							|  |  |  |           onTap: () => | 
					
						
							|  |  |  |               checkClickTime(widget.client.id, () => onTap?.call(!enabled)), | 
					
						
							| 
									
										
										
										
											2023-05-21 14:24:51 +02:00
										 |  |  |           child: Column( | 
					
						
							|  |  |  |             mainAxisAlignment: MainAxisAlignment.spaceAround, | 
					
						
							|  |  |  |             children: [ | 
					
						
							| 
									
										
										
										
											2023-05-29 16:09:23 +08:00
										 |  |  |               Expanded( | 
					
						
							|  |  |  |                 child: Icon( | 
					
						
							|  |  |  |                   iconData, | 
					
						
							|  |  |  |                   color: Colors.white, | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2023-05-21 14:24:51 +02:00
										 |  |  |               ), | 
					
						
							|  |  |  |             ], | 
					
						
							| 
									
										
										
										
											2023-05-18 22:10:49 +02:00
										 |  |  |           ), | 
					
						
							| 
									
										
										
										
											2023-05-18 11:41:16 +02:00
										 |  |  |         ), | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |       ), | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2023-11-05 21:53:21 +08:00
										 |  |  |     final crossAxisCount = 4; | 
					
						
							|  |  |  |     final spacing = 10.0; | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |     return Container( | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |       width: double.infinity, | 
					
						
							| 
									
										
										
										
											2023-11-05 21:53:21 +08:00
										 |  |  |       height: 160.0, | 
					
						
							| 
									
										
										
										
											2023-05-18 22:59:07 +02:00
										 |  |  |       margin: EdgeInsets.all(5.0), | 
					
						
							| 
									
										
										
										
											2023-05-18 22:10:49 +02:00
										 |  |  |       padding: EdgeInsets.all(5.0), | 
					
						
							|  |  |  |       decoration: BoxDecoration( | 
					
						
							|  |  |  |         borderRadius: BorderRadius.circular(10.0), | 
					
						
							|  |  |  |         color: Theme.of(context).colorScheme.background, | 
					
						
							|  |  |  |         boxShadow: [ | 
					
						
							|  |  |  |           BoxShadow( | 
					
						
							|  |  |  |             color: Colors.black.withOpacity(0.2), | 
					
						
							|  |  |  |             spreadRadius: 1, | 
					
						
							|  |  |  |             blurRadius: 1, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |             offset: Offset(0, 1.5), | 
					
						
							| 
									
										
										
										
											2023-05-18 22:10:49 +02:00
										 |  |  |           ), | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |       child: Column( | 
					
						
							| 
									
										
										
										
											2023-05-18 22:10:49 +02:00
										 |  |  |         crossAxisAlignment: CrossAxisAlignment.center, | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |         children: [ | 
					
						
							|  |  |  |           Text( | 
					
						
							|  |  |  |             translate("Permissions"), | 
					
						
							| 
									
										
										
										
											2023-05-18 22:10:49 +02:00
										 |  |  |             style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), | 
					
						
							|  |  |  |             textAlign: TextAlign.center, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |           ).marginOnly(left: 4.0, bottom: 8.0), | 
					
						
							|  |  |  |           Expanded( | 
					
						
							|  |  |  |             child: GridView.count( | 
					
						
							| 
									
										
										
										
											2023-11-05 21:53:21 +08:00
										 |  |  |               crossAxisCount: crossAxisCount, | 
					
						
							|  |  |  |               padding: EdgeInsets.symmetric(horizontal: spacing), | 
					
						
							|  |  |  |               mainAxisSpacing: spacing, | 
					
						
							|  |  |  |               crossAxisSpacing: spacing, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |               children: [ | 
					
						
							|  |  |  |                 buildPermissionIcon( | 
					
						
							|  |  |  |                   client.keyboard, | 
					
						
							| 
									
										
										
										
											2023-05-24 14:18:42 +08:00
										 |  |  |                   Icons.keyboard, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                   (enabled) { | 
					
						
							|  |  |  |                     bind.cmSwitchPermission( | 
					
						
							|  |  |  |                         connId: client.id, name: "keyboard", enabled: enabled); | 
					
						
							|  |  |  |                     setState(() { | 
					
						
							|  |  |  |                       client.keyboard = enabled; | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                   }, | 
					
						
							| 
									
										
										
										
											2023-11-06 20:12:01 +08:00
										 |  |  |                   translate('Enable keyboard/mouse'), | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                 ), | 
					
						
							|  |  |  |                 buildPermissionIcon( | 
					
						
							|  |  |  |                   client.clipboard, | 
					
						
							| 
									
										
										
										
											2023-05-24 14:18:42 +08:00
										 |  |  |                   Icons.assignment_rounded, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                   (enabled) { | 
					
						
							|  |  |  |                     bind.cmSwitchPermission( | 
					
						
							|  |  |  |                         connId: client.id, name: "clipboard", enabled: enabled); | 
					
						
							|  |  |  |                     setState(() { | 
					
						
							|  |  |  |                       client.clipboard = enabled; | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                   }, | 
					
						
							| 
									
										
										
										
											2023-11-06 20:12:01 +08:00
										 |  |  |                   translate('Enable clipboard'), | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                 ), | 
					
						
							|  |  |  |                 buildPermissionIcon( | 
					
						
							|  |  |  |                   client.audio, | 
					
						
							| 
									
										
										
										
											2023-05-24 14:18:42 +08:00
										 |  |  |                   Icons.volume_up_rounded, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                   (enabled) { | 
					
						
							|  |  |  |                     bind.cmSwitchPermission( | 
					
						
							|  |  |  |                         connId: client.id, name: "audio", enabled: enabled); | 
					
						
							|  |  |  |                     setState(() { | 
					
						
							|  |  |  |                       client.audio = enabled; | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                   }, | 
					
						
							| 
									
										
										
										
											2023-11-06 20:12:01 +08:00
										 |  |  |                   translate('Enable audio'), | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                 ), | 
					
						
							|  |  |  |                 buildPermissionIcon( | 
					
						
							|  |  |  |                   client.file, | 
					
						
							| 
									
										
										
										
											2023-05-24 14:18:42 +08:00
										 |  |  |                   Icons.upload_file_rounded, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                   (enabled) { | 
					
						
							|  |  |  |                     bind.cmSwitchPermission( | 
					
						
							|  |  |  |                         connId: client.id, name: "file", enabled: enabled); | 
					
						
							|  |  |  |                     setState(() { | 
					
						
							|  |  |  |                       client.file = enabled; | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                   }, | 
					
						
							| 
									
										
										
										
											2023-11-06 16:55:19 +08:00
										 |  |  |                   translate('Enable file copy and paste'), | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                 ), | 
					
						
							|  |  |  |                 buildPermissionIcon( | 
					
						
							|  |  |  |                   client.restart, | 
					
						
							| 
									
										
										
										
											2023-05-24 14:18:42 +08:00
										 |  |  |                   Icons.restart_alt_rounded, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                   (enabled) { | 
					
						
							|  |  |  |                     bind.cmSwitchPermission( | 
					
						
							|  |  |  |                         connId: client.id, name: "restart", enabled: enabled); | 
					
						
							|  |  |  |                     setState(() { | 
					
						
							|  |  |  |                       client.restart = enabled; | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                   }, | 
					
						
							| 
									
										
										
										
											2023-11-06 20:12:01 +08:00
										 |  |  |                   translate('Enable remote restart'), | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                 ), | 
					
						
							|  |  |  |                 buildPermissionIcon( | 
					
						
							|  |  |  |                   client.recording, | 
					
						
							| 
									
										
										
										
											2023-05-24 14:18:42 +08:00
										 |  |  |                   Icons.videocam_rounded, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                   (enabled) { | 
					
						
							|  |  |  |                     bind.cmSwitchPermission( | 
					
						
							|  |  |  |                         connId: client.id, name: "recording", enabled: enabled); | 
					
						
							|  |  |  |                     setState(() { | 
					
						
							|  |  |  |                       client.recording = enabled; | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                   }, | 
					
						
							| 
									
										
										
										
											2023-11-06 20:12:01 +08:00
										 |  |  |                   translate('Enable recording session'), | 
					
						
							| 
									
										
										
										
											2023-11-05 21:53:21 +08:00
										 |  |  |                 ), | 
					
						
							|  |  |  |                 // only windows support block input
 | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |                 if (isWindows) | 
					
						
							| 
									
										
										
										
											2023-11-05 21:53:21 +08:00
										 |  |  |                   buildPermissionIcon( | 
					
						
							|  |  |  |                     client.blockInput, | 
					
						
							|  |  |  |                     Icons.block, | 
					
						
							|  |  |  |                     (enabled) { | 
					
						
							|  |  |  |                       bind.cmSwitchPermission( | 
					
						
							|  |  |  |                           connId: client.id, | 
					
						
							|  |  |  |                           name: "block_input", | 
					
						
							|  |  |  |                           enabled: enabled); | 
					
						
							|  |  |  |                       setState(() { | 
					
						
							|  |  |  |                         client.blockInput = enabled; | 
					
						
							|  |  |  |                       }); | 
					
						
							|  |  |  |                     }, | 
					
						
							| 
									
										
										
										
											2023-11-06 16:55:19 +08:00
										 |  |  |                     translate('Enable blocking user input'), | 
					
						
							| 
									
										
										
										
											2023-11-05 21:53:21 +08:00
										 |  |  |                   ) | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |               ], | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |           ), | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-08-11 18:59:26 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  | const double buttonBottomMargin = 8; | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  | class _CmControlPanel extends StatelessWidget { | 
					
						
							|  |  |  |   final Client client; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const _CmControlPanel({Key? key, required this.client}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-10-08 20:15:02 +08:00
										 |  |  |     return client.authorized | 
					
						
							|  |  |  |         ? client.disconnected | 
					
						
							|  |  |  |             ? buildDisconnected(context) | 
					
						
							|  |  |  |             : buildAuthorized(context) | 
					
						
							|  |  |  |         : buildUnAuthorized(context); | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-17 21:46:56 +08:00
										 |  |  |   buildAuthorized(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-11-10 10:27:13 +08:00
										 |  |  |     final bool canElevate = bind.cmCanElevate(); | 
					
						
							|  |  |  |     final model = Provider.of<ServerModel>(context); | 
					
						
							| 
									
										
										
										
											2022-12-04 18:47:02 +08:00
										 |  |  |     final showElevation = canElevate && | 
					
						
							|  |  |  |         model.showElevation && | 
					
						
							|  |  |  |         client.type_() == ClientType.remote; | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |     return Column( | 
					
						
							|  |  |  |       mainAxisAlignment: MainAxisAlignment.end, | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |       children: [ | 
					
						
							| 
									
										
										
										
											2023-02-07 16:11:55 +08:00
										 |  |  |         Offstage( | 
					
						
							|  |  |  |           offstage: !client.inVoiceCall, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |           child: buildButton( | 
					
						
							|  |  |  |             context, | 
					
						
							|  |  |  |             color: Colors.red, | 
					
						
							|  |  |  |             onClick: () => closeVoiceCall(), | 
					
						
							|  |  |  |             icon: Icon( | 
					
						
							| 
									
										
										
										
											2023-05-24 14:18:42 +08:00
										 |  |  |               Icons.call_end_rounded, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |               color: Colors.white, | 
					
						
							|  |  |  |               size: 14, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             text: "Stop voice call", | 
					
						
							|  |  |  |             textColor: Colors.white, | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2023-02-07 16:11:55 +08:00
										 |  |  |         ), | 
					
						
							|  |  |  |         Offstage( | 
					
						
							|  |  |  |           offstage: !client.incomingVoiceCall, | 
					
						
							|  |  |  |           child: Row( | 
					
						
							|  |  |  |             children: [ | 
					
						
							|  |  |  |               Expanded( | 
					
						
							|  |  |  |                 child: buildButton(context, | 
					
						
							|  |  |  |                     color: MyTheme.accent, | 
					
						
							|  |  |  |                     onClick: () => handleVoiceCall(true), | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                     icon: Icon( | 
					
						
							| 
									
										
										
										
											2023-05-24 14:18:42 +08:00
										 |  |  |                       Icons.call_rounded, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                       color: Colors.white, | 
					
						
							|  |  |  |                       size: 14, | 
					
						
							|  |  |  |                     ), | 
					
						
							| 
									
										
										
										
											2023-02-07 16:11:55 +08:00
										 |  |  |                     text: "Accept", | 
					
						
							|  |  |  |                     textColor: Colors.white), | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |               Expanded( | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                 child: buildButton( | 
					
						
							|  |  |  |                   context, | 
					
						
							|  |  |  |                   color: Colors.red, | 
					
						
							|  |  |  |                   onClick: () => handleVoiceCall(false), | 
					
						
							|  |  |  |                   icon: Icon( | 
					
						
							| 
									
										
										
										
											2023-05-24 14:18:42 +08:00
										 |  |  |                     Icons.phone_disabled_rounded, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                     color: Colors.white, | 
					
						
							|  |  |  |                     size: 14, | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                   text: "Dismiss", | 
					
						
							|  |  |  |                   textColor: Colors.white, | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2023-02-07 16:11:55 +08:00
										 |  |  |               ) | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ), | 
					
						
							| 
									
										
										
										
											2023-01-17 13:28:33 +08:00
										 |  |  |         Offstage( | 
					
						
							|  |  |  |           offstage: !client.fromSwitch, | 
					
						
							|  |  |  |           child: buildButton(context, | 
					
						
							|  |  |  |               color: Colors.purple, | 
					
						
							|  |  |  |               onClick: () => handleSwitchBack(context), | 
					
						
							|  |  |  |               icon: Icon(Icons.reply, color: Colors.white), | 
					
						
							|  |  |  |               text: "Switch Sides", | 
					
						
							|  |  |  |               textColor: Colors.white), | 
					
						
							|  |  |  |         ), | 
					
						
							| 
									
										
										
										
											2022-11-10 10:27:13 +08:00
										 |  |  |         Offstage( | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |           offstage: !showElevation, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |           child: buildButton( | 
					
						
							|  |  |  |             context, | 
					
						
							|  |  |  |             color: MyTheme.accent, | 
					
						
							|  |  |  |             onClick: () { | 
					
						
							|  |  |  |               handleElevate(context); | 
					
						
							|  |  |  |               windowManager.minimize(); | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             icon: Icon( | 
					
						
							| 
									
										
										
										
											2023-05-24 14:18:42 +08:00
										 |  |  |               Icons.security_rounded, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |               color: Colors.white, | 
					
						
							|  |  |  |               size: 14, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             text: 'Elevate', | 
					
						
							|  |  |  |             textColor: Colors.white, | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2022-11-10 10:27:13 +08:00
										 |  |  |         ), | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |         Row( | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             Expanded( | 
					
						
							| 
									
										
										
										
											2023-05-19 23:21:13 +02:00
										 |  |  |               child: buildButton(context, | 
					
						
							|  |  |  |                   color: Colors.redAccent, | 
					
						
							|  |  |  |                   onClick: handleDisconnect, | 
					
						
							|  |  |  |                   text: 'Disconnect', | 
					
						
							|  |  |  |                   icon: Icon( | 
					
						
							| 
									
										
										
										
											2023-05-24 14:18:42 +08:00
										 |  |  |                     Icons.link_off_rounded, | 
					
						
							| 
									
										
										
										
											2023-05-19 23:21:13 +02:00
										 |  |  |                     color: Colors.white, | 
					
						
							|  |  |  |                     size: 14, | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                   textColor: Colors.white), | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |           ], | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |         ) | 
					
						
							|  |  |  |       ], | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |     ).marginOnly(bottom: buttonBottomMargin); | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 20:15:02 +08:00
										 |  |  |   buildDisconnected(BuildContext context) { | 
					
						
							|  |  |  |     return Row( | 
					
						
							|  |  |  |       mainAxisAlignment: MainAxisAlignment.center, | 
					
						
							|  |  |  |       children: [ | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |         Expanded( | 
					
						
							|  |  |  |             child: buildButton(context, | 
					
						
							|  |  |  |                 color: MyTheme.accent, | 
					
						
							|  |  |  |                 onClick: handleClose, | 
					
						
							|  |  |  |                 text: 'Close', | 
					
						
							|  |  |  |                 textColor: Colors.white)), | 
					
						
							| 
									
										
										
										
											2022-10-08 20:15:02 +08:00
										 |  |  |       ], | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |     ).marginOnly(bottom: buttonBottomMargin); | 
					
						
							| 
									
										
										
										
											2022-10-08 20:15:02 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-17 21:46:56 +08:00
										 |  |  |   buildUnAuthorized(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-11-10 10:27:13 +08:00
										 |  |  |     final bool canElevate = bind.cmCanElevate(); | 
					
						
							|  |  |  |     final model = Provider.of<ServerModel>(context); | 
					
						
							| 
									
										
										
										
											2022-12-04 18:47:02 +08:00
										 |  |  |     final showElevation = canElevate && | 
					
						
							|  |  |  |         model.showElevation && | 
					
						
							|  |  |  |         client.type_() == ClientType.remote; | 
					
						
							| 
									
										
										
										
											2022-11-20 15:53:08 +08:00
										 |  |  |     final showAccept = model.approveMode != 'password'; | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |     return Column( | 
					
						
							|  |  |  |       mainAxisAlignment: MainAxisAlignment.end, | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |       children: [ | 
					
						
							| 
									
										
										
										
											2022-11-10 10:27:13 +08:00
										 |  |  |         Offstage( | 
					
						
							| 
									
										
										
										
											2022-11-20 15:53:08 +08:00
										 |  |  |           offstage: !showElevation || !showAccept, | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |           child: buildButton(context, color: Colors.green[700], onClick: () { | 
					
						
							|  |  |  |             handleAccept(context); | 
					
						
							|  |  |  |             handleElevate(context); | 
					
						
							|  |  |  |             windowManager.minimize(); | 
					
						
							|  |  |  |           }, | 
					
						
							| 
									
										
										
										
											2023-06-14 21:30:05 +08:00
										 |  |  |               text: 'Accept and Elevate', | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |               icon: Icon( | 
					
						
							| 
									
										
										
										
											2023-05-24 14:18:42 +08:00
										 |  |  |                 Icons.security_rounded, | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |                 color: Colors.white, | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                 size: 14, | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |               ), | 
					
						
							| 
									
										
										
										
											2023-06-14 21:30:05 +08:00
										 |  |  |               textColor: Colors.white, | 
					
						
							|  |  |  |               tooltip: 'accept_and_elevate_btn_tooltip'), | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |         ), | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |         Row( | 
					
						
							|  |  |  |           mainAxisAlignment: MainAxisAlignment.center, | 
					
						
							|  |  |  |           children: [ | 
					
						
							| 
									
										
										
										
											2022-11-20 15:53:08 +08:00
										 |  |  |             if (showAccept) | 
					
						
							|  |  |  |               Expanded( | 
					
						
							|  |  |  |                 child: Column( | 
					
						
							|  |  |  |                   children: [ | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |                     buildButton( | 
					
						
							|  |  |  |                       context, | 
					
						
							|  |  |  |                       color: MyTheme.accent, | 
					
						
							|  |  |  |                       onClick: () { | 
					
						
							|  |  |  |                         handleAccept(context); | 
					
						
							|  |  |  |                         windowManager.minimize(); | 
					
						
							|  |  |  |                       }, | 
					
						
							|  |  |  |                       text: 'Accept', | 
					
						
							|  |  |  |                       textColor: Colors.white, | 
					
						
							|  |  |  |                     ), | 
					
						
							| 
									
										
										
										
											2022-11-20 15:53:08 +08:00
										 |  |  |                   ], | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |             Expanded( | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |               child: buildButton( | 
					
						
							|  |  |  |                 context, | 
					
						
							|  |  |  |                 color: Colors.transparent, | 
					
						
							|  |  |  |                 border: Border.all(color: Colors.grey), | 
					
						
							|  |  |  |                 onClick: handleDisconnect, | 
					
						
							|  |  |  |                 text: 'Cancel', | 
					
						
							|  |  |  |                 textColor: null, | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |           ], | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |         ), | 
					
						
							|  |  |  |       ], | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |     ).marginOnly(bottom: buttonBottomMargin); | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 21:30:05 +08:00
										 |  |  |   Widget buildButton(BuildContext context, | 
					
						
							|  |  |  |       {required Color? color, | 
					
						
							|  |  |  |       required Function() onClick, | 
					
						
							|  |  |  |       Icon? icon, | 
					
						
							|  |  |  |       BoxBorder? border, | 
					
						
							|  |  |  |       required String text, | 
					
						
							|  |  |  |       required Color? textColor, | 
					
						
							|  |  |  |       String? tooltip}) { | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |     Widget textWidget; | 
					
						
							|  |  |  |     if (icon != null) { | 
					
						
							|  |  |  |       textWidget = Text( | 
					
						
							|  |  |  |         translate(text), | 
					
						
							|  |  |  |         style: TextStyle(color: textColor), | 
					
						
							|  |  |  |         textAlign: TextAlign.center, | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       textWidget = Expanded( | 
					
						
							|  |  |  |         child: Text( | 
					
						
							|  |  |  |           translate(text), | 
					
						
							|  |  |  |           style: TextStyle(color: textColor), | 
					
						
							|  |  |  |           textAlign: TextAlign.center, | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-06-03 22:19:39 +08:00
										 |  |  |     final borderRadius = BorderRadius.circular(10.0); | 
					
						
							| 
									
										
										
										
											2023-06-14 21:30:05 +08:00
										 |  |  |     final btn = Container( | 
					
						
							| 
									
										
										
										
											2023-05-20 15:12:52 +02:00
										 |  |  |       height: 28, | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |       decoration: BoxDecoration( | 
					
						
							| 
									
										
										
										
											2023-06-03 22:19:39 +08:00
										 |  |  |           color: color, borderRadius: borderRadius, border: border), | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |       child: InkWell( | 
					
						
							| 
									
										
										
										
											2023-06-03 22:19:39 +08:00
										 |  |  |         borderRadius: borderRadius, | 
					
						
							| 
									
										
										
										
											2023-05-19 23:21:13 +02:00
										 |  |  |         onTap: () => checkClickTime(client.id, onClick), | 
					
						
							|  |  |  |         child: Row( | 
					
						
							|  |  |  |           mainAxisAlignment: MainAxisAlignment.center, | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             Offstage(offstage: icon == null, child: icon).marginOnly(right: 5), | 
					
						
							|  |  |  |             textWidget, | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2023-06-14 21:30:05 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |     return (tooltip != null | 
					
						
							|  |  |  |             ? Tooltip( | 
					
						
							|  |  |  |                 message: translate(tooltip), | 
					
						
							|  |  |  |                 child: btn, | 
					
						
							|  |  |  |               ) | 
					
						
							|  |  |  |             : btn) | 
					
						
							|  |  |  |         .marginAll(4); | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |   void handleDisconnect() { | 
					
						
							| 
									
										
										
										
											2022-08-17 21:46:56 +08:00
										 |  |  |     bind.cmCloseConnection(connId: client.id); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-17 21:46:56 +08:00
										 |  |  |   void handleAccept(BuildContext context) { | 
					
						
							|  |  |  |     final model = Provider.of<ServerModel>(context, listen: false); | 
					
						
							|  |  |  |     model.sendLoginResponse(client, true); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-10-08 20:15:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-10 10:27:13 +08:00
										 |  |  |   void handleElevate(BuildContext context) { | 
					
						
							|  |  |  |     final model = Provider.of<ServerModel>(context, listen: false); | 
					
						
							|  |  |  |     model.setShowElevation(false); | 
					
						
							|  |  |  |     bind.cmElevatePortable(connId: client.id); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-10 17:06:27 +08:00
										 |  |  |   void handleClose() async { | 
					
						
							| 
									
										
										
										
											2022-10-08 20:15:02 +08:00
										 |  |  |     await bind.cmRemoveDisconnectedConnection(connId: client.id); | 
					
						
							|  |  |  |     if (await bind.cmGetClientsLength() == 0) { | 
					
						
							|  |  |  |       windowManager.close(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-01-17 13:28:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   void handleSwitchBack(BuildContext context) { | 
					
						
							|  |  |  |     bind.cmSwitchBack(connId: client.id); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-02-07 16:11:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   void handleVoiceCall(bool accept) { | 
					
						
							|  |  |  |     bind.cmHandleIncomingVoiceCall(id: client.id, accept: accept); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void closeVoiceCall() { | 
					
						
							|  |  |  |     bind.cmCloseVoiceCall(id: client.id); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-17 21:28:36 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-30 16:50:25 +08:00
										 |  |  | void checkClickTime(int id, Function() callback) async { | 
					
						
							|  |  |  |   var clickCallbackTime = DateTime.now().millisecondsSinceEpoch; | 
					
						
							|  |  |  |   await bind.cmCheckClickTime(connId: id); | 
					
						
							|  |  |  |   Timer(const Duration(milliseconds: 120), () async { | 
					
						
							|  |  |  |     var d = clickCallbackTime - await bind.cmGetClickTime(); | 
					
						
							|  |  |  |     if (d > 120) callback(); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | class _FileTransferLogPage extends StatefulWidget { | 
					
						
							|  |  |  |   _FileTransferLogPage({Key? key}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<_FileTransferLogPage> createState() => __FileTransferLogPageState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class __FileTransferLogPageState extends State<_FileTransferLogPage> { | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return statusList(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget generateCard(Widget child) { | 
					
						
							|  |  |  |     return Container( | 
					
						
							|  |  |  |       decoration: BoxDecoration( | 
					
						
							|  |  |  |         color: Theme.of(context).cardColor, | 
					
						
							|  |  |  |         borderRadius: BorderRadius.all( | 
					
						
							|  |  |  |           Radius.circular(15.0), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       child: child, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-05 15:55:09 +08:00
										 |  |  |   iconLabel(CmFileLog item) { | 
					
						
							|  |  |  |     switch (item.action) { | 
					
						
							|  |  |  |       case CmFileAction.none: | 
					
						
							|  |  |  |         return Container(); | 
					
						
							|  |  |  |       case CmFileAction.localToRemote: | 
					
						
							|  |  |  |       case CmFileAction.remoteToLocal: | 
					
						
							|  |  |  |         return Column( | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             Transform.rotate( | 
					
						
							|  |  |  |               angle: item.action == CmFileAction.remoteToLocal ? 0 : pi, | 
					
						
							|  |  |  |               child: SvgPicture.asset( | 
					
						
							|  |  |  |                 "assets/arrow.svg", | 
					
						
							| 
									
										
										
										
											2024-02-12 21:39:19 +08:00
										 |  |  |                 colorFilter: svgColor(Theme.of(context).tabBarTheme.labelColor), | 
					
						
							| 
									
										
										
										
											2023-11-05 15:55:09 +08:00
										 |  |  |               ), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             Text(item.action == CmFileAction.remoteToLocal | 
					
						
							|  |  |  |                 ? translate('Send') | 
					
						
							|  |  |  |                 : translate('Receive')) | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       case CmFileAction.remove: | 
					
						
							|  |  |  |         return Column( | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             Icon( | 
					
						
							|  |  |  |               Icons.delete, | 
					
						
							|  |  |  |               color: Theme.of(context).tabBarTheme.labelColor, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             Text(translate('Delete')) | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       case CmFileAction.createDir: | 
					
						
							|  |  |  |         return Column( | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             Icon( | 
					
						
							|  |  |  |               Icons.create_new_folder, | 
					
						
							|  |  |  |               color: Theme.of(context).tabBarTheme.labelColor, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             Text(translate('Create Folder')) | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |   Widget statusList() { | 
					
						
							|  |  |  |     return PreferredSize( | 
					
						
							|  |  |  |       preferredSize: const Size(200, double.infinity), | 
					
						
							|  |  |  |       child: Container( | 
					
						
							| 
									
										
										
										
											2023-09-29 15:09:58 +08:00
										 |  |  |           padding: const EdgeInsets.all(12.0), | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |           child: Obx( | 
					
						
							|  |  |  |             () { | 
					
						
							|  |  |  |               final jobTable = gFFI.cmFileModel.currentJobTable; | 
					
						
							| 
									
										
										
										
											2023-11-05 15:55:09 +08:00
										 |  |  |               statusListView(List<CmFileLog> jobs) => ListView.builder( | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |                     controller: ScrollController(), | 
					
						
							|  |  |  |                     itemBuilder: (BuildContext context, int index) { | 
					
						
							|  |  |  |                       final item = jobs[index]; | 
					
						
							|  |  |  |                       return Padding( | 
					
						
							|  |  |  |                         padding: const EdgeInsets.only(bottom: 5), | 
					
						
							|  |  |  |                         child: generateCard( | 
					
						
							|  |  |  |                           Column( | 
					
						
							|  |  |  |                             mainAxisSize: MainAxisSize.min, | 
					
						
							|  |  |  |                             children: [ | 
					
						
							|  |  |  |                               Row( | 
					
						
							|  |  |  |                                 crossAxisAlignment: CrossAxisAlignment.center, | 
					
						
							|  |  |  |                                 children: [ | 
					
						
							|  |  |  |                                   SizedBox( | 
					
						
							|  |  |  |                                     width: 50, | 
					
						
							| 
									
										
										
										
											2023-11-05 15:55:09 +08:00
										 |  |  |                                     child: iconLabel(item), | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |                                   ).paddingOnly(left: 15), | 
					
						
							|  |  |  |                                   const SizedBox( | 
					
						
							|  |  |  |                                     width: 16.0, | 
					
						
							|  |  |  |                                   ), | 
					
						
							|  |  |  |                                   Expanded( | 
					
						
							|  |  |  |                                     child: Column( | 
					
						
							|  |  |  |                                       mainAxisSize: MainAxisSize.min, | 
					
						
							|  |  |  |                                       crossAxisAlignment: | 
					
						
							|  |  |  |                                           CrossAxisAlignment.start, | 
					
						
							|  |  |  |                                       children: [ | 
					
						
							|  |  |  |                                         Text( | 
					
						
							|  |  |  |                                           item.fileName, | 
					
						
							|  |  |  |                                         ).paddingSymmetric(vertical: 10), | 
					
						
							|  |  |  |                                         if (item.totalSize > 0) | 
					
						
							|  |  |  |                                           Text( | 
					
						
							|  |  |  |                                             '${translate("Total")} ${readableFileSize(item.totalSize.toDouble())}', | 
					
						
							|  |  |  |                                             style: TextStyle( | 
					
						
							|  |  |  |                                               fontSize: 12, | 
					
						
							|  |  |  |                                               color: MyTheme.darkGray, | 
					
						
							|  |  |  |                                             ), | 
					
						
							|  |  |  |                                           ), | 
					
						
							|  |  |  |                                         if (item.totalSize > 0) | 
					
						
							|  |  |  |                                           Offstage( | 
					
						
							|  |  |  |                                             offstage: item.state != | 
					
						
							|  |  |  |                                                 JobState.inProgress, | 
					
						
							|  |  |  |                                             child: Text( | 
					
						
							|  |  |  |                                               '${translate("Speed")} ${readableFileSize(item.speed)}/s', | 
					
						
							|  |  |  |                                               style: TextStyle( | 
					
						
							|  |  |  |                                                 fontSize: 12, | 
					
						
							|  |  |  |                                                 color: MyTheme.darkGray, | 
					
						
							|  |  |  |                                               ), | 
					
						
							|  |  |  |                                             ), | 
					
						
							|  |  |  |                                           ), | 
					
						
							|  |  |  |                                         Offstage( | 
					
						
							| 
									
										
										
										
											2023-11-05 15:55:09 +08:00
										 |  |  |                                           offstage: !(item.isTransfer() && | 
					
						
							|  |  |  |                                               item.state != | 
					
						
							|  |  |  |                                                   JobState.inProgress), | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |                                           child: Text( | 
					
						
							|  |  |  |                                             translate( | 
					
						
							|  |  |  |                                               item.display(), | 
					
						
							|  |  |  |                                             ), | 
					
						
							|  |  |  |                                             style: TextStyle( | 
					
						
							|  |  |  |                                               fontSize: 12, | 
					
						
							|  |  |  |                                               color: MyTheme.darkGray, | 
					
						
							|  |  |  |                                             ), | 
					
						
							|  |  |  |                                           ), | 
					
						
							|  |  |  |                                         ), | 
					
						
							|  |  |  |                                         if (item.totalSize > 0) | 
					
						
							|  |  |  |                                           Offstage( | 
					
						
							|  |  |  |                                             offstage: item.state != | 
					
						
							|  |  |  |                                                 JobState.inProgress, | 
					
						
							|  |  |  |                                             child: LinearPercentIndicator( | 
					
						
							|  |  |  |                                               padding: | 
					
						
							|  |  |  |                                                   EdgeInsets.only(right: 15), | 
					
						
							|  |  |  |                                               animateFromLastPercent: true, | 
					
						
							|  |  |  |                                               center: Text( | 
					
						
							|  |  |  |                                                 '${(item.finishedSize / item.totalSize * 100).toStringAsFixed(0)}%', | 
					
						
							|  |  |  |                                               ), | 
					
						
							|  |  |  |                                               barRadius: Radius.circular(15), | 
					
						
							|  |  |  |                                               percent: item.finishedSize / | 
					
						
							|  |  |  |                                                   item.totalSize, | 
					
						
							|  |  |  |                                               progressColor: MyTheme.accent, | 
					
						
							|  |  |  |                                               backgroundColor: | 
					
						
							|  |  |  |                                                   Theme.of(context).hoverColor, | 
					
						
							|  |  |  |                                               lineHeight: | 
					
						
							|  |  |  |                                                   kDesktopFileTransferRowHeight, | 
					
						
							|  |  |  |                                             ).paddingSymmetric(vertical: 15), | 
					
						
							|  |  |  |                                           ), | 
					
						
							|  |  |  |                                       ], | 
					
						
							|  |  |  |                                     ), | 
					
						
							|  |  |  |                                   ), | 
					
						
							|  |  |  |                                   Row( | 
					
						
							|  |  |  |                                     mainAxisAlignment: MainAxisAlignment.end, | 
					
						
							|  |  |  |                                     children: [], | 
					
						
							|  |  |  |                                   ), | 
					
						
							|  |  |  |                                 ], | 
					
						
							|  |  |  |                               ), | 
					
						
							|  |  |  |                             ], | 
					
						
							|  |  |  |                           ).paddingSymmetric(vertical: 10), | 
					
						
							|  |  |  |                         ), | 
					
						
							|  |  |  |                       ); | 
					
						
							|  |  |  |                     }, | 
					
						
							|  |  |  |                     itemCount: jobTable.length, | 
					
						
							|  |  |  |                   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               return jobTable.isEmpty | 
					
						
							|  |  |  |                   ? generateCard( | 
					
						
							|  |  |  |                       Center( | 
					
						
							|  |  |  |                         child: Column( | 
					
						
							|  |  |  |                           mainAxisAlignment: MainAxisAlignment.center, | 
					
						
							|  |  |  |                           children: [ | 
					
						
							|  |  |  |                             SvgPicture.asset( | 
					
						
							|  |  |  |                               "assets/transfer.svg", | 
					
						
							| 
									
										
										
										
											2024-02-12 21:39:19 +08:00
										 |  |  |                               colorFilter: svgColor( | 
					
						
							|  |  |  |                                   Theme.of(context).tabBarTheme.labelColor), | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |                               height: 40, | 
					
						
							|  |  |  |                             ).paddingOnly(bottom: 10), | 
					
						
							|  |  |  |                             Text( | 
					
						
							|  |  |  |                               translate("No transfers in progress"), | 
					
						
							|  |  |  |                               textAlign: TextAlign.center, | 
					
						
							| 
									
										
										
										
											2024-02-12 21:39:19 +08:00
										 |  |  |                               textScaler: TextScaler.linear(1.20), | 
					
						
							| 
									
										
										
										
											2023-09-06 16:56:39 +08:00
										 |  |  |                               style: TextStyle( | 
					
						
							|  |  |  |                                   color: | 
					
						
							|  |  |  |                                       Theme.of(context).tabBarTheme.labelColor), | 
					
						
							|  |  |  |                             ), | 
					
						
							|  |  |  |                           ], | 
					
						
							|  |  |  |                         ), | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |                   : statusListView(jobTable); | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |           )), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |