From 6b99d4d82eeb0513cc7130a80ee2d2d6198d625e Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 29 Jul 2022 12:03:24 +0800 Subject: [PATCH 1/2] 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. /// From 608f02ea219888729797d60a626fb31ae9682f67 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 29 Jul 2022 16:47:24 +0800 Subject: [PATCH 2/2] feat: dark theme Signed-off-by: Kingtous --- flutter/lib/common.dart | 21 +++++++ .../lib/desktop/pages/connection_page.dart | 10 +--- .../lib/desktop/pages/desktop_home_page.dart | 20 +++++-- .../lib/desktop/pages/file_manager_page.dart | 2 +- .../lib/desktop/widgets/peercard_widget.dart | 58 +++++++++++-------- flutter/lib/main.dart | 21 ++++--- src/flutter_ffi.rs | 14 +++-- 7 files changed, 97 insertions(+), 49 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index b896fdf9f..47a663768 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -4,6 +4,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/instance_manager.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'models/model.dart'; @@ -38,6 +39,24 @@ class MyTheme { static const Color idColor = Color(0xFF00B6F0); static const Color darkGray = Color(0xFFB9BABC); static const Color dark = Colors.black87; + + static ThemeData lightTheme = ThemeData( + brightness: Brightness.light, + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + tabBarTheme: TabBarTheme(labelColor: Colors.black87), + ); + static ThemeData darkTheme = ThemeData( + brightness: Brightness.dark, + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + tabBarTheme: TabBarTheme(labelColor: Colors.white70)); +} + +bool isDarkTheme() { + final isDark = "Y" == Get.find().getString("darkTheme"); + debugPrint("current is dark theme: $isDark"); + return isDark; } final ButtonStyle flatButtonStyle = TextButton.styleFrom( @@ -327,4 +346,6 @@ Future initGlobalFFI() async { await _globalFFI.ffiModel.init(); // trigger connection status updater await _globalFFI.bind.mainCheckConnectStatus(); + // global shared preference + await Get.putAsync(() => SharedPreferences.getInstance()); } \ No newline at end of file diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index 7a5c47c06..fe11857f3 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -16,7 +16,6 @@ import '../../mobile/pages/home_page.dart'; import '../../mobile/pages/scan_page.dart'; import '../../mobile/pages/settings_page.dart'; import '../../models/model.dart'; -import '../../models/peer_model.dart'; enum RemoteType { recently, favorite, discovered, addressBook } @@ -60,7 +59,7 @@ class _ConnectionPageState extends State { Widget build(BuildContext context) { if (_idController.text.isEmpty) _idController.text = gFFI.getId(); return Container( - decoration: BoxDecoration(color: MyTheme.grayBg), + decoration: BoxDecoration(color: isDarkTheme() ? null : MyTheme.grayBg), child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, @@ -83,7 +82,6 @@ class _ConnectionPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ TabBar( - labelColor: Colors.black87, isScrollable: true, indicatorSize: TabBarIndicatorSize.label, tabs: [ @@ -205,7 +203,7 @@ class _ConnectionPageState extends State { width: 500, padding: EdgeInsets.symmetric(horizontal: 16, vertical: 24), decoration: BoxDecoration( - color: MyTheme.white, + color: isDarkTheme() ? null : MyTheme.white, borderRadius: const BorderRadius.all(Radius.circular(13)), ), child: Ink( @@ -235,13 +233,11 @@ class _ConnectionPageState extends State { helperStyle: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, - color: MyTheme.dark, ), labelStyle: TextStyle( fontWeight: FontWeight.w600, fontSize: 26, letterSpacing: 0.2, - color: MyTheme.dark, ), ), controller: _idController, @@ -269,7 +265,6 @@ class _ConnectionPageState extends State { translate( "Transfer File", ), - style: TextStyle(color: MyTheme.dark), ), ), ), @@ -528,7 +523,6 @@ class _ConnectionPageState extends State { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: BorderSide(color: MyTheme.grayBg)), - color: Colors.white, child: Container( width: 200, height: double.infinity, diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 47c066c9c..a162c3535 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -10,6 +10,7 @@ import 'package:flutter_hbb/desktop/widgets/titlebar_widget.dart'; import 'package:flutter_hbb/models/model.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:tray_manager/tray_manager.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -64,7 +65,6 @@ class _DesktopHomePageState extends State with TrayListener { return ChangeNotifierProvider.value( value: gFFI.serverModel, child: Container( - decoration: BoxDecoration(color: MyTheme.white), child: Column( children: [ buildTip(context), @@ -339,13 +339,24 @@ class _DesktopHomePageState extends State with TrayListener { super.dispose(); } + void changeTheme(String choice) async { + if (choice == "Y") { + Get.changeTheme(MyTheme.darkTheme); + } else { + Get.changeTheme(MyTheme.lightTheme); + } + Get.find().setString("darkTheme", choice); + } + void onSelectMenu(String value) { if (value.startsWith('enable-')) { final option = gFFI.getOption(value); gFFI.setOption(value, option == "N" ? "" : "N"); } else if (value.startsWith('allow-')) { final option = gFFI.getOption(value); - gFFI.setOption(value, option == "Y" ? "" : "Y"); + final choice = option == "Y" ? "" : "Y"; + gFFI.setOption(value, choice); + changeTheme(choice); } else if (value == "stop-service") { final option = gFFI.getOption(value); gFFI.setOption(value, option == "Y" ? "" : "Y"); @@ -367,9 +378,8 @@ class _DesktopHomePageState extends State with TrayListener { } PopupMenuItem genEnablePopupMenuItem(String label, String value) { - final isEnable = label.startsWith('enable-') - ? gFFI.getOption(value) != "N" - : gFFI.getOption(value) != "Y"; + final v = gFFI.getOption(value); + final isEnable = value.startsWith('enable-') ? v != "N" : v == "Y"; return PopupMenuItem( child: Row( children: [ diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index de6d981ee..e37f56404 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -72,7 +72,7 @@ class _FileManagerPageState extends State return false; }, child: Scaffold( - backgroundColor: MyTheme.grayBg, + backgroundColor: isDarkTheme() ? MyTheme.dark : MyTheme.grayBg, body: Row( children: [ Flexible(flex: 3, child: body(isLocal: true)), diff --git a/flutter/lib/desktop/widgets/peercard_widget.dart b/flutter/lib/desktop/widgets/peercard_widget.dart index f5a4156c3..39acd0bf2 100644 --- a/flutter/lib/desktop/widgets/peercard_widget.dart +++ b/flutter/lib/desktop/widgets/peercard_widget.dart @@ -1,8 +1,7 @@ +import 'package:contextmenu/contextmenu.dart'; 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'; import '../../common.dart'; import '../../models/model.dart'; @@ -16,11 +15,10 @@ class _PeerCard extends StatefulWidget { final PopupMenuItemsFunc popupMenuItemsFunc; final PeerType type; - _PeerCard( - {required this.peer, - required this.popupMenuItemsFunc, - Key? key, - required this.type}) + _PeerCard({required this.peer, + required this.popupMenuItemsFunc, + Key? key, + required this.type}) : super(key: key); @override @@ -28,11 +26,13 @@ class _PeerCard extends StatefulWidget { } /// State for the connection page. -class _PeerCardState extends State<_PeerCard> { +class _PeerCardState extends State<_PeerCard> + with AutomaticKeepAliveClientMixin { var _menuPos; @override Widget build(BuildContext context) { + super.build(context); final peer = super.widget.peer; var deco = Rx(BoxDecoration( border: Border.all(color: Colors.transparent, width: 1.0), @@ -54,10 +54,9 @@ class _PeerCardState extends State<_PeerCard> { )); } - Widget _buildPeerTile( - BuildContext context, Peer peer, Rx deco) { + Widget _buildPeerTile(BuildContext context, Peer peer, Rx deco) { return Obx( - () => Container( + () => Container( decoration: deco.value, child: Column( mainAxisSize: MainAxisSize.min, @@ -104,7 +103,16 @@ class _PeerCardState extends State<_PeerCard> { ), ); } else { - return Text(translate("Loading")); + // alias has not arrived + return Center( + child: Text( + '${peer.username}@${peer.hostname}', + style: TextStyle( + color: Colors.white70, + fontSize: 12), + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + )); } }, ), @@ -127,7 +135,7 @@ class _PeerCardState extends State<_PeerCard> { child: CircleAvatar( radius: 5, backgroundColor: - peer.online ? Colors.green : Colors.yellow)), + peer.online ? Colors.green : Colors.yellow)), Text('${peer.id}') ]), InkWell( @@ -175,13 +183,12 @@ class _PeerCardState extends State<_PeerCard> { ); if (value == 'remove') { setState(() => gFFI.setByName('remove', '$id')); - () async { + () async { removePreference(id); }(); } else if (value == 'file') { _connect(id, isFileTransfer: true); - } else if (value == 'add-fav') { - } else if (value == 'connect') { + } else if (value == 'add-fav') {} else if (value == 'connect') { _connect(id, isFileTransfer: false); } else if (value == 'ab-delete') { gFFI.abModel.deletePeer(id); @@ -191,6 +198,8 @@ class _PeerCardState extends State<_PeerCard> { _abEditTag(id); } else if (value == 'rename') { _rename(id); + } else if (value == 'unremember-password') { + await gFFI.bind.mainForgetPassword(id: id); } } @@ -211,7 +220,7 @@ class _PeerCardState extends State<_PeerCard> { child: GestureDetector( onTap: onTap, child: Obx( - () => Container( + () => Container( decoration: BoxDecoration( color: rxTags.contains(tagName) ? Colors.blue : null, border: Border.all(color: MyTheme.darkGray), @@ -255,12 +264,12 @@ class _PeerCardState extends State<_PeerCard> { child: Wrap( children: tags .map((e) => _buildTag(e, selectedTag, onTap: () { - if (selectedTag.contains(e)) { - selectedTag.remove(e); - } else { - selectedTag.add(e); - } - })) + if (selectedTag.contains(e)) { + selectedTag.remove(e); + } else { + selectedTag.add(e); + } + })) .toList(growable: false), ), ), @@ -366,6 +375,9 @@ class _PeerCardState extends State<_PeerCard> { ); }); } + + @override + bool get wantKeepAlive => true; } abstract class BasePeerCard extends StatelessWidget { diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index bb6684438..f2ebb3134 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -6,6 +6,7 @@ import 'package:flutter_hbb/desktop/screen/desktop_file_transfer_screen.dart'; import 'package:flutter_hbb/desktop/screen/desktop_remote_screen.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; import 'package:get/route_manager.dart'; import 'package:provider/provider.dart'; @@ -32,6 +33,10 @@ Future main(List args) async { runRustDeskApp(args); } +ThemeData getCurrentTheme() { + return isDarkTheme() ? MyTheme.darkTheme : MyTheme.darkTheme; +} + void runRustDeskApp(List args) async { if (!isDesktop) { runApp(App()); @@ -47,12 +52,17 @@ void runRustDeskApp(List args) async { WindowType wType = type.windowType; switch (wType) { case WindowType.RemoteDesktop: - runApp(DesktopRemoteScreen( - params: argument, + runApp(GetMaterialApp( + theme: getCurrentTheme(), + home: DesktopRemoteScreen( + params: argument, + ), )); break; case WindowType.FileTransfer: - runApp(DesktopFileTransferScreen(params: argument)); + runApp(GetMaterialApp( + theme: getCurrentTheme(), + home: DesktopFileTransferScreen(params: argument))); break; default: break; @@ -85,10 +95,7 @@ class App extends StatelessWidget { navigatorKey: globalKey, debugShowCheckedModeBanner: false, title: 'RustDesk', - theme: ThemeData( - primarySwatch: Colors.blue, - visualDensity: VisualDensity.adaptivePlatformDensity, - ), + theme: getCurrentTheme(), home: isDesktop ? DesktopHomePage() : !isAndroid diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 30c9c7591..afbe35ec8 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -20,11 +20,11 @@ use crate::flutter::{self, Session, SESSIONS}; use crate::start_server; 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_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, + change_id, check_connect_status, forget_password, 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_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) { @@ -504,6 +504,10 @@ pub fn main_set_peer_option(id: String, key: String, value: String) { set_peer_option(id, key, value) } +pub fn main_forget_password(id: String) { + forget_password(id) +} + /// FFI for **get** commands which are idempotent. /// Return result in c string. ///