From 8d23c11312cd001cb905ef631cc971595a72b27c Mon Sep 17 00:00:00 2001 From: csf Date: Sun, 9 Oct 2022 19:41:50 +0900 Subject: [PATCH] fix abModel multi request and state didn't refresh bug --- flutter/lib/common/widgets/address_book.dart | 62 +++---- flutter/lib/common/widgets/peer_tab_page.dart | 3 +- .../lib/desktop/pages/desktop_home_page.dart | 1 - .../desktop/pages/desktop_setting_page.dart | 14 +- flutter/lib/main.dart | 3 - flutter/lib/mobile/pages/connection_page.dart | 16 +- flutter/lib/mobile/pages/scan_page.dart | 7 +- flutter/lib/mobile/pages/settings_page.dart | 168 ++---------------- flutter/lib/models/ab_model.dart | 3 +- flutter/lib/models/model.dart | 4 - flutter/lib/models/user_model.dart | 60 ++++++- 11 files changed, 111 insertions(+), 230 deletions(-) diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index 49d2eaf04..52189c8b1 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -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; @@ -30,7 +29,7 @@ class _AddressBookState extends State { @override Widget build(BuildContext context) => FutureBuilder( - future: buildAddressBook(context), + future: buildBody(context), builder: (context, snapshot) { if (snapshot.hasData) { return snapshot.data!; @@ -44,7 +43,7 @@ class _AddressBookState extends State { if (isDesktop) { loginDialog().then((success) { if (success) { - setState(() {}); + gFFI.abModel.pullAb(); } }); } else { @@ -52,41 +51,30 @@ class _AddressBookState extends State { } } - Future 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 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 _buildAddressBook(context); + } + }); } Widget _buildShowError(String error) { diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index 9a5503e26..81559a3d3 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -54,7 +54,8 @@ class _PeerTabPageState extends State bind.mainDiscover(); break; case 3: - gFFI.abModel.pullAb(); + + /// AddressBook initState will refresh ab state break; } } diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index fcc8c4991..6cb78b6a7 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -500,7 +500,6 @@ Future loginDialog() async { close(); } - // 登录dialog return CustomAlertDialog( title: Text(translate("Login")), content: ConstrainedBox( diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 1c28fdd98..1534f9394 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -761,20 +761,18 @@ class _AccountState extends State<_Account> { Widget accountAction() { return _futureBuilder(future: () async { return await gFFI.userModel.getUserName(); - }(), hasData: (data) { - String username = data as String; - return _Button( - username.isEmpty ? 'Login' : 'Logout', + }(), hasData: (_) { + return Obx(() => _Button( + gFFI.userModel.userName.value.isEmpty ? 'Login' : 'Logout', () => { - username.isEmpty + gFFI.userModel.userName.value.isEmpty ? loginDialog().then((success) { if (success) { - // refresh frame - setState(() {}); + gFFI.abModel.pullAb(); } }) : gFFI.userModel.logOut() - }); + })); }); } } diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 230199431..0d1123e05 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -20,7 +20,6 @@ import 'common.dart'; import 'consts.dart'; import 'mobile/pages/home_page.dart'; import 'mobile/pages/server_page.dart'; -import 'mobile/pages/settings_page.dart'; import 'models/platform_model.dart'; int? windowId; @@ -82,7 +81,6 @@ Future initEnv(String appType) async { // focus on multi-ffi on desktop first await initGlobalFFI(); // await Firebase.initializeApp(); - refreshCurrentUser(); _registerEventHandler(); } @@ -267,7 +265,6 @@ class _AppState extends State { ChangeNotifierProvider.value(value: gFFI.imageModel), ChangeNotifierProvider.value(value: gFFI.cursorModel), ChangeNotifierProvider.value(value: gFFI.canvasModel), - ChangeNotifierProvider.value(value: gFFI.userModel), ], child: GetMaterialApp( navigatorKey: globalKey, diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index 9d105ade6..e99226c4d 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -206,20 +206,14 @@ class WebMenu extends StatefulWidget { } class _WebMenuState extends State { - String? username; String url = ""; @override void initState() { super.initState(); () async { - final usernameRes = await getUsername(); - final urlRes = await getUrl(); + final urlRes = await bind.mainGetApiServer(); var update = false; - if (usernameRes != username) { - username = usernameRes; - update = true; - } if (urlRes != url) { url = urlRes; update = true; @@ -256,9 +250,9 @@ class _WebMenuState extends State { : [ PopupMenuItem( value: "login", - child: Text(username == null + child: Text(gFFI.userModel.userName.value.isEmpty ? translate("Login") - : '${translate("Logout")} ($username)'), + : '${translate("Logout")} (${gFFI.userModel.userName.value})'), ) ]) + [ @@ -276,10 +270,10 @@ class _WebMenuState extends State { showAbout(gFFI.dialogManager); } if (value == 'login') { - if (username == null) { + if (gFFI.userModel.userName.value.isEmpty) { showLogin(gFFI.dialogManager); } else { - logout(gFFI.dialogManager); + gFFI.userModel.logOut(); } } if (value == 'scan') { diff --git a/flutter/lib/mobile/pages/scan_page.dart b/flutter/lib/mobile/pages/scan_page.dart index 2487c0f58..3bd381d92 100644 --- a/flutter/lib/mobile/pages/scan_page.dart +++ b/flutter/lib/mobile/pages/scan_page.dart @@ -263,12 +263,13 @@ void showServerSettingsWithValue(String id, String relay, String key, if (id != id0) { bind.mainSetOption(key: "custom-rendezvous-server", value: id); } - if (relay != relay0) + if (relay != relay0) { bind.mainSetOption(key: "relay-server", value: relay); + } if (key != key0) bind.mainSetOption(key: "key", value: key); - if (api != api0) + if (api != api0) { bind.mainSetOption(key: "api-server", value: api); - gFFI.ffiModel.updateUser(); + } close(); } setState(() { diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 4f1640a03..c555314d0 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:http/http.dart' as http; import 'package:provider/provider.dart'; import 'package:settings_ui/settings_ui.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -41,8 +40,6 @@ var _localIP = ""; var _directAccessPort = ""; class _SettingsState extends State with WidgetsBindingObserver { - String? username; - @override void initState() { super.initState(); @@ -54,12 +51,6 @@ class _SettingsState extends State with WidgetsBindingObserver { update = await updateIgnoreBatteryStatus(); } - final usernameRes = await getUsername(); - if (usernameRes != username) { - update = true; - username = usernameRes; - } - final enableAbrRes = await bind.mainGetOption(key: "enable-abr") != "N"; if (enableAbrRes != _enableAbr) { update = true; @@ -273,15 +264,15 @@ class _SettingsState extends State with WidgetsBindingObserver { title: Text(translate("Account")), tiles: [ SettingsTile.navigation( - title: Text(username == null + title: Obx(() => Text(gFFI.userModel.userName.value.isEmpty ? translate("Login") - : '${translate("Logout")} ($username)'), + : '${translate("Logout")} (${gFFI.userModel.userName.value})')), leading: Icon(Icons.person), onPressed: (context) { - if (username == null) { + if (gFFI.userModel.userName.value.isEmpty) { showLogin(gFFI.dialogManager); } else { - logout(gFFI.dialogManager); + gFFI.userModel.logOut(); } }, ), @@ -438,130 +429,6 @@ void showAbout(OverlayDialogManager dialogManager) { }, clickMaskDismiss: true, backDismiss: true); } -Future login(String name, String pass) async { -/* js test CORS -const data = { username: 'example', password: 'xx' }; - -fetch('http://localhost:21114/api/login', { - method: 'POST', // or 'PUT' - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), -}) -.then(response => response.json()) -.then(data => { - console.log('Success:', data); -}) -.catch((error) => { - console.error('Error:', error); -}); -*/ - final url = getUrl(); - final body = { - 'username': name, - 'password': pass, - 'id': bind.mainGetMyId(), - 'uuid': bind.mainGetUuid() - }; - try { - final response = await http.post(Uri.parse('$url/api/login'), - headers: {"Content-Type": "application/json"}, body: json.encode(body)); - return parseResp(response.body); - } catch (e) { - print(e); - return 'Failed to access $url'; - } -} - -String parseResp(String body) { - final data = json.decode(body); - final error = data['error']; - if (error != null) { - return error!; - } - final token = data['access_token']; - if (token != null) { - bind.mainSetOption(key: "access_token", value: token); - } - final info = data['user']; - if (info != null) { - final value = json.encode(info); - bind.mainSetOption(key: "user_info", value: value); - gFFI.ffiModel.updateUser(); - } - return ''; -} - -void refreshCurrentUser() async { - final token = await bind.mainGetOption(key: "access_token"); - if (token == '') return; - final url = getUrl(); - final body = {'id': bind.mainGetMyId(), 'uuid': bind.mainGetUuid()}; - try { - final response = await http.post(Uri.parse('$url/api/currentUser'), - headers: { - "Content-Type": "application/json", - "Authorization": "Bearer $token" - }, - body: json.encode(body)); - final status = response.statusCode; - if (status == 401 || status == 400) { - resetToken(); - return; - } - parseResp(response.body); - } catch (e) { - print('$e'); - } -} - -void logout(OverlayDialogManager dialogManager) async { - final token = await bind.mainGetOption(key: "access_token"); - if (token == '') return; - final url = getUrl(); - final body = {'id': bind.mainGetMyId(), 'uuid': bind.mainGetUuid()}; - try { - await http.post(Uri.parse('$url/api/logout'), - headers: { - "Content-Type": "application/json", - "Authorization": "Bearer $token" - }, - body: json.encode(body)); - } catch (e) { - showToast('Failed to access $url'); - } - resetToken(); -} - -void resetToken() async { - await bind.mainSetOption(key: "access_token", value: ""); - await bind.mainSetOption(key: "user_info", value: ""); - gFFI.ffiModel.updateUser(); -} - -Future getUrl() async { - var url = await bind.mainGetOption(key: "api-server"); - if (url == '') { - url = await bind.mainGetOption(key: "custom-rendezvous-server"); - if (url != '') { - if (url.contains(':')) { - final tmp = url.split(':'); - if (tmp.length == 2) { - var port = int.parse(tmp[1]) - 2; - url = 'http://${tmp[0]}:$port'; - } - } else { - url = 'http://$url:21114'; - } - } - } - if (url == '') { - url = 'https://admin.rustdesk.com'; - } - return url; -} - void showLogin(OverlayDialogManager dialogManager) { final passwordController = TextEditingController(); final nameController = TextEditingController(); @@ -615,15 +482,17 @@ void showLogin(OverlayDialogManager dialogManager) { setState(() { loading = true; }); - final e = await login(name, pass); + final resp = await gFFI.userModel.login(name, pass); setState(() { loading = false; - error = e; }); - if (e == "") { - close(); + if (resp.containsKey('error')) { + error = resp['error']; + return; } + gFFI.abModel.pullAb(); } + close(); }, child: Text(translate('OK')), ), @@ -632,23 +501,6 @@ void showLogin(OverlayDialogManager dialogManager) { }); } -Future getUsername() async { - final token = await bind.mainGetOption(key: "access_token"); - String? username; - if (token != "") { - final info = await bind.mainGetOption(key: "user_info"); - if (info != "") { - try { - Map tmp = json.decode(info); - username = tmp["name"]; - } catch (e) { - print('$e'); - } - } - } - return username; -} - class ScanButton extends StatelessWidget { @override Widget build(BuildContext context) { diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index ae41e07e6..5a055fd14 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -24,8 +24,9 @@ class AbModel { FFI? get _ffi => parent.target; Future pullAb() async { + if (_ffi!.userModel.userName.isEmpty) return; abLoading.value = true; - // request + abError.value = ""; final api = "${await bind.mainGetApiServer()}/api/ab/get"; try { final resp = diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 833ab32a0..c646f5285 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -82,10 +82,6 @@ class FfiModel with ChangeNotifier { notifyListeners(); } - updateUser() { - notifyListeners(); - } - bool keyboard() => _permissions['keyboard'] != false; clear() { diff --git a/flutter/lib/models/user_model.dart b/flutter/lib/models/user_model.dart index e195c205d..e9990efa9 100644 --- a/flutter/lib/models/user_model.dart +++ b/flutter/lib/models/user_model.dart @@ -9,11 +9,65 @@ import '../common.dart'; import 'model.dart'; import 'platform_model.dart'; -class UserModel extends ChangeNotifier { +class UserModel { var userName = "".obs; WeakReference parent; - UserModel(this.parent); + UserModel(this.parent) { + refreshCurrentUser(); + } + + void refreshCurrentUser() async { + await getUserName(); + final token = await bind.mainGetLocalOption(key: "access_token"); + if (token == '') return; + final url = await bind.mainGetApiServer(); + final body = { + 'id': await bind.mainGetMyId(), + 'uuid': await bind.mainGetUuid() + }; + try { + final response = await http.post(Uri.parse('$url/api/currentUser'), + headers: { + "Content-Type": "application/json", + "Authorization": "Bearer $token" + }, + body: json.encode(body)); + final status = response.statusCode; + if (status == 401 || status == 400) { + resetToken(); + return; + } + await _parseResp(response.body); + } catch (e) { + print('Failed to refreshCurrentUser: $e'); + } + } + + void resetToken() async { + await bind.mainSetLocalOption(key: "access_token", value: ""); + await bind.mainSetLocalOption(key: "user_info", value: ""); + userName.value = ""; + } + + Future _parseResp(String body) async { + final data = json.decode(body); + final error = data['error']; + if (error != null) { + return error!; + } + final token = data['access_token']; + if (token != null) { + await bind.mainSetLocalOption(key: "access_token", value: token); + } + final info = data['user']; + if (info != null) { + final value = json.encode(info); + await bind.mainSetOption(key: "user_info", value: value); + userName.value = info["name"]; + } + return ''; + } Future getUserName() async { if (userName.isNotEmpty) { @@ -29,6 +83,7 @@ class UserModel extends ChangeNotifier { } Future logOut() async { + // TODO show toast debugPrint("start logout"); final url = await bind.mainGetApiServer(); final _ = await http.post(Uri.parse("$url/api/logout"), @@ -44,7 +99,6 @@ class UserModel extends ChangeNotifier { ]); parent.target?.abModel.clear(); userName.value = ""; - notifyListeners(); } Future> login(String userName, String pass) async {