mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-14 01:11:28 +03:00
refact: optimize, ID search peers (#10853)
* refact: optimize, preload peers Signed-off-by: fufesou <linlong1266@gmail.com> * Update dialogs.dart --------- Signed-off-by: fufesou <linlong1266@gmail.com> Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
@@ -822,7 +822,11 @@ class OverlayDialogManager {
|
|||||||
|
|
||||||
close([res]) {
|
close([res]) {
|
||||||
_dialogs.remove(dialogTag);
|
_dialogs.remove(dialogTag);
|
||||||
dialog.complete(res);
|
try {
|
||||||
|
dialog.complete(res);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("Dialog complete catch error: $e");
|
||||||
|
}
|
||||||
BackButtonInterceptor.removeByName(dialogTag);
|
BackButtonInterceptor.removeByName(dialogTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/common/formatter/id_formatter.dart';
|
import 'package:flutter_hbb/common/formatter/id_formatter.dart';
|
||||||
import '../../../models/platform_model.dart';
|
import '../../../models/platform_model.dart';
|
||||||
@@ -8,87 +7,56 @@ import 'package:flutter_hbb/common/widgets/peer_card.dart';
|
|||||||
|
|
||||||
class AllPeersLoader {
|
class AllPeersLoader {
|
||||||
List<Peer> peers = [];
|
List<Peer> peers = [];
|
||||||
bool hasMoreRecentPeers = false;
|
|
||||||
|
|
||||||
bool isPeersLoading = false;
|
bool isPeersLoading = false;
|
||||||
bool _isPartialPeersLoaded = false;
|
bool isPeersLoaded = false;
|
||||||
bool _isPeersLoaded = false;
|
|
||||||
|
final String _listenerKey = 'AllPeersLoader';
|
||||||
|
|
||||||
|
late void Function(VoidCallback) setState;
|
||||||
|
|
||||||
AllPeersLoader();
|
AllPeersLoader();
|
||||||
|
|
||||||
bool get isLoaded => _isPartialPeersLoaded || _isPeersLoaded;
|
void init(void Function(VoidCallback) setState) {
|
||||||
|
this.setState = setState;
|
||||||
void reset() {
|
gFFI.recentPeersModel.addListener(_mergeAllPeers);
|
||||||
peers.clear();
|
gFFI.lanPeersModel.addListener(_mergeAllPeers);
|
||||||
hasMoreRecentPeers = false;
|
gFFI.abModel.addPeerUpdateListener(_listenerKey, _mergeAllPeers);
|
||||||
_isPartialPeersLoaded = false;
|
gFFI.groupModel.addPeerUpdateListener(_listenerKey, _mergeAllPeers);
|
||||||
_isPeersLoaded = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getAllPeers(void Function(VoidCallback) setState) async {
|
void clear() {
|
||||||
if (isPeersLoading) {
|
gFFI.recentPeersModel.removeListener(_mergeAllPeers);
|
||||||
|
gFFI.lanPeersModel.removeListener(_mergeAllPeers);
|
||||||
|
gFFI.abModel.removePeerUpdateListener(_listenerKey);
|
||||||
|
gFFI.groupModel.removePeerUpdateListener(_listenerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getAllPeers() async {
|
||||||
|
if (isPeersLoaded || isPeersLoading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
reset();
|
|
||||||
isPeersLoading = true;
|
isPeersLoading = true;
|
||||||
|
|
||||||
|
if (gFFI.recentPeersModel.peers.isEmpty) {
|
||||||
|
bind.mainLoadRecentPeers();
|
||||||
|
}
|
||||||
|
if (gFFI.lanPeersModel.peers.isEmpty) {
|
||||||
|
bind.mainLoadLanPeers();
|
||||||
|
}
|
||||||
|
// No need to care about peers from abModel, and group model.
|
||||||
|
// Because they will pull data in `refreshCurrentUser()` on startup.
|
||||||
|
|
||||||
final startTime = DateTime.now();
|
final startTime = DateTime.now();
|
||||||
await _getAllPeers(false);
|
_mergeAllPeers();
|
||||||
if (!hasMoreRecentPeers) {
|
final diffTime = DateTime.now().difference(startTime).inMilliseconds;
|
||||||
final diffTime = DateTime.now().difference(startTime).inMilliseconds;
|
if (diffTime < 100) {
|
||||||
if (diffTime < 100) {
|
await Future.delayed(Duration(milliseconds: diffTime));
|
||||||
await Future.delayed(Duration(milliseconds: diffTime));
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
isPeersLoading = false;
|
|
||||||
_isPeersLoaded = true;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setState(() {
|
|
||||||
_isPartialPeersLoaded = true;
|
|
||||||
});
|
|
||||||
await _getAllPeers(true);
|
|
||||||
setState(() {
|
|
||||||
isPeersLoading = false;
|
|
||||||
_isPeersLoaded = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _getAllPeers(bool getAllRecentPeers) async {
|
void _mergeAllPeers() {
|
||||||
Map<String, dynamic> recentPeers =
|
|
||||||
jsonDecode(await bind.mainGetRecentPeers(getAll: getAllRecentPeers));
|
|
||||||
Map<String, dynamic> lanPeers = jsonDecode(bind.mainLoadLanPeersSync());
|
|
||||||
Map<String, dynamic> combinedPeers = {};
|
Map<String, dynamic> combinedPeers = {};
|
||||||
|
|
||||||
void mergePeers(Map<String, dynamic> peers) {
|
|
||||||
if (peers.containsKey("peers")) {
|
|
||||||
dynamic peerData = peers["peers"];
|
|
||||||
|
|
||||||
if (peerData is String) {
|
|
||||||
try {
|
|
||||||
peerData = jsonDecode(peerData);
|
|
||||||
} catch (e) {
|
|
||||||
print("Error decoding peers: $e");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (peerData is List) {
|
|
||||||
for (var peer in peerData) {
|
|
||||||
if (peer is Map && peer.containsKey("id")) {
|
|
||||||
String id = peer["id"];
|
|
||||||
if (!combinedPeers.containsKey(id)) {
|
|
||||||
combinedPeers[id] = peer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mergePeers(recentPeers);
|
|
||||||
mergePeers(lanPeers);
|
|
||||||
for (var p in gFFI.abModel.allPeers()) {
|
for (var p in gFFI.abModel.allPeers()) {
|
||||||
if (!combinedPeers.containsKey(p.id)) {
|
if (!combinedPeers.containsKey(p.id)) {
|
||||||
combinedPeers[p.id] = p.toJson();
|
combinedPeers[p.id] = p.toJson();
|
||||||
@@ -101,27 +69,36 @@ class AllPeersLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Peer> parsedPeers = [];
|
List<Peer> parsedPeers = [];
|
||||||
|
|
||||||
for (var peer in combinedPeers.values) {
|
for (var peer in combinedPeers.values) {
|
||||||
parsedPeers.add(Peer.fromJson(peer));
|
parsedPeers.add(Peer.fromJson(peer));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
Set<String> peerIds = combinedPeers.keys.toSet();
|
||||||
final List<dynamic> moreRecentPeerIds =
|
for (final peer in gFFI.lanPeersModel.peers) {
|
||||||
jsonDecode(recentPeers["ids"] ?? '[]');
|
if (!peerIds.contains(peer.id)) {
|
||||||
hasMoreRecentPeers = false;
|
parsedPeers.add(peer);
|
||||||
for (final id in moreRecentPeerIds) {
|
peerIds.add(peer.id);
|
||||||
final sid = id.toString();
|
}
|
||||||
if (!parsedPeers.any((element) => element.id == sid)) {
|
}
|
||||||
parsedPeers.add(Peer.fromJson({'id': sid}));
|
|
||||||
hasMoreRecentPeers = true;
|
for (final peer in gFFI.recentPeersModel.peers) {
|
||||||
}
|
if (!peerIds.contains(peer.id)) {
|
||||||
|
parsedPeers.add(peer);
|
||||||
|
peerIds.add(peer.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final id in gFFI.recentPeersModel.restPeerIds) {
|
||||||
|
if (!peerIds.contains(id)) {
|
||||||
|
parsedPeers.add(Peer.fromJson({'id': id}));
|
||||||
|
peerIds.add(id);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
debugPrint("Error parsing more peer ids: $e");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
peers = parsedPeers;
|
peers = parsedPeers;
|
||||||
|
setState(() {
|
||||||
|
isPeersLoading = false;
|
||||||
|
isPeersLoaded = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -716,18 +716,18 @@ abstract class BasePeerCard extends StatelessWidget {
|
|||||||
switch (tab) {
|
switch (tab) {
|
||||||
case PeerTabIndex.recent:
|
case PeerTabIndex.recent:
|
||||||
await bind.mainRemovePeer(id: id);
|
await bind.mainRemovePeer(id: id);
|
||||||
await bind.mainLoadRecentPeers();
|
bind.mainLoadRecentPeers();
|
||||||
break;
|
break;
|
||||||
case PeerTabIndex.fav:
|
case PeerTabIndex.fav:
|
||||||
final favs = (await bind.mainGetFav()).toList();
|
final favs = (await bind.mainGetFav()).toList();
|
||||||
if (favs.remove(id)) {
|
if (favs.remove(id)) {
|
||||||
await bind.mainStoreFav(favs: favs);
|
await bind.mainStoreFav(favs: favs);
|
||||||
await bind.mainLoadFavPeers();
|
bind.mainLoadFavPeers();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PeerTabIndex.lan:
|
case PeerTabIndex.lan:
|
||||||
await bind.mainRemoveDiscovered(id: id);
|
await bind.mainRemoveDiscovered(id: id);
|
||||||
await bind.mainLoadLanPeers();
|
bind.mainLoadLanPeers();
|
||||||
break;
|
break;
|
||||||
case PeerTabIndex.ab:
|
case PeerTabIndex.ab:
|
||||||
await gFFI.abModel.deletePeers([id]);
|
await gFFI.abModel.deletePeers([id]);
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
for (var p in peers) {
|
for (var p in peers) {
|
||||||
await bind.mainRemovePeer(id: p.id);
|
await bind.mainRemovePeer(id: p.id);
|
||||||
}
|
}
|
||||||
await bind.mainLoadRecentPeers();
|
bind.mainLoadRecentPeers();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
final favs = (await bind.mainGetFav()).toList();
|
final favs = (await bind.mainGetFav()).toList();
|
||||||
@@ -412,13 +412,13 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
favs.remove(p.id);
|
favs.remove(p.id);
|
||||||
}).toList();
|
}).toList();
|
||||||
await bind.mainStoreFav(favs: favs);
|
await bind.mainStoreFav(favs: favs);
|
||||||
await bind.mainLoadFavPeers();
|
bind.mainLoadFavPeers();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
for (var p in peers) {
|
for (var p in peers) {
|
||||||
await bind.mainRemoveDiscovered(id: p.id);
|
await bind.mainRemoveDiscovered(id: p.id);
|
||||||
}
|
}
|
||||||
await bind.mainLoadLanPeers();
|
bind.mainLoadLanPeers();
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
await gFFI.abModel.deletePeers(peers.map((p) => p.id).toList());
|
await gFFI.abModel.deletePeers(peers.map((p) => p.id).toList());
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
|
|
||||||
bool isWindowMinimized = false;
|
bool isWindowMinimized = false;
|
||||||
|
|
||||||
AllPeersLoader allPeersLoader = AllPeersLoader();
|
final AllPeersLoader _allPeersLoader = AllPeersLoader();
|
||||||
|
|
||||||
// https://github.com/flutter/flutter/issues/157244
|
// https://github.com/flutter/flutter/issues/157244
|
||||||
Iterable<Peer> _autocompleteOpts = [];
|
Iterable<Peer> _autocompleteOpts = [];
|
||||||
@@ -213,6 +213,7 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_allPeersLoader.init(setState);
|
||||||
_idFocusNode.addListener(onFocusChanged);
|
_idFocusNode.addListener(onFocusChanged);
|
||||||
if (_idController.text.isEmpty) {
|
if (_idController.text.isEmpty) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
@@ -232,6 +233,7 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
_idController.dispose();
|
_idController.dispose();
|
||||||
windowManager.removeListener(this);
|
windowManager.removeListener(this);
|
||||||
|
_allPeersLoader.clear();
|
||||||
_idFocusNode.removeListener(onFocusChanged);
|
_idFocusNode.removeListener(onFocusChanged);
|
||||||
_idFocusNode.dispose();
|
_idFocusNode.dispose();
|
||||||
_idEditingController.dispose();
|
_idEditingController.dispose();
|
||||||
@@ -280,8 +282,8 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
|
|
||||||
void onFocusChanged() {
|
void onFocusChanged() {
|
||||||
_idInputFocused.value = _idFocusNode.hasFocus;
|
_idInputFocused.value = _idFocusNode.hasFocus;
|
||||||
if (_idFocusNode.hasFocus && !allPeersLoader.isPeersLoading) {
|
if (_idFocusNode.hasFocus && !_allPeersLoader.isPeersLoading) {
|
||||||
allPeersLoader.getAllPeers(setState);
|
_allPeersLoader.getAllPeers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,8 +338,8 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
optionsBuilder: (TextEditingValue textEditingValue) {
|
optionsBuilder: (TextEditingValue textEditingValue) {
|
||||||
if (textEditingValue.text == '') {
|
if (textEditingValue.text == '') {
|
||||||
_autocompleteOpts = const Iterable<Peer>.empty();
|
_autocompleteOpts = const Iterable<Peer>.empty();
|
||||||
} else if (allPeersLoader.peers.isEmpty &&
|
} else if (_allPeersLoader.peers.isEmpty &&
|
||||||
!allPeersLoader.isLoaded) {
|
!_allPeersLoader.isPeersLoaded) {
|
||||||
Peer emptyPeer = Peer(
|
Peer emptyPeer = Peer(
|
||||||
id: '',
|
id: '',
|
||||||
username: '',
|
username: '',
|
||||||
@@ -364,7 +366,7 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
String textToFind = textEditingValue.text.toLowerCase();
|
String textToFind = textEditingValue.text.toLowerCase();
|
||||||
_autocompleteOpts = allPeersLoader.peers
|
_autocompleteOpts = _allPeersLoader.peers
|
||||||
.where((peer) =>
|
.where((peer) =>
|
||||||
peer.id.toLowerCase().contains(textToFind) ||
|
peer.id.toLowerCase().contains(textToFind) ||
|
||||||
peer.username
|
peer.username
|
||||||
@@ -471,8 +473,8 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
maxHeight: maxHeight,
|
maxHeight: maxHeight,
|
||||||
maxWidth: 319,
|
maxWidth: 319,
|
||||||
),
|
),
|
||||||
child: allPeersLoader.peers.isEmpty &&
|
child: _allPeersLoader.peers.isEmpty &&
|
||||||
!allPeersLoader.isLoaded
|
!_allPeersLoader.isPeersLoaded
|
||||||
? Container(
|
? Container(
|
||||||
height: 80,
|
height: 80,
|
||||||
child: Center(
|
child: Center(
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
final FocusNode _idFocusNode = FocusNode();
|
final FocusNode _idFocusNode = FocusNode();
|
||||||
final TextEditingController _idEditingController = TextEditingController();
|
final TextEditingController _idEditingController = TextEditingController();
|
||||||
|
|
||||||
AllPeersLoader allPeersLoader = AllPeersLoader();
|
final AllPeersLoader _allPeersLoader = AllPeersLoader();
|
||||||
|
|
||||||
StreamSubscription? _uniLinksSubscription;
|
StreamSubscription? _uniLinksSubscription;
|
||||||
|
|
||||||
@@ -62,6 +62,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_allPeersLoader.init(setState);
|
||||||
_idFocusNode.addListener(onFocusChanged);
|
_idFocusNode.addListener(onFocusChanged);
|
||||||
if (_idController.text.isEmpty) {
|
if (_idController.text.isEmpty) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
@@ -103,8 +104,8 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
|
|
||||||
void onFocusChanged() {
|
void onFocusChanged() {
|
||||||
_idEmpty.value = _idEditingController.text.isEmpty;
|
_idEmpty.value = _idEditingController.text.isEmpty;
|
||||||
if (_idFocusNode.hasFocus && !allPeersLoader.isPeersLoading) {
|
if (_idFocusNode.hasFocus && !_allPeersLoader.isPeersLoading) {
|
||||||
allPeersLoader.getAllPeers(setState);
|
_allPeersLoader.getAllPeers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,8 +158,8 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
optionsBuilder: (TextEditingValue textEditingValue) {
|
optionsBuilder: (TextEditingValue textEditingValue) {
|
||||||
if (textEditingValue.text == '') {
|
if (textEditingValue.text == '') {
|
||||||
_autocompleteOpts = const Iterable<Peer>.empty();
|
_autocompleteOpts = const Iterable<Peer>.empty();
|
||||||
} else if (allPeersLoader.peers.isEmpty &&
|
} else if (_allPeersLoader.peers.isEmpty &&
|
||||||
!allPeersLoader.isLoaded) {
|
!_allPeersLoader.isPeersLoaded) {
|
||||||
Peer emptyPeer = Peer(
|
Peer emptyPeer = Peer(
|
||||||
id: '',
|
id: '',
|
||||||
username: '',
|
username: '',
|
||||||
@@ -186,7 +187,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
}
|
}
|
||||||
String textToFind = textEditingValue.text.toLowerCase();
|
String textToFind = textEditingValue.text.toLowerCase();
|
||||||
|
|
||||||
_autocompleteOpts = allPeersLoader.peers
|
_autocompleteOpts = _allPeersLoader.peers
|
||||||
.where((peer) =>
|
.where((peer) =>
|
||||||
peer.id.toLowerCase().contains(textToFind) ||
|
peer.id.toLowerCase().contains(textToFind) ||
|
||||||
peer.username
|
peer.username
|
||||||
@@ -296,8 +297,9 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
maxHeight: maxHeight,
|
maxHeight: maxHeight,
|
||||||
maxWidth: 320,
|
maxWidth: 320,
|
||||||
),
|
),
|
||||||
child: allPeersLoader.peers.isEmpty &&
|
child: _allPeersLoader
|
||||||
!allPeersLoader.isLoaded
|
.peers.isEmpty &&
|
||||||
|
!_allPeersLoader.isPeersLoaded
|
||||||
? Container(
|
? Container(
|
||||||
height: 80,
|
height: 80,
|
||||||
child: Center(
|
child: Center(
|
||||||
@@ -361,6 +363,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
_uniLinksSubscription?.cancel();
|
_uniLinksSubscription?.cancel();
|
||||||
_idController.dispose();
|
_idController.dispose();
|
||||||
_idFocusNode.removeListener(onFocusChanged);
|
_idFocusNode.removeListener(onFocusChanged);
|
||||||
|
_allPeersLoader.clear();
|
||||||
_idFocusNode.dispose();
|
_idFocusNode.dispose();
|
||||||
_idEditingController.dispose();
|
_idEditingController.dispose();
|
||||||
if (Get.isRegistered<IDTextEditingController>()) {
|
if (Get.isRegistered<IDTextEditingController>()) {
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ class AbModel {
|
|||||||
String? _personalAbGuid;
|
String? _personalAbGuid;
|
||||||
RxBool legacyMode = false.obs;
|
RxBool legacyMode = false.obs;
|
||||||
|
|
||||||
|
// Only handles peers add/remove
|
||||||
|
final Map<String, VoidCallback> _peerIdUpdateListeners = {};
|
||||||
|
|
||||||
final sortTags = shouldSortTags().obs;
|
final sortTags = shouldSortTags().obs;
|
||||||
final filterByIntersection = filterAbTagByIntersection().obs;
|
final filterByIntersection = filterAbTagByIntersection().obs;
|
||||||
|
|
||||||
@@ -188,6 +191,7 @@ class AbModel {
|
|||||||
debugPrint("pull current Ab error: $e");
|
debugPrint("pull current Ab error: $e");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_callbackPeerUpdate();
|
||||||
if (listInitialized && current.initialized) {
|
if (listInitialized && current.initialized) {
|
||||||
_saveCache();
|
_saveCache();
|
||||||
}
|
}
|
||||||
@@ -419,6 +423,7 @@ class AbModel {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
_callbackPeerUpdate();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,6 +625,9 @@ class AbModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (abEntries.isNotEmpty) {
|
||||||
|
_callbackPeerUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,6 +750,20 @@ class AbModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _callbackPeerUpdate() {
|
||||||
|
for (var listener in _peerIdUpdateListeners.values) {
|
||||||
|
listener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPeerUpdateListener(String key, VoidCallback listener) {
|
||||||
|
_peerIdUpdateListeners[key] = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removePeerUpdateListener(String key) {
|
||||||
|
_peerIdUpdateListeners.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
// #endregion
|
// #endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ class GroupModel {
|
|||||||
var _cacheLoadOnceFlag = false;
|
var _cacheLoadOnceFlag = false;
|
||||||
var _statusCode = 200;
|
var _statusCode = 200;
|
||||||
|
|
||||||
|
final Map<String, VoidCallback> _peerIdUpdateListeners = {};
|
||||||
|
|
||||||
bool get emtpy => deviceGroups.isEmpty && users.isEmpty && peers.isEmpty;
|
bool get emtpy => deviceGroups.isEmpty && users.isEmpty && peers.isEmpty;
|
||||||
|
|
||||||
late final Peers peersModel;
|
late final Peers peersModel;
|
||||||
@@ -92,6 +94,7 @@ class GroupModel {
|
|||||||
.map((e) => e.online = true)
|
.map((e) => e.online = true)
|
||||||
.toList();
|
.toList();
|
||||||
groupLoadError.value = '';
|
groupLoadError.value = '';
|
||||||
|
_callbackPeerUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _getDeviceGroups(
|
Future<bool> _getDeviceGroups(
|
||||||
@@ -329,6 +332,7 @@ class GroupModel {
|
|||||||
for (final peer in data['peers']) {
|
for (final peer in data['peers']) {
|
||||||
peers.add(Peer.fromJson(peer));
|
peers.add(Peer.fromJson(peer));
|
||||||
}
|
}
|
||||||
|
_callbackPeerUpdate();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("load group cache: $e");
|
debugPrint("load group cache: $e");
|
||||||
@@ -343,4 +347,18 @@ class GroupModel {
|
|||||||
selectedAccessibleItemName.value = '';
|
selectedAccessibleItemName.value = '';
|
||||||
await bind.mainClearGroup();
|
await bind.mainClearGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _callbackPeerUpdate() {
|
||||||
|
for (var listener in _peerIdUpdateListeners.values) {
|
||||||
|
listener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPeerUpdateListener(String key, VoidCallback listener) {
|
||||||
|
_peerIdUpdateListeners[key] = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removePeerUpdateListener(String key) {
|
||||||
|
_peerIdUpdateListeners.remove(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,6 +165,11 @@ class Peers extends ChangeNotifier {
|
|||||||
final String name;
|
final String name;
|
||||||
final String loadEvent;
|
final String loadEvent;
|
||||||
List<Peer> peers = List.empty(growable: true);
|
List<Peer> peers = List.empty(growable: true);
|
||||||
|
// Part of the peers that are not in the rest peers list.
|
||||||
|
// When there're too many peers, we may want to load the front 100 peers first,
|
||||||
|
// so we can see peers in UI quickly. `restPeerIds` is the rest peers' ids.
|
||||||
|
// And then load all peers later.
|
||||||
|
List<String> restPeerIds = List.empty(growable: true);
|
||||||
final GetInitPeers? getInitPeers;
|
final GetInitPeers? getInitPeers;
|
||||||
UpdateEvent event = UpdateEvent.load;
|
UpdateEvent event = UpdateEvent.load;
|
||||||
static const _cbQueryOnlines = 'callback_query_onlines';
|
static const _cbQueryOnlines = 'callback_query_onlines';
|
||||||
@@ -238,6 +243,12 @@ class Peers extends ChangeNotifier {
|
|||||||
} else {
|
} else {
|
||||||
peers = _decodePeers(evt['peers']);
|
peers = _decodePeers(evt['peers']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restPeerIds = [];
|
||||||
|
if (evt['ids'] != null) {
|
||||||
|
restPeerIds = (evt['ids'] as String).split(',');
|
||||||
|
}
|
||||||
|
|
||||||
for (var peer in peers) {
|
for (var peer in peers) {
|
||||||
final state = onlineStates[peer.id];
|
final state = onlineStates[peer.id];
|
||||||
peer.online = state != null && state != false;
|
peer.online = state != null && state != false;
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
|
||||||
|
|
||||||
void showPeerSelectionDialog(
|
void showPeerSelectionDialog(
|
||||||
{bool singleSelection = false,
|
{bool singleSelection = false,
|
||||||
required Function(List<String>) onPeersCallback}) async {
|
required Function(List<String>) onPeersCallback}) async {
|
||||||
final peers = await bind.mainGetRecentPeers(getAll: true);
|
// load recent peers, we can directly use the peers in `gFFI.recentPeersModel`.
|
||||||
|
// The plugin is not used for now, so just left it empty here.
|
||||||
|
final peers = '';
|
||||||
if (peers.isEmpty) {
|
if (peers.isEmpty) {
|
||||||
debugPrint("load recent peers failed.");
|
// debugPrint("load recent peers failed.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Submodule libs/hbb_common updated: f94753bddb...16900b9b06
@@ -1127,84 +1127,48 @@ pub fn main_load_recent_peers() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let push_to_flutter = |peers| {
|
let push_to_flutter = |peers, ids| {
|
||||||
let data = HashMap::from([("name", "load_recent_peers".to_owned()), ("peers", peers)]);
|
let mut data =
|
||||||
|
HashMap::from([("name", "load_recent_peers".to_owned()), ("peers", peers)]);
|
||||||
|
if let Some(ids) = ids {
|
||||||
|
data.insert("ids", ids);
|
||||||
|
}
|
||||||
let _res = flutter::push_global_event(
|
let _res = flutter::push_global_event(
|
||||||
flutter::APP_TYPE_MAIN,
|
flutter::APP_TYPE_MAIN,
|
||||||
serde_json::ser::to_string(&data).unwrap_or("".to_owned()),
|
serde_json::ser::to_string(&data).unwrap_or("".to_owned()),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let load_two_times =
|
let load_two_times = vec_id_modified_time_path.len() > PeerConfig::BATCH_LOADING_COUNT
|
||||||
vec_id_modified_time_path.len() > PeerConfig::BATCH_LOADING_COUNT && cfg!(target_os = "windows");
|
&& cfg!(target_os = "windows");
|
||||||
let mut all_peers = vec![];
|
let mut all_peers = vec![];
|
||||||
if load_two_times {
|
if load_two_times {
|
||||||
let next_from = load_recent_peers(&vec_id_modified_time_path, false, &mut all_peers, 0);
|
let next_from = load_recent_peers(&vec_id_modified_time_path, false, &mut all_peers, 0);
|
||||||
push_to_flutter(serde_json::ser::to_string(&all_peers).unwrap_or("".to_owned()));
|
let rest_ids = if next_from < vec_id_modified_time_path.len() {
|
||||||
|
Some(
|
||||||
|
vec_id_modified_time_path[next_from..]
|
||||||
|
.iter()
|
||||||
|
.map(|(id, _, _)| id.clone())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
push_to_flutter(
|
||||||
|
serde_json::ser::to_string(&all_peers).unwrap_or("".to_owned()),
|
||||||
|
rest_ids,
|
||||||
|
);
|
||||||
let _ = load_recent_peers(&vec_id_modified_time_path, true, &mut all_peers, next_from);
|
let _ = load_recent_peers(&vec_id_modified_time_path, true, &mut all_peers, next_from);
|
||||||
} else {
|
} else {
|
||||||
let _ = load_recent_peers(&vec_id_modified_time_path, true, &mut all_peers, 0);
|
let _ = load_recent_peers(&vec_id_modified_time_path, true, &mut all_peers, 0);
|
||||||
}
|
}
|
||||||
push_to_flutter(serde_json::ser::to_string(&all_peers).unwrap_or("".to_owned()));
|
// Don't check if `all_peers` is empty, because we need this message to update the state in the flutter side.
|
||||||
}
|
push_to_flutter(
|
||||||
}
|
serde_json::ser::to_string(&all_peers).unwrap_or("".to_owned()),
|
||||||
|
None,
|
||||||
fn get_partial_recent_peers(
|
|
||||||
vec_id_modified_time_path: Vec<(String, SystemTime, std::path::PathBuf)>,
|
|
||||||
count: usize,
|
|
||||||
) -> String {
|
|
||||||
let (peers, next_from) = PeerConfig::batch_peers(
|
|
||||||
&vec_id_modified_time_path,
|
|
||||||
0,
|
|
||||||
Some(count.min(vec_id_modified_time_path.len())),
|
|
||||||
);
|
|
||||||
let peer_maps: Vec<_> = peers
|
|
||||||
.into_iter()
|
|
||||||
.map(|(id, _, p)| peer_to_map(id, p))
|
|
||||||
.collect();
|
|
||||||
let mut data = HashMap::from([(
|
|
||||||
"peers",
|
|
||||||
serde_json::ser::to_string(&peer_maps).unwrap_or("[]".to_owned()),
|
|
||||||
)]);
|
|
||||||
if next_from < vec_id_modified_time_path.len() {
|
|
||||||
let ids: Vec<_> = vec_id_modified_time_path[next_from..]
|
|
||||||
.iter()
|
|
||||||
.map(|(id, _, _)| id.clone())
|
|
||||||
.collect();
|
|
||||||
data.insert(
|
|
||||||
"ids",
|
|
||||||
serde_json::ser::to_string(&ids).unwrap_or("[]".to_owned()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return serde_json::ser::to_string(&data).unwrap_or("".to_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main_get_recent_peers(get_all: bool) -> String {
|
|
||||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
|
||||||
let vec_id_modified_time_path = PeerConfig::get_vec_id_modified_time_path(&None);
|
|
||||||
|
|
||||||
let load_two_times = !get_all
|
|
||||||
&& vec_id_modified_time_path.len() > PeerConfig::BATCH_LOADING_COUNT
|
|
||||||
&& cfg!(target_os = "windows");
|
|
||||||
let load_count = if load_two_times {
|
|
||||||
PeerConfig::BATCH_LOADING_COUNT
|
|
||||||
} else {
|
|
||||||
vec_id_modified_time_path.len()
|
|
||||||
};
|
|
||||||
return get_partial_recent_peers(vec_id_modified_time_path, load_count);
|
|
||||||
}
|
|
||||||
"".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main_load_lan_peers_sync() -> SyncReturn<String> {
|
|
||||||
let data = HashMap::from([
|
|
||||||
("name", "load_lan_peers".to_owned()),
|
|
||||||
(
|
|
||||||
"peers",
|
|
||||||
serde_json::to_string(&get_lan_peers()).unwrap_or_default(),
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
return SyncReturn(serde_json::ser::to_string(&data).unwrap_or("".to_owned()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_load_recent_peers_for_ab(filter: String) -> String {
|
pub fn main_load_recent_peers_for_ab(filter: String) -> String {
|
||||||
|
|||||||
Reference in New Issue
Block a user