diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index 1b26acb57..d5441acfe 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -3,6 +3,7 @@ import 'package:flutter_hbb/common/formatter/id_formatter.dart'; import 'package:flutter_hbb/common/widgets/peer_card.dart'; import 'package:flutter_hbb/common/widgets/peers_view.dart'; import 'package:flutter_hbb/desktop/widgets/popup_menu.dart'; +import 'package:flutter_hbb/models/state_model.dart'; import '../../consts.dart'; import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu; import 'package:get/get.dart'; @@ -29,15 +30,18 @@ class _AddressBookState extends State { } @override - Widget build(BuildContext context) => FutureBuilder( - future: buildBody(context), - builder: (context, snapshot) { - if (snapshot.hasData) { - return snapshot.data!; - } else { - return const Offstage(); - } - }); + Widget build(BuildContext context) => Obx(() => Offstage( + offstage: stateGlobal.svcStatus.value != SvcStatus.ready, + child: FutureBuilder( + future: buildBody(context), + builder: (context, snapshot) { + if (snapshot.hasData) { + return snapshot.data!; + } else { + return const Offstage(); + } + }), + )); Future buildBody(BuildContext context) async { return Obx(() { @@ -54,6 +58,9 @@ class _AddressBookState extends State { if (gFFI.abModel.abError.isNotEmpty) { return _buildShowError(gFFI.abModel.abError.value); } + if (gFFI.abModel.fromServer.isFalse) { + return Offstage(); + } return isDesktop ? _buildAddressBookDesktop() : _buildAddressBookMobile(); diff --git a/flutter/lib/common/widgets/login.dart b/flutter/lib/common/widgets/login.dart index c9ac48069..81cfa1f47 100644 --- a/flutter/lib/common/widgets/login.dart +++ b/flutter/lib/common/widgets/login.dart @@ -96,7 +96,7 @@ class ConfigOP { class WidgetOP extends StatefulWidget { final ConfigOP config; final RxString curOP; - final Function(String) cbLogin; + final Function(Map) cbLogin; const WidgetOP({ Key? key, required this.config, @@ -153,9 +153,8 @@ class _WidgetOPState extends State { } if (authBody != null) { _updateTimer?.cancel(); - final String username = authBody['user']['name']; widget.curOP.value = ''; - widget.cbLogin(username); + widget.cbLogin(authBody as Map); } setState(() { @@ -255,7 +254,7 @@ class _WidgetOPState extends State { class LoginWidgetOP extends StatelessWidget { final List ops; final RxString curOP; - final Function(String) cbLogin; + final Function(Map) cbLogin; LoginWidgetOP({ Key? key, @@ -368,7 +367,8 @@ const kAuthReqTypeOidc = 'oidc/'; /// common login dialog for desktop /// call this directly Future loginDialog() async { - var username = TextEditingController(); + var username = + TextEditingController(text: UserModel.getLocalUserInfo()?['name'] ?? ''); var password = TextEditingController(); final userFocusNode = FocusNode()..requestFocus(); Timer(Duration(milliseconds: 100), () => userFocusNode..requestFocus()); @@ -480,8 +480,17 @@ Future loginDialog() async { .where((op) => oidcOptions.contains(op.op.toLowerCase())) .toList(), curOP: curOP, - cbLogin: (String username) { - gFFI.userModel.userName.value = username; + cbLogin: (Map authBody) { + try { + final loginResp = + gFFI.userModel.getLoginResponseFromAuthBody(authBody); + if (loginResp.access_token != null) { + bind.mainSetLocalOption( + key: 'access_token', value: loginResp.access_token!); + } + } catch (e) { + debugPrint('Failed too parse oidc login body: "$authBody"'); + } close(true); }, ), diff --git a/flutter/lib/common/widgets/my_group.dart b/flutter/lib/common/widgets/my_group.dart index 53f789ae1..3642ae85f 100644 --- a/flutter/lib/common/widgets/my_group.dart +++ b/flutter/lib/common/widgets/my_group.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hbb/common/hbbs/hbbs.dart'; import 'package:flutter_hbb/common/widgets/peers_view.dart'; +import 'package:flutter_hbb/models/state_model.dart'; import 'package:get/get.dart'; import '../../common.dart'; @@ -26,15 +27,20 @@ class _MyGroupState extends State { } @override - Widget build(BuildContext context) => FutureBuilder( - future: buildBody(context), - builder: (context, snapshot) { - if (snapshot.hasData) { - return snapshot.data!; - } else { - return const Offstage(); - } - }); + Widget build(BuildContext context) { + return Obx(() => Offstage( + offstage: stateGlobal.svcStatus.value != SvcStatus.ready, + child: FutureBuilder( + future: buildBody(context), + builder: (context, snapshot) { + if (snapshot.hasData) { + return snapshot.data!; + } else { + return const Offstage(); + } + }), + )); + } Future buildBody(BuildContext context) async { return Obx(() { diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index 6d3f8994c..5d0addbca 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -42,7 +42,6 @@ class _ConnectionPageState extends State final FocusNode _idFocusNode = FocusNode(); var svcStopped = Get.find(tag: 'stop-service'); - var svcStatusCode = 0.obs; var svcIsUsingPublicServer = true.obs; bool isWindowMinimized = false; @@ -253,9 +252,9 @@ class _ConnectionPageState extends State width: 8, decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), - color: svcStopped.value || svcStatusCode.value == 0 + color: svcStopped.value || stateGlobal.svcStatus.value == SvcStatus.connecting ? kColorWarn - : (svcStatusCode.value == 1 + : (stateGlobal.svcStatus.value == SvcStatus.ready ? Color.fromARGB(255, 50, 190, 166) : Color.fromARGB(255, 224, 79, 95)), ), @@ -263,9 +262,9 @@ class _ConnectionPageState extends State Text( svcStopped.value ? translate("Service is not running") - : svcStatusCode.value == 0 + : stateGlobal.svcStatus.value == SvcStatus.connecting ? translate("connecting_status") - : svcStatusCode.value == -1 + : stateGlobal.svcStatus.value == SvcStatus.notReady ? translate("not_ready_status") : translate('Ready'), style: TextStyle(fontSize: em)), @@ -286,7 +285,7 @@ class _ConnectionPageState extends State Flexible( child: Offstage( offstage: !(!svcStopped.value && - svcStatusCode.value == 1 && + stateGlobal.svcStatus.value == SvcStatus.ready && svcIsUsingPublicServer.value), child: Row( crossAxisAlignment: CrossAxisAlignment.center, @@ -330,7 +329,25 @@ class _ConnectionPageState extends State updateStatus() async { final status = jsonDecode(await bind.mainGetConnectStatus()) as Map; - svcStatusCode.value = status["status_num"]; + final statusNum = status['status_num'] as int; + final preStatus = stateGlobal.svcStatus.value; + if (statusNum == 0) { + stateGlobal.svcStatus.value = SvcStatus.connecting; + } else if (statusNum == -1) { + stateGlobal.svcStatus.value = SvcStatus.notReady; + } else if (statusNum == 1) { + stateGlobal.svcStatus.value = SvcStatus.ready; + if (preStatus != SvcStatus.ready) { + gFFI.userModel.refreshCurrentUser(); + gFFI.groupModel.pull(); + } + } else { + stateGlobal.svcStatus.value = SvcStatus.notReady; + } + if (stateGlobal.svcStatus.value != SvcStatus.ready) { + gFFI.userModel.isAdmin.value = false; + gFFI.groupModel.reset(); + } svcIsUsingPublicServer.value = await bind.mainIsUsingPublicServer(); } } diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index 133343297..8bcb3f7a6 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -10,12 +10,13 @@ import 'package:http/http.dart' as http; import '../common.dart'; class AbModel { - var abLoading = false.obs; - var abError = "".obs; - var tags = [].obs; - var peers = List.empty(growable: true).obs; + final abLoading = false.obs; + final abError = "".obs; + final tags = [].obs; + final RxBool fromServer = false.obs; + final peers = List.empty(growable: true).obs; - var selectedTags = List.empty(growable: true).obs; + final selectedTags = List.empty(growable: true).obs; WeakReference parent; @@ -49,8 +50,10 @@ class AbModel { } } } + fromServer.value = true; return resp.body; } else { + fromServer.value = true; return ""; } } catch (err) { diff --git a/flutter/lib/models/state_model.dart b/flutter/lib/models/state_model.dart index 155700262..f7b4f8cc2 100644 --- a/flutter/lib/models/state_model.dart +++ b/flutter/lib/models/state_model.dart @@ -6,6 +6,8 @@ import 'package:get/get.dart'; import '../consts.dart'; +enum SvcStatus { notReady, connecting, ready } + class StateGlobal { int _windowId = -1; bool _fullscreen = false; @@ -16,6 +18,7 @@ class StateGlobal { final RxDouble _windowBorderWidth = RxDouble(kWindowBorderWidth); final RxBool showRemoteToolBar = false.obs; final RxInt displaysCount = 0.obs; + final svcStatus = SvcStatus.notReady.obs; // Use for desktop -> remote toolbar -> resolution final Map> _lastResolutionGroupValues = {}; diff --git a/flutter/lib/models/user_model.dart b/flutter/lib/models/user_model.dart index bbb805826..7835e50b1 100644 --- a/flutter/lib/models/user_model.dart +++ b/flutter/lib/models/user_model.dart @@ -24,6 +24,7 @@ class UserModel { await _updateOtherModels(); return; } + _updateLocalUserInfo(); final url = await bind.mainGetApiServer(); final body = { 'id': await bind.mainGetMyId(), @@ -48,7 +49,7 @@ class UserModel { } final user = UserPayload.fromJson(data); - await _parseAndUpdateUser(user); + _parseAndUpdateUser(user); } catch (e) { print('Failed to refreshCurrentUser: $e'); } finally { @@ -56,6 +57,22 @@ class UserModel { } } + static Map? getLocalUserInfo() { + try { + return json.decode(bind.mainGetLocalOption(key: 'user_info')); + } catch (e) { + print('Failed to get local user info: $e'); + } + return null; + } + + _updateLocalUserInfo() { + final userInfo = getLocalUserInfo(); + if (userInfo != null) { + userName.value = userInfo['name']; + } + } + Future reset() async { await bind.mainSetLocalOption(key: 'access_token', value: ''); await gFFI.abModel.reset(); @@ -64,7 +81,7 @@ class UserModel { gFFI.peerTabModel.check_dynamic_tabs(); } - Future _parseAndUpdateUser(UserPayload user) async { + _parseAndUpdateUser(UserPayload user) { userName.value = user.name; isAdmin.value = user.isAdmin; } @@ -110,11 +127,14 @@ class UserModel { print("login: jsonDecode resp body failed: ${e.toString()}"); rethrow; } - if (resp.statusCode != 200) { throw RequestException(resp.statusCode, body['error'] ?? ''); } + return getLoginResponseFromAuthBody(body); + } + + LoginResponse getLoginResponseFromAuthBody(Map body) { final LoginResponse loginResponse; try { loginResponse = LoginResponse.fromJson(body); @@ -124,7 +144,7 @@ class UserModel { } if (loginResponse.user != null) { - await _parseAndUpdateUser(loginResponse.user!); + _parseAndUpdateUser(loginResponse.user!); } return loginResponse;