Merge pull request #5404 from 21pages/ab

fix ab ui not updating immediately
This commit is contained in:
RustDesk 2023-08-16 14:04:11 +08:00 committed by GitHub
commit 1141b99cf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 285 additions and 127 deletions

View File

@ -48,13 +48,14 @@ class _AddressBookState extends State<AddressBook> {
return Column( return Column(
children: [ children: [
_buildNotEmptyLoading(), _buildNotEmptyLoading(),
_buildRetryProgress(),
_buildErrorBanner( _buildErrorBanner(
err: gFFI.abModel.pullError, err: gFFI.abModel.pullError,
retry: null, retry: null,
close: () => gFFI.abModel.pullError.value = ''), close: () => gFFI.abModel.pullError.value = ''),
_buildErrorBanner( _buildErrorBanner(
err: gFFI.abModel.pushError, err: gFFI.abModel.pushError,
retry: () => gFFI.abModel.pushAb(), retry: () => gFFI.abModel.pushAb(isRetry: true),
close: () => gFFI.abModel.pushError.value = ''), close: () => gFFI.abModel.pushError.value = ''),
Expanded( Expanded(
child: isDesktop child: isDesktop
@ -136,6 +137,13 @@ class _AddressBookState extends State<AddressBook> {
)); ));
} }
Widget _buildRetryProgress() {
return Obx(() => Offstage(
offstage: !gFFI.abModel.retrying.value,
child: LinearProgressIndicator(),
));
}
Widget _buildAddressBookDesktop() { Widget _buildAddressBookDesktop() {
return Row( return Row(
children: [ children: [
@ -339,7 +347,7 @@ class _AddressBookState extends State<AddressBook> {
return; return;
} }
gFFI.abModel.addId(id, aliasController.text.trim(), selectedTag); gFFI.abModel.addId(id, aliasController.text.trim(), selectedTag);
await gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
this.setState(() {}); this.setState(() {});
// final currentPeers // final currentPeers
} }
@ -448,7 +456,7 @@ class _AddressBookState extends State<AddressBook> {
for (final tag in tags) { for (final tag in tags) {
gFFI.abModel.addTag(tag); gFFI.abModel.addTag(tag);
} }
await gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
// final currentPeers // final currentPeers
} }
close(); close();

View File

@ -1,9 +1,11 @@
import 'dart:io'; import 'dart:io';
import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hbb/common/widgets/dialog.dart'; import 'package:flutter_hbb/common/widgets/dialog.dart';
import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/ab_model.dart';
import 'package:flutter_hbb/models/peer_tab_model.dart'; import 'package:flutter_hbb/models/peer_tab_model.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -582,6 +584,7 @@ abstract class BasePeerCard extends StatelessWidget {
), ),
proc: () { proc: () {
bind.mainCreateShortcut(id: id); bind.mainCreateShortcut(id: id);
showToast(translate('Successful'));
}, },
padding: menuPadding, padding: menuPadding,
dismissOnClicked: true, dismissOnClicked: true,
@ -597,6 +600,7 @@ abstract class BasePeerCard extends StatelessWidget {
setter: (bool v) async { setter: (bool v) async {
await bind.mainSetPeerOption( await bind.mainSetPeerOption(
id: id, key: key, value: bool2option(key, v)); id: id, key: key, value: bool2option(key, v));
showToast(translate('Successful'));
}, },
padding: menuPadding, padding: menuPadding,
dismissOnClicked: true, dismissOnClicked: true,
@ -633,6 +637,7 @@ abstract class BasePeerCard extends StatelessWidget {
id: id, id: id,
key: kOptionForceAlwaysRelay, key: kOptionForceAlwaysRelay,
value: bool2option(kOptionForceAlwaysRelay, v)); value: bool2option(kOptionForceAlwaysRelay, v));
showToast(translate('Successful'));
}, },
padding: menuPadding, padding: menuPadding,
dismissOnClicked: true, dismissOnClicked: true,
@ -657,6 +662,7 @@ abstract class BasePeerCard extends StatelessWidget {
gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
} else { } else {
await bind.mainSetPeerAlias(id: id, alias: newName); await bind.mainSetPeerAlias(id: id, alias: newName);
showToast(translate('Successful'));
_update(); _update();
} }
} }
@ -706,11 +712,25 @@ abstract class BasePeerCard extends StatelessWidget {
break; break;
case PeerTabIndex.ab: case PeerTabIndex.ab:
gFFI.abModel.deletePeer(id); gFFI.abModel.deletePeer(id);
await gFFI.abModel.pushAb(); final future = gFFI.abModel.pushAb();
if (shouldSyncAb() && await bind.mainPeerExists(id: peer.id)) {
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'));
}
});
}
break; break;
case PeerTabIndex.group: case PeerTabIndex.group:
break; break;
} }
if (tab != PeerTabIndex.ab) {
showToast(translate('Successful'));
}
} }
deletePeerConfirmDialog(onSubmit, deletePeerConfirmDialog(onSubmit,
@ -730,6 +750,7 @@ abstract class BasePeerCard extends StatelessWidget {
), ),
proc: () { proc: () {
bind.mainForgetPassword(id: id); bind.mainForgetPassword(id: id);
showToast(translate('Successful'));
}, },
padding: menuPadding, padding: menuPadding,
dismissOnClicked: true, dismissOnClicked: true,
@ -762,6 +783,7 @@ abstract class BasePeerCard extends StatelessWidget {
favs.add(id); favs.add(id);
await bind.mainStoreFav(favs: favs); await bind.mainStoreFav(favs: favs);
} }
showToast(translate('Successful'));
}(); }();
}, },
padding: menuPadding, padding: menuPadding,
@ -796,6 +818,7 @@ abstract class BasePeerCard extends StatelessWidget {
await bind.mainStoreFav(favs: favs); await bind.mainStoreFav(favs: favs);
await reloadFunc(); await reloadFunc();
} }
showToast(translate('Successful'));
}(); }();
}, },
padding: menuPadding, padding: menuPadding,
@ -817,7 +840,7 @@ abstract class BasePeerCard extends StatelessWidget {
} }
if (!gFFI.abModel.idContainBy(peer.id)) { if (!gFFI.abModel.idContainBy(peer.id)) {
gFFI.abModel.addPeer(peer); gFFI.abModel.addPeer(peer);
await gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
} }
}(); }();
}, },
@ -1049,7 +1072,7 @@ class AddressBookPeerCard extends BasePeerCard {
proc: () { proc: () {
editAbTagDialog(gFFI.abModel.getPeerTags(id), (selectedTag) async { editAbTagDialog(gFFI.abModel.getPeerTags(id), (selectedTag) async {
gFFI.abModel.changeTagForPeer(id, selectedTag); gFFI.abModel.changeTagForPeer(id, selectedTag);
await gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
}); });
}, },
padding: super.menuPadding, padding: super.menuPadding,
@ -1121,6 +1144,7 @@ void _rdpDialog(String id) async {
id: id, key: 'rdp_username', value: username); id: id, key: 'rdp_username', value: username);
await bind.mainSetPeerOption( await bind.mainSetPeerOption(
id: id, key: 'rdp_password', value: password); id: id, key: 'rdp_password', value: password);
showToast(translate('Successful'));
close(); close();
} }

View File

@ -1,3 +1,4 @@
import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/address_book.dart'; import 'package:flutter_hbb/common/widgets/address_book.dart';
import 'package:flutter_hbb/common/widgets/dialog.dart'; import 'package:flutter_hbb/common/widgets/dialog.dart';
@ -7,6 +8,7 @@ import 'package:flutter_hbb/common/widgets/peer_card.dart';
import 'package:flutter_hbb/common/widgets/animated_rotation_widget.dart'; import 'package:flutter_hbb/common/widgets/animated_rotation_widget.dart';
import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart'; import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
import 'package:flutter_hbb/models/ab_model.dart';
import 'package:flutter_hbb/models/peer_tab_model.dart'; import 'package:flutter_hbb/models/peer_tab_model.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -126,8 +128,7 @@ class _PeerTabPageState extends State<PeerTabPage>
child: Icon( child: Icon(
Icons.tag_rounded, Icons.tag_rounded,
size: 18, size: 18,
)) )))),
)),
onTap: () async { onTap: () async {
await bind.mainSetLocalOption( await bind.mainSetLocalOption(
key: "hideAbTagsPanel", key: "hideAbTagsPanel",
@ -226,8 +227,7 @@ class _PeerTabPageState extends State<PeerTabPage>
Icons.refresh, Icons.refresh,
size: 18, size: 18,
color: textColor, color: textColor,
)) )))),
)),
), ),
); );
} }
@ -264,8 +264,7 @@ class _PeerTabPageState extends State<PeerTabPage>
: Icons.grid_view_rounded, : Icons.grid_view_rounded,
size: 18, size: 18,
color: textColor, color: textColor,
)) )))),
)),
); );
} }
@ -334,14 +333,36 @@ class _PeerTabPageState extends State<PeerTabPage>
await bind.mainLoadLanPeers(); await bind.mainLoadLanPeers();
break; break;
case 3: case 3:
{
bool hasSynced = false;
if (shouldSyncAb()) {
for (var p in peers) {
if (await bind.mainPeerExists(id: p.id)) {
hasSynced = true;
}
}
}
gFFI.abModel.deletePeers(peers.map((p) => p.id).toList()); gFFI.abModel.deletePeers(peers.map((p) => p.id).toList());
await gFFI.abModel.pushAb(); final future = gFFI.abModel.pushAb();
if (hasSynced) {
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'));
}
});
}
}
break; break;
default: default:
break; break;
} }
gFFI.peerTabModel.setMultiSelectionMode(false); gFFI.peerTabModel.setMultiSelectionMode(false);
showToast(translate('Successful')); if (model.currentTab != 3) showToast(translate('Successful'));
} }
deletePeerConfirmDialog(onSubmit, translate('Delete')); deletePeerConfirmDialog(onSubmit, translate('Delete'));
@ -384,11 +405,18 @@ class _PeerTabPageState extends State<PeerTabPage>
!gFFI.userModel.isLogin || model.currentTab == PeerTabIndex.ab.index, !gFFI.userModel.isLogin || model.currentTab == PeerTabIndex.ab.index,
child: InkWell( child: InkWell(
onTap: () { onTap: () {
if (gFFI.abModel.isFull(true)) {
return;
}
final peers = model.selectedPeers; final peers = model.selectedPeers;
gFFI.abModel.addPeers(peers); gFFI.abModel.addPeers(peers);
gFFI.abModel.pushAb(); final future = gFFI.abModel.pushAb();
model.setMultiSelectionMode(false); model.setMultiSelectionMode(false);
showToast(translate('Successful')); Future.delayed(Duration.zero, () async {
await future;
await Future.delayed(Duration(seconds: 2)); // toast
gFFI.abModel.isFull(true);
});
}, },
child: Tooltip( child: Tooltip(
message: translate('Add to Address Book'), message: translate('Add to Address Book'),
@ -483,8 +511,7 @@ class _PeerSearchBarState extends State<PeerSearchBar> {
child: Icon( child: Icon(
Icons.search_rounded, Icons.search_rounded,
color: Theme.of(context).hintColor, color: Theme.of(context).hintColor,
)) )));
);
} }
Widget _buildSearchBar() { Widget _buildSearchBar() {
@ -554,8 +581,7 @@ class _PeerSearchBarState extends State<PeerSearchBar> {
}, },
icon: Tooltip( icon: Tooltip(
message: translate('Close'), message: translate('Close'),
child: child: Icon(
Icon(
Icons.close, Icons.close,
color: Theme.of(context).hintColor, color: Theme.of(context).hintColor,
)), )),

View File

@ -30,6 +30,7 @@ class AbModel {
final tags = [].obs; final tags = [].obs;
final peers = List<Peer>.empty(growable: true).obs; final peers = List<Peer>.empty(growable: true).obs;
final sortTags = shouldSortTags().obs; final sortTags = shouldSortTags().obs;
final retrying = false.obs;
bool get emtpy => peers.isEmpty && tags.isEmpty; bool get emtpy => peers.isEmpty && tags.isEmpty;
final selectedTags = List<String>.empty(growable: true).obs; final selectedTags = List<String>.empty(growable: true).obs;
@ -55,10 +56,11 @@ class AbModel {
if (gFFI.userModel.userName.isEmpty) return; if (gFFI.userModel.userName.isEmpty) return;
if (abLoading.value) return; if (abLoading.value) return;
if (!force && initialized) return; if (!force && initialized) return;
DateTime startTime = DateTime.now();
if (pushError.isNotEmpty) { if (pushError.isNotEmpty) {
try { try {
// push to retry // push to retry
pushAb(toast: false); pushAb(toastIfFail: false, toastIfSucc: false);
} catch (_) {} } catch (_) {}
} }
if (!quiet) { if (!quiet) {
@ -112,14 +114,14 @@ class AbModel {
} }
} }
} finally { } finally {
if (initialized) { var ms =
// make loading effect obvious (Duration(milliseconds: 300) - DateTime.now().difference(startTime))
Future.delayed(Duration(milliseconds: 300), () { .inMilliseconds;
ms = ms > 0 ? ms : 0;
Future.delayed(Duration(milliseconds: ms), () {
abLoading.value = false; abLoading.value = false;
}); });
} else {
abLoading.value = false;
}
initialized = true; initialized = true;
_syncAllFromRecent = true; _syncAllFromRecent = true;
_timerCounter = 0; _timerCounter = 0;
@ -161,11 +163,17 @@ class AbModel {
} }
} }
void addPeers(List<Peer> ps) { bool addPeers(List<Peer> ps) {
bool allAdded = true;
for (var p in ps) { for (var p in ps) {
if (!isFull(false)) {
addPeer(p); addPeer(p);
} else {
allAdded = false;
} }
} }
return allAdded;
}
void addTag(String tag) async { void addTag(String tag) async {
if (tagContainBy(tag)) { if (tagContainBy(tag)) {
@ -198,13 +206,22 @@ class AbModel {
it.first.alias = alias; it.first.alias = alias;
} }
Future<void> pushAb({bool toast = true}) async { Future<bool> pushAb(
debugPrint("pushAb"); {bool toastIfFail = true,
bool toastIfSucc = true,
bool isRetry = false}) async {
debugPrint(
"pushAb: toastIfFail:$toastIfFail, toastIfSucc:$toastIfSucc, isRetry:$isRetry");
pushError.value = ''; pushError.value = '';
if (isRetry) retrying.value = true;
DateTime startTime = DateTime.now();
bool ret = false;
try { try {
// avoid double pushes in a row // avoid double pushes in a row
_syncAllFromRecent = true; _syncAllFromRecent = true;
syncFromRecent(push: false); await syncFromRecent(push: false);
//https: //stackoverflow.com/questions/68249333/flutter-getx-updating-item-in-children-list-is-not-reactive
peers.refresh();
final api = "${await bind.mainGetApiServer()}/api/ab"; final api = "${await bind.mainGetApiServer()}/api/ab";
var authHeaders = getHttpHeaders(); var authHeaders = getHttpHeaders();
authHeaders['Content-Type'] = "application/json"; authHeaders['Content-Type'] = "application/json";
@ -224,12 +241,14 @@ class AbModel {
} }
if (resp.statusCode == 200 && if (resp.statusCode == 200 &&
(resp.body.isEmpty || resp.body.toLowerCase() == 'null')) { (resp.body.isEmpty || resp.body.toLowerCase() == 'null')) {
ret = true;
_saveCache(); _saveCache();
} else { } else {
Map<String, dynamic> json = _jsonDecode(resp.body, resp.statusCode); Map<String, dynamic> json = _jsonDecode(resp.body, resp.statusCode);
if (json.containsKey('error')) { if (json.containsKey('error')) {
throw json['error']; throw json['error'];
} else if (resp.statusCode == 200) { } else if (resp.statusCode == 200) {
ret = true;
_saveCache(); _saveCache();
} else { } else {
throw 'HTTP ${resp.statusCode}'; throw 'HTTP ${resp.statusCode}';
@ -238,12 +257,25 @@ class AbModel {
} catch (e) { } catch (e) {
pushError.value = pushError.value =
'${translate('push_ab_failed_tip')}: ${translate(e.toString())}'; '${translate('push_ab_failed_tip')}: ${translate(e.toString())}';
if (toast && gFFI.peerTabModel.currentTab != PeerTabIndex.ab.index) { }
_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); BotToast.showText(contentColor: Colors.red, text: pushError.value);
} }
} finally { if (ret && toastIfSucc) {
_syncAllFromRecent = true; showToast(translate('Successful'));
} }
return ret;
} }
Peer? find(String id) { Peer? find(String id) {
@ -333,42 +365,37 @@ class AbModel {
rdpUsername: r.rdpUsername); rdpUsername: r.rdpUsername);
} }
void syncFromRecent({bool push = true}) async { Future<void> syncFromRecent({bool push = true}) async {
if (!_syncFromRecentLock) { if (!_syncFromRecentLock) {
_syncFromRecentLock = true; _syncFromRecentLock = true;
_syncFromRecentWithoutLock(push: push); await _syncFromRecentWithoutLock(push: push);
_syncFromRecentLock = false; _syncFromRecentLock = false;
} }
} }
void _syncFromRecentWithoutLock({bool push = true}) async { Future<void> _syncFromRecentWithoutLock({bool push = true}) async {
bool shouldSync(Peer a, Peer b) { bool shouldSync(Peer r, Peer p) {
return a.hash != b.hash || return r.hash != p.hash ||
a.username != b.username || r.username != p.username ||
a.platform != b.platform || r.platform != p.platform ||
a.hostname != b.hostname || r.hostname != p.hostname ||
a.alias != b.alias; (p.alias.isEmpty && r.alias.isNotEmpty);
} }
Future<List<Peer>> getRecentPeers() async { Future<List<Peer>> getRecentPeers() async {
try { try {
if (peers.isEmpty) [];
List<String> filteredPeerIDs; List<String> filteredPeerIDs;
if (_syncAllFromRecent) { if (_syncAllFromRecent) {
_syncAllFromRecent = false; _syncAllFromRecent = false;
filteredPeerIDs = peers.map((e) => e.id).toList(); filteredPeerIDs = [];
} else { } else {
final new_stored_str = await bind.mainGetNewStoredPeers(); final new_stored_str = await bind.mainGetNewStoredPeers();
if (new_stored_str.isEmpty) return []; if (new_stored_str.isEmpty) return [];
List<String> new_stores = filteredPeerIDs = (jsonDecode(new_stored_str) as List<dynamic>)
(jsonDecode(new_stored_str) as List<dynamic>)
.map((e) => e.toString()) .map((e) => e.toString())
.toList(); .toList();
final abPeerIds = peers.map((e) => e.id).toList();
filteredPeerIDs =
new_stores.where((e) => abPeerIds.contains(e)).toList();
}
if (filteredPeerIDs.isEmpty) return []; if (filteredPeerIDs.isEmpty) return [];
}
final loadStr = await bind.mainLoadRecentPeersForAb( final loadStr = await bind.mainLoadRecentPeersForAb(
filter: jsonEncode(filteredPeerIDs)); filter: jsonEncode(filteredPeerIDs));
if (loadStr.isEmpty) { if (loadStr.isEmpty) {
@ -390,28 +417,34 @@ class AbModel {
try { try {
if (!shouldSyncAb()) return; if (!shouldSyncAb()) return;
final oldPeers = peers.toList();
final recents = await getRecentPeers(); final recents = await getRecentPeers();
if (recents.isEmpty) return; if (recents.isEmpty) return;
for (var i = 0; i < peers.length; i++) { bool syncChanged = false;
var p = peers[i]; bool uiChanged = false;
var r = recents.firstWhereOrNull((r) => p.id == r.id); for (var i = 0; i < recents.length; i++) {
if (r != null) { var r = recents[i];
peers[i] = merge(r, p); var index = peers.indexWhere((e) => e.id == r.id);
if (index < 0) {
if (!isFull(false)) {
peers.add(r);
syncChanged = true;
uiChanged = true;
} }
} else {
if (!r.equal(peers[index])) {
uiChanged = true;
} }
bool changed = false; if (shouldSync(r, peers[index])) {
for (var i = 0; i < peers.length; i++) { syncChanged = true;
final o = oldPeers[i]; }
final p = peers[i]; peers[index] = merge(r, peers[index]);
if (shouldSync(o, p)) {
changed = true;
break;
} }
} }
// Be careful with loop calls // Be careful with loop calls
if (changed && push) { if (syncChanged && push) {
pushAb(); pushAb(toastIfSucc: false);
} else if (uiChanged) {
peers.refresh();
} }
} catch (e) { } catch (e) {
debugPrint('syncFromRecent:$e'); debugPrint('syncFromRecent:$e');

View File

@ -1,6 +1,8 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'platform_model.dart'; import 'platform_model.dart';
// ignore: depend_on_referenced_packages
import 'package:collection/collection.dart';
class Peer { class Peer {
final String id; final String id;
@ -87,6 +89,18 @@ class Peer {
rdpPort: '', rdpPort: '',
rdpUsername: '', rdpUsername: '',
); );
bool equal(Peer other) {
return id == other.id &&
hash == other.hash &&
username == other.username &&
hostname == other.hostname &&
platform == other.platform &&
alias == other.alias &&
tags.equals(other.tags) &&
forceAlwaysRelay == other.forceAlwaysRelay &&
rdpPort == other.rdpPort &&
rdpUsername == other.rdpUsername;
}
} }
enum UpdateEvent { online, load } enum UpdateEvent { online, load }

View File

@ -1079,6 +1079,10 @@ impl PeerConfig {
Default::default() Default::default()
} }
pub fn exists(id: &str) -> bool {
Self::path(id).exists()
}
serde_field_string!( serde_field_string!(
default_view_style, default_view_style,
deserialize_view_style, deserialize_view_style,

View File

@ -841,6 +841,10 @@ pub fn main_peer_has_password(id: String) -> bool {
peer_has_password(id) peer_has_password(id)
} }
pub fn main_peer_exists(id: String) -> bool {
peer_exists(&id)
}
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 peers: Vec<HashMap<&str, String>> = PeerConfig::peers(None)
@ -883,8 +887,13 @@ pub fn main_load_recent_peers_sync() -> SyncReturn<String> {
pub fn main_load_recent_peers_for_ab(filter: String) -> String { pub fn main_load_recent_peers_for_ab(filter: String) -> String {
let id_filters = serde_json::from_str::<Vec<String>>(&filter).unwrap_or_default(); let id_filters = serde_json::from_str::<Vec<String>>(&filter).unwrap_or_default();
let id_filters = if id_filters.is_empty() {
None
} else {
Some(id_filters)
};
if !config::APP_DIR.read().unwrap().is_empty() { if !config::APP_DIR.read().unwrap().is_empty() {
let peers: Vec<HashMap<&str, String>> = PeerConfig::peers(Some(id_filters)) let peers: Vec<HashMap<&str, String>> = PeerConfig::peers(id_filters)
.drain(..) .drain(..)
.map(|(id, _, p)| peer_to_map_ab(id, p)) .map(|(id, _, p)| peer_to_map_ab(id, p))
.collect(); .collect();

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", "未成功获取地址簿"), ("pull_ab_failed_tip", "未成功获取地址簿"),
("push_ab_failed_tip", "未成功上传地址簿"), ("push_ab_failed_tip", "未成功上传地址簿"),
("synced_peer_readded_tip", "最近会话中存在的设备将会被重新添加到地址簿。"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", "Tags umschalten"), ("Toggle Tags", "Tags umschalten"),
("pull_ab_failed_tip", "Aktualisierung des Adressbuchs fehlgeschlagen"), ("pull_ab_failed_tip", "Aktualisierung des Adressbuchs fehlgeschlagen"),
("push_ab_failed_tip", "Synchronisierung des Adressbuchs mit dem Server fehlgeschlagen"), ("push_ab_failed_tip", "Synchronisierung des Adressbuchs mit dem Server fehlgeschlagen"),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -76,5 +76,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", "You have reached the maximum number of managed devices."), ("exceed_max_devices", "You have reached the maximum number of managed devices."),
("pull_ab_failed_tip", "Failed to refresh address book"), ("pull_ab_failed_tip", "Failed to refresh address book"),
("push_ab_failed_tip", "Failed to sync address book to server"), ("push_ab_failed_tip", "Failed to sync address book to server"),
("synced_peer_readded_tip", "The devices present in the recent sessions will be re-added to the address book."),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", "Alternar Etiquetas"), ("Toggle Tags", "Alternar Etiquetas"),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", "Attiva/disattiva tag"), ("Toggle Tags", "Attiva/disattiva tag"),
("pull_ab_failed_tip", "Impossibile aggiornare la rubrica"), ("pull_ab_failed_tip", "Impossibile aggiornare la rubrica"),
("push_ab_failed_tip", "Impossibile sincronizzare la rubrica con il server"), ("push_ab_failed_tip", "Impossibile sincronizzare la rubrica con il server"),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", "Переключить метки"), ("Toggle Tags", "Переключить метки"),
("pull_ab_failed_tip", "Невозможно обновить адресную книгу"), ("pull_ab_failed_tip", "Невозможно обновить адресную книгу"),
("push_ab_failed_tip", "Невозможно синхронизировать адресную книгу с сервером"), ("push_ab_failed_tip", "Невозможно синхронизировать адресную книгу с сервером"),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", "未成功獲取地址簿"), ("pull_ab_failed_tip", "未成功獲取地址簿"),
("push_ab_failed_tip", "未成功上傳地址簿"), ("push_ab_failed_tip", "未成功上傳地址簿"),
("synced_peer_readded_tip", "最近會話中存在的設備將會被重新添加到地址簿。"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -537,5 +537,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Toggle Tags", ""), ("Toggle Tags", ""),
("pull_ab_failed_tip", ""), ("pull_ab_failed_tip", ""),
("push_ab_failed_tip", ""), ("push_ab_failed_tip", ""),
("synced_peer_readded_tip", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -648,6 +648,11 @@ pub fn peer_to_map_ab(id: String, p: PeerConfig) -> HashMap<&'static str, String
m m
} }
#[cfg(feature = "flutter")]
pub fn peer_exists(id: &str) -> bool {
PeerConfig::exists(id)
}
#[inline] #[inline]
pub fn get_lan_peers() -> Vec<HashMap<&'static str, String>> { pub fn get_lan_peers() -> Vec<HashMap<&'static str, String>> {
config::LanPeers::load() config::LanPeers::load()