Merge pull request #4723 from dignow/fix/user_login_state

Fix/user login state
This commit is contained in:
RustDesk 2023-06-21 17:06:07 +08:00 committed by GitHub
commit eb686e2728
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 106 additions and 41 deletions

View File

@ -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/peer_card.dart';
import 'package:flutter_hbb/common/widgets/peers_view.dart'; import 'package:flutter_hbb/common/widgets/peers_view.dart';
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart'; import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
import 'package:flutter_hbb/models/state_model.dart';
import '../../consts.dart'; import '../../consts.dart';
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu; import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -29,15 +30,18 @@ class _AddressBookState extends State<AddressBook> {
} }
@override @override
Widget build(BuildContext context) => FutureBuilder<Widget>( Widget build(BuildContext context) => Obx(() => Offstage(
future: buildBody(context), offstage: stateGlobal.svcStatus.value != SvcStatus.ready,
builder: (context, snapshot) { child: FutureBuilder<Widget>(
if (snapshot.hasData) { future: buildBody(context),
return snapshot.data!; builder: (context, snapshot) {
} else { if (snapshot.hasData) {
return const Offstage(); return snapshot.data!;
} } else {
}); return const Offstage();
}
}),
));
Future<Widget> buildBody(BuildContext context) async { Future<Widget> buildBody(BuildContext context) async {
return Obx(() { return Obx(() {
@ -54,6 +58,9 @@ class _AddressBookState extends State<AddressBook> {
if (gFFI.abModel.abError.isNotEmpty) { if (gFFI.abModel.abError.isNotEmpty) {
return _buildShowError(gFFI.abModel.abError.value); return _buildShowError(gFFI.abModel.abError.value);
} }
if (gFFI.abModel.fromServer.isFalse) {
return Offstage();
}
return isDesktop return isDesktop
? _buildAddressBookDesktop() ? _buildAddressBookDesktop()
: _buildAddressBookMobile(); : _buildAddressBookMobile();

View File

@ -96,7 +96,7 @@ class ConfigOP {
class WidgetOP extends StatefulWidget { class WidgetOP extends StatefulWidget {
final ConfigOP config; final ConfigOP config;
final RxString curOP; final RxString curOP;
final Function(String) cbLogin; final Function(Map<String, dynamic>) cbLogin;
const WidgetOP({ const WidgetOP({
Key? key, Key? key,
required this.config, required this.config,
@ -153,9 +153,8 @@ class _WidgetOPState extends State<WidgetOP> {
} }
if (authBody != null) { if (authBody != null) {
_updateTimer?.cancel(); _updateTimer?.cancel();
final String username = authBody['user']['name'];
widget.curOP.value = ''; widget.curOP.value = '';
widget.cbLogin(username); widget.cbLogin(authBody as Map<String, dynamic>);
} }
setState(() { setState(() {
@ -255,7 +254,7 @@ class _WidgetOPState extends State<WidgetOP> {
class LoginWidgetOP extends StatelessWidget { class LoginWidgetOP extends StatelessWidget {
final List<ConfigOP> ops; final List<ConfigOP> ops;
final RxString curOP; final RxString curOP;
final Function(String) cbLogin; final Function(Map<String, dynamic>) cbLogin;
LoginWidgetOP({ LoginWidgetOP({
Key? key, Key? key,
@ -368,7 +367,8 @@ const kAuthReqTypeOidc = 'oidc/';
/// common login dialog for desktop /// common login dialog for desktop
/// call this directly /// call this directly
Future<bool?> loginDialog() async { Future<bool?> loginDialog() async {
var username = TextEditingController(); var username =
TextEditingController(text: UserModel.getLocalUserInfo()?['name'] ?? '');
var password = TextEditingController(); var password = TextEditingController();
final userFocusNode = FocusNode()..requestFocus(); final userFocusNode = FocusNode()..requestFocus();
Timer(Duration(milliseconds: 100), () => userFocusNode..requestFocus()); Timer(Duration(milliseconds: 100), () => userFocusNode..requestFocus());
@ -480,8 +480,17 @@ Future<bool?> loginDialog() async {
.where((op) => oidcOptions.contains(op.op.toLowerCase())) .where((op) => oidcOptions.contains(op.op.toLowerCase()))
.toList(), .toList(),
curOP: curOP, curOP: curOP,
cbLogin: (String username) { cbLogin: (Map<String, dynamic> authBody) {
gFFI.userModel.userName.value = username; 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); close(true);
}, },
), ),

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/hbbs/hbbs.dart'; import 'package:flutter_hbb/common/hbbs/hbbs.dart';
import 'package:flutter_hbb/common/widgets/peers_view.dart'; import 'package:flutter_hbb/common/widgets/peers_view.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../common.dart'; import '../../common.dart';
@ -26,15 +27,20 @@ class _MyGroupState extends State<MyGroup> {
} }
@override @override
Widget build(BuildContext context) => FutureBuilder<Widget>( Widget build(BuildContext context) {
future: buildBody(context), return Obx(() => Offstage(
builder: (context, snapshot) { offstage: stateGlobal.svcStatus.value != SvcStatus.ready,
if (snapshot.hasData) { child: FutureBuilder<Widget>(
return snapshot.data!; future: buildBody(context),
} else { builder: (context, snapshot) {
return const Offstage(); if (snapshot.hasData) {
} return snapshot.data!;
}); } else {
return const Offstage();
}
}),
));
}
Future<Widget> buildBody(BuildContext context) async { Future<Widget> buildBody(BuildContext context) async {
return Obx(() { return Obx(() {

View File

@ -42,7 +42,6 @@ class _ConnectionPageState extends State<ConnectionPage>
final FocusNode _idFocusNode = FocusNode(); final FocusNode _idFocusNode = FocusNode();
var svcStopped = Get.find<RxBool>(tag: 'stop-service'); var svcStopped = Get.find<RxBool>(tag: 'stop-service');
var svcStatusCode = 0.obs;
var svcIsUsingPublicServer = true.obs; var svcIsUsingPublicServer = true.obs;
bool isWindowMinimized = false; bool isWindowMinimized = false;
@ -253,9 +252,9 @@ class _ConnectionPageState extends State<ConnectionPage>
width: 8, width: 8,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
color: svcStopped.value || svcStatusCode.value == 0 color: svcStopped.value || stateGlobal.svcStatus.value == SvcStatus.connecting
? kColorWarn ? kColorWarn
: (svcStatusCode.value == 1 : (stateGlobal.svcStatus.value == SvcStatus.ready
? Color.fromARGB(255, 50, 190, 166) ? Color.fromARGB(255, 50, 190, 166)
: Color.fromARGB(255, 224, 79, 95)), : Color.fromARGB(255, 224, 79, 95)),
), ),
@ -263,9 +262,9 @@ class _ConnectionPageState extends State<ConnectionPage>
Text( Text(
svcStopped.value svcStopped.value
? translate("Service is not running") ? translate("Service is not running")
: svcStatusCode.value == 0 : stateGlobal.svcStatus.value == SvcStatus.connecting
? translate("connecting_status") ? translate("connecting_status")
: svcStatusCode.value == -1 : stateGlobal.svcStatus.value == SvcStatus.notReady
? translate("not_ready_status") ? translate("not_ready_status")
: translate('Ready'), : translate('Ready'),
style: TextStyle(fontSize: em)), style: TextStyle(fontSize: em)),
@ -286,7 +285,7 @@ class _ConnectionPageState extends State<ConnectionPage>
Flexible( Flexible(
child: Offstage( child: Offstage(
offstage: !(!svcStopped.value && offstage: !(!svcStopped.value &&
svcStatusCode.value == 1 && stateGlobal.svcStatus.value == SvcStatus.ready &&
svcIsUsingPublicServer.value), svcIsUsingPublicServer.value),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
@ -330,7 +329,25 @@ class _ConnectionPageState extends State<ConnectionPage>
updateStatus() async { updateStatus() async {
final status = final status =
jsonDecode(await bind.mainGetConnectStatus()) as Map<String, dynamic>; jsonDecode(await bind.mainGetConnectStatus()) as Map<String, dynamic>;
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(); svcIsUsingPublicServer.value = await bind.mainIsUsingPublicServer();
} }
} }

View File

@ -10,12 +10,13 @@ import 'package:http/http.dart' as http;
import '../common.dart'; import '../common.dart';
class AbModel { class AbModel {
var abLoading = false.obs; final abLoading = false.obs;
var abError = "".obs; final abError = "".obs;
var tags = [].obs; final tags = [].obs;
var peers = List<Peer>.empty(growable: true).obs; final RxBool fromServer = false.obs;
final peers = List<Peer>.empty(growable: true).obs;
var selectedTags = List<String>.empty(growable: true).obs; final selectedTags = List<String>.empty(growable: true).obs;
WeakReference<FFI> parent; WeakReference<FFI> parent;
@ -49,8 +50,10 @@ class AbModel {
} }
} }
} }
fromServer.value = true;
return resp.body; return resp.body;
} else { } else {
fromServer.value = true;
return ""; return "";
} }
} catch (err) { } catch (err) {

View File

@ -6,6 +6,8 @@ import 'package:get/get.dart';
import '../consts.dart'; import '../consts.dart';
enum SvcStatus { notReady, connecting, ready }
class StateGlobal { class StateGlobal {
int _windowId = -1; int _windowId = -1;
bool _fullscreen = false; bool _fullscreen = false;
@ -16,6 +18,7 @@ class StateGlobal {
final RxDouble _windowBorderWidth = RxDouble(kWindowBorderWidth); final RxDouble _windowBorderWidth = RxDouble(kWindowBorderWidth);
final RxBool showRemoteToolBar = false.obs; final RxBool showRemoteToolBar = false.obs;
final RxInt displaysCount = 0.obs; final RxInt displaysCount = 0.obs;
final svcStatus = SvcStatus.notReady.obs;
// Use for desktop -> remote toolbar -> resolution // Use for desktop -> remote toolbar -> resolution
final Map<String, Map<int, String?>> _lastResolutionGroupValues = {}; final Map<String, Map<int, String?>> _lastResolutionGroupValues = {};

View File

@ -24,6 +24,7 @@ class UserModel {
await _updateOtherModels(); await _updateOtherModels();
return; return;
} }
_updateLocalUserInfo();
final url = await bind.mainGetApiServer(); final url = await bind.mainGetApiServer();
final body = { final body = {
'id': await bind.mainGetMyId(), 'id': await bind.mainGetMyId(),
@ -48,7 +49,7 @@ class UserModel {
} }
final user = UserPayload.fromJson(data); final user = UserPayload.fromJson(data);
await _parseAndUpdateUser(user); _parseAndUpdateUser(user);
} catch (e) { } catch (e) {
print('Failed to refreshCurrentUser: $e'); print('Failed to refreshCurrentUser: $e');
} finally { } finally {
@ -56,6 +57,22 @@ class UserModel {
} }
} }
static Map<String, dynamic>? 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<void> reset() async { Future<void> reset() async {
await bind.mainSetLocalOption(key: 'access_token', value: ''); await bind.mainSetLocalOption(key: 'access_token', value: '');
await gFFI.abModel.reset(); await gFFI.abModel.reset();
@ -64,7 +81,7 @@ class UserModel {
gFFI.peerTabModel.check_dynamic_tabs(); gFFI.peerTabModel.check_dynamic_tabs();
} }
Future<void> _parseAndUpdateUser(UserPayload user) async { _parseAndUpdateUser(UserPayload user) {
userName.value = user.name; userName.value = user.name;
isAdmin.value = user.isAdmin; isAdmin.value = user.isAdmin;
} }
@ -110,11 +127,14 @@ class UserModel {
print("login: jsonDecode resp body failed: ${e.toString()}"); print("login: jsonDecode resp body failed: ${e.toString()}");
rethrow; rethrow;
} }
if (resp.statusCode != 200) { if (resp.statusCode != 200) {
throw RequestException(resp.statusCode, body['error'] ?? ''); throw RequestException(resp.statusCode, body['error'] ?? '');
} }
return getLoginResponseFromAuthBody(body);
}
LoginResponse getLoginResponseFromAuthBody(Map<String, dynamic> body) {
final LoginResponse loginResponse; final LoginResponse loginResponse;
try { try {
loginResponse = LoginResponse.fromJson(body); loginResponse = LoginResponse.fromJson(body);
@ -124,7 +144,7 @@ class UserModel {
} }
if (loginResponse.user != null) { if (loginResponse.user != null) {
await _parseAndUpdateUser(loginResponse.user!); _parseAndUpdateUser(loginResponse.user!);
} }
return loginResponse; return loginResponse;