Merge pull request #1696 from Heap-Hop/address_book

[address books] optimize performance, fix bugs and add mobile tag actions
This commit is contained in:
RustDesk
2022-10-10 20:21:37 +08:00
committed by GitHub
13 changed files with 251 additions and 308 deletions

View File

@@ -9,7 +9,6 @@ import 'package:get/get.dart';
import '../../common.dart';
import '../../desktop/pages/desktop_home_page.dart';
import '../../mobile/pages/settings_page.dart';
import '../../models/platform_model.dart';
class AddressBook extends StatefulWidget {
final EdgeInsets? menuPadding;
@@ -22,6 +21,8 @@ class AddressBook extends StatefulWidget {
}
class _AddressBookState extends State<AddressBook> {
var menuPos = RelativeRect.fill;
@override
void initState() {
super.initState();
@@ -30,7 +31,7 @@ class _AddressBookState extends State<AddressBook> {
@override
Widget build(BuildContext context) => FutureBuilder<Widget>(
future: buildAddressBook(context),
future: buildBody(context),
builder: (context, snapshot) {
if (snapshot.hasData) {
return snapshot.data!;
@@ -44,7 +45,7 @@ class _AddressBookState extends State<AddressBook> {
if (isDesktop) {
loginDialog().then((success) {
if (success) {
setState(() {});
gFFI.abModel.pullAb();
}
});
} else {
@@ -52,41 +53,32 @@ class _AddressBookState extends State<AddressBook> {
}
}
Future<Widget> buildAddressBook(BuildContext context) async {
final token = await bind.mainGetLocalOption(key: 'access_token');
if (token.trim().isEmpty) {
return Center(
child: InkWell(
onTap: handleLogin,
child: Text(
translate("Login"),
style: const TextStyle(decoration: TextDecoration.underline),
Future<Widget> buildBody(BuildContext context) async {
return Obx(() {
if (gFFI.userModel.userName.value.isEmpty) {
return Center(
child: InkWell(
onTap: handleLogin,
child: Text(
translate("Login"),
style: const TextStyle(decoration: TextDecoration.underline),
),
),
),
);
}
final model = gFFI.abModel;
return FutureBuilder(
future: model.pullAb(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return _buildAddressBook(context);
} else if (snapshot.hasError) {
return _buildShowError(snapshot.error.toString());
} else {
return Obx(() {
if (model.abLoading.value) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (model.abError.isNotEmpty) {
return _buildShowError(model.abError.value);
} else {
return const Offstage();
}
});
}
});
);
} else {
if (gFFI.abModel.abLoading.value) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (gFFI.abModel.abError.isNotEmpty) {
return _buildShowError(gFFI.abModel.abError.value);
}
return isDesktop
? _buildAddressBookDesktop()
: _buildAddressBookMobile();
}
});
}
Widget _buildShowError(String error) {
@@ -104,8 +96,7 @@ class _AddressBookState extends State<AddressBook> {
));
}
Widget _buildAddressBook(BuildContext context) {
var pos = RelativeRect.fill;
Widget _buildAddressBookDesktop() {
return Row(
children: [
Card(
@@ -121,20 +112,7 @@ class _AddressBookState extends State<AddressBook> {
const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(translate('Tags')),
GestureDetector(
onTapDown: (e) {
final x = e.globalPosition.dx;
final y = e.globalPosition.dy;
pos = RelativeRect.fromLTRB(x, y, x, y);
},
onTap: () => _showMenu(pos),
child: ActionMore()),
],
),
_buildTagHeader(),
Expanded(
child: Container(
width: double.infinity,
@@ -142,40 +120,98 @@ class _AddressBookState extends State<AddressBook> {
decoration: BoxDecoration(
border: Border.all(color: MyTheme.darkGray),
borderRadius: BorderRadius.circular(2)),
child: Obx(
() => Wrap(
children: gFFI.abModel.tags
.map((e) => AddressBookTag(
name: e,
tags: gFFI.abModel.selectedTags,
onTap: () {
if (gFFI.abModel.selectedTags.contains(e)) {
gFFI.abModel.selectedTags.remove(e);
} else {
gFFI.abModel.selectedTags.add(e);
}
}))
.toList(),
),
),
child: _buildTags(),
).marginSymmetric(vertical: 8.0),
)
],
),
),
).marginOnly(right: 8.0),
Expanded(
child: Align(
alignment: Alignment.topLeft,
child: Obx(() => AddressBookPeersView(
menuPadding: widget.menuPadding,
initPeers: gFFI.abModel.peers.value,
))),
)
_buildPeersViews()
],
);
}
Widget _buildAddressBookMobile() {
return Column(
children: [
Card(
margin: EdgeInsets.symmetric(horizontal: 1.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
side:
BorderSide(color: Theme.of(context).scaffoldBackgroundColor)),
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildTagHeader(),
Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: MyTheme.darkGray),
borderRadius: BorderRadius.circular(4)),
child: _buildTags(),
).marginSymmetric(vertical: 8.0),
],
),
),
),
Divider(),
_buildPeersViews()
],
);
}
Widget _buildTagHeader() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(translate('Tags')),
GestureDetector(
onTapDown: (e) {
final x = e.globalPosition.dx;
final y = e.globalPosition.dy;
menuPos = RelativeRect.fromLTRB(x, y, x, y);
},
onTap: () => _showMenu(menuPos),
child: ActionMore()),
],
);
}
Widget _buildTags() {
return Obx(
() => Wrap(
children: gFFI.abModel.tags
.map((e) => AddressBookTag(
name: e,
tags: gFFI.abModel.selectedTags,
onTap: () {
if (gFFI.abModel.selectedTags.contains(e)) {
gFFI.abModel.selectedTags.remove(e);
} else {
gFFI.abModel.selectedTags.add(e);
}
}))
.toList(),
),
);
}
Widget _buildPeersViews() {
return Expanded(
child: Align(
alignment: Alignment.topLeft,
child: Obx(() => AddressBookPeersView(
menuPadding: widget.menuPadding,
initPeers: gFFI.abModel.peers.value,
))),
);
}
void _showMenu(RelativeRect pos) {
final items = [
getEntry(translate("Add ID"), abAddId),

View File

@@ -54,7 +54,8 @@ class _PeerTabPageState extends State<PeerTabPage>
bind.mainDiscover();
break;
case 3:
gFFI.abModel.pullAb();
/// AddressBook initState will refresh ab state
break;
}
}

View File

@@ -13,6 +13,7 @@ import '../../models/peer_model.dart';
import '../../models/platform_model.dart';
import 'peer_card.dart';
typedef PeerFilter = bool Function(Peer peer);
typedef PeerCardBuilder = Widget Function(Peer peer);
/// for peer search text, global obs value
@@ -22,10 +23,14 @@ final peerSearchTextController =
class _PeersView extends StatefulWidget {
final Peers peers;
final PeerFilter? peerFilter;
final PeerCardBuilder peerCardBuilder;
const _PeersView(
{required this.peers, required this.peerCardBuilder, Key? key})
{required this.peers,
required this.peerCardBuilder,
this.peerFilter,
Key? key})
: super(key: key);
@override
@@ -173,11 +178,33 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
}
}();
}
Future<List<Peer>>? matchPeers(String searchText, List<Peer> peers) async {
if (widget.peerFilter != null) {
peers = peers.where((peer) => widget.peerFilter!(peer)).toList();
}
searchText = searchText.trim();
if (searchText.isEmpty) {
return peers;
}
searchText = searchText.toLowerCase();
final matches =
await Future.wait(peers.map((peer) => matchPeer(searchText, peer)));
final filteredList = List<Peer>.empty(growable: true);
for (var i = 0; i < peers.length; i++) {
if (matches[i]) {
filteredList.add(peers[i]);
}
}
return filteredList;
}
}
abstract class BasePeersView extends StatelessWidget {
final String name;
final String loadEvent;
final PeerFilter? peerFilter;
final PeerCardBuilder peerCardBuilder;
final List<Peer> initPeers;
@@ -185,6 +212,7 @@ abstract class BasePeersView extends StatelessWidget {
Key? key,
required this.name,
required this.loadEvent,
this.peerFilter,
required this.peerCardBuilder,
required this.initPeers,
}) : super(key: key);
@@ -193,6 +221,7 @@ abstract class BasePeersView extends StatelessWidget {
Widget build(BuildContext context) {
return _PeersView(
peers: Peers(name: name, loadEvent: loadEvent, peers: initPeers),
peerFilter: peerFilter,
peerCardBuilder: peerCardBuilder);
}
}
@@ -273,13 +302,12 @@ class AddressBookPeersView extends BasePeersView {
key: key,
name: 'address book peer',
loadEvent: 'load_address_book_peers',
peerCardBuilder: (Peer peer) => Obx(() => Offstage(
key: ValueKey("off${peer.id}"),
offstage: !_hitTag(gFFI.abModel.selectedTags, peer.tags),
child: AddressBookPeerCard(
peer: peer,
menuPadding: menuPadding,
))),
peerFilter: (Peer peer) =>
_hitTag(gFFI.abModel.selectedTags, peer.tags),
peerCardBuilder: (Peer peer) => AddressBookPeerCard(
peer: peer,
menuPadding: menuPadding,
),
initPeers: initPeers,
);