| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | import 'package:flutter/material.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'; | 
					
						
							| 
									
										
										
										
											2022-10-20 23:03:54 +08:00
										 |  |  | import 'package:flutter_hbb/desktop/widgets/login.dart'; | 
					
						
							| 
									
										
										
										
											2022-10-08 19:28:20 +09:00
										 |  |  | 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'; | 
					
						
							|  |  |  | import '../../desktop/pages/desktop_home_page.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-22 17:38:18 +08:00
										 |  |  | import '../../mobile/pages/settings_page.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(); | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  |     WidgetsBinding.instance.addPostFrameCallback((_) => gFFI.abModel.pullAb()); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @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(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   handleLogin() { | 
					
						
							| 
									
										
										
										
											2022-09-22 17:38:18 +08:00
										 |  |  |     // TODO refactor login dialog for desktop and mobile
 | 
					
						
							|  |  |  |     if (isDesktop) { | 
					
						
							|  |  |  |       loginDialog().then((success) { | 
					
						
							|  |  |  |         if (success) { | 
					
						
							| 
									
										
										
										
											2022-10-09 19:41:50 +09:00
										 |  |  |           gFFI.abModel.pullAb(); | 
					
						
							| 
									
										
										
										
											2022-09-22 17:38:18 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       showLogin(gFFI.dialogManager); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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( | 
					
						
							|  |  |  |             onTap: handleLogin, | 
					
						
							|  |  |  |             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: () { | 
					
						
							|  |  |  |               setState(() {}); | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             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 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 ids = field.trim().split(RegExp(r"[\s,;\n]+")); | 
					
						
							|  |  |  |           field = ids.join(','); | 
					
						
							|  |  |  |           for (final newId in ids) { | 
					
						
							|  |  |  |             if (gFFI.abModel.idContainBy(newId)) { | 
					
						
							|  |  |  |               continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             gFFI.abModel.addId(newId); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											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: [ | 
					
						
							|  |  |  |             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: [ | 
					
						
							|  |  |  |           TextButton(onPressed: close, child: Text(translate("Cancel"))), | 
					
						
							|  |  |  |           TextButton(onPressed: submit, child: Text(translate("OK"))), | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |         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: [ | 
					
						
							|  |  |  |           TextButton(onPressed: close, child: Text(translate("Cancel"))), | 
					
						
							|  |  |  |           TextButton(onPressed: submit, child: Text(translate("OK"))), | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |         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
										 |  |  | } |