From 6b99d4d82eeb0513cc7130a80ee2d2d6198d625e Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 29 Jul 2022 12:03:24 +0800 Subject: [PATCH] add: peer rename Signed-off-by: Kingtous --- .../lib/desktop/pages/connection_page.dart | 188 ++---------------- .../lib/desktop/widgets/peercard_widget.dart | 147 ++++++++++++-- flutter/lib/models/ab_model.dart | 10 + flutter/lib/models/model.dart | 8 + src/flutter_ffi.rs | 14 +- 5 files changed, 168 insertions(+), 199 deletions(-) diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index 42c41f8b9..7a5c47c06 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -105,7 +105,7 @@ class _ConnectionPageState extends State { RecentPeerWidget(), FavoritePeerWidget(), DiscoveredPeerWidget(), - AddressBookPeerWidget(), + // AddressBookPeerWidget(), // FutureBuilder( // future: getPeers(rType: RemoteType.recently), // builder: (context, snapshot) { @@ -133,15 +133,15 @@ class _ConnectionPageState extends State { // return Offstage(); // } // }), - // FutureBuilder( - // future: buildAddressBook(context), - // builder: (context, snapshot) { - // if (snapshot.hasData) { - // return snapshot.data!; - // } else { - // return Offstage(); - // } - // }), + FutureBuilder( + future: buildAddressBook(context), + builder: (context, snapshot) { + if (snapshot.hasData) { + return snapshot.data!; + } else { + return Offstage(); + } + }), ]).paddingSymmetric(horizontal: 12.0, vertical: 4.0)) ], )), @@ -334,154 +334,6 @@ class _ConnectionPageState extends State { return true; } - /// Get all the saved peers. - Future getPeers({RemoteType rType = RemoteType.recently}) async { - final space = 8.0; - final cards = []; - List peers; - switch (rType) { - case RemoteType.recently: - peers = gFFI.peers(); - break; - case RemoteType.favorite: - peers = await gFFI.bind.mainGetFav().then((peers) async { - final peersEntities = await Future.wait(peers - .map((id) => gFFI.bind.mainGetPeers(id: id)) - .toList(growable: false)) - .then((peers_str) { - final len = peers_str.length; - final ps = List.empty(growable: true); - for (var i = 0; i < len; i++) { - print("${peers[i]}: ${peers_str[i]}"); - ps.add(Peer.fromJson(peers[i], jsonDecode(peers_str[i])['info'])); - } - return ps; - }); - return peersEntities; - }); - break; - case RemoteType.discovered: - peers = await gFFI.bind.mainGetLanPeers().then((peers_string) { - print(peers_string); - return []; - }); - break; - case RemoteType.addressBook: - peers = gFFI.abModel.peers.map((e) { - return Peer.fromJson(e['id'], e); - }).toList(); - break; - } - peers.forEach((p) { - var deco = Rx(BoxDecoration( - border: Border.all(color: Colors.transparent, width: 1.0), - borderRadius: BorderRadius.circular(20))); - cards.add(Obx( - () => Offstage( - offstage: !hitTag(gFFI.abModel.selectedTags, p.tags) && - rType == RemoteType.addressBook, - child: Container( - width: 225, - height: 150, - child: Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20)), - child: MouseRegion( - onEnter: (evt) { - deco.value = BoxDecoration( - border: Border.all(color: Colors.blue, width: 1.0), - borderRadius: BorderRadius.circular(20)); - }, - onExit: (evt) { - deco.value = BoxDecoration( - border: - Border.all(color: Colors.transparent, width: 1.0), - borderRadius: BorderRadius.circular(20)); - }, - child: Obx( - () => Container( - decoration: deco.value, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: Container( - decoration: BoxDecoration( - color: - str2color('${p.id}${p.platform}', 0x7f), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20), - topRight: Radius.circular(20), - ), - ), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - Container( - padding: const EdgeInsets.all(6), - child: getPlatformImage( - '${p.platform}'), - ), - Row( - children: [ - Expanded( - child: Tooltip( - message: - '${p.username}@${p.hostname}', - child: Text( - '${p.username}@${p.hostname}', - style: TextStyle( - color: Colors.white70, - fontSize: 12), - textAlign: TextAlign.center, - overflow: - TextOverflow.ellipsis, - ), - ), - ), - ], - ), - ], - ).paddingAll(4.0), - ), - ], - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text("${p.id}"), - InkWell( - child: Icon(Icons.more_vert), - onTapDown: (e) { - final x = e.globalPosition.dx; - final y = e.globalPosition.dy; - _menuPos = - RelativeRect.fromLTRB(x, y, x, y); - }, - onTap: () { - showPeerMenu(context, p.id, rType); - }), - ], - ).paddingSymmetric(vertical: 8.0, horizontal: 12.0) - ], - ), - ), - ), - ))), - ), - )); - }); - return SingleChildScrollView( - child: Wrap(children: cards, spacing: space, runSpacing: space)); - } - /// Show the peer menu and handle user's choice. /// User might remove the peer or send a file to the peer. void showPeerMenu(BuildContext context, String id, RemoteType rType) async { @@ -736,24 +588,8 @@ class _ConnectionPageState extends State { ), ).marginOnly(right: 8.0), Expanded( - child: FutureBuilder( - future: getPeers(rType: RemoteType.addressBook), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [Expanded(child: snapshot.data!)], - ); - } else if (snapshot.hasError) { - return Container( - alignment: Alignment.center, - child: Text('${snapshot.error}')); - } else { - return Container( - alignment: Alignment.center, - child: CircularProgressIndicator()); - } - }), + child: Align( + alignment: Alignment.topLeft, child: AddressBookPeerWidget()), ) ], ); diff --git a/flutter/lib/desktop/widgets/peercard_widget.dart b/flutter/lib/desktop/widgets/peercard_widget.dart index 02b146457..f5a4156c3 100644 --- a/flutter/lib/desktop/widgets/peercard_widget.dart +++ b/flutter/lib/desktop/widgets/peercard_widget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:get/get.dart'; import 'package:contextmenu/contextmenu.dart'; @@ -8,12 +9,18 @@ import '../../models/model.dart'; import '../../models/peer_model.dart'; typedef PopupMenuItemsFunc = Future>> Function(); +enum PeerType { recent, fav, discovered, ab } class _PeerCard extends StatefulWidget { final Peer peer; final PopupMenuItemsFunc popupMenuItemsFunc; + final PeerType type; - _PeerCard({required this.peer, required this.popupMenuItemsFunc, Key? key}) + _PeerCard( + {required this.peer, + required this.popupMenuItemsFunc, + Key? key, + required this.type}) : super(key: key); @override @@ -78,15 +85,28 @@ class _PeerCardState extends State<_PeerCard> { Row( children: [ Expanded( - child: Tooltip( - message: '${peer.username}@${peer.hostname}', - child: Text( - '${peer.username}@${peer.hostname}', - style: TextStyle( - color: Colors.white70, fontSize: 12), - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - ), + child: FutureBuilder( + future: gFFI.getPeerOption(peer.id, 'alias'), + builder: (_, snapshot) { + if (snapshot.hasData) { + final name = snapshot.data!.isEmpty + ? '${peer.username}@${peer.hostname}' + : snapshot.data!; + return Tooltip( + message: name, + child: Text( + name, + style: TextStyle( + color: Colors.white70, + fontSize: 12), + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + ), + ); + } else { + return Text(translate("Loading")); + } + }, ), ), ], @@ -169,6 +189,8 @@ class _PeerCardState extends State<_PeerCard> { setState(() {}); } else if (value == 'ab-edit-tag') { _abEditTag(id); + } else if (value == 'rename') { + _rename(id); } } @@ -265,15 +287,101 @@ class _PeerCardState extends State<_PeerCard> { ); }); } + + void _rename(String id) async { + var isInProgress = false; + var name = await gFFI.getPeerOption(id, 'alias'); + if (widget.type == PeerType.ab) { + final peer = gFFI.abModel.peers.firstWhere((p) => id == p['id']); + if (peer == null) { + // this should not happen + } else { + name = peer['alias'] ?? ""; + } + } + final k = GlobalKey(); + DialogManager.show((setState, close) { + return CustomAlertDialog( + title: Text(translate("Rename")), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Form( + key: k, + child: TextFormField( + controller: TextEditingController(text: name), + decoration: InputDecoration(border: OutlineInputBorder()), + onChanged: (newStr) { + name = newStr; + }, + validator: (s) { + if (s == null || s.isEmpty) { + return translate("Empty"); + } + return null; + }, + onSaved: (s) { + name = s ?? "unnamed"; + }, + ), + ), + ), + Offstage(offstage: !isInProgress, child: LinearProgressIndicator()) + ], + ), + actions: [ + TextButton( + onPressed: () { + close(); + }, + child: Text(translate("Cancel"))), + TextButton( + onPressed: () async { + setState(() { + isInProgress = true; + }); + if (k.currentState != null) { + if (k.currentState!.validate()) { + k.currentState!.save(); + await gFFI.setPeerOption(id, 'alias', name); + if (widget.type == PeerType.ab) { + gFFI.abModel.setPeerOption(id, 'alias', name); + await gFFI.abModel.updateAb(); + } else { + Future.delayed(Duration.zero, () { + this.setState(() {}); + }); + } + close(); + } + } + setState(() { + isInProgress = false; + }); + }, + child: Text(translate("OK"))), + ], + ); + }); + } } abstract class BasePeerCard extends StatelessWidget { final Peer peer; - BasePeerCard({required this.peer, Key? key}) : super(key: key); + final PeerType type; + + BasePeerCard({required this.peer, required this.type, Key? key}) + : super(key: key); @override Widget build(BuildContext context) { - return _PeerCard(peer: peer, popupMenuItemsFunc: _getPopupMenuItems); + return _PeerCard( + peer: peer, + popupMenuItemsFunc: _getPopupMenuItems, + type: type, + ); } @protected @@ -281,7 +389,8 @@ abstract class BasePeerCard extends StatelessWidget { } class RecentPeerCard extends BasePeerCard { - RecentPeerCard({required Peer peer, Key? key}) : super(peer: peer, key: key); + RecentPeerCard({required Peer peer, Key? key}) + : super(peer: peer, key: key, type: PeerType.recent); Future>> _getPopupMenuItems() async { debugPrint("call RecentPeerCard _getPopupMenuItems"); @@ -297,15 +406,13 @@ class RecentPeerCard extends BasePeerCard { PopupMenuItem( child: Text(translate('Unremember Password')), value: 'unremember-password'), - PopupMenuItem( - child: Text(translate('Edit Tag')), value: 'ab-edit-tag'), ]; } } class FavoritePeerCard extends BasePeerCard { FavoritePeerCard({required Peer peer, Key? key}) - : super(peer: peer, key: key); + : super(peer: peer, key: key, type: PeerType.fav); Future>> _getPopupMenuItems() async { debugPrint("call FavoritePeerCard _getPopupMenuItems"); @@ -329,7 +436,7 @@ class FavoritePeerCard extends BasePeerCard { class DiscoveredPeerCard extends BasePeerCard { DiscoveredPeerCard({required Peer peer, Key? key}) - : super(peer: peer, key: key); + : super(peer: peer, key: key, type: PeerType.discovered); Future>> _getPopupMenuItems() async { debugPrint("call DiscoveredPeerCard _getPopupMenuItems"); @@ -345,15 +452,13 @@ class DiscoveredPeerCard extends BasePeerCard { PopupMenuItem( child: Text(translate('Unremember Password')), value: 'unremember-password'), - PopupMenuItem( - child: Text(translate('Edit Tag')), value: 'ab-edit-tag'), ]; } } class AddressBookPeerCard extends BasePeerCard { AddressBookPeerCard({required Peer peer, Key? key}) - : super(peer: peer, key: key); + : super(peer: peer, key: key, type: PeerType.ab); Future>> _getPopupMenuItems() async { debugPrint("call AddressBookPeerCard _getPopupMenuItems"); @@ -372,6 +477,8 @@ class AddressBookPeerCard extends BasePeerCard { value: 'unremember-password'), PopupMenuItem( child: Text(translate('Add to Favorites')), value: 'add-fav'), + PopupMenuItem( + child: Text(translate('Edit Tag')), value: 'ab-edit-tag'), ]; } } diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index 165e3d8d1..bfdb6fa1a 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -141,6 +141,16 @@ class AbModel with ChangeNotifier { } } + void setPeerOption(String id, String key, String value) { + final it = peers.where((p0) => p0['id'] == id); + if (it.isEmpty) { + debugPrint("${id} is not exists"); + return; + } else { + it.first[key] = value; + } + } + void clear() { peers.clear(); tags.clear(); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index bc64ff6f5..67313623c 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1010,6 +1010,14 @@ class FFI { return bind.mainSetLocalOption(key: key, value: value); } + Future getPeerOption(String id, String key) { + return bind.mainGetPeerOption(id: id, key: key); + } + + Future setPeerOption(String id, String key, String value) { + return bind.mainSetPeerOption(id: id, key: key, value: value); + } + void setOption(String name, String value) { Map res = Map() ..["name"] = name diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 57e7db87d..30c9c7591 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -22,9 +22,9 @@ use crate::ui_interface; use crate::ui_interface::{ change_id, check_connect_status, get_api_server, get_app_name, get_async_job_status, get_connect_status, get_fav, get_id, get_lan_peers, get_license, get_local_option, get_options, - get_peer, get_socks, get_sound_inputs, get_uuid, get_version, has_rendezvous_service, - is_ok_change_id, post_request, set_local_option, set_options, set_socks, store_fav, - test_if_valid_server, using_public_server, + get_peer, get_peer_option, get_socks, get_sound_inputs, get_uuid, get_version, + has_rendezvous_service, is_ok_change_id, post_request, set_local_option, set_options, + set_peer_option, set_socks, store_fav, test_if_valid_server, using_public_server, }; fn initialize(app_dir: &str) { @@ -496,6 +496,14 @@ pub fn main_get_uuid() -> String { get_uuid() } +pub fn main_get_peer_option(id: String, key: String) -> String { + get_peer_option(id, key) +} + +pub fn main_set_peer_option(id: String, key: String, value: String) { + set_peer_option(id, key, value) +} + /// FFI for **get** commands which are idempotent. /// Return result in c string. ///