| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-22 15:35:46 +08:00
										 |  |  | import 'package:flutter_hbb/common/widgets/peers_view.dart'; | 
					
						
							|  |  |  | import 'package:flutter_hbb/common/widgets/peer_card.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | import 'package:flutter_hbb/consts.dart'; | 
					
						
							|  |  |  | import 'package:get/get.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import '../../common.dart'; | 
					
						
							|  |  |  | import '../../models/platform_model.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PeerTabPage extends StatefulWidget { | 
					
						
							|  |  |  |   final List<String> tabs; | 
					
						
							|  |  |  |   final List<Widget> children; | 
					
						
							|  |  |  |   const PeerTabPage({required this.tabs, required this.children, Key? key}) | 
					
						
							|  |  |  |       : super(key: key); | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<PeerTabPage> createState() => _PeerTabPageState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _PeerTabPageState extends State<PeerTabPage> | 
					
						
							|  |  |  |     with SingleTickerProviderStateMixin { | 
					
						
							|  |  |  |   final RxInt _tabIndex = 0.obs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							| 
									
										
										
										
											2022-09-21 14:56:01 +08:00
										 |  |  |     () async { | 
					
						
							|  |  |  |       await bind.mainGetLocalOption(key: 'peer-tab-index').then((value) { | 
					
						
							|  |  |  |         if (value == '') return; | 
					
						
							|  |  |  |         final tab = int.parse(value); | 
					
						
							|  |  |  |         _tabIndex.value = tab; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       await bind.mainGetLocalOption(key: 'peer-card-ui-type').then((value) { | 
					
						
							|  |  |  |         if (value == '') return; | 
					
						
							|  |  |  |         final tab = int.parse(value); | 
					
						
							|  |  |  |         peerCardUiType.value = | 
					
						
							|  |  |  |             tab == PeerUiType.list.index ? PeerUiType.list : PeerUiType.grid; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }(); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |     super.initState(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // hard code for now
 | 
					
						
							| 
									
										
										
										
											2022-09-21 14:56:01 +08:00
										 |  |  |   Future<void> _handleTabSelection(int index) async { | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |     _tabIndex.value = index; | 
					
						
							| 
									
										
										
										
											2022-09-21 14:56:01 +08:00
										 |  |  |     await bind.mainSetLocalOption( | 
					
						
							|  |  |  |         key: 'peer-tab-index', value: index.toString()); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |     switch (index) { | 
					
						
							|  |  |  |       case 0: | 
					
						
							|  |  |  |         bind.mainLoadRecentPeers(); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       case 1: | 
					
						
							|  |  |  |         bind.mainLoadFavPeers(); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       case 2: | 
					
						
							|  |  |  |         bind.mainDiscover(); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       case 3: | 
					
						
							| 
									
										
										
										
											2022-10-09 19:41:50 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |         /// AddressBook initState will refresh ab state
 | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return Column( | 
					
						
							|  |  |  |       textBaseline: TextBaseline.ideographic, | 
					
						
							|  |  |  |       crossAxisAlignment: CrossAxisAlignment.start, | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         SizedBox( | 
					
						
							|  |  |  |           height: 28, | 
					
						
							|  |  |  |           child: Container( | 
					
						
							| 
									
										
										
										
											2022-09-21 17:16:09 +08:00
										 |  |  |               padding: isDesktop ? null : EdgeInsets.symmetric(horizontal: 2), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |               constraints: isDesktop ? null : kMobilePageConstraints, | 
					
						
							|  |  |  |               child: Row( | 
					
						
							|  |  |  |                 crossAxisAlignment: CrossAxisAlignment.center, | 
					
						
							|  |  |  |                 children: [ | 
					
						
							| 
									
										
										
										
											2022-09-28 11:20:57 +08:00
										 |  |  |                   Expanded(child: _createSwitchBar(context)), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                   const SizedBox(width: 10), | 
					
						
							|  |  |  |                   const PeerSearchBar(), | 
					
						
							|  |  |  |                   Offstage( | 
					
						
							|  |  |  |                       offstage: !isDesktop, | 
					
						
							|  |  |  |                       child: _createPeerViewTypeSwitch(context) | 
					
						
							|  |  |  |                           .marginOnly(left: 13)), | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |               )), | 
					
						
							|  |  |  |         ), | 
					
						
							| 
									
										
										
										
											2022-09-28 11:20:57 +08:00
										 |  |  |         _createPeersView(), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |       ], | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-28 11:20:57 +08:00
										 |  |  |   Widget _createSwitchBar(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |     final textColor = Theme.of(context).textTheme.titleLarge?.color; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |     return ListView( | 
					
						
							|  |  |  |         scrollDirection: Axis.horizontal, | 
					
						
							|  |  |  |         shrinkWrap: true, | 
					
						
							|  |  |  |         controller: ScrollController(), | 
					
						
							|  |  |  |         children: super.widget.tabs.asMap().entries.map((t) { | 
					
						
							| 
									
										
										
										
											2022-09-21 14:56:01 +08:00
										 |  |  |           return Obx(() => InkWell( | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                 child: Container( | 
					
						
							|  |  |  |                     padding: const EdgeInsets.symmetric(horizontal: 8), | 
					
						
							|  |  |  |                     decoration: BoxDecoration( | 
					
						
							|  |  |  |                       color: _tabIndex.value == t.key | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |                           ? Theme.of(context).backgroundColor | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                           : null, | 
					
						
							| 
									
										
										
										
											2022-09-22 16:45:14 +08:00
										 |  |  |                       borderRadius: BorderRadius.circular(isDesktop ? 2 : 6), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                     ), | 
					
						
							|  |  |  |                     child: Align( | 
					
						
							|  |  |  |                       alignment: Alignment.center, | 
					
						
							|  |  |  |                       child: Text( | 
					
						
							|  |  |  |                         t.value, | 
					
						
							|  |  |  |                         textAlign: TextAlign.center, | 
					
						
							|  |  |  |                         style: TextStyle( | 
					
						
							|  |  |  |                             height: 1, | 
					
						
							|  |  |  |                             fontSize: 14, | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |                             color: | 
					
						
							|  |  |  |                                 _tabIndex.value == t.key ? textColor : textColor | 
					
						
							|  |  |  |                                   ?..withOpacity(0.5)), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                       ), | 
					
						
							|  |  |  |                     )), | 
					
						
							| 
									
										
										
										
											2022-09-21 14:56:01 +08:00
										 |  |  |                 onTap: () async => await _handleTabSelection(t.key), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |               )); | 
					
						
							|  |  |  |         }).toList()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-28 11:20:57 +08:00
										 |  |  |   Widget _createPeersView() { | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |     final verticalMargin = isDesktop ? 12.0 : 6.0; | 
					
						
							|  |  |  |     return Expanded( | 
					
						
							| 
									
										
										
										
											2022-09-28 11:20:57 +08:00
										 |  |  |       child: Obx(() => widget | 
					
						
							|  |  |  |               .children[_tabIndex.value]) //: (to) => _tabIndex.value = to)
 | 
					
						
							|  |  |  |           .marginSymmetric(vertical: verticalMargin), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _createPeerViewTypeSwitch(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |     final textColor = Theme.of(context).textTheme.titleLarge?.color; | 
					
						
							|  |  |  |     final activeDeco = BoxDecoration(color: Theme.of(context).backgroundColor); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |     return Row( | 
					
						
							| 
									
										
										
										
											2022-09-21 14:56:01 +08:00
										 |  |  |       children: [PeerUiType.grid, PeerUiType.list] | 
					
						
							|  |  |  |           .map((type) => Obx( | 
					
						
							|  |  |  |                 () => Container( | 
					
						
							|  |  |  |                   padding: EdgeInsets.all(4.0), | 
					
						
							|  |  |  |                   decoration: peerCardUiType.value == type ? activeDeco : null, | 
					
						
							|  |  |  |                   child: InkWell( | 
					
						
							|  |  |  |                       onTap: () async { | 
					
						
							|  |  |  |                         await bind.mainSetLocalOption( | 
					
						
							|  |  |  |                             key: 'peer-card-ui-type', | 
					
						
							|  |  |  |                             value: type.index.toString()); | 
					
						
							|  |  |  |                         peerCardUiType.value = type; | 
					
						
							|  |  |  |                       }, | 
					
						
							|  |  |  |                       child: Icon( | 
					
						
							|  |  |  |                         type == PeerUiType.grid | 
					
						
							|  |  |  |                             ? Icons.grid_view_rounded | 
					
						
							|  |  |  |                             : Icons.list, | 
					
						
							|  |  |  |                         size: 18, | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |                         color: | 
					
						
							|  |  |  |                             peerCardUiType.value == type ? textColor : textColor | 
					
						
							|  |  |  |                               ?..withOpacity(0.5), | 
					
						
							| 
									
										
										
										
											2022-09-21 14:56:01 +08:00
										 |  |  |                       )), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               )) | 
					
						
							|  |  |  |           .toList(), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PeerSearchBar extends StatefulWidget { | 
					
						
							|  |  |  |   const PeerSearchBar({Key? key}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<StatefulWidget> createState() => _PeerSearchBarState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _PeerSearchBarState extends State<PeerSearchBar> { | 
					
						
							|  |  |  |   var drawer = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return drawer | 
					
						
							|  |  |  |         ? _buildSearchBar() | 
					
						
							|  |  |  |         : IconButton( | 
					
						
							|  |  |  |             alignment: Alignment.centerRight, | 
					
						
							|  |  |  |             padding: const EdgeInsets.only(right: 2), | 
					
						
							|  |  |  |             onPressed: () { | 
					
						
							|  |  |  |               setState(() { | 
					
						
							|  |  |  |                 drawer = true; | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  |             }, | 
					
						
							| 
									
										
										
										
											2022-09-23 17:16:25 +08:00
										 |  |  |             icon: Icon( | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |               Icons.search_rounded, | 
					
						
							| 
									
										
										
										
											2022-09-23 17:16:25 +08:00
										 |  |  |               color: Theme.of(context).hintColor, | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |             )); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildSearchBar() { | 
					
						
							|  |  |  |     RxBool focused = false.obs; | 
					
						
							|  |  |  |     FocusNode focusNode = FocusNode(); | 
					
						
							|  |  |  |     focusNode.addListener(() => focused.value = focusNode.hasFocus); | 
					
						
							|  |  |  |     return Container( | 
					
						
							|  |  |  |       width: 120, | 
					
						
							|  |  |  |       decoration: BoxDecoration( | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |         color: Theme.of(context).backgroundColor, | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |         borderRadius: BorderRadius.circular(6), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       child: Obx(() => Row( | 
					
						
							|  |  |  |             children: [ | 
					
						
							|  |  |  |               Expanded( | 
					
						
							|  |  |  |                 child: Row( | 
					
						
							|  |  |  |                   children: [ | 
					
						
							|  |  |  |                     Icon( | 
					
						
							|  |  |  |                       Icons.search_rounded, | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |                       color: Theme.of(context).hintColor, | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                     ).marginSymmetric(horizontal: 4), | 
					
						
							|  |  |  |                     Expanded( | 
					
						
							|  |  |  |                       child: TextField( | 
					
						
							|  |  |  |                         autofocus: true, | 
					
						
							|  |  |  |                         controller: peerSearchTextController, | 
					
						
							|  |  |  |                         onChanged: (searchText) { | 
					
						
							|  |  |  |                           peerSearchText.value = searchText; | 
					
						
							|  |  |  |                         }, | 
					
						
							|  |  |  |                         focusNode: focusNode, | 
					
						
							|  |  |  |                         textAlign: TextAlign.start, | 
					
						
							|  |  |  |                         maxLines: 1, | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |                         cursorColor: Theme.of(context) | 
					
						
							|  |  |  |                             .textTheme | 
					
						
							|  |  |  |                             .titleLarge | 
					
						
							|  |  |  |                             ?.color | 
					
						
							|  |  |  |                             ?.withOpacity(0.5), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                         cursorHeight: 18, | 
					
						
							|  |  |  |                         cursorWidth: 1, | 
					
						
							|  |  |  |                         style: const TextStyle(fontSize: 14), | 
					
						
							|  |  |  |                         decoration: InputDecoration( | 
					
						
							|  |  |  |                           contentPadding: | 
					
						
							|  |  |  |                               const EdgeInsets.symmetric(vertical: 6), | 
					
						
							|  |  |  |                           hintText: | 
					
						
							|  |  |  |                               focused.value ? null : translate("Search ID"), | 
					
						
							|  |  |  |                           hintStyle: TextStyle( | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |                               fontSize: 14, color: Theme.of(context).hintColor), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                           border: InputBorder.none, | 
					
						
							|  |  |  |                           isDense: true, | 
					
						
							|  |  |  |                         ), | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                     // Icon(Icons.close),
 | 
					
						
							|  |  |  |                     IconButton( | 
					
						
							|  |  |  |                         alignment: Alignment.centerRight, | 
					
						
							|  |  |  |                         padding: const EdgeInsets.only(right: 2), | 
					
						
							|  |  |  |                         onPressed: () { | 
					
						
							|  |  |  |                           setState(() { | 
					
						
							|  |  |  |                             peerSearchTextController.clear(); | 
					
						
							|  |  |  |                             peerSearchText.value = ""; | 
					
						
							|  |  |  |                             drawer = false; | 
					
						
							|  |  |  |                           }); | 
					
						
							|  |  |  |                         }, | 
					
						
							| 
									
										
										
										
											2022-09-23 17:16:25 +08:00
										 |  |  |                         icon: Icon( | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                           Icons.close, | 
					
						
							| 
									
										
										
										
											2022-09-23 17:16:25 +08:00
										 |  |  |                           color: Theme.of(context).hintColor, | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                         )), | 
					
						
							|  |  |  |                   ], | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ) | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |           )), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |