| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  | import 'package:flutter_hbb/common/formatter/id_formatter.dart'; | 
					
						
							| 
									
										
										
										
											2022-10-08 19:28:20 +09:00
										 |  |  | import 'package:flutter_hbb/common/widgets/peer_card.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-22 15:35:46 +08:00
										 |  |  | import 'package:flutter_hbb/common/widgets/peers_view.dart'; | 
					
						
							| 
									
										
										
										
											2022-10-08 19:28:20 +09:00
										 |  |  | import 'package:flutter_hbb/desktop/widgets/popup_menu.dart'; | 
					
						
							|  |  |  | import '../../consts.dart'; | 
					
						
							|  |  |  | import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | import 'package:get/get.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import '../../common.dart'; | 
					
						
							| 
									
										
										
										
											2023-01-08 23:30:34 +09:00
										 |  |  | import 'login.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | class AddressBook extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2022-09-23 12:20:40 +08:00
										 |  |  |   final EdgeInsets? menuPadding; | 
					
						
							|  |  |  |   const AddressBook({Key? key, this.menuPadding}) : super(key: key); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<StatefulWidget> createState() { | 
					
						
							|  |  |  |     return _AddressBookState(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _AddressBookState extends State<AddressBook> { | 
					
						
							| 
									
										
										
										
											2022-10-10 18:27:26 +09:00
										 |  |  |   var menuPos = RelativeRect.fill; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) => FutureBuilder<Widget>( | 
					
						
							| 
									
										
										
										
											2022-10-09 19:41:50 +09:00
										 |  |  |       future: buildBody(context), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |       builder: (context, snapshot) { | 
					
						
							|  |  |  |         if (snapshot.hasData) { | 
					
						
							|  |  |  |           return snapshot.data!; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           return const Offstage(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-09 19:41:50 +09:00
										 |  |  |   Future<Widget> buildBody(BuildContext context) async { | 
					
						
							|  |  |  |     return Obx(() { | 
					
						
							|  |  |  |       if (gFFI.userModel.userName.value.isEmpty) { | 
					
						
							|  |  |  |         return Center( | 
					
						
							|  |  |  |           child: InkWell( | 
					
						
							| 
									
										
										
										
											2023-01-06 19:26:19 +09:00
										 |  |  |             onTap: loginDialog, | 
					
						
							| 
									
										
										
										
											2022-10-09 19:41:50 +09:00
										 |  |  |             child: Text( | 
					
						
							|  |  |  |               translate("Login"), | 
					
						
							|  |  |  |               style: const TextStyle(decoration: TextDecoration.underline), | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |           ), | 
					
						
							| 
									
										
										
										
											2022-10-09 19:41:50 +09:00
										 |  |  |         ); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         if (gFFI.abModel.abLoading.value) { | 
					
						
							|  |  |  |           return const Center( | 
					
						
							|  |  |  |             child: CircularProgressIndicator(), | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (gFFI.abModel.abError.isNotEmpty) { | 
					
						
							|  |  |  |           return _buildShowError(gFFI.abModel.abError.value); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-10-10 18:27:26 +09:00
										 |  |  |         return isDesktop | 
					
						
							|  |  |  |             ? _buildAddressBookDesktop() | 
					
						
							|  |  |  |             : _buildAddressBookMobile(); | 
					
						
							| 
									
										
										
										
											2022-10-09 19:41:50 +09:00
										 |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  |   Widget _buildShowError(String error) { | 
					
						
							|  |  |  |     return Center( | 
					
						
							|  |  |  |         child: Column( | 
					
						
							|  |  |  |       mainAxisAlignment: MainAxisAlignment.center, | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         Text(translate(error)), | 
					
						
							|  |  |  |         TextButton( | 
					
						
							|  |  |  |             onPressed: () { | 
					
						
							| 
									
										
										
										
											2022-11-30 11:13:02 +08:00
										 |  |  |               gFFI.abModel.pullAb(); | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  |             }, | 
					
						
							|  |  |  |             child: Text(translate("Retry"))) | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |     )); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-10 18:27:26 +09:00
										 |  |  |   Widget _buildAddressBookDesktop() { | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  |     return Row( | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         Card( | 
					
						
							|  |  |  |           margin: EdgeInsets.symmetric(horizontal: 4.0), | 
					
						
							|  |  |  |           shape: RoundedRectangleBorder( | 
					
						
							|  |  |  |               borderRadius: BorderRadius.circular(12), | 
					
						
							|  |  |  |               side: | 
					
						
							|  |  |  |                   BorderSide(color: Theme.of(context).scaffoldBackgroundColor)), | 
					
						
							|  |  |  |           child: Container( | 
					
						
							|  |  |  |             width: 200, | 
					
						
							|  |  |  |             height: double.infinity, | 
					
						
							|  |  |  |             padding: | 
					
						
							|  |  |  |                 const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), | 
					
						
							|  |  |  |             child: Column( | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |               children: [ | 
					
						
							| 
									
										
										
										
											2022-10-10 18:27:26 +09:00
										 |  |  |                 _buildTagHeader(), | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  |                 Expanded( | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                   child: Container( | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  |                     width: double.infinity, | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                     height: double.infinity, | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  |                     decoration: BoxDecoration( | 
					
						
							|  |  |  |                         border: Border.all(color: MyTheme.darkGray), | 
					
						
							|  |  |  |                         borderRadius: BorderRadius.circular(2)), | 
					
						
							| 
									
										
										
										
											2022-10-10 18:27:26 +09:00
										 |  |  |                     child: _buildTags(), | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  |                   ).marginSymmetric(vertical: 8.0), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                 ) | 
					
						
							|  |  |  |               ], | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  |             ), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ).marginOnly(right: 8.0), | 
					
						
							| 
									
										
										
										
											2022-10-10 18:27:26 +09:00
										 |  |  |         _buildPeersViews() | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  |       ], | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-10 18:27:26 +09:00
										 |  |  |   Widget _buildAddressBookMobile() { | 
					
						
							|  |  |  |     return Column( | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         Card( | 
					
						
							|  |  |  |           margin: EdgeInsets.symmetric(horizontal: 1.0), | 
					
						
							|  |  |  |           shape: RoundedRectangleBorder( | 
					
						
							|  |  |  |               borderRadius: BorderRadius.circular(6), | 
					
						
							|  |  |  |               side: | 
					
						
							|  |  |  |                   BorderSide(color: Theme.of(context).scaffoldBackgroundColor)), | 
					
						
							|  |  |  |           child: Container( | 
					
						
							|  |  |  |             padding: | 
					
						
							|  |  |  |                 const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), | 
					
						
							|  |  |  |             child: Column( | 
					
						
							|  |  |  |               mainAxisSize: MainAxisSize.min, | 
					
						
							|  |  |  |               children: [ | 
					
						
							|  |  |  |                 _buildTagHeader(), | 
					
						
							|  |  |  |                 Container( | 
					
						
							|  |  |  |                   width: double.infinity, | 
					
						
							|  |  |  |                   decoration: BoxDecoration( | 
					
						
							|  |  |  |                       border: Border.all(color: MyTheme.darkGray), | 
					
						
							|  |  |  |                       borderRadius: BorderRadius.circular(4)), | 
					
						
							|  |  |  |                   child: _buildTags(), | 
					
						
							|  |  |  |                 ).marginSymmetric(vertical: 8.0), | 
					
						
							|  |  |  |               ], | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         Divider(), | 
					
						
							|  |  |  |         _buildPeersViews() | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildTagHeader() { | 
					
						
							|  |  |  |     return Row( | 
					
						
							|  |  |  |       mainAxisAlignment: MainAxisAlignment.spaceBetween, | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         Text(translate('Tags')), | 
					
						
							|  |  |  |         GestureDetector( | 
					
						
							|  |  |  |             onTapDown: (e) { | 
					
						
							|  |  |  |               final x = e.globalPosition.dx; | 
					
						
							|  |  |  |               final y = e.globalPosition.dy; | 
					
						
							|  |  |  |               menuPos = RelativeRect.fromLTRB(x, y, x, y); | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             onTap: () => _showMenu(menuPos), | 
					
						
							|  |  |  |             child: ActionMore()), | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildTags() { | 
					
						
							|  |  |  |     return Obx( | 
					
						
							|  |  |  |       () => Wrap( | 
					
						
							|  |  |  |         children: gFFI.abModel.tags | 
					
						
							|  |  |  |             .map((e) => AddressBookTag( | 
					
						
							|  |  |  |                 name: e, | 
					
						
							|  |  |  |                 tags: gFFI.abModel.selectedTags, | 
					
						
							|  |  |  |                 onTap: () { | 
					
						
							|  |  |  |                   if (gFFI.abModel.selectedTags.contains(e)) { | 
					
						
							|  |  |  |                     gFFI.abModel.selectedTags.remove(e); | 
					
						
							|  |  |  |                   } else { | 
					
						
							|  |  |  |                     gFFI.abModel.selectedTags.add(e); | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 })) | 
					
						
							|  |  |  |             .toList(), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildPeersViews() { | 
					
						
							|  |  |  |     return Expanded( | 
					
						
							|  |  |  |       child: Align( | 
					
						
							|  |  |  |           alignment: Alignment.topLeft, | 
					
						
							|  |  |  |           child: Obx(() => AddressBookPeersView( | 
					
						
							|  |  |  |                 menuPadding: widget.menuPadding, | 
					
						
							|  |  |  |                 initPeers: gFFI.abModel.peers.value, | 
					
						
							|  |  |  |               ))), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 19:28:20 +09:00
										 |  |  |   void _showMenu(RelativeRect pos) { | 
					
						
							|  |  |  |     final items = [ | 
					
						
							|  |  |  |       getEntry(translate("Add ID"), abAddId), | 
					
						
							|  |  |  |       getEntry(translate("Add Tag"), abAddTag), | 
					
						
							|  |  |  |       getEntry(translate("Unselect all tags"), gFFI.abModel.unsetSelectedTags), | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     mod_menu.showMenu( | 
					
						
							|  |  |  |       context: context, | 
					
						
							|  |  |  |       position: pos, | 
					
						
							|  |  |  |       items: items | 
					
						
							|  |  |  |           .map((e) => e.build( | 
					
						
							|  |  |  |               context, | 
					
						
							|  |  |  |               MenuConfig( | 
					
						
							|  |  |  |                   commonColor: CustomPopupMenuTheme.commonColor, | 
					
						
							|  |  |  |                   height: CustomPopupMenuTheme.height, | 
					
						
							|  |  |  |                   dividerHeight: CustomPopupMenuTheme.dividerHeight))) | 
					
						
							|  |  |  |           .expand((i) => i) | 
					
						
							|  |  |  |           .toList(), | 
					
						
							|  |  |  |       elevation: 8, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void abAddId() async { | 
					
						
							|  |  |  |     var isInProgress = false; | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |     IDTextEditingController idController = IDTextEditingController(text: ''); | 
					
						
							|  |  |  |     TextEditingController aliasController = TextEditingController(text: ''); | 
					
						
							|  |  |  |     final tags = List.of(gFFI.abModel.tags); | 
					
						
							|  |  |  |     var selectedTag = List<dynamic>.empty(growable: true).obs; | 
					
						
							|  |  |  |     final style = TextStyle(fontSize: 14.0); | 
					
						
							|  |  |  |     String? errorMsg; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     gFFI.dialogManager.show((setState, close) { | 
					
						
							|  |  |  |       submit() async { | 
					
						
							|  |  |  |         setState(() { | 
					
						
							|  |  |  |           isInProgress = true; | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |           errorMsg = null; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |         String id = idController.id; | 
					
						
							|  |  |  |         if (id.isEmpty) { | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |           // pass
 | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |           if (gFFI.abModel.idContainBy(id)) { | 
					
						
							|  |  |  |             setState(() { | 
					
						
							|  |  |  |               isInProgress = false; | 
					
						
							|  |  |  |               errorMsg = translate('ID already exists'); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |             return; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |           gFFI.abModel.addId(id, aliasController.text.trim(), selectedTag); | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  |           await gFFI.abModel.pushAb(); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |           this.setState(() {}); | 
					
						
							|  |  |  |           // final currentPeers
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         close(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return CustomAlertDialog( | 
					
						
							|  |  |  |         title: Text(translate("Add ID")), | 
					
						
							|  |  |  |         content: Column( | 
					
						
							|  |  |  |           crossAxisAlignment: CrossAxisAlignment.start, | 
					
						
							|  |  |  |           children: [ | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |             Column( | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |               children: [ | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |                 Align( | 
					
						
							|  |  |  |                   alignment: Alignment.centerLeft, | 
					
						
							|  |  |  |                   child: Row( | 
					
						
							|  |  |  |                     children: [ | 
					
						
							|  |  |  |                       Text( | 
					
						
							|  |  |  |                         '*', | 
					
						
							|  |  |  |                         style: TextStyle(color: Colors.red, fontSize: 14), | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                       Text( | 
					
						
							|  |  |  |                         'ID', | 
					
						
							|  |  |  |                         style: style, | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                       ), | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |                     ], | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 TextField( | 
					
						
							|  |  |  |                   controller: idController, | 
					
						
							|  |  |  |                   inputFormatters: [IDTextInputFormatter()], | 
					
						
							|  |  |  |                   decoration: InputDecoration( | 
					
						
							|  |  |  |                       isDense: true, | 
					
						
							|  |  |  |                       border: OutlineInputBorder(), | 
					
						
							|  |  |  |                       errorText: errorMsg), | 
					
						
							|  |  |  |                   style: style, | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 Align( | 
					
						
							|  |  |  |                   alignment: Alignment.centerLeft, | 
					
						
							|  |  |  |                   child: Text( | 
					
						
							|  |  |  |                     translate('Alias'), | 
					
						
							|  |  |  |                     style: style, | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                 ).marginOnly(top: 8, bottom: 2), | 
					
						
							|  |  |  |                 TextField( | 
					
						
							|  |  |  |                   controller: aliasController, | 
					
						
							|  |  |  |                   decoration: InputDecoration( | 
					
						
							|  |  |  |                     border: OutlineInputBorder(), | 
					
						
							|  |  |  |                     isDense: true, | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                   style: style, | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 Align( | 
					
						
							|  |  |  |                   alignment: Alignment.centerLeft, | 
					
						
							|  |  |  |                   child: Text( | 
					
						
							|  |  |  |                     translate('Tags'), | 
					
						
							|  |  |  |                     style: style, | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                 ).marginOnly(top: 8), | 
					
						
							|  |  |  |                 Container( | 
					
						
							|  |  |  |                   child: Wrap( | 
					
						
							|  |  |  |                     children: tags | 
					
						
							|  |  |  |                         .map((e) => AddressBookTag( | 
					
						
							|  |  |  |                             name: e, | 
					
						
							|  |  |  |                             tags: selectedTag, | 
					
						
							|  |  |  |                             onTap: () { | 
					
						
							|  |  |  |                               if (selectedTag.contains(e)) { | 
					
						
							|  |  |  |                                 selectedTag.remove(e); | 
					
						
							|  |  |  |                               } else { | 
					
						
							|  |  |  |                                 selectedTag.add(e); | 
					
						
							|  |  |  |                               } | 
					
						
							|  |  |  |                             }, | 
					
						
							|  |  |  |                             showActionMenu: false)) | 
					
						
							|  |  |  |                         .toList(growable: false), | 
					
						
							|  |  |  |                   ), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                 ), | 
					
						
							|  |  |  |               ], | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             const SizedBox( | 
					
						
							|  |  |  |               height: 4.0, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             Offstage( | 
					
						
							|  |  |  |                 offstage: !isInProgress, child: const LinearProgressIndicator()) | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         actions: [ | 
					
						
							| 
									
										
										
										
											2023-01-15 19:46:16 +08:00
										 |  |  |           dialogButton("Cancel", onPressed: close, isOutline: true), | 
					
						
							|  |  |  |           dialogButton("OK", onPressed: submit), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |         ], | 
					
						
							|  |  |  |         onSubmit: submit, | 
					
						
							|  |  |  |         onCancel: close, | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void abAddTag() async { | 
					
						
							|  |  |  |     var field = ""; | 
					
						
							|  |  |  |     var msg = ""; | 
					
						
							|  |  |  |     var isInProgress = false; | 
					
						
							|  |  |  |     TextEditingController controller = TextEditingController(text: field); | 
					
						
							|  |  |  |     gFFI.dialogManager.show((setState, close) { | 
					
						
							|  |  |  |       submit() async { | 
					
						
							|  |  |  |         setState(() { | 
					
						
							|  |  |  |           msg = ""; | 
					
						
							|  |  |  |           isInProgress = true; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         field = controller.text.trim(); | 
					
						
							|  |  |  |         if (field.isEmpty) { | 
					
						
							|  |  |  |           // pass
 | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           final tags = field.trim().split(RegExp(r"[\s,;\n]+")); | 
					
						
							|  |  |  |           field = tags.join(','); | 
					
						
							|  |  |  |           for (final tag in tags) { | 
					
						
							|  |  |  |             gFFI.abModel.addTag(tag); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  |           await gFFI.abModel.pushAb(); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |           // final currentPeers
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         close(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return CustomAlertDialog( | 
					
						
							|  |  |  |         title: Text(translate("Add Tag")), | 
					
						
							|  |  |  |         content: Column( | 
					
						
							|  |  |  |           crossAxisAlignment: CrossAxisAlignment.start, | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             Text(translate("whitelist_sep")), | 
					
						
							|  |  |  |             const SizedBox( | 
					
						
							|  |  |  |               height: 8.0, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             Row( | 
					
						
							|  |  |  |               children: [ | 
					
						
							|  |  |  |                 Expanded( | 
					
						
							|  |  |  |                   child: TextField( | 
					
						
							|  |  |  |                     maxLines: null, | 
					
						
							|  |  |  |                     decoration: InputDecoration( | 
					
						
							|  |  |  |                       border: const OutlineInputBorder(), | 
					
						
							|  |  |  |                       errorText: msg.isEmpty ? null : translate(msg), | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                     controller: controller, | 
					
						
							|  |  |  |                     focusNode: FocusNode()..requestFocus(), | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ], | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             const SizedBox( | 
					
						
							|  |  |  |               height: 4.0, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             Offstage( | 
					
						
							|  |  |  |                 offstage: !isInProgress, child: const LinearProgressIndicator()) | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         actions: [ | 
					
						
							| 
									
										
										
										
											2023-01-15 19:46:16 +08:00
										 |  |  |           dialogButton("Cancel", onPressed: close, isOutline: true), | 
					
						
							|  |  |  |           dialogButton("OK", onPressed: submit), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |         ], | 
					
						
							|  |  |  |         onSubmit: submit, | 
					
						
							|  |  |  |         onCancel: close, | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-10-08 17:39:05 +09:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 17:39:05 +09:00
										 |  |  | class AddressBookTag extends StatelessWidget { | 
					
						
							|  |  |  |   final String name; | 
					
						
							|  |  |  |   final RxList<dynamic> tags; | 
					
						
							|  |  |  |   final Function()? onTap; | 
					
						
							| 
									
										
										
										
											2022-10-08 19:52:02 +09:00
										 |  |  |   final bool showActionMenu; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 17:39:05 +09:00
										 |  |  |   const AddressBookTag( | 
					
						
							|  |  |  |       {Key? key, | 
					
						
							|  |  |  |       required this.name, | 
					
						
							|  |  |  |       required this.tags, | 
					
						
							|  |  |  |       this.onTap, | 
					
						
							| 
									
										
										
										
											2022-10-08 19:52:02 +09:00
										 |  |  |       this.showActionMenu = true}) | 
					
						
							| 
									
										
										
										
											2022-10-08 17:39:05 +09:00
										 |  |  |       : super(key: key); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 17:39:05 +09:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-10-08 19:52:02 +09:00
										 |  |  |     var pos = RelativeRect.fill; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void setPosition(TapDownDetails e) { | 
					
						
							|  |  |  |       final x = e.globalPosition.dx; | 
					
						
							|  |  |  |       final y = e.globalPosition.dy; | 
					
						
							|  |  |  |       pos = RelativeRect.fromLTRB(x, y, x, y); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return GestureDetector( | 
					
						
							| 
									
										
										
										
											2022-10-08 17:39:05 +09:00
										 |  |  |       onTap: onTap, | 
					
						
							| 
									
										
										
										
											2022-10-08 19:52:02 +09:00
										 |  |  |       onTapDown: showActionMenu ? setPosition : null, | 
					
						
							|  |  |  |       onSecondaryTapDown: showActionMenu ? setPosition : null, | 
					
						
							|  |  |  |       onSecondaryTap: showActionMenu ? () => _showMenu(context, pos) : null, | 
					
						
							|  |  |  |       onLongPress: showActionMenu ? () => _showMenu(context, pos) : null, | 
					
						
							| 
									
										
										
										
											2022-10-08 17:39:05 +09:00
										 |  |  |       child: Obx( | 
					
						
							|  |  |  |         () => Container( | 
					
						
							|  |  |  |           decoration: BoxDecoration( | 
					
						
							|  |  |  |               color: tags.contains(name) ? Colors.blue : null, | 
					
						
							|  |  |  |               border: Border.all(color: MyTheme.darkGray), | 
					
						
							|  |  |  |               borderRadius: BorderRadius.circular(6)), | 
					
						
							|  |  |  |           margin: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 8.0), | 
					
						
							|  |  |  |           padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 8.0), | 
					
						
							|  |  |  |           child: Text(name, | 
					
						
							|  |  |  |               style: | 
					
						
							|  |  |  |                   TextStyle(color: tags.contains(name) ? Colors.white : null)), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |         ), | 
					
						
							| 
									
										
										
										
											2022-10-08 17:39:05 +09:00
										 |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-10-08 19:52:02 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |   void _showMenu(BuildContext context, RelativeRect pos) { | 
					
						
							|  |  |  |     final items = [ | 
					
						
							|  |  |  |       getEntry(translate("Delete"), () { | 
					
						
							|  |  |  |         gFFI.abModel.deleteTag(name); | 
					
						
							|  |  |  |         gFFI.abModel.pushAb(); | 
					
						
							|  |  |  |         Future.delayed(Duration.zero, () => Get.back()); | 
					
						
							|  |  |  |       }), | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     mod_menu.showMenu( | 
					
						
							|  |  |  |       context: context, | 
					
						
							|  |  |  |       position: pos, | 
					
						
							|  |  |  |       items: items | 
					
						
							|  |  |  |           .map((e) => e.build( | 
					
						
							|  |  |  |               context, | 
					
						
							|  |  |  |               MenuConfig( | 
					
						
							|  |  |  |                   commonColor: CustomPopupMenuTheme.commonColor, | 
					
						
							|  |  |  |                   height: CustomPopupMenuTheme.height, | 
					
						
							|  |  |  |                   dividerHeight: CustomPopupMenuTheme.dividerHeight))) | 
					
						
							|  |  |  |           .expand((i) => i) | 
					
						
							|  |  |  |           .toList(), | 
					
						
							|  |  |  |       elevation: 8, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MenuEntryButton<String> getEntry(String title, VoidCallback proc) { | 
					
						
							|  |  |  |   return MenuEntryButton<String>( | 
					
						
							|  |  |  |     childBuilder: (TextStyle? style) => Text( | 
					
						
							|  |  |  |       title, | 
					
						
							|  |  |  |       style: style, | 
					
						
							|  |  |  |     ), | 
					
						
							|  |  |  |     proc: proc, | 
					
						
							|  |  |  |     padding: kDesktopMenuPadding, | 
					
						
							|  |  |  |     dismissOnClicked: true, | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | } |