| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  | import 'dart:async'; | 
					
						
							| 
									
										
										
										
											2022-07-25 16:26:51 +08:00
										 |  |  | import 'dart:convert'; | 
					
						
							| 
									
										
										
										
											2023-07-28 17:53:02 +08:00
										 |  |  | import 'dart:io'; | 
					
						
							| 
									
										
										
										
											2022-07-25 16:26:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2023-09-14 10:17:03 +08:00
										 |  |  | import 'package:flutter_hbb/common/widgets/peers_view.dart'; | 
					
						
							| 
									
										
										
										
											2022-07-25 16:26:51 +08:00
										 |  |  | import 'package:flutter_hbb/models/model.dart'; | 
					
						
							| 
									
										
										
										
											2022-10-08 16:53:03 +09:00
										 |  |  | import 'package:flutter_hbb/models/peer_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-03 22:03:31 +08:00
										 |  |  | import 'package:flutter_hbb/models/platform_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  | import 'package:get/get.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-23 15:10:10 +08:00
										 |  |  | import 'package:bot_toast/bot_toast.dart'; | 
					
						
							| 
									
										
										
										
											2022-07-25 16:26:51 +08:00
										 |  |  | import 'package:http/http.dart' as http; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 17:52:36 +08:00
										 |  |  | import '../common.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-26 19:53:57 +08:00
										 |  |  | final syncAbOption = 'sync-ab-with-recent-sessions'; | 
					
						
							|  |  |  | bool shouldSyncAb() { | 
					
						
							|  |  |  |   return bind.mainGetLocalOption(key: syncAbOption).isNotEmpty; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | final sortAbTagsOption = 'sync-ab-tags'; | 
					
						
							|  |  |  | bool shouldSortTags() { | 
					
						
							|  |  |  |   return bind.mainGetLocalOption(key: sortAbTagsOption).isNotEmpty; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-08 17:13:24 +09:00
										 |  |  | class AbModel { | 
					
						
							| 
									
										
										
										
											2023-06-21 16:08:45 +08:00
										 |  |  |   final abLoading = false.obs; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |   final pullError = "".obs; | 
					
						
							|  |  |  |   final pushError = "".obs; | 
					
						
							| 
									
										
										
										
											2023-06-21 16:08:45 +08:00
										 |  |  |   final tags = [].obs; | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |   final RxMap<String, int> tagColors = Map<String, int>.fromEntries([]).obs; | 
					
						
							| 
									
										
										
										
											2023-06-21 16:08:45 +08:00
										 |  |  |   final peers = List<Peer>.empty(growable: true).obs; | 
					
						
							| 
									
										
										
										
											2023-07-26 19:53:57 +08:00
										 |  |  |   final sortTags = shouldSortTags().obs; | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |   final retrying = false.obs; | 
					
						
							| 
									
										
										
										
											2023-08-11 15:27:50 +08:00
										 |  |  |   bool get emtpy => peers.isEmpty && tags.isEmpty; | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 16:08:45 +08:00
										 |  |  |   final selectedTags = List<String>.empty(growable: true).obs; | 
					
						
							| 
									
										
										
										
											2023-06-23 15:10:10 +08:00
										 |  |  |   var initialized = false; | 
					
						
							| 
									
										
										
										
											2023-07-26 20:43:18 +08:00
										 |  |  |   var licensedDevices = 0; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |   var _syncAllFromRecent = true; | 
					
						
							|  |  |  |   var _syncFromRecentLock = false; | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |   var _timerCounter = 0; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |   var _cacheLoadOnceFlag = false; | 
					
						
							| 
									
										
										
										
											2022-07-25 16:26:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   WeakReference<FFI> parent; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |   AbModel(this.parent) { | 
					
						
							|  |  |  |     if (desktopType == DesktopType.main) { | 
					
						
							|  |  |  |       Timer.periodic(Duration(milliseconds: 500), (timer) async { | 
					
						
							| 
									
										
										
										
											2023-08-16 14:35:29 +08:00
										 |  |  |         if (_timerCounter++ % 6 == 0) { | 
					
						
							|  |  |  |           if (!gFFI.userModel.isLogin) return; | 
					
						
							|  |  |  |           syncFromRecent(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-07-25 16:26:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-23 15:10:10 +08:00
										 |  |  |   Future<void> pullAb({force = true, quiet = false}) async { | 
					
						
							| 
									
										
										
										
											2023-08-11 15:27:50 +08:00
										 |  |  |     debugPrint("pullAb, force:$force, quiet:$quiet"); | 
					
						
							| 
									
										
										
										
											2023-08-16 14:35:29 +08:00
										 |  |  |     if (!gFFI.userModel.isLogin) return; | 
					
						
							| 
									
										
										
										
											2023-06-23 15:10:10 +08:00
										 |  |  |     if (abLoading.value) return; | 
					
						
							|  |  |  |     if (!force && initialized) return; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |     if (pushError.isNotEmpty) { | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         // push to retry
 | 
					
						
							| 
									
										
										
										
											2023-08-18 19:45:19 +08:00
										 |  |  |         await pushAb(toastIfFail: false, toastIfSucc: false); | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |       } catch (_) {} | 
					
						
							| 
									
										
										
										
											2023-08-11 15:27:50 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-06-23 15:10:10 +08:00
										 |  |  |     if (!quiet) { | 
					
						
							|  |  |  |       abLoading.value = true; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |       pullError.value = ""; | 
					
						
							| 
									
										
										
										
											2023-06-23 15:10:10 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-07-31 17:13:39 +08:00
										 |  |  |     final api = "${await bind.mainGetApiServer()}/api/ab"; | 
					
						
							| 
									
										
										
										
											2023-08-10 17:16:53 +08:00
										 |  |  |     int? statusCode; | 
					
						
							| 
									
										
										
										
											2022-07-26 17:14:52 +08:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2023-03-27 17:02:46 +02:00
										 |  |  |       var authHeaders = getHttpHeaders(); | 
					
						
							|  |  |  |       authHeaders['Content-Type'] = "application/json"; | 
					
						
							| 
									
										
										
										
											2023-07-28 17:53:02 +08:00
										 |  |  |       authHeaders['Accept-Encoding'] = "gzip"; | 
					
						
							|  |  |  |       final resp = await http.get(Uri.parse(api), headers: authHeaders); | 
					
						
							| 
									
										
										
										
											2023-08-10 17:16:53 +08:00
										 |  |  |       statusCode = resp.statusCode; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |       if (resp.body.toLowerCase() == "null") { | 
					
						
							|  |  |  |         // normal reply, emtpy ab return null
 | 
					
						
							|  |  |  |         tags.clear(); | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |         tagColors.clear(); | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |         peers.clear(); | 
					
						
							|  |  |  |       } else if (resp.body.isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2023-08-15 12:09:33 +08:00
										 |  |  |         Map<String, dynamic> json = | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |             _jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode); | 
					
						
							| 
									
										
										
										
											2022-09-15 11:06:44 +08:00
										 |  |  |         if (json.containsKey('error')) { | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |           throw json['error']; | 
					
						
							| 
									
										
										
										
											2022-09-15 11:06:44 +08:00
										 |  |  |         } else if (json.containsKey('data')) { | 
					
						
							| 
									
										
										
										
											2023-07-26 20:43:18 +08:00
										 |  |  |           try { | 
					
						
							|  |  |  |             gFFI.abModel.licensedDevices = json['licensed_devices']; | 
					
						
							|  |  |  |             // ignore: empty_catches
 | 
					
						
							|  |  |  |           } catch (e) {} | 
					
						
							| 
									
										
										
										
											2022-09-15 11:06:44 +08:00
										 |  |  |           final data = jsonDecode(json['data']); | 
					
						
							| 
									
										
										
										
											2022-11-30 11:13:02 +08:00
										 |  |  |           if (data != null) { | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |             _deserialize(data); | 
					
						
							| 
									
										
										
										
											2023-08-11 15:27:50 +08:00
										 |  |  |             _saveCache(); // save on success
 | 
					
						
							| 
									
										
										
										
											2022-10-08 16:53:03 +09:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-09-15 11:06:44 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-07-26 17:14:52 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } catch (err) { | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |       if (!quiet) { | 
					
						
							|  |  |  |         pullError.value = | 
					
						
							|  |  |  |             '${translate('pull_ab_failed_tip')}: ${translate(err.toString())}'; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-07-26 17:14:52 +08:00
										 |  |  |     } finally { | 
					
						
							| 
									
										
										
										
											2023-08-18 12:52:50 +08:00
										 |  |  |       abLoading.value = false; | 
					
						
							| 
									
										
										
										
											2023-06-23 15:10:10 +08:00
										 |  |  |       initialized = true; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |       _syncAllFromRecent = true; | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |       _timerCounter = 0; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |       if (pullError.isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2023-08-10 17:16:53 +08:00
										 |  |  |         if (statusCode == 401) { | 
					
						
							| 
									
										
										
										
											2023-09-14 10:17:03 +08:00
										 |  |  |           gFFI.userModel.reset(resetOther: true); | 
					
						
							| 
									
										
										
										
											2023-08-10 17:16:53 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-09-14 10:17:03 +08:00
										 |  |  |       platformFFI.tryHandle({'name': LoadEvent.addressBook}); | 
					
						
							| 
									
										
										
										
											2022-07-25 16:26:51 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |   void addId(String id, String alias, List<dynamic> tags) { | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |     if (idContainBy(id)) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |     final peer = Peer.fromJson({ | 
					
						
							|  |  |  |       'id': id, | 
					
						
							|  |  |  |       'alias': alias, | 
					
						
							|  |  |  |       'tags': tags, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2023-09-21 16:34:04 +08:00
										 |  |  |     _mergePeerFromGroup(peer); | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |     peers.add(peer); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-26 20:43:18 +08:00
										 |  |  |   bool isFull(bool warn) { | 
					
						
							|  |  |  |     final res = licensedDevices > 0 && peers.length >= licensedDevices; | 
					
						
							|  |  |  |     if (res && warn) { | 
					
						
							|  |  |  |       BotToast.showText( | 
					
						
							|  |  |  |           contentColor: Colors.red, text: translate("exceed_max_devices")); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-27 12:16:45 +08:00
										 |  |  |   void addPeer(Peer peer) { | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |     final index = peers.indexWhere((e) => e.id == peer.id); | 
					
						
							|  |  |  |     if (index >= 0) { | 
					
						
							| 
									
										
										
										
											2023-08-17 21:17:31 +08:00
										 |  |  |       merge(peer, peers[index]); | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |     } else { | 
					
						
							|  |  |  |       peers.add(peer); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-16 13:30:38 +08:00
										 |  |  |   bool addPeers(List<Peer> ps) { | 
					
						
							|  |  |  |     bool allAdded = true; | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |     for (var p in ps) { | 
					
						
							| 
									
										
										
										
											2023-08-16 13:30:38 +08:00
										 |  |  |       if (!isFull(false)) { | 
					
						
							|  |  |  |         addPeer(p); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         allAdded = false; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-16 13:30:38 +08:00
										 |  |  |     return allAdded; | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |   void addTag(String tag) async { | 
					
						
							|  |  |  |     if (tagContainBy(tag)) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     tags.add(tag); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void changeTagForPeer(String id, List<dynamic> tags) { | 
					
						
							| 
									
										
										
										
											2022-10-08 16:53:03 +09:00
										 |  |  |     final it = peers.where((element) => element.id == id); | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |     if (it.isEmpty) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-10-08 16:53:03 +09:00
										 |  |  |     it.first.tags = tags; | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |   void changeTagForPeers(List<String> ids, List<dynamic> tags) { | 
					
						
							|  |  |  |     peers.map((e) { | 
					
						
							|  |  |  |       if (ids.contains(e.id)) { | 
					
						
							|  |  |  |         e.tags = tags; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }).toList(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-15 09:23:55 +08:00
										 |  |  |   void changeAlias({required String id, required String alias}) { | 
					
						
							|  |  |  |     final it = peers.where((element) => element.id == id); | 
					
						
							|  |  |  |     if (it.isEmpty) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     it.first.alias = alias; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-21 08:39:47 +08:00
										 |  |  |   bool changePassword(String id, String hash) { | 
					
						
							| 
									
										
										
										
											2023-08-17 18:21:37 +08:00
										 |  |  |     final it = peers.where((element) => element.id == id); | 
					
						
							| 
									
										
										
										
											2023-08-21 08:39:47 +08:00
										 |  |  |     if (it.isNotEmpty) { | 
					
						
							|  |  |  |       if (it.first.hash != hash) { | 
					
						
							|  |  |  |         it.first.hash = hash; | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-08-17 18:21:37 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-21 08:39:47 +08:00
										 |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2023-08-17 18:21:37 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |   Future<bool> pushAb( | 
					
						
							|  |  |  |       {bool toastIfFail = true, | 
					
						
							|  |  |  |       bool toastIfSucc = true, | 
					
						
							|  |  |  |       bool isRetry = false}) async { | 
					
						
							|  |  |  |     debugPrint( | 
					
						
							|  |  |  |         "pushAb: toastIfFail:$toastIfFail, toastIfSucc:$toastIfSucc, isRetry:$isRetry"); | 
					
						
							| 
									
										
										
										
											2023-08-21 08:39:47 +08:00
										 |  |  |     if (!gFFI.userModel.isLogin) return false; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |     pushError.value = ''; | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |     if (isRetry) retrying.value = true; | 
					
						
							|  |  |  |     DateTime startTime = DateTime.now(); | 
					
						
							|  |  |  |     bool ret = false; | 
					
						
							| 
									
										
										
										
											2022-09-19 10:22:40 +08:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |       // avoid double pushes in a row
 | 
					
						
							|  |  |  |       _syncAllFromRecent = true; | 
					
						
							| 
									
										
										
										
											2023-08-16 08:09:03 +08:00
										 |  |  |       await syncFromRecent(push: false); | 
					
						
							|  |  |  |       //https: //stackoverflow.com/questions/68249333/flutter-getx-updating-item-in-children-list-is-not-reactive
 | 
					
						
							|  |  |  |       peers.refresh(); | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |       final api = "${await bind.mainGetApiServer()}/api/ab"; | 
					
						
							|  |  |  |       var authHeaders = getHttpHeaders(); | 
					
						
							|  |  |  |       authHeaders['Content-Type'] = "application/json"; | 
					
						
							| 
									
										
										
										
											2023-08-23 12:20:19 +08:00
										 |  |  |       final body = jsonEncode({"data": jsonEncode(_serialize())}); | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |       http.Response resp; | 
					
						
							|  |  |  |       // support compression
 | 
					
						
							| 
									
										
										
										
											2023-08-15 12:52:01 +08:00
										 |  |  |       if (licensedDevices > 0 && body.length > 1024) { | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |         authHeaders['Content-Encoding'] = "gzip"; | 
					
						
							|  |  |  |         resp = await http.post(Uri.parse(api), | 
					
						
							|  |  |  |             headers: authHeaders, body: GZipCodec().encode(utf8.encode(body))); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         resp = | 
					
						
							|  |  |  |             await http.post(Uri.parse(api), headers: authHeaders, body: body); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-08-15 12:09:33 +08:00
										 |  |  |       if (resp.statusCode == 200 && | 
					
						
							|  |  |  |           (resp.body.isEmpty || resp.body.toLowerCase() == 'null')) { | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |         ret = true; | 
					
						
							| 
									
										
										
										
											2023-08-15 12:09:33 +08:00
										 |  |  |         _saveCache(); | 
					
						
							|  |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2023-09-14 10:17:03 +08:00
										 |  |  |         Map<String, dynamic> json = | 
					
						
							|  |  |  |             _jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode); | 
					
						
							| 
									
										
										
										
											2023-08-15 12:09:33 +08:00
										 |  |  |         if (json.containsKey('error')) { | 
					
						
							|  |  |  |           throw json['error']; | 
					
						
							|  |  |  |         } else if (resp.statusCode == 200) { | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |           ret = true; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |           _saveCache(); | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2023-08-15 12:09:33 +08:00
										 |  |  |           throw 'HTTP ${resp.statusCode}'; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-09-19 10:22:40 +08:00
										 |  |  |     } catch (e) { | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |       pushError.value = | 
					
						
							|  |  |  |           '${translate('push_ab_failed_tip')}: ${translate(e.toString())}'; | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-16 10:18:29 +08:00
										 |  |  |     _syncAllFromRecent = true; | 
					
						
							|  |  |  |     if (isRetry) { | 
					
						
							|  |  |  |       var ms = | 
					
						
							|  |  |  |           (Duration(milliseconds: 200) - DateTime.now().difference(startTime)) | 
					
						
							|  |  |  |               .inMilliseconds; | 
					
						
							|  |  |  |       ms = ms > 0 ? ms : 0; | 
					
						
							|  |  |  |       Future.delayed(Duration(milliseconds: ms), () { | 
					
						
							|  |  |  |         retrying.value = false; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!ret && toastIfFail) { | 
					
						
							|  |  |  |       BotToast.showText(contentColor: Colors.red, text: pushError.value); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (ret && toastIfSucc) { | 
					
						
							|  |  |  |       showToast(translate('Successful')); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ret; | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |   Peer? find(String id) { | 
					
						
							|  |  |  |     return peers.firstWhereOrNull((e) => e.id == id); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |   bool idContainBy(String id) { | 
					
						
							| 
									
										
										
										
											2022-10-08 16:53:03 +09:00
										 |  |  |     return peers.where((element) => element.id == id).isNotEmpty; | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool tagContainBy(String tag) { | 
					
						
							|  |  |  |     return tags.where((element) => element == tag).isNotEmpty; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void deletePeer(String id) { | 
					
						
							| 
									
										
										
										
											2022-10-08 16:53:03 +09:00
										 |  |  |     peers.removeWhere((element) => element.id == id); | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-03 16:48:14 +08:00
										 |  |  |   void deletePeers(List<String> ids) { | 
					
						
							|  |  |  |     peers.removeWhere((e) => ids.contains(e.id)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |   void deleteTag(String tag) { | 
					
						
							| 
									
										
										
										
											2022-10-08 19:56:04 +09:00
										 |  |  |     gFFI.abModel.selectedTags.remove(tag); | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |     tags.removeWhere((element) => element == tag); | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |     tagColors.remove(tag); | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |     for (var peer in peers) { | 
					
						
							| 
									
										
										
										
											2022-10-08 16:53:03 +09:00
										 |  |  |       if (peer.tags.isEmpty) { | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |         continue; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-10-08 16:53:03 +09:00
										 |  |  |       if (peer.tags.contains(tag)) { | 
					
						
							|  |  |  |         ((peer.tags)).remove(tag); | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 10:08:33 +08:00
										 |  |  |   void renameTag(String oldTag, String newTag) { | 
					
						
							|  |  |  |     if (tags.contains(newTag)) return; | 
					
						
							|  |  |  |     tags.value = tags.map((e) { | 
					
						
							|  |  |  |       if (e == oldTag) { | 
					
						
							|  |  |  |         return newTag; | 
					
						
							|  |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2023-08-10 11:12:58 +08:00
										 |  |  |         return e; | 
					
						
							| 
									
										
										
										
											2023-08-10 10:08:33 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |     }).toList(); | 
					
						
							|  |  |  |     selectedTags.value = selectedTags.map((e) { | 
					
						
							|  |  |  |       if (e == oldTag) { | 
					
						
							|  |  |  |         return newTag; | 
					
						
							|  |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2023-08-10 11:12:58 +08:00
										 |  |  |         return e; | 
					
						
							| 
									
										
										
										
											2023-08-10 10:08:33 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |     }).toList(); | 
					
						
							|  |  |  |     for (var peer in peers) { | 
					
						
							|  |  |  |       peer.tags = peer.tags.map((e) { | 
					
						
							|  |  |  |         if (e == oldTag) { | 
					
						
							|  |  |  |           return newTag; | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2023-08-10 11:12:58 +08:00
										 |  |  |           return e; | 
					
						
							| 
									
										
										
										
											2023-08-10 10:08:33 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |       }).toList(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |     int? oldColor = tagColors[oldTag]; | 
					
						
							|  |  |  |     if (oldColor != null) { | 
					
						
							|  |  |  |       tagColors.remove(oldTag); | 
					
						
							|  |  |  |       tagColors.addAll({newTag: oldColor}); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-10 10:08:33 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |   void unsetSelectedTags() { | 
					
						
							|  |  |  |     selectedTags.clear(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   List<dynamic> getPeerTags(String id) { | 
					
						
							| 
									
										
										
										
											2022-10-08 16:53:03 +09:00
										 |  |  |     final it = peers.where((p0) => p0.id == id); | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |     if (it.isEmpty) { | 
					
						
							|  |  |  |       return []; | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2022-10-08 16:53:03 +09:00
										 |  |  |       return it.first.tags; | 
					
						
							| 
									
										
										
										
											2022-07-26 17:03:19 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-07-27 14:29:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |   Color getTagColor(String tag) { | 
					
						
							|  |  |  |     int? colorValue = tagColors[tag]; | 
					
						
							|  |  |  |     if (colorValue != null) { | 
					
						
							|  |  |  |       return Color(colorValue); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return str2color2(tag, existing: tagColors.values.toList()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   setTagColor(String tag, Color color) { | 
					
						
							|  |  |  |     if (tags.contains(tag)) { | 
					
						
							|  |  |  |       tagColors[tag] = color.value; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-17 21:17:31 +08:00
										 |  |  |   void merge(Peer r, Peer p) { | 
					
						
							|  |  |  |     p.hash = r.hash.isEmpty ? p.hash : r.hash; | 
					
						
							|  |  |  |     p.username = r.username.isEmpty ? p.username : r.username; | 
					
						
							|  |  |  |     p.hostname = r.hostname.isEmpty ? p.hostname : r.hostname; | 
					
						
							|  |  |  |     p.alias = p.alias.isEmpty ? r.alias : p.alias; | 
					
						
							|  |  |  |     p.forceAlwaysRelay = r.forceAlwaysRelay; | 
					
						
							|  |  |  |     p.rdpPort = r.rdpPort; | 
					
						
							|  |  |  |     p.rdpUsername = r.rdpUsername; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-16 08:09:03 +08:00
										 |  |  |   Future<void> syncFromRecent({bool push = true}) async { | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |     if (!_syncFromRecentLock) { | 
					
						
							|  |  |  |       _syncFromRecentLock = true; | 
					
						
							| 
									
										
										
										
											2023-08-16 08:09:03 +08:00
										 |  |  |       await _syncFromRecentWithoutLock(push: push); | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |       _syncFromRecentLock = false; | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-16 08:09:03 +08:00
										 |  |  |   Future<void> _syncFromRecentWithoutLock({bool push = true}) async { | 
					
						
							| 
									
										
										
										
											2023-08-16 22:43:17 +08:00
										 |  |  |     bool peerSyncEqual(Peer a, Peer b) { | 
					
						
							|  |  |  |       return a.hash == b.hash && | 
					
						
							|  |  |  |           a.username == b.username && | 
					
						
							|  |  |  |           a.platform == b.platform && | 
					
						
							|  |  |  |           a.hostname == b.hostname && | 
					
						
							|  |  |  |           a.alias == b.alias; | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Future<List<Peer>> getRecentPeers() async { | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         List<String> filteredPeerIDs; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |         if (_syncAllFromRecent) { | 
					
						
							|  |  |  |           _syncAllFromRecent = false; | 
					
						
							| 
									
										
										
										
											2023-08-16 08:59:50 +08:00
										 |  |  |           filteredPeerIDs = []; | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |         } else { | 
					
						
							|  |  |  |           final new_stored_str = await bind.mainGetNewStoredPeers(); | 
					
						
							|  |  |  |           if (new_stored_str.isEmpty) return []; | 
					
						
							| 
									
										
										
										
											2023-08-16 08:59:50 +08:00
										 |  |  |           filteredPeerIDs = (jsonDecode(new_stored_str) as List<dynamic>) | 
					
						
							|  |  |  |               .map((e) => e.toString()) | 
					
						
							|  |  |  |               .toList(); | 
					
						
							|  |  |  |           if (filteredPeerIDs.isEmpty) return []; | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |         final loadStr = await bind.mainLoadRecentPeersForAb( | 
					
						
							|  |  |  |             filter: jsonEncode(filteredPeerIDs)); | 
					
						
							|  |  |  |         if (loadStr.isEmpty) { | 
					
						
							|  |  |  |           return []; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         List<dynamic> mapPeers = jsonDecode(loadStr); | 
					
						
							|  |  |  |         List<Peer> recents = List.empty(growable: true); | 
					
						
							|  |  |  |         for (var m in mapPeers) { | 
					
						
							|  |  |  |           if (m is Map<String, dynamic>) { | 
					
						
							|  |  |  |             recents.add(Peer.fromJson(m)); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return recents; | 
					
						
							|  |  |  |       } catch (e) { | 
					
						
							|  |  |  |         debugPrint('getRecentPeers:$e'); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return []; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       if (!shouldSyncAb()) return; | 
					
						
							|  |  |  |       final recents = await getRecentPeers(); | 
					
						
							|  |  |  |       if (recents.isEmpty) return; | 
					
						
							| 
									
										
										
										
											2023-08-16 08:59:50 +08:00
										 |  |  |       bool uiChanged = false; | 
					
						
							| 
									
										
										
										
											2023-08-16 22:43:17 +08:00
										 |  |  |       bool needSync = false; | 
					
						
							| 
									
										
										
										
											2023-08-16 08:59:50 +08:00
										 |  |  |       for (var i = 0; i < recents.length; i++) { | 
					
						
							|  |  |  |         var r = recents[i]; | 
					
						
							|  |  |  |         var index = peers.indexWhere((e) => e.id == r.id); | 
					
						
							|  |  |  |         if (index < 0) { | 
					
						
							| 
									
										
										
										
											2023-08-16 13:30:38 +08:00
										 |  |  |           if (!isFull(false)) { | 
					
						
							|  |  |  |             peers.add(r); | 
					
						
							|  |  |  |             uiChanged = true; | 
					
						
							| 
									
										
										
										
											2023-08-16 22:43:17 +08:00
										 |  |  |             needSync = true; | 
					
						
							| 
									
										
										
										
											2023-08-16 13:30:38 +08:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-08-16 08:59:50 +08:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2023-08-16 22:43:17 +08:00
										 |  |  |           Peer old = Peer.copy(peers[index]); | 
					
						
							| 
									
										
										
										
											2023-08-17 21:17:31 +08:00
										 |  |  |           merge(r, peers[index]); | 
					
						
							| 
									
										
										
										
											2023-08-16 22:43:17 +08:00
										 |  |  |           if (!peerSyncEqual(peers[index], old)) { | 
					
						
							|  |  |  |             needSync = true; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-08-17 21:17:31 +08:00
										 |  |  |           if (!old.equal(peers[index])) { | 
					
						
							|  |  |  |             uiChanged = true; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       // Be careful with loop calls
 | 
					
						
							| 
									
										
										
										
											2023-08-16 22:43:17 +08:00
										 |  |  |       if (needSync && push) { | 
					
						
							| 
									
										
										
										
											2023-08-16 14:35:29 +08:00
										 |  |  |         pushAb(toastIfSucc: false, toastIfFail: false); | 
					
						
							| 
									
										
										
										
											2023-08-16 08:59:50 +08:00
										 |  |  |       } else if (uiChanged) { | 
					
						
							|  |  |  |         peers.refresh(); | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       debugPrint('syncFromRecent:$e'); | 
					
						
							| 
									
										
										
										
											2022-11-28 18:16:29 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-11 15:27:50 +08:00
										 |  |  |   _saveCache() { | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |       var m = _serialize(); | 
					
						
							|  |  |  |       m.addAll(<String, dynamic>{ | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |         "access_token": bind.mainGetLocalOption(key: 'access_token'), | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2023-08-02 22:25:54 +08:00
										 |  |  |       bind.mainSaveAb(json: jsonEncode(m)); | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       debugPrint('ab save:$e'); | 
					
						
							| 
									
										
										
										
											2022-07-29 12:03:24 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-08-11 15:27:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-24 17:56:35 +08:00
										 |  |  |   Future<void> loadCache() async { | 
					
						
							| 
									
										
										
										
											2023-08-11 15:27:50 +08:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2023-09-14 10:17:03 +08:00
										 |  |  |       if (_cacheLoadOnceFlag || abLoading.value || initialized) return; | 
					
						
							| 
									
										
										
										
											2023-08-13 14:46:04 +08:00
										 |  |  |       _cacheLoadOnceFlag = true; | 
					
						
							| 
									
										
										
										
											2023-08-11 15:27:50 +08:00
										 |  |  |       final access_token = bind.mainGetLocalOption(key: 'access_token'); | 
					
						
							|  |  |  |       if (access_token.isEmpty) return; | 
					
						
							|  |  |  |       final cache = await bind.mainLoadAb(); | 
					
						
							| 
									
										
										
										
											2023-09-14 10:17:03 +08:00
										 |  |  |       if (abLoading.value) return; | 
					
						
							| 
									
										
										
										
											2023-08-11 15:27:50 +08:00
										 |  |  |       final data = jsonDecode(cache); | 
					
						
							|  |  |  |       if (data == null || data['access_token'] != access_token) return; | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |       _deserialize(data); | 
					
						
							| 
									
										
										
										
											2023-08-11 15:27:50 +08:00
										 |  |  |     } catch (e) { | 
					
						
							|  |  |  |       debugPrint("load ab cache: $e"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-08-15 12:09:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |   Map<String, dynamic> _jsonDecodeResp(String body, int statusCode) { | 
					
						
							| 
									
										
										
										
											2023-08-15 12:09:33 +08:00
										 |  |  |     try { | 
					
						
							|  |  |  |       Map<String, dynamic> json = jsonDecode(body); | 
					
						
							|  |  |  |       return json; | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       final err = body.isNotEmpty && body.length < 128 ? body : e.toString(); | 
					
						
							|  |  |  |       if (statusCode != 200) { | 
					
						
							|  |  |  |         throw 'HTTP $statusCode, $err'; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       throw err; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-08-17 18:21:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-22 19:07:01 +08:00
										 |  |  |   Map<String, dynamic> _serialize() { | 
					
						
							|  |  |  |     final peersJsonData = peers.map((e) => e.toAbUploadJson()).toList(); | 
					
						
							|  |  |  |     final tagColorJsonData = jsonEncode(tagColors); | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       "tags": tags, | 
					
						
							|  |  |  |       "peers": peersJsonData, | 
					
						
							|  |  |  |       "tag_colors": tagColorJsonData | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _deserialize(dynamic data) { | 
					
						
							|  |  |  |     if (data == null) return; | 
					
						
							|  |  |  |     final oldOnlineIDs = peers.where((e) => e.online).map((e) => e.id).toList(); | 
					
						
							|  |  |  |     tags.clear(); | 
					
						
							|  |  |  |     tagColors.clear(); | 
					
						
							|  |  |  |     peers.clear(); | 
					
						
							|  |  |  |     if (data['tags'] is List) { | 
					
						
							|  |  |  |       tags.value = data['tags']; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (data['peers'] is List) { | 
					
						
							|  |  |  |       for (final peer in data['peers']) { | 
					
						
							|  |  |  |         peers.add(Peer.fromJson(peer)); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (isFull(false)) { | 
					
						
							|  |  |  |       peers.removeRange(licensedDevices, peers.length); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // restore online
 | 
					
						
							|  |  |  |     peers | 
					
						
							|  |  |  |         .where((e) => oldOnlineIDs.contains(e.id)) | 
					
						
							|  |  |  |         .map((e) => e.online = true) | 
					
						
							|  |  |  |         .toList(); | 
					
						
							|  |  |  |     if (data['tag_colors'] is String) { | 
					
						
							|  |  |  |       Map<String, dynamic> map = jsonDecode(data['tag_colors']); | 
					
						
							|  |  |  |       tagColors.value = Map<String, int>.from(map); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // add color to tag
 | 
					
						
							|  |  |  |     final tagsWithoutColor = | 
					
						
							|  |  |  |         tags.toList().where((e) => !tagColors.containsKey(e)).toList(); | 
					
						
							|  |  |  |     for (var t in tagsWithoutColor) { | 
					
						
							|  |  |  |       tagColors[t] = str2color2(t, existing: tagColors.values.toList()).value; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-17 18:21:37 +08:00
										 |  |  |   reSyncToast(Future<bool> future) { | 
					
						
							|  |  |  |     if (!shouldSyncAb()) return; | 
					
						
							|  |  |  |     Future.delayed(Duration.zero, () async { | 
					
						
							|  |  |  |       final succ = await future; | 
					
						
							|  |  |  |       if (succ) { | 
					
						
							|  |  |  |         await Future.delayed(Duration(seconds: 2)); // success msg
 | 
					
						
							|  |  |  |         BotToast.showText( | 
					
						
							|  |  |  |             contentColor: Colors.lightBlue, | 
					
						
							|  |  |  |             text: translate('synced_peer_readded_tip')); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-09-14 10:17:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   reset() async { | 
					
						
							|  |  |  |     pullError.value = ''; | 
					
						
							|  |  |  |     pushError.value = ''; | 
					
						
							|  |  |  |     tags.clear(); | 
					
						
							|  |  |  |     peers.clear(); | 
					
						
							|  |  |  |     await bind.mainClearAb(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-09-21 16:34:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   _mergePeerFromGroup(Peer p) { | 
					
						
							|  |  |  |     final g = gFFI.groupModel.peers.firstWhereOrNull((e) => p.id == e.id); | 
					
						
							|  |  |  |     if (g == null) return; | 
					
						
							|  |  |  |     if (p.username.isEmpty) { | 
					
						
							|  |  |  |       p.username = g.username; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (p.hostname.isEmpty) { | 
					
						
							|  |  |  |       p.hostname = g.hostname; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (p.platform.isEmpty) { | 
					
						
							|  |  |  |       p.platform = g.platform; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-07-25 16:26:51 +08:00
										 |  |  | } |