| 
									
										
										
										
											2023-09-14 16:30:45 +08:00
										 |  |  | import 'dart:convert'; | 
					
						
							| 
									
										
										
										
											2023-08-04 13:11:24 +08:00
										 |  |  | import 'dart:math'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2024-05-18 23:13:54 +08:00
										 |  |  | import 'package:flutter_hbb/consts.dart'; | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  | import 'package:flutter_hbb/models/peer_model.dart'; | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  | import 'package:flutter_hbb/models/platform_model.dart'; | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  | import 'package:get/get.dart'; | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import '../common.dart'; | 
					
						
							|  |  |  | import 'model.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  | enum PeerTabIndex { | 
					
						
							|  |  |  |   recent, | 
					
						
							|  |  |  |   fav, | 
					
						
							|  |  |  |   lan, | 
					
						
							|  |  |  |   ab, | 
					
						
							|  |  |  |   group, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  | class PeerTabModel with ChangeNotifier { | 
					
						
							|  |  |  |   WeakReference<FFI> parent; | 
					
						
							|  |  |  |   int get currentTab => _currentTab; | 
					
						
							|  |  |  |   int _currentTab = 0; // index in tabNames
 | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |   static const int maxTabCount = 5; | 
					
						
							|  |  |  |   static const List<String> tabNames = [ | 
					
						
							| 
									
										
										
										
											2023-11-06 20:12:01 +08:00
										 |  |  |     'Recent sessions', | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |     'Favorites', | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |     'Discovered', | 
					
						
							|  |  |  |     'Address book', | 
					
						
							|  |  |  |     'Group', | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |   ]; | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |   static const List<IconData> icons = [ | 
					
						
							| 
									
										
										
										
											2023-06-23 12:58:27 +08:00
										 |  |  |     Icons.access_time_filled, | 
					
						
							| 
									
										
										
										
											2023-06-21 16:04:52 +08:00
										 |  |  |     Icons.star, | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |     Icons.explore, | 
					
						
							|  |  |  |     IconFont.addressBook, | 
					
						
							|  |  |  |     Icons.group, | 
					
						
							| 
									
										
										
										
											2023-06-21 16:04:52 +08:00
										 |  |  |   ]; | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |   List<bool> isEnabled = List.from([ | 
					
						
							|  |  |  |     true, | 
					
						
							|  |  |  |     true, | 
					
						
							|  |  |  |     !isWeb, | 
					
						
							|  |  |  |     !(bind.isDisableAb() || bind.isDisableAccount()), | 
					
						
							| 
									
										
										
										
											2024-05-28 20:10:12 +08:00
										 |  |  |     !(bind.isDisableGroupPanel() || bind.isDisableAccount()), | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |   ]); | 
					
						
							|  |  |  |   final List<bool> _isVisible = List.filled(maxTabCount, true, growable: false); | 
					
						
							|  |  |  |   List<bool> get isVisibleEnabled => () { | 
					
						
							|  |  |  |         final list = _isVisible.toList(); | 
					
						
							|  |  |  |         for (int i = 0; i < maxTabCount; i++) { | 
					
						
							|  |  |  |           list[i] = list[i] && isEnabled[i]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return list; | 
					
						
							|  |  |  |       }(); | 
					
						
							|  |  |  |   final List<int> orders = | 
					
						
							|  |  |  |       List.generate(maxTabCount, (index) => index, growable: false); | 
					
						
							|  |  |  |   List<int> get visibleEnabledOrderedIndexs => | 
					
						
							|  |  |  |       orders.where((e) => isVisibleEnabled[e]).toList(); | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |   List<Peer> _selectedPeers = List.empty(growable: true); | 
					
						
							|  |  |  |   List<Peer> get selectedPeers => _selectedPeers; | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |   bool _multiSelectionMode = false; | 
					
						
							|  |  |  |   bool get multiSelectionMode => _multiSelectionMode; | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |   List<Peer> _currentTabCachedPeers = List.empty(growable: true); | 
					
						
							|  |  |  |   List<Peer> get currentTabCachedPeers => _currentTabCachedPeers; | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |   bool _isShiftDown = false; | 
					
						
							|  |  |  |   bool get isShiftDown => _isShiftDown; | 
					
						
							|  |  |  |   String _lastId = ''; | 
					
						
							|  |  |  |   String get lastId => _lastId; | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   PeerTabModel(this.parent) { | 
					
						
							| 
									
										
										
										
											2023-09-14 16:30:45 +08:00
										 |  |  |     // visible
 | 
					
						
							|  |  |  |     try { | 
					
						
							| 
									
										
										
										
											2024-05-18 23:13:54 +08:00
										 |  |  |       final option = bind.getLocalFlutterOption(k: kOptionPeerTabVisible); | 
					
						
							| 
									
										
										
										
											2023-09-14 16:30:45 +08:00
										 |  |  |       if (option.isNotEmpty) { | 
					
						
							|  |  |  |         List<dynamic> decodeList = jsonDecode(option); | 
					
						
							|  |  |  |         if (decodeList.length == _isVisible.length) { | 
					
						
							|  |  |  |           for (int i = 0; i < _isVisible.length; i++) { | 
					
						
							|  |  |  |             if (decodeList[i] is bool) { | 
					
						
							|  |  |  |               _isVisible[i] = decodeList[i]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       debugPrint("failed to get peer tab visible list:$e"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |     // order
 | 
					
						
							|  |  |  |     try { | 
					
						
							| 
									
										
										
										
											2024-05-18 23:13:54 +08:00
										 |  |  |       final option = bind.getLocalFlutterOption(k: kOptionPeerTabOrder); | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |       if (option.isNotEmpty) { | 
					
						
							|  |  |  |         List<dynamic> decodeList = jsonDecode(option); | 
					
						
							|  |  |  |         if (decodeList.length == maxTabCount) { | 
					
						
							|  |  |  |           var sortedList = decodeList.toList(); | 
					
						
							|  |  |  |           sortedList.sort(); | 
					
						
							|  |  |  |           bool valid = true; | 
					
						
							|  |  |  |           for (int i = 0; i < maxTabCount; i++) { | 
					
						
							|  |  |  |             if (sortedList[i] is! int || sortedList[i] != i) { | 
					
						
							|  |  |  |               valid = false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           if (valid) { | 
					
						
							|  |  |  |             for (int i = 0; i < orders.length; i++) { | 
					
						
							|  |  |  |               orders[i] = decodeList[i]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       debugPrint("failed to get peer tab order list: $e"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |     // init currentTab
 | 
					
						
							|  |  |  |     _currentTab = | 
					
						
							| 
									
										
										
										
											2024-05-18 23:13:54 +08:00
										 |  |  |         int.tryParse(bind.getLocalFlutterOption(k: kOptionPeerTabIndex)) ?? 0; | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |     if (_currentTab < 0 || _currentTab >= maxTabCount) { | 
					
						
							| 
									
										
										
										
											2023-06-21 16:04:52 +08:00
										 |  |  |       _currentTab = 0; | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |     _trySetCurrentTabToFirstVisibleEnabled(); | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   setCurrentTab(int index) { | 
					
						
							|  |  |  |     if (_currentTab != index) { | 
					
						
							|  |  |  |       _currentTab = index; | 
					
						
							|  |  |  |       notifyListeners(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-14 10:17:03 +08:00
										 |  |  |   String tabTooltip(int index) { | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |     if (index >= 0 && index < tabNames.length) { | 
					
						
							| 
									
										
										
										
											2023-09-14 10:17:03 +08:00
										 |  |  |       return translate(tabNames[index]); | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |     return index.toString(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 16:04:52 +08:00
										 |  |  |   IconData tabIcon(int index) { | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |     if (index >= 0 && index < icons.length) { | 
					
						
							| 
									
										
										
										
											2023-06-21 16:04:52 +08:00
										 |  |  |       return icons[index]; | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-06-21 16:04:52 +08:00
										 |  |  |     return Icons.help; | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |   setMultiSelectionMode(bool mode) { | 
					
						
							|  |  |  |     _multiSelectionMode = mode; | 
					
						
							|  |  |  |     if (!mode) { | 
					
						
							|  |  |  |       _selectedPeers.clear(); | 
					
						
							|  |  |  |       _lastId = ''; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     notifyListeners(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   select(Peer peer) { | 
					
						
							|  |  |  |     if (!_multiSelectionMode) { | 
					
						
							|  |  |  |       // https://github.com/flutter/flutter/issues/101275#issuecomment-1604541700
 | 
					
						
							|  |  |  |       // After onTap, the shift key should be pressed for a while when not in multiselection mode,
 | 
					
						
							|  |  |  |       // because onTap is delayed when onDoubleTap is not null
 | 
					
						
							|  |  |  |       if (isDesktop && !_isShiftDown) return; | 
					
						
							|  |  |  |       _multiSelectionMode = true; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-04 13:11:24 +08:00
										 |  |  |     final cached = _currentTabCachedPeers.map((e) => e.id).toList(); | 
					
						
							|  |  |  |     int thisIndex = cached.indexOf(peer.id); | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |     int lastIndex = cached.indexOf(_lastId); | 
					
						
							|  |  |  |     if (_isShiftDown && thisIndex >= 0 && lastIndex >= 0) { | 
					
						
							|  |  |  |       int start = min(thisIndex, lastIndex); | 
					
						
							|  |  |  |       int end = max(thisIndex, lastIndex); | 
					
						
							|  |  |  |       bool remove = isPeerSelected(peer.id); | 
					
						
							| 
									
										
										
										
											2023-08-04 13:11:24 +08:00
										 |  |  |       for (var i = start; i <= end; i++) { | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |         if (remove) { | 
					
						
							|  |  |  |           if (isPeerSelected(cached[i])) { | 
					
						
							|  |  |  |             _selectedPeers.removeWhere((p) => p.id == cached[i]); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           if (!isPeerSelected(cached[i])) { | 
					
						
							|  |  |  |             _selectedPeers.add(_currentTabCachedPeers[i]); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-08-04 13:11:24 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2023-08-04 13:11:24 +08:00
										 |  |  |       if (isPeerSelected(peer.id)) { | 
					
						
							|  |  |  |         _selectedPeers.removeWhere((p) => p.id == peer.id); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         _selectedPeers.add(peer); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  |     _lastId = peer.id; | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |     notifyListeners(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   setCurrentTabCachedPeers(List<Peer> peers) { | 
					
						
							|  |  |  |     Future.delayed(Duration.zero, () { | 
					
						
							|  |  |  |       _currentTabCachedPeers = peers; | 
					
						
							|  |  |  |       notifyListeners(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   selectAll() { | 
					
						
							|  |  |  |     _selectedPeers = _currentTabCachedPeers.toList(); | 
					
						
							|  |  |  |     notifyListeners(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool isPeerSelected(String id) { | 
					
						
							|  |  |  |     return selectedPeers.firstWhereOrNull((p) => p.id == id) != null; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-08-09 22:00:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   setShiftDown(bool v) { | 
					
						
							|  |  |  |     if (_isShiftDown != v) { | 
					
						
							|  |  |  |       _isShiftDown = v; | 
					
						
							|  |  |  |       if (_multiSelectionMode) { | 
					
						
							|  |  |  |         notifyListeners(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-09-14 16:30:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   setTabVisible(int index, bool visible) { | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |     if (index >= 0 && index < maxTabCount) { | 
					
						
							| 
									
										
										
										
											2023-09-14 16:30:45 +08:00
										 |  |  |       if (_isVisible[index] != visible) { | 
					
						
							|  |  |  |         _isVisible[index] = visible; | 
					
						
							|  |  |  |         if (index == _currentTab && !visible) { | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |           _trySetCurrentTabToFirstVisibleEnabled(); | 
					
						
							|  |  |  |         } else if (visible && visibleEnabledOrderedIndexs.length == 1) { | 
					
						
							| 
									
										
										
										
											2023-09-14 16:30:45 +08:00
										 |  |  |           _currentTab = index; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |           bind.setLocalFlutterOption( | 
					
						
							| 
									
										
										
										
											2024-05-18 23:13:54 +08:00
										 |  |  |               k: kOptionPeerTabVisible, v: jsonEncode(_isVisible)); | 
					
						
							| 
									
										
										
										
											2023-09-14 16:30:45 +08:00
										 |  |  |         } catch (_) {} | 
					
						
							|  |  |  |         notifyListeners(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |   _trySetCurrentTabToFirstVisibleEnabled() { | 
					
						
							|  |  |  |     if (!visibleEnabledOrderedIndexs.contains(_currentTab)) { | 
					
						
							|  |  |  |       if (visibleEnabledOrderedIndexs.isNotEmpty) { | 
					
						
							|  |  |  |         _currentTab = visibleEnabledOrderedIndexs.first; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   reorder(int oldIndex, int newIndex) { | 
					
						
							|  |  |  |     if (oldIndex < newIndex) { | 
					
						
							|  |  |  |       newIndex -= 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (oldIndex < 0 || oldIndex >= visibleEnabledOrderedIndexs.length) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (newIndex < 0 || newIndex >= visibleEnabledOrderedIndexs.length) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     final oldTabValue = visibleEnabledOrderedIndexs[oldIndex]; | 
					
						
							|  |  |  |     final newTabValue = visibleEnabledOrderedIndexs[newIndex]; | 
					
						
							|  |  |  |     int oldValueIndex = orders.indexOf(oldTabValue); | 
					
						
							|  |  |  |     int newValueIndex = orders.indexOf(newTabValue); | 
					
						
							|  |  |  |     final list = orders.toList(); | 
					
						
							|  |  |  |     if (oldIndex != -1 && newIndex != -1) { | 
					
						
							|  |  |  |       list.removeAt(oldValueIndex); | 
					
						
							|  |  |  |       list.insert(newValueIndex, oldTabValue); | 
					
						
							|  |  |  |       for (int i = 0; i < list.length; i++) { | 
					
						
							|  |  |  |         orders[i] = list[i]; | 
					
						
							| 
									
										
										
										
											2023-09-14 16:30:45 +08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2024-05-18 23:13:54 +08:00
										 |  |  |       bind.setLocalFlutterOption(k: kOptionPeerTabOrder, v: jsonEncode(orders)); | 
					
						
							| 
									
										
										
										
											2024-04-06 17:53:03 +08:00
										 |  |  |       notifyListeners(); | 
					
						
							| 
									
										
										
										
											2023-09-14 16:30:45 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  | } |