| 
									
										
										
										
											2022-11-14 15:41:43 +08:00
										 |  |  | import 'dart:io'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  | import 'package:flutter/services.dart'; | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  | import 'package:flutter_hbb/common/widgets/dialog.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-23 15:12:50 +08:00
										 |  |  | import 'package:flutter_hbb/consts.dart'; | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  | import 'package:flutter_hbb/models/peer_tab_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | import 'package:get/get.dart'; | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  | import 'package:provider/provider.dart'; | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import '../../common.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-02 17:19:44 +08:00
										 |  |  | import '../../common/formatter/id_formatter.dart'; | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | import '../../models/peer_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-23 17:21:50 +08:00
										 |  |  | import '../../models/platform_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu; | 
					
						
							|  |  |  | import '../../desktop/widgets/popup_menu.dart'; | 
					
						
							| 
									
										
										
										
											2023-08-13 18:13:06 +08:00
										 |  |  | import 'dart:math' as math; | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | typedef PopupMenuEntryBuilder = Future<List<mod_menu.PopupMenuEntry<String>>> | 
					
						
							|  |  |  |     Function(BuildContext); | 
					
						
							| 
									
										
										
										
											2022-07-28 14:06:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-23 17:21:50 +08:00
										 |  |  | enum PeerUiType { grid, list } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | final peerCardUiType = PeerUiType.grid.obs; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | class _PeerCard extends StatefulWidget { | 
					
						
							|  |  |  |   final Peer peer; | 
					
						
							| 
									
										
										
										
											2023-08-18 15:49:15 +08:00
										 |  |  |   final PeerTabIndex tab; | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |   final Function(BuildContext, String) connect; | 
					
						
							|  |  |  |   final PopupMenuEntryBuilder popupMenuEntryBuilder; | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 00:35:19 -07:00
										 |  |  |   const _PeerCard( | 
					
						
							| 
									
										
										
										
											2022-08-03 22:03:31 +08:00
										 |  |  |       {required this.peer, | 
					
						
							| 
									
										
										
										
											2023-08-18 15:49:15 +08:00
										 |  |  |       required this.tab, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       required this.connect, | 
					
						
							|  |  |  |       required this.popupMenuEntryBuilder, | 
					
						
							|  |  |  |       Key? key}) | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |       : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   _PeerCardState createState() => _PeerCardState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// State for the connection page.
 | 
					
						
							| 
									
										
										
										
											2022-07-29 16:47:24 +08:00
										 |  |  | class _PeerCardState extends State<_PeerCard> | 
					
						
							|  |  |  |     with AutomaticKeepAliveClientMixin { | 
					
						
							| 
									
										
										
										
											2022-09-01 22:36:40 -07:00
										 |  |  |   var _menuPos = RelativeRect.fill; | 
					
						
							| 
									
										
										
										
											2022-10-08 19:28:20 +09:00
										 |  |  |   final double _cardRadius = 16; | 
					
						
							| 
									
										
										
										
											2023-03-07 20:31:20 +01:00
										 |  |  |   final double _tileRadius = 5; | 
					
						
							| 
									
										
										
										
											2022-08-24 11:01:58 +08:00
										 |  |  |   final double _borderWidth = 2; | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-07-29 16:47:24 +08:00
										 |  |  |     super.build(context); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |     if (isDesktop) { | 
					
						
							|  |  |  |       return _buildDesktop(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       return _buildMobile(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildMobile() { | 
					
						
							|  |  |  |     final peer = super.widget.peer; | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |     final PeerTabModel peerTabModel = Provider.of(context); | 
					
						
							| 
									
										
										
										
											2023-08-22 11:41:57 +08:00
										 |  |  |     return Card( | 
					
						
							| 
									
										
										
										
											2022-09-21 17:16:09 +08:00
										 |  |  |         margin: EdgeInsets.symmetric(horizontal: 2), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |         child: GestureDetector( | 
					
						
							| 
									
										
										
										
											2023-08-22 11:41:57 +08:00
										 |  |  |           onTap: () { | 
					
						
							|  |  |  |             if (peerTabModel.multiSelectionMode) { | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |               peerTabModel.select(peer); | 
					
						
							| 
									
										
										
										
											2023-08-22 11:41:57 +08:00
										 |  |  |             } else { | 
					
						
							|  |  |  |               if (!isWebDesktop) { | 
					
						
							|  |  |  |                 connectInPeerTab(context, peer.id, widget.tab); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           onDoubleTap: isWebDesktop | 
					
						
							|  |  |  |               ? () => connectInPeerTab(context, peer.id, widget.tab) | 
					
						
							|  |  |  |               : null, | 
					
						
							|  |  |  |           onLongPress: () { | 
					
						
							|  |  |  |             peerTabModel.select(peer); | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           child: Container( | 
					
						
							| 
									
										
										
										
											2022-09-28 21:07:44 +08:00
										 |  |  |               padding: EdgeInsets.only(left: 12, top: 8, bottom: 8), | 
					
						
							| 
									
										
										
										
											2023-08-22 11:41:57 +08:00
										 |  |  |               child: _buildPeerTile(context, peer, null)), | 
					
						
							|  |  |  |         )); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildDesktop() { | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |     final PeerTabModel peerTabModel = Provider.of(context); | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |     final peer = super.widget.peer; | 
					
						
							| 
									
										
										
										
											2023-03-07 20:31:20 +01:00
										 |  |  |     var deco = Rx<BoxDecoration?>( | 
					
						
							|  |  |  |       BoxDecoration( | 
					
						
							| 
									
										
										
										
											2022-08-24 11:01:58 +08:00
										 |  |  |         border: Border.all(color: Colors.transparent, width: _borderWidth), | 
					
						
							| 
									
										
										
										
											2023-03-07 20:31:20 +01:00
										 |  |  |         borderRadius: BorderRadius.circular( | 
					
						
							|  |  |  |           peerCardUiType.value == PeerUiType.grid ? _cardRadius : _tileRadius, | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-08-23 17:21:50 +08:00
										 |  |  |     return MouseRegion( | 
					
						
							|  |  |  |       onEnter: (evt) { | 
					
						
							|  |  |  |         deco.value = BoxDecoration( | 
					
						
							| 
									
										
										
										
											2023-03-07 20:31:20 +01:00
										 |  |  |           border: Border.all( | 
					
						
							|  |  |  |               color: Theme.of(context).colorScheme.primary, | 
					
						
							|  |  |  |               width: _borderWidth), | 
					
						
							|  |  |  |           borderRadius: BorderRadius.circular( | 
					
						
							|  |  |  |             peerCardUiType.value == PeerUiType.grid ? _cardRadius : _tileRadius, | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2022-08-23 17:21:50 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |       onExit: (evt) { | 
					
						
							|  |  |  |         deco.value = BoxDecoration( | 
					
						
							| 
									
										
										
										
											2023-03-07 20:31:20 +01:00
										 |  |  |           border: Border.all(color: Colors.transparent, width: _borderWidth), | 
					
						
							|  |  |  |           borderRadius: BorderRadius.circular( | 
					
						
							|  |  |  |             peerCardUiType.value == PeerUiType.grid ? _cardRadius : _tileRadius, | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2022-08-23 17:21:50 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |       child: GestureDetector( | 
					
						
							| 
									
										
										
										
											2023-08-10 10:08:33 +08:00
										 |  |  |           onDoubleTap: | 
					
						
							|  |  |  |               peerTabModel.multiSelectionMode || peerTabModel.isShiftDown | 
					
						
							|  |  |  |                   ? null | 
					
						
							|  |  |  |                   : () => widget.connect(context, peer.id), | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |           onTap: () => peerTabModel.select(peer), | 
					
						
							|  |  |  |           onLongPress: () => peerTabModel.select(peer), | 
					
						
							| 
									
										
										
										
											2022-08-23 17:21:50 +08:00
										 |  |  |           child: Obx(() => peerCardUiType.value == PeerUiType.grid | 
					
						
							|  |  |  |               ? _buildPeerCard(context, peer, deco) | 
					
						
							|  |  |  |               : _buildPeerTile(context, peer, deco))), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 22:03:31 +08:00
										 |  |  |   Widget _buildPeerTile( | 
					
						
							| 
									
										
										
										
											2023-08-22 11:41:57 +08:00
										 |  |  |       BuildContext context, Peer peer, Rx<BoxDecoration?>? deco) { | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |     final name = | 
					
						
							|  |  |  |         '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |     final greyStyle = TextStyle( | 
					
						
							|  |  |  |         fontSize: 11, | 
					
						
							|  |  |  |         color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6)); | 
					
						
							| 
									
										
										
										
											2023-08-22 11:41:57 +08:00
										 |  |  |     final child = Row( | 
					
						
							|  |  |  |       mainAxisSize: MainAxisSize.max, | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         Container( | 
					
						
							|  |  |  |           decoration: BoxDecoration( | 
					
						
							|  |  |  |             color: str2color('${peer.id}${peer.platform}', 0x7f), | 
					
						
							|  |  |  |             borderRadius: isMobile | 
					
						
							|  |  |  |                 ? BorderRadius.circular(_tileRadius) | 
					
						
							|  |  |  |                 : BorderRadius.only( | 
					
						
							|  |  |  |                     topLeft: Radius.circular(_tileRadius), | 
					
						
							|  |  |  |                     bottomLeft: Radius.circular(_tileRadius), | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |           alignment: Alignment.center, | 
					
						
							|  |  |  |           width: isMobile ? 50 : 42, | 
					
						
							|  |  |  |           height: isMobile ? 50 : null, | 
					
						
							|  |  |  |           child: getPlatformImage(peer.platform, size: isMobile ? 38 : 30) | 
					
						
							|  |  |  |               .paddingAll(6), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         Expanded( | 
					
						
							|  |  |  |           child: Container( | 
					
						
							|  |  |  |             decoration: BoxDecoration( | 
					
						
							|  |  |  |               color: Theme.of(context).colorScheme.background, | 
					
						
							|  |  |  |               borderRadius: BorderRadius.only( | 
					
						
							|  |  |  |                 topRight: Radius.circular(_tileRadius), | 
					
						
							|  |  |  |                 bottomRight: Radius.circular(_tileRadius), | 
					
						
							| 
									
										
										
										
											2022-08-23 17:21:50 +08:00
										 |  |  |               ), | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2023-08-22 11:41:57 +08:00
										 |  |  |             child: Row( | 
					
						
							|  |  |  |               children: [ | 
					
						
							|  |  |  |                 Expanded( | 
					
						
							|  |  |  |                   child: Column( | 
					
						
							|  |  |  |                     children: [ | 
					
						
							|  |  |  |                       Row(children: [ | 
					
						
							|  |  |  |                         getOnline(isMobile ? 4 : 8, peer.online), | 
					
						
							|  |  |  |                         Expanded( | 
					
						
							| 
									
										
										
										
											2022-09-22 15:59:51 +08:00
										 |  |  |                             child: Text( | 
					
						
							| 
									
										
										
										
											2023-08-22 11:41:57 +08:00
										 |  |  |                           peer.alias.isEmpty ? formatID(peer.id) : peer.alias, | 
					
						
							|  |  |  |                           overflow: TextOverflow.ellipsis, | 
					
						
							|  |  |  |                           style: Theme.of(context).textTheme.titleSmall, | 
					
						
							|  |  |  |                         )), | 
					
						
							|  |  |  |                       ]).marginOnly(top: isMobile ? 0 : 2), | 
					
						
							|  |  |  |                       Align( | 
					
						
							|  |  |  |                         alignment: Alignment.centerLeft, | 
					
						
							|  |  |  |                         child: Text( | 
					
						
							|  |  |  |                           name, | 
					
						
							|  |  |  |                           style: isMobile ? null : greyStyle, | 
					
						
							|  |  |  |                           textAlign: TextAlign.start, | 
					
						
							|  |  |  |                           overflow: TextOverflow.ellipsis, | 
					
						
							|  |  |  |                         ), | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                     ], | 
					
						
							|  |  |  |                   ).marginOnly(top: 2), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 isMobile | 
					
						
							|  |  |  |                     ? checkBoxOrActionMoreMobile(peer) | 
					
						
							|  |  |  |                     : checkBoxOrActionMoreDesktop(peer, isTile: true), | 
					
						
							|  |  |  |               ], | 
					
						
							|  |  |  |             ).paddingOnly(left: 10.0, top: 3.0), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       ], | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |     final colors = | 
					
						
							|  |  |  |         _frontN(peer.tags, 25).map((e) => gFFI.abModel.getTagColor(e)).toList(); | 
					
						
							| 
									
										
										
										
											2023-08-13 18:13:06 +08:00
										 |  |  |     return Tooltip( | 
					
						
							| 
									
										
										
										
											2023-08-22 11:41:57 +08:00
										 |  |  |       message: isMobile | 
					
						
							|  |  |  |           ? '' | 
					
						
							|  |  |  |           : peer.tags.isNotEmpty | 
					
						
							|  |  |  |               ? '${translate('Tags')}: ${peer.tags.join(', ')}' | 
					
						
							|  |  |  |               : '', | 
					
						
							| 
									
										
										
										
											2023-08-13 18:13:06 +08:00
										 |  |  |       child: Stack(children: [ | 
					
						
							| 
									
										
										
										
											2023-08-22 11:41:57 +08:00
										 |  |  |         deco == null | 
					
						
							|  |  |  |             ? child | 
					
						
							|  |  |  |             : Obx( | 
					
						
							|  |  |  |                 () => Container( | 
					
						
							|  |  |  |                   foregroundDecoration: deco.value, | 
					
						
							|  |  |  |                   child: child, | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |         if (colors.isNotEmpty) | 
					
						
							|  |  |  |           Positioned( | 
					
						
							|  |  |  |             top: 2, | 
					
						
							| 
									
										
										
										
											2023-08-22 11:41:57 +08:00
										 |  |  |             right: isMobile ? 20 : 10, | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |             child: CustomPaint( | 
					
						
							|  |  |  |               painter: TagPainter(radius: 3, colors: colors), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ) | 
					
						
							| 
									
										
										
										
											2023-08-13 18:13:06 +08:00
										 |  |  |       ]), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-23 17:21:50 +08:00
										 |  |  |   Widget _buildPeerCard( | 
					
						
							|  |  |  |       BuildContext context, Peer peer, Rx<BoxDecoration?> deco) { | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |     final name = | 
					
						
							|  |  |  |         '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; | 
					
						
							| 
									
										
										
										
											2023-08-13 18:13:06 +08:00
										 |  |  |     final child = Card( | 
					
						
							| 
									
										
										
										
											2022-08-24 11:01:58 +08:00
										 |  |  |       color: Colors.transparent, | 
					
						
							|  |  |  |       elevation: 0, | 
					
						
							|  |  |  |       margin: EdgeInsets.zero, | 
					
						
							| 
									
										
										
										
											2022-08-24 14:57:41 +08:00
										 |  |  |       child: Obx( | 
					
						
							|  |  |  |         () => Container( | 
					
						
							| 
									
										
										
										
											2022-08-24 11:01:58 +08:00
										 |  |  |           foregroundDecoration: deco.value, | 
					
						
							|  |  |  |           child: ClipRRect( | 
					
						
							| 
									
										
										
										
											2022-10-08 19:28:20 +09:00
										 |  |  |             borderRadius: BorderRadius.circular(_cardRadius - _borderWidth), | 
					
						
							| 
									
										
										
										
											2022-08-24 11:01:58 +08:00
										 |  |  |             child: Column( | 
					
						
							|  |  |  |               mainAxisSize: MainAxisSize.min, | 
					
						
							|  |  |  |               mainAxisAlignment: MainAxisAlignment.center, | 
					
						
							|  |  |  |               children: [ | 
					
						
							|  |  |  |                 Expanded( | 
					
						
							|  |  |  |                   child: Container( | 
					
						
							| 
									
										
										
										
											2022-08-24 14:57:41 +08:00
										 |  |  |                     color: str2color('${peer.id}${peer.platform}', 0x7f), | 
					
						
							| 
									
										
										
										
											2022-08-24 11:01:58 +08:00
										 |  |  |                     child: Row( | 
					
						
							|  |  |  |                       children: [ | 
					
						
							|  |  |  |                         Expanded( | 
					
						
							|  |  |  |                           child: Column( | 
					
						
							|  |  |  |                             crossAxisAlignment: CrossAxisAlignment.center, | 
					
						
							|  |  |  |                             children: [ | 
					
						
							|  |  |  |                               Container( | 
					
						
							|  |  |  |                                 padding: const EdgeInsets.all(6), | 
					
						
							| 
									
										
										
										
											2022-09-13 21:36:38 +08:00
										 |  |  |                                 child: | 
					
						
							|  |  |  |                                     getPlatformImage(peer.platform, size: 60), | 
					
						
							| 
									
										
										
										
											2023-08-13 18:13:06 +08:00
										 |  |  |                               ).marginOnly(top: 4), | 
					
						
							| 
									
										
										
										
											2022-08-24 11:01:58 +08:00
										 |  |  |                               Row( | 
					
						
							|  |  |  |                                 children: [ | 
					
						
							|  |  |  |                                   Expanded( | 
					
						
							| 
									
										
										
										
											2022-09-21 21:20:19 +08:00
										 |  |  |                                     child: Tooltip( | 
					
						
							|  |  |  |                                       message: name, | 
					
						
							|  |  |  |                                       waitDuration: const Duration(seconds: 1), | 
					
						
							|  |  |  |                                       child: Text( | 
					
						
							|  |  |  |                                         name, | 
					
						
							|  |  |  |                                         style: const TextStyle( | 
					
						
							|  |  |  |                                             color: Colors.white70, | 
					
						
							|  |  |  |                                             fontSize: 12), | 
					
						
							|  |  |  |                                         textAlign: TextAlign.center, | 
					
						
							|  |  |  |                                         overflow: TextOverflow.ellipsis, | 
					
						
							|  |  |  |                                       ), | 
					
						
							|  |  |  |                                     ), | 
					
						
							| 
									
										
										
										
											2022-08-24 14:57:41 +08:00
										 |  |  |                                   ), | 
					
						
							| 
									
										
										
										
											2022-08-24 11:01:58 +08:00
										 |  |  |                                 ], | 
					
						
							|  |  |  |                               ), | 
					
						
							|  |  |  |                             ], | 
					
						
							|  |  |  |                           ).paddingAll(4.0), | 
					
						
							|  |  |  |                         ), | 
					
						
							|  |  |  |                       ], | 
					
						
							|  |  |  |                     ), | 
					
						
							| 
									
										
										
										
											2022-08-24 14:57:41 +08:00
										 |  |  |                   ), | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2022-08-24 11:01:58 +08:00
										 |  |  |                 Container( | 
					
						
							| 
									
										
										
										
											2023-02-23 16:49:31 +01:00
										 |  |  |                   color: Theme.of(context).colorScheme.background, | 
					
						
							| 
									
										
										
										
											2022-08-24 11:01:58 +08:00
										 |  |  |                   child: Row( | 
					
						
							|  |  |  |                     mainAxisAlignment: MainAxisAlignment.spaceBetween, | 
					
						
							|  |  |  |                     children: [ | 
					
						
							| 
									
										
										
										
											2022-09-22 15:59:51 +08:00
										 |  |  |                       Expanded( | 
					
						
							|  |  |  |                           child: Row(children: [ | 
					
						
							| 
									
										
										
										
											2022-09-25 21:15:00 +08:00
										 |  |  |                         getOnline(8, peer.online), | 
					
						
							| 
									
										
										
										
											2022-09-22 15:59:51 +08:00
										 |  |  |                         Expanded( | 
					
						
							|  |  |  |                             child: Text( | 
					
						
							|  |  |  |                           peer.alias.isEmpty ? formatID(peer.id) : peer.alias, | 
					
						
							|  |  |  |                           overflow: TextOverflow.ellipsis, | 
					
						
							| 
									
										
										
										
											2022-09-27 19:42:05 +08:00
										 |  |  |                           style: Theme.of(context).textTheme.titleSmall, | 
					
						
							| 
									
										
										
										
											2022-09-22 15:59:51 +08:00
										 |  |  |                         )), | 
					
						
							|  |  |  |                       ]).paddingSymmetric(vertical: 8)), | 
					
						
							| 
									
										
										
										
											2023-08-19 08:20:48 +08:00
										 |  |  |                       checkBoxOrActionMoreDesktop(peer, isTile: false), | 
					
						
							| 
									
										
										
										
											2022-08-24 11:01:58 +08:00
										 |  |  |                     ], | 
					
						
							| 
									
										
										
										
											2022-08-26 13:02:15 +08:00
										 |  |  |                   ).paddingSymmetric(horizontal: 12.0), | 
					
						
							| 
									
										
										
										
											2022-08-24 11:01:58 +08:00
										 |  |  |                 ) | 
					
						
							|  |  |  |               ], | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-08-24 14:57:41 +08:00
										 |  |  |           ), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2022-08-23 17:21:50 +08:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-08-13 18:13:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |     final colors = | 
					
						
							|  |  |  |         _frontN(peer.tags, 25).map((e) => gFFI.abModel.getTagColor(e)).toList(); | 
					
						
							| 
									
										
										
										
											2023-08-13 18:13:06 +08:00
										 |  |  |     return Tooltip( | 
					
						
							|  |  |  |       message: peer.tags.isNotEmpty | 
					
						
							|  |  |  |           ? '${translate('Tags')}: ${peer.tags.join(', ')}' | 
					
						
							|  |  |  |           : '', | 
					
						
							|  |  |  |       child: Stack(children: [ | 
					
						
							|  |  |  |         child, | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |         if (colors.isNotEmpty) | 
					
						
							|  |  |  |           Positioned( | 
					
						
							|  |  |  |             top: 4, | 
					
						
							|  |  |  |             right: 12, | 
					
						
							|  |  |  |             child: CustomPaint( | 
					
						
							|  |  |  |               painter: TagPainter(radius: 4, colors: colors), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ) | 
					
						
							| 
									
										
										
										
											2023-08-13 18:13:06 +08:00
										 |  |  |       ]), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   List _frontN<T>(List list, int n) { | 
					
						
							|  |  |  |     if (list.length <= n) { | 
					
						
							|  |  |  |       return list; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       return list.sublist(0, n); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-08-23 17:21:50 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |   Widget checkBoxOrActionMoreMobile(Peer peer) { | 
					
						
							|  |  |  |     final PeerTabModel peerTabModel = Provider.of(context); | 
					
						
							|  |  |  |     final selected = peerTabModel.isPeerSelected(peer.id); | 
					
						
							|  |  |  |     if (peerTabModel.multiSelectionMode) { | 
					
						
							|  |  |  |       return Padding( | 
					
						
							|  |  |  |         padding: const EdgeInsets.all(12), | 
					
						
							|  |  |  |         child: selected | 
					
						
							|  |  |  |             ? Icon( | 
					
						
							|  |  |  |                 Icons.check_box, | 
					
						
							|  |  |  |                 color: MyTheme.accent, | 
					
						
							|  |  |  |               ) | 
					
						
							|  |  |  |             : Icon(Icons.check_box_outline_blank), | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       return InkWell( | 
					
						
							|  |  |  |           child: const Padding( | 
					
						
							|  |  |  |               padding: EdgeInsets.all(12), child: Icon(Icons.more_vert)), | 
					
						
							|  |  |  |           onTapDown: (e) { | 
					
						
							|  |  |  |             final x = e.globalPosition.dx; | 
					
						
							|  |  |  |             final y = e.globalPosition.dy; | 
					
						
							|  |  |  |             _menuPos = RelativeRect.fromLTRB(x, y, x, y); | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           onTap: () { | 
					
						
							|  |  |  |             _showPeerMenu(peer.id); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-19 08:20:48 +08:00
										 |  |  |   Widget checkBoxOrActionMoreDesktop(Peer peer, {required bool isTile}) { | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |     final PeerTabModel peerTabModel = Provider.of(context); | 
					
						
							|  |  |  |     final selected = peerTabModel.isPeerSelected(peer.id); | 
					
						
							|  |  |  |     if (peerTabModel.multiSelectionMode) { | 
					
						
							|  |  |  |       final icon = selected | 
					
						
							|  |  |  |           ? Icon( | 
					
						
							|  |  |  |               Icons.check_box, | 
					
						
							|  |  |  |               color: MyTheme.accent, | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |           : Icon(Icons.check_box_outline_blank); | 
					
						
							|  |  |  |       bool last = peerTabModel.isShiftDown && peer.id == peerTabModel.lastId; | 
					
						
							| 
									
										
										
										
											2023-08-19 08:20:48 +08:00
										 |  |  |       double right = isTile ? 4 : 0; | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |       if (last) { | 
					
						
							|  |  |  |         return Container( | 
					
						
							|  |  |  |           decoration: BoxDecoration( | 
					
						
							|  |  |  |               border: Border.all(color: MyTheme.accent, width: 1)), | 
					
						
							|  |  |  |           child: icon, | 
					
						
							| 
									
										
										
										
											2023-08-19 08:20:48 +08:00
										 |  |  |         ).marginOnly(right: right); | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2023-08-19 08:20:48 +08:00
										 |  |  |         return icon.marginOnly(right: right); | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       return _actionMore(peer); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-01 22:36:40 -07:00
										 |  |  |   Widget _actionMore(Peer peer) => Listener( | 
					
						
							|  |  |  |       onPointerDown: (e) { | 
					
						
							|  |  |  |         final x = e.position.dx; | 
					
						
							|  |  |  |         final y = e.position.dy; | 
					
						
							|  |  |  |         _menuPos = RelativeRect.fromLTRB(x, y, x, y); | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |       onPointerUp: (_) => _showPeerMenu(peer.id), | 
					
						
							| 
									
										
										
										
											2023-06-21 19:39:55 +08:00
										 |  |  |       child: build_more(context)); | 
					
						
							| 
									
										
										
										
											2022-09-01 22:36:40 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /// Show the peer menu and handle user's choice.
 | 
					
						
							|  |  |  |   /// User might remove the peer or send a file to the peer.
 | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   void _showPeerMenu(String id) async { | 
					
						
							| 
									
										
										
										
											2022-09-01 22:36:40 -07:00
										 |  |  |     await mod_menu.showMenu( | 
					
						
							|  |  |  |       context: context, | 
					
						
							|  |  |  |       position: _menuPos, | 
					
						
							|  |  |  |       items: await super.widget.popupMenuEntryBuilder(context), | 
					
						
							|  |  |  |       elevation: 8, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   bool get wantKeepAlive => true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | abstract class BasePeerCard extends StatelessWidget { | 
					
						
							|  |  |  |   final Peer peer; | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |   final PeerTabIndex tab; | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |   final EdgeInsets? menuPadding; | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |   BasePeerCard( | 
					
						
							|  |  |  |       {required this.peer, required this.tab, this.menuPadding, Key? key}) | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |       : super(key: key); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return _PeerCard( | 
					
						
							|  |  |  |       peer: peer, | 
					
						
							| 
									
										
										
										
											2023-08-18 15:49:15 +08:00
										 |  |  |       tab: tab, | 
					
						
							|  |  |  |       connect: (BuildContext context, String id) => | 
					
						
							|  |  |  |           connectInPeerTab(context, id, tab), | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       popupMenuEntryBuilder: _buildPopupMenuEntry, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<List<mod_menu.PopupMenuEntry<String>>> _buildPopupMenuEntry( | 
					
						
							|  |  |  |           BuildContext context) async => | 
					
						
							|  |  |  |       (await _buildMenuItems(context)) | 
					
						
							|  |  |  |           .map((e) => e.build( | 
					
						
							|  |  |  |               context, | 
					
						
							|  |  |  |               const MenuConfig( | 
					
						
							| 
									
										
										
										
											2022-10-08 19:28:20 +09:00
										 |  |  |                   commonColor: CustomPopupMenuTheme.commonColor, | 
					
						
							|  |  |  |                   height: CustomPopupMenuTheme.height, | 
					
						
							|  |  |  |                   dividerHeight: CustomPopupMenuTheme.dividerHeight))) | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |           .expand((i) => i) | 
					
						
							|  |  |  |           .toList(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @protected | 
					
						
							|  |  |  |   Future<List<MenuEntryBase<String>>> _buildMenuItems(BuildContext context); | 
					
						
							| 
									
										
										
										
											2022-08-26 13:02:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |   MenuEntryBase<String> _connectCommonAction( | 
					
						
							| 
									
										
										
										
											2023-08-02 23:10:31 +08:00
										 |  |  |     BuildContext context, | 
					
						
							|  |  |  |     String id, | 
					
						
							|  |  |  |     String title, { | 
					
						
							|  |  |  |     bool isFileTransfer = false, | 
					
						
							|  |  |  |     bool isTcpTunneling = false, | 
					
						
							|  |  |  |     bool isRDP = false, | 
					
						
							|  |  |  |   }) { | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     return MenuEntryButton<String>( | 
					
						
							|  |  |  |       childBuilder: (TextStyle? style) => Text( | 
					
						
							| 
									
										
										
										
											2022-09-21 21:20:19 +08:00
										 |  |  |         title, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |         style: style, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       proc: () { | 
					
						
							| 
									
										
										
										
											2023-08-18 15:49:15 +08:00
										 |  |  |         connectInPeerTab( | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |           context, | 
					
						
							|  |  |  |           peer.id, | 
					
						
							| 
									
										
										
										
											2023-08-18 15:49:15 +08:00
										 |  |  |           tab, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |           isFileTransfer: isFileTransfer, | 
					
						
							|  |  |  |           isTcpTunneling: isTcpTunneling, | 
					
						
							|  |  |  |           isRDP: isRDP, | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |       padding: menuPadding, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       dismissOnClicked: true, | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |   @protected | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  |   MenuEntryBase<String> _connectAction(BuildContext context, Peer peer) { | 
					
						
							| 
									
										
										
										
											2022-09-21 21:20:19 +08:00
										 |  |  |     return _connectCommonAction( | 
					
						
							| 
									
										
										
										
											2023-08-02 23:10:31 +08:00
										 |  |  |       context, | 
					
						
							|  |  |  |       peer.id, | 
					
						
							|  |  |  |       (peer.alias.isEmpty | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  |           ? translate('Connect') | 
					
						
							|  |  |  |           : '${translate('Connect')} ${peer.id}'), | 
					
						
							| 
									
										
										
										
											2023-08-02 23:10:31 +08:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @protected | 
					
						
							|  |  |  |   MenuEntryBase<String> _transferFileAction(BuildContext context, String id) { | 
					
						
							|  |  |  |     return _connectCommonAction( | 
					
						
							|  |  |  |       context, | 
					
						
							|  |  |  |       id, | 
					
						
							| 
									
										
										
										
											2022-09-21 21:20:19 +08:00
										 |  |  |       translate('Transfer File'), | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       isFileTransfer: true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @protected | 
					
						
							|  |  |  |   MenuEntryBase<String> _tcpTunnelingAction(BuildContext context, String id) { | 
					
						
							|  |  |  |     return _connectCommonAction( | 
					
						
							|  |  |  |       context, | 
					
						
							|  |  |  |       id, | 
					
						
							| 
									
										
										
										
											2022-09-21 21:20:19 +08:00
										 |  |  |       translate('TCP Tunneling'), | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       isTcpTunneling: true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @protected | 
					
						
							|  |  |  |   MenuEntryBase<String> _rdpAction(BuildContext context, String id) { | 
					
						
							|  |  |  |     return MenuEntryButton<String>( | 
					
						
							| 
									
										
										
										
											2022-09-01 22:36:40 -07:00
										 |  |  |       childBuilder: (TextStyle? style) => Container( | 
					
						
							|  |  |  |           alignment: AlignmentDirectional.center, | 
					
						
							| 
									
										
										
										
											2022-10-08 19:28:20 +09:00
										 |  |  |           height: CustomPopupMenuTheme.height, | 
					
						
							| 
									
										
										
										
											2022-09-01 22:36:40 -07:00
										 |  |  |           child: Row( | 
					
						
							|  |  |  |             children: [ | 
					
						
							|  |  |  |               Text( | 
					
						
							|  |  |  |                 translate('RDP'), | 
					
						
							|  |  |  |                 style: style, | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |               Expanded( | 
					
						
							|  |  |  |                   child: Align( | 
					
						
							|  |  |  |                 alignment: Alignment.centerRight, | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |                 child: Transform.scale( | 
					
						
							|  |  |  |                     scale: 0.8, | 
					
						
							|  |  |  |                     child: IconButton( | 
					
						
							|  |  |  |                       icon: const Icon(Icons.edit), | 
					
						
							|  |  |  |                       padding: EdgeInsets.zero, | 
					
						
							|  |  |  |                       onPressed: () { | 
					
						
							|  |  |  |                         if (Navigator.canPop(context)) { | 
					
						
							|  |  |  |                           Navigator.pop(context); | 
					
						
							|  |  |  |                         } | 
					
						
							| 
									
										
										
										
											2023-01-22 20:23:11 +08:00
										 |  |  |                         _rdpDialog(id); | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |                       }, | 
					
						
							|  |  |  |                     )), | 
					
						
							| 
									
										
										
										
											2022-09-01 22:36:40 -07:00
										 |  |  |               )) | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |           )), | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       proc: () { | 
					
						
							| 
									
										
										
										
											2023-08-18 15:49:15 +08:00
										 |  |  |         connectInPeerTab(context, id, tab, isRDP: true); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |       padding: menuPadding, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       dismissOnClicked: true, | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  |   @protected | 
					
						
							|  |  |  |   MenuEntryBase<String> _wolAction(String id) { | 
					
						
							|  |  |  |     return MenuEntryButton<String>( | 
					
						
							|  |  |  |       childBuilder: (TextStyle? style) => Text( | 
					
						
							|  |  |  |         translate('WOL'), | 
					
						
							|  |  |  |         style: style, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       proc: () { | 
					
						
							|  |  |  |         bind.mainWol(id: id); | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |       padding: menuPadding, | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  |       dismissOnClicked: true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-25 20:21:13 +03:00
										 |  |  |   /// Only available on Windows.
 | 
					
						
							| 
									
										
										
										
											2022-11-14 15:41:43 +08:00
										 |  |  |   @protected | 
					
						
							|  |  |  |   MenuEntryBase<String> _createShortCutAction(String id) { | 
					
						
							|  |  |  |     return MenuEntryButton<String>( | 
					
						
							|  |  |  |       childBuilder: (TextStyle? style) => Text( | 
					
						
							|  |  |  |         translate('Create Desktop Shortcut'), | 
					
						
							|  |  |  |         style: style, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       proc: () { | 
					
						
							|  |  |  |         bind.mainCreateShortcut(id: id); | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |         showToast(translate('Successful')); | 
					
						
							| 
									
										
										
										
											2022-11-14 15:41:43 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |       padding: menuPadding, | 
					
						
							|  |  |  |       dismissOnClicked: true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  |   Future<MenuEntryBase<String>> _openNewConnInAction( | 
					
						
							|  |  |  |       String id, String label, String key) async { | 
					
						
							|  |  |  |     return MenuEntrySwitch<String>( | 
					
						
							|  |  |  |       switchType: SwitchType.scheckbox, | 
					
						
							|  |  |  |       text: translate(label), | 
					
						
							|  |  |  |       getter: () async => mainGetPeerBoolOptionSync(id, key), | 
					
						
							|  |  |  |       setter: (bool v) async { | 
					
						
							|  |  |  |         await bind.mainSetPeerOption( | 
					
						
							|  |  |  |             id: id, key: key, value: bool2option(key, v)); | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |         showToast(translate('Successful')); | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |       padding: menuPadding, | 
					
						
							|  |  |  |       dismissOnClicked: true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _openInTabsAction(String id) async => | 
					
						
							| 
									
										
										
										
											2023-08-08 14:45:58 +08:00
										 |  |  |       await _openNewConnInAction(id, 'Open in New Tab', kOptionOpenInTabs); | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-08 18:06:31 +08:00
										 |  |  |   _openInWindowsAction(String id) async => await _openNewConnInAction( | 
					
						
							|  |  |  |       id, 'Open in New Window', kOptionOpenInWindows); | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   _openNewConnInOptAction(String id) async => | 
					
						
							|  |  |  |       mainGetLocalBoolOptionSync(kOptionOpenNewConnInTabs) | 
					
						
							|  |  |  |           ? await _openInWindowsAction(id) | 
					
						
							|  |  |  |           : await _openInTabsAction(id); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 20:23:11 +08:00
										 |  |  |   @protected | 
					
						
							|  |  |  |   Future<bool> _isForceAlwaysRelay(String id) async { | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  |     return (await bind.mainGetPeerOption(id: id, key: kOptionForceAlwaysRelay)) | 
					
						
							| 
									
										
										
										
											2023-01-22 20:23:11 +08:00
										 |  |  |         .isNotEmpty; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |   @protected | 
					
						
							|  |  |  |   Future<MenuEntryBase<String>> _forceAlwaysRelayAction(String id) async { | 
					
						
							|  |  |  |     return MenuEntrySwitch<String>( | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |       switchType: SwitchType.scheckbox, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       text: translate('Always connect via relay'), | 
					
						
							|  |  |  |       getter: () async { | 
					
						
							| 
									
										
										
										
											2023-01-22 20:23:11 +08:00
										 |  |  |         return await _isForceAlwaysRelay(id); | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |       setter: (bool v) async { | 
					
						
							|  |  |  |         await bind.mainSetPeerOption( | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  |             id: id, | 
					
						
							|  |  |  |             key: kOptionForceAlwaysRelay, | 
					
						
							|  |  |  |             value: bool2option(kOptionForceAlwaysRelay, v)); | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |         showToast(translate('Successful')); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |       padding: menuPadding, | 
					
						
							|  |  |  |       dismissOnClicked: true, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |   @protected | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |   MenuEntryBase<String> _renameAction(String id) { | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     return MenuEntryButton<String>( | 
					
						
							|  |  |  |       childBuilder: (TextStyle? style) => Text( | 
					
						
							|  |  |  |         translate('Rename'), | 
					
						
							|  |  |  |         style: style, | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2023-08-10 10:08:33 +08:00
										 |  |  |       proc: () async { | 
					
						
							|  |  |  |         String oldName = await _getAlias(id); | 
					
						
							|  |  |  |         renameDialog( | 
					
						
							|  |  |  |             oldName: oldName, | 
					
						
							|  |  |  |             onSubmit: (String newName) async { | 
					
						
							|  |  |  |               if (newName != oldName) { | 
					
						
							| 
									
										
										
										
											2023-08-15 09:23:55 +08:00
										 |  |  |                 if (tab == PeerTabIndex.ab) { | 
					
						
							|  |  |  |                   gFFI.abModel.changeAlias(id: id, alias: newName); | 
					
						
							| 
									
										
										
										
											2023-08-17 18:21:37 +08:00
										 |  |  |                   await bind.mainSetPeerAlias(id: id, alias: newName); | 
					
						
							| 
									
										
										
										
											2023-08-15 09:23:55 +08:00
										 |  |  |                   gFFI.abModel.pushAb(); | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                   await bind.mainSetPeerAlias(id: id, alias: newName); | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |                   showToast(translate('Successful')); | 
					
						
							| 
									
										
										
										
											2023-08-15 09:23:55 +08:00
										 |  |  |                   _update(); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-08-10 10:08:33 +08:00
										 |  |  |               } | 
					
						
							|  |  |  |             }); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |       padding: menuPadding, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       dismissOnClicked: true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |   @protected | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |   MenuEntryBase<String> _removeAction(String id) { | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     return MenuEntryButton<String>( | 
					
						
							| 
									
										
										
										
											2023-02-23 13:07:59 +01:00
										 |  |  |       childBuilder: (TextStyle? style) => Row( | 
					
						
							|  |  |  |         children: [ | 
					
						
							|  |  |  |           Text( | 
					
						
							| 
									
										
										
										
											2023-02-23 13:11:49 +01:00
										 |  |  |             translate('Delete'), | 
					
						
							| 
									
										
										
										
											2023-02-23 13:07:59 +01:00
										 |  |  |             style: style?.copyWith(color: Colors.red), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |           Expanded( | 
					
						
							|  |  |  |               child: Align( | 
					
						
							|  |  |  |             alignment: Alignment.centerRight, | 
					
						
							|  |  |  |             child: Transform.scale( | 
					
						
							|  |  |  |               scale: 0.8, | 
					
						
							|  |  |  |               child: Icon(Icons.delete_forever, color: Colors.red), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ).marginOnly(right: 4)), | 
					
						
							|  |  |  |         ], | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       ), | 
					
						
							|  |  |  |       proc: () { | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |         onSubmit() async { | 
					
						
							|  |  |  |           switch (tab) { | 
					
						
							|  |  |  |             case PeerTabIndex.recent: | 
					
						
							|  |  |  |               await bind.mainRemovePeer(id: id); | 
					
						
							|  |  |  |               await bind.mainLoadRecentPeers(); | 
					
						
							|  |  |  |               break; | 
					
						
							|  |  |  |             case PeerTabIndex.fav: | 
					
						
							|  |  |  |               final favs = (await bind.mainGetFav()).toList(); | 
					
						
							|  |  |  |               if (favs.remove(id)) { | 
					
						
							|  |  |  |                 await bind.mainStoreFav(favs: favs); | 
					
						
							|  |  |  |                 await bind.mainLoadFavPeers(); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |               break; | 
					
						
							|  |  |  |             case PeerTabIndex.lan: | 
					
						
							|  |  |  |               await bind.mainRemoveDiscovered(id: id); | 
					
						
							|  |  |  |               await bind.mainLoadLanPeers(); | 
					
						
							|  |  |  |               break; | 
					
						
							|  |  |  |             case PeerTabIndex.ab: | 
					
						
							|  |  |  |               gFFI.abModel.deletePeer(id); | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |               final future = gFFI.abModel.pushAb(); | 
					
						
							| 
									
										
										
										
											2023-08-17 18:21:37 +08:00
										 |  |  |               if (await bind.mainPeerExists(id: peer.id)) { | 
					
						
							|  |  |  |                 gFFI.abModel.reSyncToast(future); | 
					
						
							| 
									
										
										
										
											2023-08-16 08:59:50 +08:00
										 |  |  |               } | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |               break; | 
					
						
							|  |  |  |             case PeerTabIndex.group: | 
					
						
							|  |  |  |               break; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |           if (tab != PeerTabIndex.ab) { | 
					
						
							|  |  |  |             showToast(translate('Successful')); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-08 18:06:31 +08:00
										 |  |  |         deletePeerConfirmDialog(onSubmit, | 
					
						
							|  |  |  |             '${translate('Delete')} "${peer.alias.isEmpty ? formatID(peer.id) : peer.alias}"?'); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |       padding: menuPadding, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       dismissOnClicked: true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |   @protected | 
					
						
							|  |  |  |   MenuEntryBase<String> _unrememberPasswordAction(String id) { | 
					
						
							|  |  |  |     return MenuEntryButton<String>( | 
					
						
							|  |  |  |       childBuilder: (TextStyle? style) => Text( | 
					
						
							|  |  |  |         translate('Unremember Password'), | 
					
						
							|  |  |  |         style: style, | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2023-08-17 18:21:37 +08:00
										 |  |  |       proc: () async { | 
					
						
							| 
									
										
										
										
											2023-08-21 08:39:47 +08:00
										 |  |  |         bool result = gFFI.abModel.changePassword(id, ''); | 
					
						
							|  |  |  |         await bind.mainForgetPassword(id: id); | 
					
						
							| 
									
										
										
										
											2023-08-23 08:15:56 +08:00
										 |  |  |         bool toast = false; | 
					
						
							| 
									
										
										
										
											2023-08-21 08:39:47 +08:00
										 |  |  |         if (result) { | 
					
						
							| 
									
										
										
										
											2023-08-23 08:15:56 +08:00
										 |  |  |           toast = tab == PeerTabIndex.ab; | 
					
						
							| 
									
										
										
										
											2023-08-21 08:39:47 +08:00
										 |  |  |           gFFI.abModel.pushAb(toastIfFail: toast, toastIfSucc: toast); | 
					
						
							| 
									
										
										
										
											2023-08-17 18:21:37 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-08-23 08:15:56 +08:00
										 |  |  |         if (!toast) showToast(translate('Successful')); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |       padding: menuPadding, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       dismissOnClicked: true, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-07-29 12:03:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |   @protected | 
					
						
							|  |  |  |   MenuEntryBase<String> _addFavAction(String id) { | 
					
						
							|  |  |  |     return MenuEntryButton<String>( | 
					
						
							| 
									
										
										
										
											2023-02-17 22:37:08 +01:00
										 |  |  |       childBuilder: (TextStyle? style) => Row( | 
					
						
							|  |  |  |         children: [ | 
					
						
							|  |  |  |           Text( | 
					
						
							|  |  |  |             translate('Add to Favorites'), | 
					
						
							|  |  |  |             style: style, | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |           Expanded( | 
					
						
							|  |  |  |               child: Align( | 
					
						
							|  |  |  |             alignment: Alignment.centerRight, | 
					
						
							|  |  |  |             child: Transform.scale( | 
					
						
							|  |  |  |               scale: 0.8, | 
					
						
							|  |  |  |               child: Icon(Icons.star_outline), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ).marginOnly(right: 4)), | 
					
						
							|  |  |  |         ], | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       ), | 
					
						
							|  |  |  |       proc: () { | 
					
						
							|  |  |  |         () async { | 
					
						
							|  |  |  |           final favs = (await bind.mainGetFav()).toList(); | 
					
						
							|  |  |  |           if (!favs.contains(id)) { | 
					
						
							|  |  |  |             favs.add(id); | 
					
						
							| 
									
										
										
										
											2022-09-14 22:22:23 -07:00
										 |  |  |             await bind.mainStoreFav(favs: favs); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |           showToast(translate('Successful')); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |         }(); | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |       padding: menuPadding, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       dismissOnClicked: true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @protected | 
					
						
							| 
									
										
										
										
											2022-09-14 22:22:23 -07:00
										 |  |  |   MenuEntryBase<String> _rmFavAction( | 
					
						
							|  |  |  |       String id, Future<void> Function() reloadFunc) { | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     return MenuEntryButton<String>( | 
					
						
							| 
									
										
										
										
											2023-02-17 22:37:08 +01:00
										 |  |  |       childBuilder: (TextStyle? style) => Row( | 
					
						
							|  |  |  |         children: [ | 
					
						
							|  |  |  |           Text( | 
					
						
							|  |  |  |             translate('Remove from Favorites'), | 
					
						
							|  |  |  |             style: style, | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |           Expanded( | 
					
						
							|  |  |  |               child: Align( | 
					
						
							|  |  |  |             alignment: Alignment.centerRight, | 
					
						
							|  |  |  |             child: Transform.scale( | 
					
						
							|  |  |  |               scale: 0.8, | 
					
						
							|  |  |  |               child: Icon(Icons.star), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ).marginOnly(right: 4)), | 
					
						
							|  |  |  |         ], | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       ), | 
					
						
							|  |  |  |       proc: () { | 
					
						
							|  |  |  |         () async { | 
					
						
							|  |  |  |           final favs = (await bind.mainGetFav()).toList(); | 
					
						
							|  |  |  |           if (favs.remove(id)) { | 
					
						
							| 
									
										
										
										
											2022-09-14 22:22:23 -07:00
										 |  |  |             await bind.mainStoreFav(favs: favs); | 
					
						
							|  |  |  |             await reloadFunc(); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |           showToast(translate('Successful')); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |         }(); | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |       padding: menuPadding, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       dismissOnClicked: true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |   @protected | 
					
						
							|  |  |  |   MenuEntryBase<String> _addToAb(Peer peer) { | 
					
						
							|  |  |  |     return MenuEntryButton<String>( | 
					
						
							|  |  |  |       childBuilder: (TextStyle? style) => Text( | 
					
						
							|  |  |  |         translate('Add to Address Book'), | 
					
						
							|  |  |  |         style: style, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       proc: () { | 
					
						
							|  |  |  |         () async { | 
					
						
							| 
									
										
										
										
											2023-07-26 20:43:18 +08:00
										 |  |  |           if (gFFI.abModel.isFull(true)) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |           if (!gFFI.abModel.idContainBy(peer.id)) { | 
					
						
							|  |  |  |             gFFI.abModel.addPeer(peer); | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |             gFFI.abModel.pushAb(); | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |           } | 
					
						
							|  |  |  |         }(); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       padding: menuPadding, | 
					
						
							|  |  |  |       dismissOnClicked: true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 20:23:11 +08:00
										 |  |  |   @protected | 
					
						
							|  |  |  |   Future<String> _getAlias(String id) async => | 
					
						
							|  |  |  |       await bind.mainGetPeerOption(id: id, key: 'alias'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @protected | 
					
						
							|  |  |  |   void _update(); | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  | class RecentPeerCard extends BasePeerCard { | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |   RecentPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key}) | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |       : super( | 
					
						
							|  |  |  |             peer: peer, | 
					
						
							|  |  |  |             tab: PeerTabIndex.recent, | 
					
						
							|  |  |  |             menuPadding: menuPadding, | 
					
						
							|  |  |  |             key: key); | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |   Future<List<MenuEntryBase<String>>> _buildMenuItems( | 
					
						
							|  |  |  |       BuildContext context) async { | 
					
						
							|  |  |  |     final List<MenuEntryBase<String>> menuItems = [ | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  |       _connectAction(context, peer), | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       _transferFileAction(context, peer.id), | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2023-02-20 20:25:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     final List favs = (await bind.mainGetFav()).toList(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 21:58:11 +08:00
										 |  |  |     if (isDesktop && peer.platform != 'Android') { | 
					
						
							| 
									
										
										
										
											2022-09-22 16:45:14 +08:00
										 |  |  |       menuItems.add(_tcpTunnelingAction(context, peer.id)); | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-08 14:22:32 +08:00
										 |  |  |     // menuItems.add(await _openNewConnInOptAction(peer.id));
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     menuItems.add(await _forceAlwaysRelayAction(peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-22 16:45:14 +08:00
										 |  |  |     if (peer.platform == 'Windows') { | 
					
						
							|  |  |  |       menuItems.add(_rdpAction(context, peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-11-14 15:41:43 +08:00
										 |  |  |     if (Platform.isWindows) { | 
					
						
							|  |  |  |       menuItems.add(_createShortCutAction(peer.id)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  |     menuItems.add(MenuEntryDivider()); | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |     menuItems.add(_renameAction(peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-25 21:03:19 +08:00
										 |  |  |     if (await bind.mainPeerHasPassword(id: peer.id)) { | 
					
						
							|  |  |  |       menuItems.add(_unrememberPasswordAction(peer.id)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-02-20 20:25:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!favs.contains(peer.id)) { | 
					
						
							|  |  |  |       menuItems.add(_addFavAction(peer.id)); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       menuItems.add(_rmFavAction(peer.id, () async {})); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:39:01 +01:00
										 |  |  |     if (gFFI.userModel.userName.isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2023-02-24 13:39:28 +08:00
										 |  |  |       if (!gFFI.abModel.idContainBy(peer.id)) { | 
					
						
							|  |  |  |         menuItems.add(_addToAb(peer)); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-02-23 13:39:01 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:07:59 +01:00
										 |  |  |     menuItems.add(MenuEntryDivider()); | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |     menuItems.add(_removeAction(peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     return menuItems; | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-01-22 20:23:11 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @protected | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void _update() => bind.mainLoadRecentPeers(); | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FavoritePeerCard extends BasePeerCard { | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |   FavoritePeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key}) | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |       : super( | 
					
						
							|  |  |  |             peer: peer, | 
					
						
							|  |  |  |             tab: PeerTabIndex.fav, | 
					
						
							|  |  |  |             menuPadding: menuPadding, | 
					
						
							|  |  |  |             key: key); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Future<List<MenuEntryBase<String>>> _buildMenuItems( | 
					
						
							|  |  |  |       BuildContext context) async { | 
					
						
							|  |  |  |     final List<MenuEntryBase<String>> menuItems = [ | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  |       _connectAction(context, peer), | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       _transferFileAction(context, peer.id), | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2022-09-29 21:58:11 +08:00
										 |  |  |     if (isDesktop && peer.platform != 'Android') { | 
					
						
							| 
									
										
										
										
											2022-09-22 16:45:14 +08:00
										 |  |  |       menuItems.add(_tcpTunnelingAction(context, peer.id)); | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-08 14:22:32 +08:00
										 |  |  |     // menuItems.add(await _openNewConnInOptAction(peer.id));
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     menuItems.add(await _forceAlwaysRelayAction(peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-22 16:45:14 +08:00
										 |  |  |     if (peer.platform == 'Windows') { | 
					
						
							|  |  |  |       menuItems.add(_rdpAction(context, peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-11-14 15:41:43 +08:00
										 |  |  |     if (Platform.isWindows) { | 
					
						
							|  |  |  |       menuItems.add(_createShortCutAction(peer.id)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  |     menuItems.add(MenuEntryDivider()); | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |     menuItems.add(_renameAction(peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-25 21:03:19 +08:00
										 |  |  |     if (await bind.mainPeerHasPassword(id: peer.id)) { | 
					
						
							|  |  |  |       menuItems.add(_unrememberPasswordAction(peer.id)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-14 22:22:23 -07:00
										 |  |  |     menuItems.add(_rmFavAction(peer.id, () async { | 
					
						
							|  |  |  |       await bind.mainLoadFavPeers(); | 
					
						
							|  |  |  |     })); | 
					
						
							| 
									
										
										
										
											2023-02-23 13:39:01 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (gFFI.userModel.userName.isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2023-02-24 13:39:28 +08:00
										 |  |  |       if (!gFFI.abModel.idContainBy(peer.id)) { | 
					
						
							|  |  |  |         menuItems.add(_addToAb(peer)); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-02-23 13:39:01 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:07:59 +01:00
										 |  |  |     menuItems.add(MenuEntryDivider()); | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |     menuItems.add(_removeAction(peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     return menuItems; | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-01-22 20:23:11 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @protected | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void _update() => bind.mainLoadFavPeers(); | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DiscoveredPeerCard extends BasePeerCard { | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |   DiscoveredPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key}) | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |       : super( | 
					
						
							|  |  |  |             peer: peer, | 
					
						
							|  |  |  |             tab: PeerTabIndex.lan, | 
					
						
							|  |  |  |             menuPadding: menuPadding, | 
					
						
							|  |  |  |             key: key); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Future<List<MenuEntryBase<String>>> _buildMenuItems( | 
					
						
							|  |  |  |       BuildContext context) async { | 
					
						
							|  |  |  |     final List<MenuEntryBase<String>> menuItems = [ | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  |       _connectAction(context, peer), | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       _transferFileAction(context, peer.id), | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2023-02-21 15:58:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     final List favs = (await bind.mainGetFav()).toList(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 21:58:11 +08:00
										 |  |  |     if (isDesktop && peer.platform != 'Android') { | 
					
						
							| 
									
										
										
										
											2022-09-22 16:45:14 +08:00
										 |  |  |       menuItems.add(_tcpTunnelingAction(context, peer.id)); | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-08 14:22:32 +08:00
										 |  |  |     // menuItems.add(await _openNewConnInOptAction(peer.id));
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     menuItems.add(await _forceAlwaysRelayAction(peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-22 16:45:14 +08:00
										 |  |  |     if (peer.platform == 'Windows') { | 
					
						
							|  |  |  |       menuItems.add(_rdpAction(context, peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     menuItems.add(_wolAction(peer.id)); | 
					
						
							| 
									
										
										
										
											2022-11-14 15:41:43 +08:00
										 |  |  |     if (Platform.isWindows) { | 
					
						
							|  |  |  |       menuItems.add(_createShortCutAction(peer.id)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-02-23 13:39:01 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-06 17:17:51 +08:00
										 |  |  |     if (!favs.contains(peer.id)) { | 
					
						
							|  |  |  |       menuItems.add(_addFavAction(peer.id)); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       menuItems.add(_rmFavAction(peer.id, () async {})); | 
					
						
							| 
									
										
										
										
											2023-02-21 15:58:00 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:39:01 +01:00
										 |  |  |     if (gFFI.userModel.userName.isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2023-02-24 13:39:28 +08:00
										 |  |  |       if (!gFFI.abModel.idContainBy(peer.id)) { | 
					
						
							|  |  |  |         menuItems.add(_addToAb(peer)); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-02-23 13:39:01 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:07:59 +01:00
										 |  |  |     menuItems.add(MenuEntryDivider()); | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |     menuItems.add(_removeAction(peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     return menuItems; | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-01-22 20:23:11 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @protected | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void _update() => bind.mainLoadLanPeers(); | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class AddressBookPeerCard extends BasePeerCard { | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |   AddressBookPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key}) | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |       : super( | 
					
						
							|  |  |  |             peer: peer, | 
					
						
							|  |  |  |             tab: PeerTabIndex.ab, | 
					
						
							|  |  |  |             menuPadding: menuPadding, | 
					
						
							|  |  |  |             key: key); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Future<List<MenuEntryBase<String>>> _buildMenuItems( | 
					
						
							|  |  |  |       BuildContext context) async { | 
					
						
							|  |  |  |     final List<MenuEntryBase<String>> menuItems = [ | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  |       _connectAction(context, peer), | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       _transferFileAction(context, peer.id), | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2022-09-29 21:58:11 +08:00
										 |  |  |     if (isDesktop && peer.platform != 'Android') { | 
					
						
							| 
									
										
										
										
											2022-09-22 16:45:14 +08:00
										 |  |  |       menuItems.add(_tcpTunnelingAction(context, peer.id)); | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-08 14:22:32 +08:00
										 |  |  |     // menuItems.add(await _openNewConnInOptAction(peer.id));
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     menuItems.add(await _forceAlwaysRelayAction(peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-22 16:45:14 +08:00
										 |  |  |     if (peer.platform == 'Windows') { | 
					
						
							|  |  |  |       menuItems.add(_rdpAction(context, peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-11-14 15:41:43 +08:00
										 |  |  |     if (Platform.isWindows) { | 
					
						
							|  |  |  |       menuItems.add(_createShortCutAction(peer.id)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  |     menuItems.add(MenuEntryDivider()); | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |     menuItems.add(_renameAction(peer.id)); | 
					
						
							| 
									
										
										
										
											2023-08-17 18:21:37 +08:00
										 |  |  |     if (peer.hash.isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2022-09-25 21:03:19 +08:00
										 |  |  |       menuItems.add(_unrememberPasswordAction(peer.id)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-10-08 17:39:05 +09:00
										 |  |  |     if (gFFI.abModel.tags.isNotEmpty) { | 
					
						
							|  |  |  |       menuItems.add(_editTagAction(peer.id)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-02-23 13:39:01 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-23 13:07:59 +01:00
										 |  |  |     menuItems.add(MenuEntryDivider()); | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |     menuItems.add(_removeAction(peer.id)); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |     return menuItems; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 20:23:11 +08:00
										 |  |  |   @protected | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2023-08-11 15:27:50 +08:00
										 |  |  |   void _update() => gFFI.abModel.pullAb(quiet: true); | 
					
						
							| 
									
										
										
										
											2023-01-22 20:23:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |   @protected | 
					
						
							|  |  |  |   MenuEntryBase<String> _editTagAction(String id) { | 
					
						
							|  |  |  |     return MenuEntryButton<String>( | 
					
						
							|  |  |  |       childBuilder: (TextStyle? style) => Text( | 
					
						
							|  |  |  |         translate('Edit Tag'), | 
					
						
							|  |  |  |         style: style, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       proc: () { | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |         editAbTagDialog(gFFI.abModel.getPeerTags(id), (selectedTag) async { | 
					
						
							|  |  |  |           gFFI.abModel.changeTagForPeer(id, selectedTag); | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |           gFFI.abModel.pushAb(); | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |       padding: super.menuPadding, | 
					
						
							| 
									
										
										
										
											2022-09-01 03:56:12 -07:00
										 |  |  |       dismissOnClicked: true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-08-15 09:23:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @protected | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Future<String> _getAlias(String id) async => | 
					
						
							|  |  |  |       gFFI.abModel.find(id)?.alias ?? ''; | 
					
						
							| 
									
										
										
										
											2022-07-27 22:56:28 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-08-25 14:35:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-11 21:40:35 +08:00
										 |  |  | class MyGroupPeerCard extends BasePeerCard { | 
					
						
							|  |  |  |   MyGroupPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key}) | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |       : super( | 
					
						
							|  |  |  |             peer: peer, | 
					
						
							|  |  |  |             tab: PeerTabIndex.group, | 
					
						
							|  |  |  |             menuPadding: menuPadding, | 
					
						
							|  |  |  |             key: key); | 
					
						
							| 
									
										
										
										
											2022-12-11 21:40:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Future<List<MenuEntryBase<String>>> _buildMenuItems( | 
					
						
							|  |  |  |       BuildContext context) async { | 
					
						
							|  |  |  |     final List<MenuEntryBase<String>> menuItems = [ | 
					
						
							| 
									
										
										
										
											2023-08-08 12:12:35 +08:00
										 |  |  |       _connectAction(context, peer), | 
					
						
							| 
									
										
										
										
											2022-12-11 21:40:35 +08:00
										 |  |  |       _transferFileAction(context, peer.id), | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  |     if (isDesktop && peer.platform != 'Android') { | 
					
						
							|  |  |  |       menuItems.add(_tcpTunnelingAction(context, peer.id)); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-08 14:22:32 +08:00
										 |  |  |     // menuItems.add(await _openNewConnInOptAction(peer.id));
 | 
					
						
							| 
									
										
										
										
											2022-12-11 21:40:35 +08:00
										 |  |  |     menuItems.add(await _forceAlwaysRelayAction(peer.id)); | 
					
						
							|  |  |  |     if (peer.platform == 'Windows') { | 
					
						
							|  |  |  |       menuItems.add(_rdpAction(context, peer.id)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (Platform.isWindows) { | 
					
						
							|  |  |  |       menuItems.add(_createShortCutAction(peer.id)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     menuItems.add(MenuEntryDivider()); | 
					
						
							|  |  |  |     menuItems.add(_renameAction(peer.id)); | 
					
						
							|  |  |  |     if (await bind.mainPeerHasPassword(id: peer.id)) { | 
					
						
							|  |  |  |       menuItems.add(_unrememberPasswordAction(peer.id)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return menuItems; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 20:23:11 +08:00
										 |  |  |   @protected | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void _update() => gFFI.groupModel.pull(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 20:23:11 +08:00
										 |  |  | void _rdpDialog(String id) async { | 
					
						
							|  |  |  |   final port = await bind.mainGetPeerOption(id: id, key: 'rdp_port'); | 
					
						
							|  |  |  |   final username = await bind.mainGetPeerOption(id: id, key: 'rdp_username'); | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |   final portController = TextEditingController(text: port); | 
					
						
							|  |  |  |   final userController = TextEditingController(text: username); | 
					
						
							| 
									
										
										
										
											2022-10-08 19:28:20 +09:00
										 |  |  |   final passwordController = TextEditingController( | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |       text: await bind.mainGetPeerOption(id: id, key: 'rdp_password')); | 
					
						
							|  |  |  |   RxBool secure = true.obs; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-08 12:34:19 +08:00
										 |  |  |   gFFI.dialogManager.show((setState, close, context) { | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |     submit() async { | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |       String port = portController.text.trim(); | 
					
						
							|  |  |  |       String username = userController.text; | 
					
						
							|  |  |  |       String password = passwordController.text; | 
					
						
							|  |  |  |       await bind.mainSetPeerOption(id: id, key: 'rdp_port', value: port); | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |       await bind.mainSetPeerOption( | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |           id: id, key: 'rdp_username', value: username); | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |       await bind.mainSetPeerOption( | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |           id: id, key: 'rdp_password', value: password); | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |       showToast(translate('Successful')); | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |       close(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |     return CustomAlertDialog( | 
					
						
							| 
									
										
										
										
											2023-03-07 22:03:52 +01:00
										 |  |  |       title: Text(translate('RDP Settings')), | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |       content: ConstrainedBox( | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |         constraints: const BoxConstraints(minWidth: 500), | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |         child: Column( | 
					
						
							|  |  |  |           crossAxisAlignment: CrossAxisAlignment.start, | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             Row( | 
					
						
							|  |  |  |               children: [ | 
					
						
							| 
									
										
										
										
											2023-03-08 02:13:55 +01:00
										 |  |  |                 isDesktop | 
					
						
							|  |  |  |                     ? ConstrainedBox( | 
					
						
							|  |  |  |                         constraints: const BoxConstraints(minWidth: 140), | 
					
						
							|  |  |  |                         child: Text( | 
					
						
							|  |  |  |                           "${translate('Port')}:", | 
					
						
							|  |  |  |                           textAlign: TextAlign.right, | 
					
						
							|  |  |  |                         ).marginOnly(right: 10)) | 
					
						
							|  |  |  |                     : SizedBox.shrink(), | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |                 Expanded( | 
					
						
							|  |  |  |                   child: TextField( | 
					
						
							|  |  |  |                     inputFormatters: [ | 
					
						
							|  |  |  |                       FilteringTextInputFormatter.allow(RegExp( | 
					
						
							|  |  |  |                           r'^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$')) | 
					
						
							|  |  |  |                     ], | 
					
						
							| 
									
										
										
										
											2023-03-08 02:13:55 +01:00
										 |  |  |                     decoration: InputDecoration( | 
					
						
							|  |  |  |                         labelText: isDesktop ? null : translate('Port'), | 
					
						
							|  |  |  |                         hintText: '3389'), | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |                     controller: portController, | 
					
						
							| 
									
										
										
										
											2023-02-12 09:03:13 +08:00
										 |  |  |                     autofocus: true, | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |                   ), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ], | 
					
						
							| 
									
										
										
										
											2023-03-08 02:13:55 +01:00
										 |  |  |             ).marginOnly(bottom: isDesktop ? 8 : 0), | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |             Row( | 
					
						
							|  |  |  |               children: [ | 
					
						
							| 
									
										
										
										
											2023-03-08 02:13:55 +01:00
										 |  |  |                 isDesktop | 
					
						
							|  |  |  |                     ? ConstrainedBox( | 
					
						
							|  |  |  |                         constraints: const BoxConstraints(minWidth: 140), | 
					
						
							|  |  |  |                         child: Text( | 
					
						
							|  |  |  |                           "${translate('Username')}:", | 
					
						
							|  |  |  |                           textAlign: TextAlign.right, | 
					
						
							|  |  |  |                         ).marginOnly(right: 10)) | 
					
						
							|  |  |  |                     : SizedBox.shrink(), | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |                 Expanded( | 
					
						
							|  |  |  |                   child: TextField( | 
					
						
							| 
									
										
										
										
											2023-03-08 02:13:55 +01:00
										 |  |  |                     decoration: InputDecoration( | 
					
						
							| 
									
										
										
										
											2023-03-17 17:32:34 +01:00
										 |  |  |                         labelText: isDesktop ? null : translate('Username')), | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |                     controller: userController, | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ], | 
					
						
							| 
									
										
										
										
											2023-03-08 02:13:55 +01:00
										 |  |  |             ).marginOnly(bottom: isDesktop ? 8 : 0), | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |             Row( | 
					
						
							|  |  |  |               children: [ | 
					
						
							| 
									
										
										
										
											2023-03-08 02:13:55 +01:00
										 |  |  |                 isDesktop | 
					
						
							|  |  |  |                     ? ConstrainedBox( | 
					
						
							|  |  |  |                         constraints: const BoxConstraints(minWidth: 140), | 
					
						
							|  |  |  |                         child: Text( | 
					
						
							|  |  |  |                           "${translate('Password')}:", | 
					
						
							|  |  |  |                           textAlign: TextAlign.right, | 
					
						
							|  |  |  |                         ).marginOnly(right: 10)) | 
					
						
							|  |  |  |                     : SizedBox.shrink(), | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |                 Expanded( | 
					
						
							|  |  |  |                   child: Obx(() => TextField( | 
					
						
							|  |  |  |                         obscureText: secure.value, | 
					
						
							|  |  |  |                         decoration: InputDecoration( | 
					
						
							| 
									
										
										
										
											2023-03-08 02:13:55 +01:00
										 |  |  |                             labelText: isDesktop ? null : translate('Password'), | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |                             suffixIcon: IconButton( | 
					
						
							|  |  |  |                                 onPressed: () => secure.value = !secure.value, | 
					
						
							|  |  |  |                                 icon: Icon(secure.value | 
					
						
							|  |  |  |                                     ? Icons.visibility_off | 
					
						
							|  |  |  |                                     : Icons.visibility))), | 
					
						
							| 
									
										
										
										
											2022-10-08 19:28:20 +09:00
										 |  |  |                         controller: passwordController, | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |                       )), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ], | 
					
						
							| 
									
										
										
										
											2023-06-06 20:41:20 +02:00
										 |  |  |             ) | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |           ], | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       actions: [ | 
					
						
							| 
									
										
										
										
											2023-01-15 19:46:16 +08:00
										 |  |  |         dialogButton("Cancel", onPressed: close, isOutline: true), | 
					
						
							|  |  |  |         dialogButton("OK", onPressed: submit), | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |       ], | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |       onSubmit: submit, | 
					
						
							|  |  |  |       onCancel: close, | 
					
						
							| 
									
										
										
										
											2022-08-26 11:35:28 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-09-23 15:12:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-25 21:15:00 +08:00
										 |  |  | Widget getOnline(double rightPadding, bool online) { | 
					
						
							| 
									
										
										
										
											2022-09-23 15:12:50 +08:00
										 |  |  |   return Tooltip( | 
					
						
							|  |  |  |       message: translate(online ? 'Online' : 'Offline'), | 
					
						
							|  |  |  |       waitDuration: const Duration(seconds: 1), | 
					
						
							|  |  |  |       child: Padding( | 
					
						
							| 
									
										
										
										
											2022-09-25 21:15:00 +08:00
										 |  |  |           padding: EdgeInsets.fromLTRB(0, 4, rightPadding, 4), | 
					
						
							| 
									
										
										
										
											2022-09-23 15:12:50 +08:00
										 |  |  |           child: CircleAvatar( | 
					
						
							|  |  |  |               radius: 3, backgroundColor: online ? Colors.green : kColorWarn))); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-10-08 19:28:20 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 19:39:55 +08:00
										 |  |  | Widget build_more(BuildContext context, {bool invert = false}) { | 
					
						
							|  |  |  |   final RxBool hover = false.obs; | 
					
						
							|  |  |  |   return InkWell( | 
					
						
							|  |  |  |       borderRadius: BorderRadius.circular(14), | 
					
						
							|  |  |  |       onTap: () {}, | 
					
						
							|  |  |  |       onHover: (value) => hover.value = value, | 
					
						
							|  |  |  |       child: Obx(() => CircleAvatar( | 
					
						
							|  |  |  |           radius: 14, | 
					
						
							|  |  |  |           backgroundColor: hover.value | 
					
						
							|  |  |  |               ? (invert | 
					
						
							|  |  |  |                   ? Theme.of(context).colorScheme.background | 
					
						
							|  |  |  |                   : Theme.of(context).scaffoldBackgroundColor) | 
					
						
							|  |  |  |               : (invert | 
					
						
							|  |  |  |                   ? Theme.of(context).scaffoldBackgroundColor | 
					
						
							|  |  |  |                   : Theme.of(context).colorScheme.background), | 
					
						
							|  |  |  |           child: Icon(Icons.more_vert, | 
					
						
							|  |  |  |               size: 18, | 
					
						
							|  |  |  |               color: hover.value | 
					
						
							|  |  |  |                   ? Theme.of(context).textTheme.titleLarge?.color | 
					
						
							|  |  |  |                   : Theme.of(context) | 
					
						
							|  |  |  |                       .textTheme | 
					
						
							|  |  |  |                       .titleLarge | 
					
						
							|  |  |  |                       ?.color | 
					
						
							|  |  |  |                       ?.withOpacity(0.5))))); | 
					
						
							| 
									
										
										
										
											2022-10-08 19:28:20 +09:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-08-13 18:13:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | class TagPainter extends CustomPainter { | 
					
						
							|  |  |  |   final double radius; | 
					
						
							|  |  |  |   late final List<Color> colors; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   TagPainter({required this.radius, required List<Color> colors}) { | 
					
						
							|  |  |  |     this.colors = colors.reversed.toList(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void paint(Canvas canvas, Size size) { | 
					
						
							|  |  |  |     double x = 0; | 
					
						
							|  |  |  |     double y = radius; | 
					
						
							|  |  |  |     for (int i = 0; i < colors.length; i++) { | 
					
						
							|  |  |  |       Paint paint = Paint(); | 
					
						
							|  |  |  |       paint.color = colors[i]; | 
					
						
							|  |  |  |       x -= radius + 1; | 
					
						
							|  |  |  |       if (i == colors.length - 1) { | 
					
						
							|  |  |  |         canvas.drawCircle(Offset(x, y), radius, paint); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         Path path = Path(); | 
					
						
							|  |  |  |         path.addArc(Rect.fromCircle(center: Offset(x, y), radius: radius), | 
					
						
							|  |  |  |             math.pi * 4 / 3, math.pi * 4 / 3); | 
					
						
							|  |  |  |         path.addArc( | 
					
						
							|  |  |  |             Rect.fromCircle(center: Offset(x - radius, y), radius: radius), | 
					
						
							|  |  |  |             math.pi * 5 / 3, | 
					
						
							|  |  |  |             math.pi * 2 / 3); | 
					
						
							|  |  |  |         path.fillType = PathFillType.evenOdd; | 
					
						
							|  |  |  |         canvas.drawPath(path, paint); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   bool shouldRepaint(covariant CustomPainter oldDelegate) { | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-08-18 15:49:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | void connectInPeerTab(BuildContext context, String id, PeerTabIndex tab, | 
					
						
							|  |  |  |     {bool isFileTransfer = false, | 
					
						
							|  |  |  |     bool isTcpTunneling = false, | 
					
						
							|  |  |  |     bool isRDP = false}) async { | 
					
						
							|  |  |  |   if (tab == PeerTabIndex.ab) { | 
					
						
							|  |  |  |     // If recent peer's alias is empty, set it to ab's alias
 | 
					
						
							|  |  |  |     // Because the platform is not set, it may not take effect, but it is more important not to display if the connection is not successful
 | 
					
						
							|  |  |  |     Peer? p = gFFI.abModel.find(id); | 
					
						
							|  |  |  |     if (p != null && | 
					
						
							|  |  |  |         p.alias.isNotEmpty && | 
					
						
							|  |  |  |         (await bind.mainGetPeerOption(id: id, key: "alias")).isEmpty) { | 
					
						
							|  |  |  |       await bind.mainSetPeerAlias( | 
					
						
							|  |  |  |         id: id, | 
					
						
							|  |  |  |         alias: p.alias, | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   connect(context, id, | 
					
						
							|  |  |  |       isFileTransfer: isFileTransfer, | 
					
						
							|  |  |  |       isTcpTunneling: isTcpTunneling, | 
					
						
							|  |  |  |       isRDP: isRDP); | 
					
						
							|  |  |  | } |