mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-15 19:41:29 +03:00
refact: optimize, loading recent peers (#10847)
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -6,56 +6,123 @@ import 'package:flutter_hbb/models/peer_model.dart';
|
|||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/peer_card.dart';
|
import 'package:flutter_hbb/common/widgets/peer_card.dart';
|
||||||
|
|
||||||
Future<List<Peer>> getAllPeers() async {
|
class AllPeersLoader {
|
||||||
Map<String, dynamic> recentPeers = jsonDecode(bind.mainLoadRecentPeersSync());
|
List<Peer> peers = [];
|
||||||
Map<String, dynamic> lanPeers = jsonDecode(bind.mainLoadLanPeersSync());
|
bool hasMoreRecentPeers = false;
|
||||||
Map<String, dynamic> combinedPeers = {};
|
|
||||||
|
|
||||||
void mergePeers(Map<String, dynamic> peers) {
|
bool isPeersLoading = false;
|
||||||
if (peers.containsKey("peers")) {
|
bool _isPartialPeersLoaded = false;
|
||||||
dynamic peerData = peers["peers"];
|
bool _isPeersLoaded = false;
|
||||||
|
|
||||||
if (peerData is String) {
|
AllPeersLoader();
|
||||||
try {
|
|
||||||
peerData = jsonDecode(peerData);
|
bool get isLoaded => _isPartialPeersLoaded || _isPeersLoaded;
|
||||||
} catch (e) {
|
|
||||||
print("Error decoding peers: $e");
|
void reset() {
|
||||||
return;
|
peers.clear();
|
||||||
}
|
hasMoreRecentPeers = false;
|
||||||
|
_isPartialPeersLoaded = false;
|
||||||
|
_isPeersLoaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getAllPeers(void Function(VoidCallback) setState) async {
|
||||||
|
if (isPeersLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reset();
|
||||||
|
isPeersLoading = true;
|
||||||
|
|
||||||
|
final startTime = DateTime.now();
|
||||||
|
await _getAllPeers(false);
|
||||||
|
if (!hasMoreRecentPeers) {
|
||||||
|
final diffTime = DateTime.now().difference(startTime).inMilliseconds;
|
||||||
|
if (diffTime < 100) {
|
||||||
|
await Future.delayed(Duration(milliseconds: diffTime));
|
||||||
}
|
}
|
||||||
|
setState(() {
|
||||||
|
isPeersLoading = false;
|
||||||
|
_isPeersLoaded = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_isPartialPeersLoaded = true;
|
||||||
|
});
|
||||||
|
await _getAllPeers(true);
|
||||||
|
setState(() {
|
||||||
|
isPeersLoading = false;
|
||||||
|
_isPeersLoaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (peerData is List) {
|
Future<void> _getAllPeers(bool getAllRecentPeers) async {
|
||||||
for (var peer in peerData) {
|
Map<String, dynamic> recentPeers =
|
||||||
if (peer is Map && peer.containsKey("id")) {
|
jsonDecode(await bind.mainGetRecentPeers(getAll: getAllRecentPeers));
|
||||||
String id = peer["id"];
|
Map<String, dynamic> lanPeers = jsonDecode(bind.mainLoadLanPeersSync());
|
||||||
if (!combinedPeers.containsKey(id)) {
|
Map<String, dynamic> combinedPeers = {};
|
||||||
combinedPeers[id] = peer;
|
|
||||||
|
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(recentPeers);
|
||||||
mergePeers(lanPeers);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
for (var p in gFFI.groupModel.peers.map((e) => Peer.copy(e)).toList()) {
|
||||||
for (var p in gFFI.groupModel.peers.map((e) => Peer.copy(e)).toList()) {
|
if (!combinedPeers.containsKey(p.id)) {
|
||||||
if (!combinedPeers.containsKey(p.id)) {
|
combinedPeers[p.id] = p.toJson();
|
||||||
combinedPeers[p.id] = p.toJson();
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
|
final List<dynamic> moreRecentPeerIds =
|
||||||
|
jsonDecode(recentPeers["ids"] ?? '[]');
|
||||||
|
hasMoreRecentPeers = false;
|
||||||
|
for (final id in moreRecentPeerIds) {
|
||||||
|
final sid = id.toString();
|
||||||
|
if (!parsedPeers.any((element) => element.id == sid)) {
|
||||||
|
parsedPeers.add(Peer.fromJson({'id': sid}));
|
||||||
|
hasMoreRecentPeers = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("Error parsing more peer ids: $e");
|
||||||
|
}
|
||||||
|
|
||||||
|
peers = parsedPeers;
|
||||||
}
|
}
|
||||||
return parsedPeers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AutocompletePeerTile extends StatefulWidget {
|
class AutocompletePeerTile extends StatefulWidget {
|
||||||
|
|||||||
@@ -200,18 +200,20 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
final _idController = IDTextEditingController();
|
final _idController = IDTextEditingController();
|
||||||
|
|
||||||
final RxBool _idInputFocused = false.obs;
|
final RxBool _idInputFocused = false.obs;
|
||||||
|
final FocusNode _idFocusNode = FocusNode();
|
||||||
|
final TextEditingController _idEditingController = TextEditingController();
|
||||||
|
|
||||||
bool isWindowMinimized = false;
|
bool isWindowMinimized = false;
|
||||||
List<Peer> peers = [];
|
|
||||||
|
|
||||||
bool isPeersLoading = false;
|
AllPeersLoader allPeersLoader = AllPeersLoader();
|
||||||
bool isPeersLoaded = false;
|
|
||||||
// https://github.com/flutter/flutter/issues/157244
|
// https://github.com/flutter/flutter/issues/157244
|
||||||
Iterable<Peer> _autocompleteOpts = [];
|
Iterable<Peer> _autocompleteOpts = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_idFocusNode.addListener(onFocusChanged);
|
||||||
if (_idController.text.isEmpty) {
|
if (_idController.text.isEmpty) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
final lastRemoteId = await bind.mainGetLastRemoteId();
|
final lastRemoteId = await bind.mainGetLastRemoteId();
|
||||||
@@ -230,6 +232,9 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
_idController.dispose();
|
_idController.dispose();
|
||||||
windowManager.removeListener(this);
|
windowManager.removeListener(this);
|
||||||
|
_idFocusNode.removeListener(onFocusChanged);
|
||||||
|
_idFocusNode.dispose();
|
||||||
|
_idEditingController.dispose();
|
||||||
if (Get.isRegistered<IDTextEditingController>()) {
|
if (Get.isRegistered<IDTextEditingController>()) {
|
||||||
Get.delete<IDTextEditingController>();
|
Get.delete<IDTextEditingController>();
|
||||||
}
|
}
|
||||||
@@ -273,6 +278,13 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
bind.mainOnMainWindowClose();
|
bind.mainOnMainWindowClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onFocusChanged() {
|
||||||
|
_idInputFocused.value = _idFocusNode.hasFocus;
|
||||||
|
if (_idFocusNode.hasFocus && !allPeersLoader.isPeersLoading) {
|
||||||
|
allPeersLoader.getAllPeers(setState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isOutgoingOnly = bind.isOutgoingOnly();
|
final isOutgoingOnly = bind.isOutgoingOnly();
|
||||||
@@ -304,18 +316,6 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
connect(context, id, isFileTransfer: isFileTransfer);
|
connect(context, id, isFileTransfer: isFileTransfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _fetchPeers() async {
|
|
||||||
setState(() {
|
|
||||||
isPeersLoading = true;
|
|
||||||
});
|
|
||||||
await Future.delayed(Duration(milliseconds: 100));
|
|
||||||
peers = await getAllPeers();
|
|
||||||
setState(() {
|
|
||||||
isPeersLoading = false;
|
|
||||||
isPeersLoaded = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// UI for the remote ID TextField.
|
/// UI for the remote ID TextField.
|
||||||
/// Search for a peer.
|
/// Search for a peer.
|
||||||
Widget _buildRemoteIDTextField(BuildContext context) {
|
Widget _buildRemoteIDTextField(BuildContext context) {
|
||||||
@@ -332,11 +332,12 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Autocomplete<Peer>(
|
child: RawAutocomplete<Peer>(
|
||||||
optionsBuilder: (TextEditingValue textEditingValue) {
|
optionsBuilder: (TextEditingValue textEditingValue) {
|
||||||
if (textEditingValue.text == '') {
|
if (textEditingValue.text == '') {
|
||||||
_autocompleteOpts = const Iterable<Peer>.empty();
|
_autocompleteOpts = const Iterable<Peer>.empty();
|
||||||
} else if (peers.isEmpty && !isPeersLoaded) {
|
} else if (allPeersLoader.peers.isEmpty &&
|
||||||
|
!allPeersLoader.isLoaded) {
|
||||||
Peer emptyPeer = Peer(
|
Peer emptyPeer = Peer(
|
||||||
id: '',
|
id: '',
|
||||||
username: '',
|
username: '',
|
||||||
@@ -363,7 +364,7 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
String textToFind = textEditingValue.text.toLowerCase();
|
String textToFind = textEditingValue.text.toLowerCase();
|
||||||
_autocompleteOpts = peers
|
_autocompleteOpts = allPeersLoader.peers
|
||||||
.where((peer) =>
|
.where((peer) =>
|
||||||
peer.id.toLowerCase().contains(textToFind) ||
|
peer.id.toLowerCase().contains(textToFind) ||
|
||||||
peer.username
|
peer.username
|
||||||
@@ -377,6 +378,8 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
}
|
}
|
||||||
return _autocompleteOpts;
|
return _autocompleteOpts;
|
||||||
},
|
},
|
||||||
|
focusNode: _idFocusNode,
|
||||||
|
textEditingController: _idEditingController,
|
||||||
fieldViewBuilder: (
|
fieldViewBuilder: (
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
TextEditingController fieldTextEditingController,
|
TextEditingController fieldTextEditingController,
|
||||||
@@ -385,17 +388,17 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
) {
|
) {
|
||||||
fieldTextEditingController.text = _idController.text;
|
fieldTextEditingController.text = _idController.text;
|
||||||
Get.put<TextEditingController>(fieldTextEditingController);
|
Get.put<TextEditingController>(fieldTextEditingController);
|
||||||
fieldFocusNode.addListener(() async {
|
|
||||||
_idInputFocused.value = fieldFocusNode.hasFocus;
|
// The listener will be added multiple times when the widget is rebuilt.
|
||||||
if (fieldFocusNode.hasFocus && !isPeersLoading) {
|
// We may need to use the `RawAutocomplete` to get the focus node.
|
||||||
_fetchPeers();
|
|
||||||
}
|
// Temporarily remove Selection because Selection can cause users to accidentally delete previously entered content during input.
|
||||||
});
|
// final textLength =
|
||||||
final textLength =
|
// fieldTextEditingController.value.text.length;
|
||||||
fieldTextEditingController.value.text.length;
|
// // Select all to facilitate removing text, just following the behavior of address input of chrome.
|
||||||
// select all to facilitate removing text, just following the behavior of address input of chrome
|
// fieldTextEditingController.selection =
|
||||||
fieldTextEditingController.selection =
|
// TextSelection(baseOffset: 0, extentOffset: textLength);
|
||||||
TextSelection(baseOffset: 0, extentOffset: textLength);
|
|
||||||
return Obx(() => TextField(
|
return Obx(() => TextField(
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
enableSuggestions: false,
|
enableSuggestions: false,
|
||||||
@@ -468,7 +471,8 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
maxHeight: maxHeight,
|
maxHeight: maxHeight,
|
||||||
maxWidth: 319,
|
maxWidth: 319,
|
||||||
),
|
),
|
||||||
child: peers.isEmpty && isPeersLoading
|
child: allPeersLoader.peers.isEmpty &&
|
||||||
|
!allPeersLoader.isLoaded
|
||||||
? Container(
|
? Container(
|
||||||
height: 80,
|
height: 80,
|
||||||
child: Center(
|
child: Center(
|
||||||
|
|||||||
@@ -41,10 +41,11 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
final _idController = IDTextEditingController();
|
final _idController = IDTextEditingController();
|
||||||
final RxBool _idEmpty = true.obs;
|
final RxBool _idEmpty = true.obs;
|
||||||
|
|
||||||
List<Peer> peers = [];
|
final FocusNode _idFocusNode = FocusNode();
|
||||||
|
final TextEditingController _idEditingController = TextEditingController();
|
||||||
|
|
||||||
|
AllPeersLoader allPeersLoader = AllPeersLoader();
|
||||||
|
|
||||||
bool isPeersLoading = false;
|
|
||||||
bool isPeersLoaded = false;
|
|
||||||
StreamSubscription? _uniLinksSubscription;
|
StreamSubscription? _uniLinksSubscription;
|
||||||
|
|
||||||
// https://github.com/flutter/flutter/issues/157244
|
// https://github.com/flutter/flutter/issues/157244
|
||||||
@@ -61,6 +62,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_idFocusNode.addListener(onFocusChanged);
|
||||||
if (_idController.text.isEmpty) {
|
if (_idController.text.isEmpty) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
final lastRemoteId = await bind.mainGetLastRemoteId();
|
final lastRemoteId = await bind.mainGetLastRemoteId();
|
||||||
@@ -99,6 +101,13 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
connect(context, id);
|
connect(context, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onFocusChanged() {
|
||||||
|
_idEmpty.value = _idEditingController.text.isEmpty;
|
||||||
|
if (_idFocusNode.hasFocus && !allPeersLoader.isPeersLoading) {
|
||||||
|
allPeersLoader.getAllPeers(setState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// UI for software update.
|
/// UI for software update.
|
||||||
/// If _updateUrl] is not empty, shows a button to update the software.
|
/// If _updateUrl] is not empty, shows a button to update the software.
|
||||||
Widget _buildUpdateUI(String updateUrl) {
|
Widget _buildUpdateUI(String updateUrl) {
|
||||||
@@ -127,18 +136,6 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
color: Colors.white, fontWeight: FontWeight.bold))));
|
color: Colors.white, fontWeight: FontWeight.bold))));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _fetchPeers() async {
|
|
||||||
setState(() {
|
|
||||||
isPeersLoading = true;
|
|
||||||
});
|
|
||||||
await Future.delayed(Duration(milliseconds: 100));
|
|
||||||
peers = await getAllPeers();
|
|
||||||
setState(() {
|
|
||||||
isPeersLoading = false;
|
|
||||||
isPeersLoaded = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// UI for the remote ID TextField.
|
/// UI for the remote ID TextField.
|
||||||
/// Search for a peer and connect to it if the id exists.
|
/// Search for a peer and connect to it if the id exists.
|
||||||
Widget _buildRemoteIDTextField() {
|
Widget _buildRemoteIDTextField() {
|
||||||
@@ -156,11 +153,12 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||||
child: Autocomplete<Peer>(
|
child: RawAutocomplete<Peer>(
|
||||||
optionsBuilder: (TextEditingValue textEditingValue) {
|
optionsBuilder: (TextEditingValue textEditingValue) {
|
||||||
if (textEditingValue.text == '') {
|
if (textEditingValue.text == '') {
|
||||||
_autocompleteOpts = const Iterable<Peer>.empty();
|
_autocompleteOpts = const Iterable<Peer>.empty();
|
||||||
} else if (peers.isEmpty && !isPeersLoaded) {
|
} else if (allPeersLoader.peers.isEmpty &&
|
||||||
|
!allPeersLoader.isLoaded) {
|
||||||
Peer emptyPeer = Peer(
|
Peer emptyPeer = Peer(
|
||||||
id: '',
|
id: '',
|
||||||
username: '',
|
username: '',
|
||||||
@@ -188,7 +186,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
}
|
}
|
||||||
String textToFind = textEditingValue.text.toLowerCase();
|
String textToFind = textEditingValue.text.toLowerCase();
|
||||||
|
|
||||||
_autocompleteOpts = peers
|
_autocompleteOpts = allPeersLoader.peers
|
||||||
.where((peer) =>
|
.where((peer) =>
|
||||||
peer.id.toLowerCase().contains(textToFind) ||
|
peer.id.toLowerCase().contains(textToFind) ||
|
||||||
peer.username
|
peer.username
|
||||||
@@ -202,6 +200,8 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
}
|
}
|
||||||
return _autocompleteOpts;
|
return _autocompleteOpts;
|
||||||
},
|
},
|
||||||
|
focusNode: _idFocusNode,
|
||||||
|
textEditingController: _idEditingController,
|
||||||
fieldViewBuilder: (BuildContext context,
|
fieldViewBuilder: (BuildContext context,
|
||||||
TextEditingController fieldTextEditingController,
|
TextEditingController fieldTextEditingController,
|
||||||
FocusNode fieldFocusNode,
|
FocusNode fieldFocusNode,
|
||||||
@@ -209,18 +209,14 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
fieldTextEditingController.text = _idController.text;
|
fieldTextEditingController.text = _idController.text;
|
||||||
Get.put<TextEditingController>(
|
Get.put<TextEditingController>(
|
||||||
fieldTextEditingController);
|
fieldTextEditingController);
|
||||||
fieldFocusNode.addListener(() async {
|
|
||||||
_idEmpty.value =
|
// Temporarily remove Selection because Selection can cause users to accidentally delete previously entered content during input.
|
||||||
fieldTextEditingController.text.isEmpty;
|
// final textLength =
|
||||||
if (fieldFocusNode.hasFocus && !isPeersLoading) {
|
// fieldTextEditingController.value.text.length;
|
||||||
_fetchPeers();
|
// // select all to facilitate removing text, just following the behavior of address input of chrome
|
||||||
}
|
// fieldTextEditingController.selection = TextSelection(
|
||||||
});
|
// baseOffset: 0, extentOffset: textLength);
|
||||||
final textLength =
|
|
||||||
fieldTextEditingController.value.text.length;
|
|
||||||
// select all to facilitate removing text, just following the behavior of address input of chrome
|
|
||||||
fieldTextEditingController.selection = TextSelection(
|
|
||||||
baseOffset: 0, extentOffset: textLength);
|
|
||||||
return AutoSizeTextField(
|
return AutoSizeTextField(
|
||||||
controller: fieldTextEditingController,
|
controller: fieldTextEditingController,
|
||||||
focusNode: fieldFocusNode,
|
focusNode: fieldFocusNode,
|
||||||
@@ -300,7 +296,8 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
maxHeight: maxHeight,
|
maxHeight: maxHeight,
|
||||||
maxWidth: 320,
|
maxWidth: 320,
|
||||||
),
|
),
|
||||||
child: peers.isEmpty && isPeersLoading
|
child: allPeersLoader.peers.isEmpty &&
|
||||||
|
!allPeersLoader.isLoaded
|
||||||
? Container(
|
? Container(
|
||||||
height: 80,
|
height: 80,
|
||||||
child: Center(
|
child: Center(
|
||||||
@@ -363,6 +360,9 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
_uniLinksSubscription?.cancel();
|
_uniLinksSubscription?.cancel();
|
||||||
_idController.dispose();
|
_idController.dispose();
|
||||||
|
_idFocusNode.removeListener(onFocusChanged);
|
||||||
|
_idFocusNode.dispose();
|
||||||
|
_idEditingController.dispose();
|
||||||
if (Get.isRegistered<IDTextEditingController>()) {
|
if (Get.isRegistered<IDTextEditingController>()) {
|
||||||
Get.delete<IDTextEditingController>();
|
Get.delete<IDTextEditingController>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ import 'package:flutter_hbb/models/platform_model.dart';
|
|||||||
|
|
||||||
void showPeerSelectionDialog(
|
void showPeerSelectionDialog(
|
||||||
{bool singleSelection = false,
|
{bool singleSelection = false,
|
||||||
required Function(List<String>) onPeersCallback}) {
|
required Function(List<String>) onPeersCallback}) async {
|
||||||
final peers = bind.mainLoadRecentPeersSync();
|
final peers = await bind.mainGetRecentPeers(getAll: true);
|
||||||
if (peers.isEmpty) {
|
if (peers.isEmpty) {
|
||||||
debugPrint("load recent peers sync failed.");
|
debugPrint("load recent peers failed.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> map = jsonDecode(peers);
|
Map<String, dynamic> map = jsonDecode(peers);
|
||||||
List<dynamic> peersList = map['peers'] ?? [];
|
List<dynamic> peersList = map['peers'] ?? [];
|
||||||
final selected = List<String>.empty(growable: true);
|
final selected = List<String>.empty(growable: true);
|
||||||
|
|||||||
Submodule libs/hbb_common updated: e7d210e03a...f94753bddb
@@ -169,6 +169,8 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
init_plugins(&args);
|
init_plugins(&args);
|
||||||
if args.is_empty() || crate::common::is_empty_uni_link(&args[0]) {
|
if args.is_empty() || crate::common::is_empty_uni_link(&args[0]) {
|
||||||
|
#[cfg(windows)]
|
||||||
|
hbb_common::config::PeerConfig::preload_peers();
|
||||||
std::thread::spawn(move || crate::start_server(false, no_server));
|
std::thread::spawn(move || crate::start_server(false, no_server));
|
||||||
} else {
|
} else {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
|||||||
@@ -1101,44 +1101,99 @@ pub fn main_peer_exists(id: String) -> bool {
|
|||||||
peer_exists(&id)
|
peer_exists(&id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_recent_peers(
|
||||||
|
vec_id_modified_time_path: &Vec<(String, SystemTime, std::path::PathBuf)>,
|
||||||
|
to_end: bool,
|
||||||
|
all_peers: &mut Vec<HashMap<&str, String>>,
|
||||||
|
from: usize,
|
||||||
|
) -> usize {
|
||||||
|
let to = if to_end {
|
||||||
|
Some(vec_id_modified_time_path.len())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let mut peers_next = PeerConfig::batch_peers(vec_id_modified_time_path, from, to);
|
||||||
|
// There may be less peers than the batch size.
|
||||||
|
// But no need to consider this case, because it is a rare case.
|
||||||
|
let peers = peers_next.0.drain(..).map(|(id, _, p)| peer_to_map(id, p));
|
||||||
|
all_peers.extend(peers);
|
||||||
|
peers_next.1
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main_load_recent_peers() {
|
pub fn main_load_recent_peers() {
|
||||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
if !config::APP_DIR.read().unwrap().is_empty() {
|
||||||
let peers: Vec<HashMap<&str, String>> = PeerConfig::peers(None)
|
let vec_id_modified_time_path = PeerConfig::get_vec_id_modified_time_path(&None);
|
||||||
.drain(..)
|
if vec_id_modified_time_path.is_empty() {
|
||||||
.map(|(id, _, p)| peer_to_map(id, p))
|
return;
|
||||||
.collect();
|
}
|
||||||
|
|
||||||
let data = HashMap::from([
|
let push_to_flutter = |peers| {
|
||||||
("name", "load_recent_peers".to_owned()),
|
let data = HashMap::from([("name", "load_recent_peers".to_owned()), ("peers", peers)]);
|
||||||
(
|
let _res = flutter::push_global_event(
|
||||||
"peers",
|
flutter::APP_TYPE_MAIN,
|
||||||
serde_json::ser::to_string(&peers).unwrap_or("".to_owned()),
|
serde_json::ser::to_string(&data).unwrap_or("".to_owned()),
|
||||||
),
|
);
|
||||||
]);
|
};
|
||||||
let _res = flutter::push_global_event(
|
|
||||||
flutter::APP_TYPE_MAIN,
|
let load_two_times =
|
||||||
serde_json::ser::to_string(&data).unwrap_or("".to_owned()),
|
vec_id_modified_time_path.len() > PeerConfig::BATCH_LOADING_COUNT && cfg!(target_os = "windows");
|
||||||
);
|
let mut all_peers = vec![];
|
||||||
|
if load_two_times {
|
||||||
|
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 _ = load_recent_peers(&vec_id_modified_time_path, true, &mut all_peers, next_from);
|
||||||
|
} else {
|
||||||
|
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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_load_recent_peers_sync() -> SyncReturn<String> {
|
fn get_partial_recent_peers(
|
||||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
vec_id_modified_time_path: Vec<(String, SystemTime, std::path::PathBuf)>,
|
||||||
let peers: Vec<HashMap<&str, String>> = PeerConfig::peers(None)
|
count: usize,
|
||||||
.drain(..)
|
) -> String {
|
||||||
.map(|(id, _, p)| peer_to_map(id, p))
|
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();
|
.collect();
|
||||||
|
data.insert(
|
||||||
let data = HashMap::from([
|
"ids",
|
||||||
("name", "load_recent_peers".to_owned()),
|
serde_json::ser::to_string(&ids).unwrap_or("[]".to_owned()),
|
||||||
(
|
);
|
||||||
"peers",
|
|
||||||
serde_json::ser::to_string(&peers).unwrap_or("".to_owned()),
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
return SyncReturn(serde_json::ser::to_string(&data).unwrap_or("".to_owned()));
|
|
||||||
}
|
}
|
||||||
SyncReturn("".to_string())
|
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> {
|
pub fn main_load_lan_peers_sync() -> SyncReturn<String> {
|
||||||
@@ -1172,11 +1227,11 @@ pub fn main_load_recent_peers_for_ab(filter: String) -> String {
|
|||||||
pub fn main_load_fav_peers() {
|
pub fn main_load_fav_peers() {
|
||||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
if !config::APP_DIR.read().unwrap().is_empty() {
|
||||||
let favs = get_fav();
|
let favs = get_fav();
|
||||||
let mut recent = PeerConfig::peers(None);
|
let mut recent = PeerConfig::peers(Some(favs.clone()));
|
||||||
let mut lan = config::LanPeers::load()
|
let mut lan = config::LanPeers::load()
|
||||||
.peers
|
.peers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|d| recent.iter().all(|r| r.0 != d.id))
|
.filter(|d| favs.contains(&d.id) && recent.iter().all(|r| r.0 != d.id))
|
||||||
.map(|d| {
|
.map(|d| {
|
||||||
(
|
(
|
||||||
d.id.clone(),
|
d.id.clone(),
|
||||||
@@ -1195,13 +1250,7 @@ pub fn main_load_fav_peers() {
|
|||||||
recent.append(&mut lan);
|
recent.append(&mut lan);
|
||||||
let peers: Vec<HashMap<&str, String>> = recent
|
let peers: Vec<HashMap<&str, String>> = recent
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(id, _, p)| {
|
.map(|(id, _, p)| peer_to_map(id, p))
|
||||||
if favs.contains(&id) {
|
|
||||||
Some(peer_to_map(id, p))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let data = HashMap::from([
|
let data = HashMap::from([
|
||||||
|
|||||||
@@ -319,6 +319,9 @@ impl InvokeUiSession for SciterHandler {
|
|||||||
ConnType::DEFAULT_CONN => {
|
ConnType::DEFAULT_CONN => {
|
||||||
crate::keyboard::client::start_grab_loop();
|
crate::keyboard::client::start_grab_loop();
|
||||||
}
|
}
|
||||||
|
// Left empty code from compilation.
|
||||||
|
// Please replace the code in the PR.
|
||||||
|
ConnType::VIEW_CAMERA => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user