mirror of
https://github.com/rustdesk/rustdesk.git
synced 2025-06-10 20:23:07 +08:00
refactor user login:
1. opt request json type. 2. desktop and mobile use same loginDialog. 3. opt loginDialog UI style. 4. opt login request Exception catch.
This commit is contained in:
parent
1867502ef7
commit
3e357159f3
@ -1367,7 +1367,7 @@ connect(BuildContext context, String id,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, String>> getHttpHeaders() async {
|
Map<String, String> getHttpHeaders() {
|
||||||
return {
|
return {
|
||||||
'Authorization': 'Bearer ${bind.mainGetLocalOption(key: 'access_token')}'
|
'Authorization': 'Bearer ${bind.mainGetLocalOption(key: 'access_token')}'
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,22 @@
|
|||||||
import 'package:flutter_hbb/models/peer_model.dart';
|
import 'package:flutter_hbb/models/peer_model.dart';
|
||||||
|
|
||||||
|
class HttpType {
|
||||||
|
static const kAuthReqTypeAccount = "account";
|
||||||
|
static const kAuthReqTypeMobile = "mobile";
|
||||||
|
static const kAuthReqTypeSMSCode = "sms_code";
|
||||||
|
static const kAuthReqTypeEmailCode = "email_code";
|
||||||
|
|
||||||
|
static const kAuthResTypeToken = "access_token";
|
||||||
|
static const kAuthResTypeEmailCheck = "email_check";
|
||||||
|
}
|
||||||
|
|
||||||
class UserPayload {
|
class UserPayload {
|
||||||
String name = '';
|
String name = '';
|
||||||
String email = '';
|
String email = '';
|
||||||
String note = '';
|
String note = '';
|
||||||
int? status;
|
int? status;
|
||||||
String grp = '';
|
String grp = '';
|
||||||
bool is_admin = false;
|
bool isAdmin = false;
|
||||||
|
|
||||||
UserPayload.fromJson(Map<String, dynamic> json)
|
UserPayload.fromJson(Map<String, dynamic> json)
|
||||||
: name = json['name'] ?? '',
|
: name = json['name'] ?? '',
|
||||||
@ -14,7 +24,7 @@ class UserPayload {
|
|||||||
note = json['note'] ?? '',
|
note = json['note'] ?? '',
|
||||||
status = json['status'],
|
status = json['status'],
|
||||||
grp = json['grp'] ?? '',
|
grp = json['grp'] ?? '',
|
||||||
is_admin = json['is_admin'] == true;
|
isAdmin = json['is_admin'] == true;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PeerPayload {
|
class PeerPayload {
|
||||||
@ -37,3 +47,81 @@ class PeerPayload {
|
|||||||
return Peer.fromJson({"id": p.id});
|
return Peer.fromJson({"id": p.id});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LoginRequest {
|
||||||
|
String? username;
|
||||||
|
String? password;
|
||||||
|
String? id;
|
||||||
|
String? uuid;
|
||||||
|
bool? autoLogin;
|
||||||
|
String? type;
|
||||||
|
String? verificationCode;
|
||||||
|
String? deviceInfo;
|
||||||
|
|
||||||
|
LoginRequest(
|
||||||
|
{this.username,
|
||||||
|
this.password,
|
||||||
|
this.id,
|
||||||
|
this.uuid,
|
||||||
|
this.autoLogin,
|
||||||
|
this.type,
|
||||||
|
this.verificationCode,
|
||||||
|
this.deviceInfo});
|
||||||
|
|
||||||
|
LoginRequest.fromJson(Map<String, dynamic> json) {
|
||||||
|
username = json['username'];
|
||||||
|
password = json['password'];
|
||||||
|
id = json['id'];
|
||||||
|
uuid = json['uuid'];
|
||||||
|
autoLogin = json['autoLogin'];
|
||||||
|
type = json['type'];
|
||||||
|
verificationCode = json['verificationCode'];
|
||||||
|
deviceInfo = json['deviceInfo'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
|
data['username'] = username ?? '';
|
||||||
|
data['password'] = password ?? '';
|
||||||
|
data['id'] = id ?? '';
|
||||||
|
data['uuid'] = uuid ?? '';
|
||||||
|
data['autoLogin'] = autoLogin ?? '';
|
||||||
|
data['type'] = type ?? '';
|
||||||
|
data['verificationCode'] = verificationCode ?? '';
|
||||||
|
data['deviceInfo'] = deviceInfo ?? '';
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoginResponse {
|
||||||
|
String? access_token;
|
||||||
|
String? type;
|
||||||
|
UserPayload? user;
|
||||||
|
|
||||||
|
LoginResponse({this.access_token, this.type, this.user});
|
||||||
|
|
||||||
|
LoginResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
access_token = json['access_token'];
|
||||||
|
type = json['type'];
|
||||||
|
print("user: ${json['user']}");
|
||||||
|
print("user id: ${json['user']['id']}");
|
||||||
|
print("user name: ${json['user']['name']}");
|
||||||
|
print("user email: ${json['user']['id']}");
|
||||||
|
print("user note: ${json['user']['note']}");
|
||||||
|
print("user status: ${json['user']['status']}");
|
||||||
|
print("user grp: ${json['user']['grp']}");
|
||||||
|
print("user is_admin: ${json['user']['is_admin']}");
|
||||||
|
user = json['user'] != null ? UserPayload.fromJson(json['user']) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RequestException implements Exception {
|
||||||
|
int statusCode;
|
||||||
|
String cause;
|
||||||
|
RequestException(this.statusCode, this.cause);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return "RequestException, statusCode: $statusCode, error: $cause";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,8 +9,6 @@ import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../desktop/pages/desktop_home_page.dart';
|
|
||||||
import '../../mobile/pages/settings_page.dart';
|
|
||||||
|
|
||||||
class AddressBook extends StatefulWidget {
|
class AddressBook extends StatefulWidget {
|
||||||
final EdgeInsets? menuPadding;
|
final EdgeInsets? menuPadding;
|
||||||
@ -41,21 +39,12 @@ class _AddressBookState extends State<AddressBook> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
handleLogin() {
|
|
||||||
// TODO refactor login dialog for desktop and mobile
|
|
||||||
if (isDesktop) {
|
|
||||||
loginDialog();
|
|
||||||
} else {
|
|
||||||
showLogin(gFFI.dialogManager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Widget> buildBody(BuildContext context) async {
|
Future<Widget> buildBody(BuildContext context) async {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
if (gFFI.userModel.userName.value.isEmpty) {
|
if (gFFI.userModel.userName.value.isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: handleLogin,
|
onTap: loginDialog,
|
||||||
child: Text(
|
child: Text(
|
||||||
translate("Login"),
|
translate("Login"),
|
||||||
style: const TextStyle(decoration: TextDecoration.underline),
|
style: const TextStyle(decoration: TextDecoration.underline),
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hbb/common/hbbs/hbbs.dart';
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
@ -9,18 +10,21 @@ import 'package:url_launcher/url_launcher.dart';
|
|||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
|
|
||||||
final _kMidButtonPadding = const EdgeInsets.fromLTRB(15, 0, 15, 0);
|
|
||||||
|
|
||||||
class _IconOP extends StatelessWidget {
|
class _IconOP extends StatelessWidget {
|
||||||
final String icon;
|
final String icon;
|
||||||
final double iconWidth;
|
final double iconWidth;
|
||||||
const _IconOP({Key? key, required this.icon, required this.iconWidth})
|
final EdgeInsets margin;
|
||||||
|
const _IconOP(
|
||||||
|
{Key? key,
|
||||||
|
required this.icon,
|
||||||
|
required this.iconWidth,
|
||||||
|
this.margin = const EdgeInsets.symmetric(horizontal: 4.0)})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 4.0),
|
margin: margin,
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
'assets/$icon.svg',
|
'assets/$icon.svg',
|
||||||
width: iconWidth,
|
width: iconWidth,
|
||||||
@ -50,33 +54,33 @@ class ButtonOP extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(children: [
|
return Row(children: [
|
||||||
Expanded(
|
Container(
|
||||||
child: Container(
|
height: height,
|
||||||
height: height,
|
width: 200,
|
||||||
padding: _kMidButtonPadding,
|
child: Obx(() => ElevatedButton(
|
||||||
child: Obx(() => ElevatedButton(
|
style: ElevatedButton.styleFrom(
|
||||||
style: ElevatedButton.styleFrom(
|
primary: curOP.value.isEmpty || curOP.value == op
|
||||||
primary: curOP.value.isEmpty || curOP.value == op
|
? primaryColor
|
||||||
? primaryColor
|
: Colors.grey,
|
||||||
: Colors.grey,
|
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
|
||||||
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
|
onPressed: curOP.value.isEmpty || curOP.value == op ? onTap : null,
|
||||||
onPressed:
|
child: Row(
|
||||||
curOP.value.isEmpty || curOP.value == op ? onTap : null,
|
children: [
|
||||||
child: Stack(children: [
|
SizedBox(
|
||||||
Center(child: Text('${translate("Continue with")} $op')),
|
width: 30,
|
||||||
Align(
|
child: _IconOP(
|
||||||
alignment: Alignment.centerLeft,
|
icon: op,
|
||||||
child: SizedBox(
|
iconWidth: iconWidth,
|
||||||
width: 120,
|
margin: EdgeInsets.only(right: 5),
|
||||||
child: _IconOP(
|
)),
|
||||||
icon: op,
|
Expanded(
|
||||||
iconWidth: iconWidth,
|
child: FittedBox(
|
||||||
)),
|
fit: BoxFit.scaleDown,
|
||||||
),
|
child: Center(
|
||||||
]),
|
child: Text('${translate("Continue with")} $op')))),
|
||||||
)),
|
],
|
||||||
),
|
))),
|
||||||
)
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,22 +281,25 @@ class LoginWidgetOP extends StatelessWidget {
|
|||||||
children.removeLast();
|
children.removeLast();
|
||||||
}
|
}
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Container(
|
||||||
mainAxisSize: MainAxisSize.min,
|
width: 200,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
child: Column(
|
||||||
children: children,
|
mainAxisSize: MainAxisSize.min,
|
||||||
));
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: children,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoginWidgetUserPass extends StatelessWidget {
|
class LoginWidgetUserPass extends StatelessWidget {
|
||||||
final String username;
|
final TextEditingController username;
|
||||||
final String pass;
|
final TextEditingController pass;
|
||||||
final String usernameMsg;
|
final String? usernameMsg;
|
||||||
final String passMsg;
|
final String? passMsg;
|
||||||
final bool isInProgress;
|
final bool isInProgress;
|
||||||
final RxString curOP;
|
final RxString curOP;
|
||||||
final Function(String, String) onLogin;
|
final RxBool autoLogin;
|
||||||
|
final Function() onLogin;
|
||||||
const LoginWidgetUserPass({
|
const LoginWidgetUserPass({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.username,
|
required this.username,
|
||||||
@ -301,129 +308,135 @@ class LoginWidgetUserPass extends StatelessWidget {
|
|||||||
required this.passMsg,
|
required this.passMsg,
|
||||||
required this.isInProgress,
|
required this.isInProgress,
|
||||||
required this.curOP,
|
required this.curOP,
|
||||||
|
required this.autoLogin,
|
||||||
required this.onLogin,
|
required this.onLogin,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var userController = TextEditingController(text: username);
|
return Padding(
|
||||||
var pwdController = TextEditingController(text: pass);
|
padding: EdgeInsets.all(0),
|
||||||
return Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 8.0),
|
||||||
|
DialogTextField(
|
||||||
|
title: '${translate("Username")}:',
|
||||||
|
controller: username,
|
||||||
|
autoFocus: true,
|
||||||
|
errorText: usernameMsg),
|
||||||
|
DialogTextField(
|
||||||
|
title: '${translate("Password")}:',
|
||||||
|
obscureText: true,
|
||||||
|
controller: pass,
|
||||||
|
errorText: passMsg),
|
||||||
|
Obx(() => CheckboxListTile(
|
||||||
|
contentPadding: const EdgeInsets.all(0),
|
||||||
|
dense: true,
|
||||||
|
controlAffinity: ListTileControlAffinity.leading,
|
||||||
|
title: Text(
|
||||||
|
translate("Remember me"),
|
||||||
|
),
|
||||||
|
value: autoLogin.value,
|
||||||
|
onChanged: (v) {
|
||||||
|
if (v == null) return;
|
||||||
|
autoLogin.value = v;
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
Offstage(
|
||||||
|
offstage: !isInProgress,
|
||||||
|
child: const LinearProgressIndicator()),
|
||||||
|
const SizedBox(height: 12.0),
|
||||||
|
FittedBox(
|
||||||
|
child:
|
||||||
|
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||||
|
Container(
|
||||||
|
height: 38,
|
||||||
|
width: 200,
|
||||||
|
child: Obx(() => ElevatedButton(
|
||||||
|
child: Text(
|
||||||
|
translate('Login'),
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
onPressed:
|
||||||
|
curOP.value.isEmpty || curOP.value == 'rustdesk'
|
||||||
|
? () {
|
||||||
|
onLogin();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DialogTextField extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final bool autoFocus;
|
||||||
|
final bool obscureText;
|
||||||
|
final String? errorText;
|
||||||
|
final TextEditingController controller;
|
||||||
|
final FocusNode focusNode = FocusNode();
|
||||||
|
|
||||||
|
DialogTextField(
|
||||||
|
{Key? key,
|
||||||
|
this.autoFocus = false,
|
||||||
|
this.obscureText = false,
|
||||||
|
this.errorText,
|
||||||
|
required this.title,
|
||||||
|
required this.controller})
|
||||||
|
: super(key: key) {
|
||||||
|
// todo mobile requestFocus, on mobile, widget will reload every time the text changes
|
||||||
|
if (autoFocus && isDesktop) {
|
||||||
|
Timer(Duration(milliseconds: 200), () => focusNode.requestFocus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(
|
Expanded(
|
||||||
height: 8.0,
|
child: TextField(
|
||||||
),
|
decoration: InputDecoration(
|
||||||
Container(
|
labelText: title,
|
||||||
padding: _kMidButtonPadding,
|
border: const OutlineInputBorder(),
|
||||||
child: Row(
|
errorText: errorText),
|
||||||
children: [
|
controller: controller,
|
||||||
ConstrainedBox(
|
focusNode: focusNode,
|
||||||
constraints: const BoxConstraints(minWidth: 100),
|
autofocus: true,
|
||||||
child: Text(
|
obscureText: obscureText,
|
||||||
'${translate("Username")}:',
|
|
||||||
textAlign: TextAlign.start,
|
|
||||||
).marginOnly(bottom: 16.0)),
|
|
||||||
const SizedBox(
|
|
||||||
width: 24.0,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: TextField(
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: const OutlineInputBorder(),
|
|
||||||
errorText: usernameMsg.isNotEmpty ? usernameMsg : null),
|
|
||||||
controller: userController,
|
|
||||||
focusNode: FocusNode()..requestFocus(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
|
||||||
height: 8.0,
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: _kMidButtonPadding,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(minWidth: 100),
|
|
||||||
child: Text('${translate("Password")}:')
|
|
||||||
.marginOnly(bottom: 16.0)),
|
|
||||||
const SizedBox(
|
|
||||||
width: 24.0,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: TextField(
|
|
||||||
obscureText: true,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: const OutlineInputBorder(),
|
|
||||||
errorText: passMsg.isNotEmpty ? passMsg : null),
|
|
||||||
controller: pwdController,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 4.0,
|
|
||||||
),
|
|
||||||
Offstage(
|
|
||||||
offstage: !isInProgress, child: const LinearProgressIndicator()),
|
|
||||||
const SizedBox(
|
|
||||||
height: 12.0,
|
|
||||||
),
|
|
||||||
Row(children: [
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
height: 38,
|
|
||||||
padding: _kMidButtonPadding,
|
|
||||||
child: Obx(() => ElevatedButton(
|
|
||||||
style: curOP.value.isEmpty || curOP.value == 'rustdesk'
|
|
||||||
? null
|
|
||||||
: ElevatedButton.styleFrom(
|
|
||||||
primary: Colors.grey,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
translate('Login'),
|
|
||||||
style: TextStyle(fontSize: 16),
|
|
||||||
),
|
|
||||||
onPressed: curOP.value.isEmpty || curOP.value == 'rustdesk'
|
|
||||||
? () {
|
|
||||||
onLogin(userController.text, pwdController.text);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
],
|
],
|
||||||
);
|
).paddingSymmetric(vertical: 4.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// common login dialog for desktop
|
/// common login dialog for desktop
|
||||||
/// call this directly
|
/// call this directly
|
||||||
Future<bool> loginDialog() async {
|
Future<bool?> loginDialog() async {
|
||||||
String username = '';
|
var username = TextEditingController();
|
||||||
var usernameMsg = '';
|
var password = TextEditingController();
|
||||||
String pass = '';
|
|
||||||
var passMsg = '';
|
String? usernameMsg;
|
||||||
|
String? passwordMsg;
|
||||||
var isInProgress = false;
|
var isInProgress = false;
|
||||||
var completer = Completer<bool>();
|
final autoLogin = true.obs;
|
||||||
final RxString curOP = ''.obs;
|
final RxString curOP = ''.obs;
|
||||||
|
|
||||||
gFFI.dialogManager.show((setState, close) {
|
return gFFI.dialogManager.show<bool>((setState, close) {
|
||||||
cancel() {
|
cancel() {
|
||||||
isInProgress = false;
|
isInProgress = false;
|
||||||
completer.complete(false);
|
close(false);
|
||||||
close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onLogin(String username0, String pass0) async {
|
onLogin() async {
|
||||||
setState(() {
|
setState(() {
|
||||||
usernameMsg = '';
|
usernameMsg = null;
|
||||||
passMsg = '';
|
passwordMsg = null;
|
||||||
isInProgress = true;
|
isInProgress = true;
|
||||||
});
|
});
|
||||||
cancel() {
|
cancel() {
|
||||||
@ -436,30 +449,44 @@ Future<bool> loginDialog() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
curOP.value = 'rustdesk';
|
curOP.value = 'rustdesk';
|
||||||
username = username0;
|
if (username.text.isEmpty) {
|
||||||
pass = pass0;
|
|
||||||
if (username.isEmpty) {
|
|
||||||
usernameMsg = translate('Username missed');
|
usernameMsg = translate('Username missed');
|
||||||
cancel();
|
cancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (pass.isEmpty) {
|
if (password.text.isEmpty) {
|
||||||
passMsg = translate('Password missed');
|
passwordMsg = translate('Password missed');
|
||||||
cancel();
|
cancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final resp = await gFFI.userModel.login(username, pass);
|
final resp = await gFFI.userModel.login(LoginRequest(
|
||||||
if (resp.containsKey('error')) {
|
username: username.text,
|
||||||
passMsg = resp['error'];
|
password: password.text,
|
||||||
cancel();
|
id: await bind.mainGetMyId(),
|
||||||
return;
|
uuid: await bind.mainGetUuid(),
|
||||||
|
autoLogin: autoLogin.value,
|
||||||
|
type: HttpType.kAuthReqTypeAccount));
|
||||||
|
|
||||||
|
switch (resp.type) {
|
||||||
|
case HttpType.kAuthResTypeToken:
|
||||||
|
if (resp.access_token != null) {
|
||||||
|
bind.mainSetLocalOption(
|
||||||
|
key: 'access_token', value: resp.access_token!);
|
||||||
|
close(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HttpType.kAuthResTypeEmailCheck:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// {access_token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJndWlkIjoiMDFkZjQ2ZjgtZjg3OS00MDE0LTk5Y2QtMGMwYzM2MmViZGJlIiwiZXhwIjoxNjYxNDg2NzYwfQ.GZpe1oI8TfM5yTYNrpcwbI599P4Z_-b2GmnwNl2Lr-w,
|
} on RequestException catch (err) {
|
||||||
// token_type: Bearer, user: {id: , name: admin, email: null, note: null, status: null, grp: null, is_admin: true}}
|
passwordMsg = translate(err.cause);
|
||||||
debugPrint('$resp');
|
debugPrintStack(label: err.toString());
|
||||||
completer.complete(true);
|
cancel();
|
||||||
|
return;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
passwordMsg = "Unknown Error";
|
||||||
debugPrintStack(label: err.toString());
|
debugPrintStack(label: err.toString());
|
||||||
cancel();
|
cancel();
|
||||||
return;
|
return;
|
||||||
@ -469,53 +496,50 @@ Future<bool> loginDialog() async {
|
|||||||
|
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
title: Text(translate('Login')),
|
title: Text(translate('Login')),
|
||||||
content: ConstrainedBox(
|
contentBoxConstraints: BoxConstraints(minWidth: 400),
|
||||||
constraints: const BoxConstraints(minWidth: 500),
|
content: Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
children: [
|
const SizedBox(
|
||||||
const SizedBox(
|
height: 8.0,
|
||||||
height: 8.0,
|
),
|
||||||
),
|
LoginWidgetUserPass(
|
||||||
LoginWidgetUserPass(
|
username: username,
|
||||||
username: username,
|
pass: password,
|
||||||
pass: pass,
|
usernameMsg: usernameMsg,
|
||||||
usernameMsg: usernameMsg,
|
passMsg: passwordMsg,
|
||||||
passMsg: passMsg,
|
isInProgress: isInProgress,
|
||||||
isInProgress: isInProgress,
|
curOP: curOP,
|
||||||
curOP: curOP,
|
autoLogin: autoLogin,
|
||||||
onLogin: onLogin,
|
onLogin: onLogin,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8.0,
|
height: 8.0,
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
translate('or'),
|
translate('or'),
|
||||||
style: TextStyle(fontSize: 16),
|
style: TextStyle(fontSize: 16),
|
||||||
)),
|
)),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8.0,
|
height: 8.0,
|
||||||
),
|
),
|
||||||
LoginWidgetOP(
|
LoginWidgetOP(
|
||||||
ops: [
|
ops: [
|
||||||
ConfigOP(op: 'Github', iconWidth: 20),
|
ConfigOP(op: 'Github', iconWidth: 20),
|
||||||
ConfigOP(op: 'Google', iconWidth: 20),
|
ConfigOP(op: 'Google', iconWidth: 20),
|
||||||
ConfigOP(op: 'Okta', iconWidth: 38),
|
ConfigOP(op: 'Okta', iconWidth: 38),
|
||||||
],
|
],
|
||||||
curOP: curOP,
|
curOP: curOP,
|
||||||
cbLogin: (String username) {
|
cbLogin: (String username) {
|
||||||
gFFI.userModel.userName.value = username;
|
gFFI.userModel.userName.value = username;
|
||||||
completer.complete(true);
|
close(true);
|
||||||
close();
|
},
|
||||||
},
|
),
|
||||||
),
|
],
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
actions: [msgBoxButton(translate('Close'), cancel)],
|
actions: [msgBoxButton(translate('Close'), cancel)],
|
||||||
onCancel: cancel,
|
onCancel: cancel,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return completer.future;
|
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,9 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../common/widgets/address_book.dart';
|
|
||||||
import '../../common/widgets/peer_tab_page.dart';
|
import '../../common/widgets/peer_tab_page.dart';
|
||||||
import '../../common/widgets/peers_view.dart';
|
|
||||||
import '../../consts.dart';
|
import '../../consts.dart';
|
||||||
|
import '../../desktop/widgets/login.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import 'home_page.dart';
|
import 'home_page.dart';
|
||||||
@ -258,7 +257,7 @@ class _WebMenuState extends State<WebMenu> {
|
|||||||
}
|
}
|
||||||
if (value == 'login') {
|
if (value == 'login') {
|
||||||
if (gFFI.userModel.userName.value.isEmpty) {
|
if (gFFI.userModel.userName.value.isEmpty) {
|
||||||
showLogin(gFFI.dialogManager);
|
loginDialog();
|
||||||
} else {
|
} else {
|
||||||
gFFI.userModel.logOut();
|
gFFI.userModel.logOut();
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import 'package:url_launcher/url_launcher.dart';
|
|||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../common/widgets/dialog.dart';
|
import '../../common/widgets/dialog.dart';
|
||||||
|
import '../../desktop/widgets/login.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../widgets/dialog.dart';
|
import '../widgets/dialog.dart';
|
||||||
@ -300,7 +301,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
leading: Icon(Icons.person),
|
leading: Icon(Icons.person),
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
if (gFFI.userModel.userName.value.isEmpty) {
|
if (gFFI.userModel.userName.value.isEmpty) {
|
||||||
showLogin(gFFI.dialogManager);
|
loginDialog();
|
||||||
} else {
|
} else {
|
||||||
gFFI.userModel.logOut();
|
gFFI.userModel.logOut();
|
||||||
}
|
}
|
||||||
@ -482,77 +483,6 @@ void showAbout(OverlayDialogManager dialogManager) {
|
|||||||
}, clickMaskDismiss: true, backDismiss: true);
|
}, clickMaskDismiss: true, backDismiss: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showLogin(OverlayDialogManager dialogManager) {
|
|
||||||
final passwordController = TextEditingController();
|
|
||||||
final nameController = TextEditingController();
|
|
||||||
var loading = false;
|
|
||||||
var error = '';
|
|
||||||
dialogManager.show((setState, close) {
|
|
||||||
return CustomAlertDialog(
|
|
||||||
title: Text(translate('Login')),
|
|
||||||
content: Column(mainAxisSize: MainAxisSize.min, children: [
|
|
||||||
TextField(
|
|
||||||
autofocus: true,
|
|
||||||
autocorrect: false,
|
|
||||||
enableSuggestions: false,
|
|
||||||
keyboardType: TextInputType.visiblePassword,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: translate('Username'),
|
|
||||||
),
|
|
||||||
controller: nameController,
|
|
||||||
),
|
|
||||||
PasswordWidget(controller: passwordController, autoFocus: false),
|
|
||||||
]),
|
|
||||||
actions: (loading
|
|
||||||
? <Widget>[CircularProgressIndicator()]
|
|
||||||
: (error != ""
|
|
||||||
? <Widget>[
|
|
||||||
Text(translate(error),
|
|
||||||
style: TextStyle(color: Colors.red))
|
|
||||||
]
|
|
||||||
: <Widget>[])) +
|
|
||||||
<Widget>[
|
|
||||||
TextButton(
|
|
||||||
style: flatButtonStyle,
|
|
||||||
onPressed: loading
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
close();
|
|
||||||
setState(() {
|
|
||||||
loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Text(translate('Cancel')),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
style: flatButtonStyle,
|
|
||||||
onPressed: loading
|
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
final name = nameController.text.trim();
|
|
||||||
final pass = passwordController.text.trim();
|
|
||||||
if (name != "" && pass != "") {
|
|
||||||
setState(() {
|
|
||||||
loading = true;
|
|
||||||
});
|
|
||||||
final resp = await gFFI.userModel.login(name, pass);
|
|
||||||
setState(() {
|
|
||||||
loading = false;
|
|
||||||
});
|
|
||||||
if (resp.containsKey('error')) {
|
|
||||||
error = resp['error'];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close();
|
|
||||||
},
|
|
||||||
child: Text(translate('OK')),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScanButton extends StatelessWidget {
|
class ScanButton extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -27,8 +27,7 @@ class AbModel {
|
|||||||
abError.value = "";
|
abError.value = "";
|
||||||
final api = "${await bind.mainGetApiServer()}/api/ab/get";
|
final api = "${await bind.mainGetApiServer()}/api/ab/get";
|
||||||
try {
|
try {
|
||||||
final resp =
|
final resp = await http.post(Uri.parse(api), headers: getHttpHeaders());
|
||||||
await http.post(Uri.parse(api), headers: await getHttpHeaders());
|
|
||||||
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
||||||
Map<String, dynamic> json = jsonDecode(resp.body);
|
Map<String, dynamic> json = jsonDecode(resp.body);
|
||||||
if (json.containsKey('error')) {
|
if (json.containsKey('error')) {
|
||||||
@ -102,7 +101,7 @@ class AbModel {
|
|||||||
Future<void> pushAb() async {
|
Future<void> pushAb() async {
|
||||||
abLoading.value = true;
|
abLoading.value = true;
|
||||||
final api = "${await bind.mainGetApiServer()}/api/ab";
|
final api = "${await bind.mainGetApiServer()}/api/ab";
|
||||||
var authHeaders = await getHttpHeaders();
|
var authHeaders = getHttpHeaders();
|
||||||
authHeaders['Content-Type'] = "application/json";
|
authHeaders['Content-Type'] = "application/json";
|
||||||
final peersJsonData = peers.map((e) => e.toJson()).toList();
|
final peersJsonData = peers.map((e) => e.toJson()).toList();
|
||||||
final body = jsonEncode({
|
final body = jsonEncode({
|
||||||
|
@ -59,7 +59,7 @@ class GroupModel {
|
|||||||
if (gFFI.userModel.isAdmin.isFalse)
|
if (gFFI.userModel.isAdmin.isFalse)
|
||||||
'grp': gFFI.userModel.groupName.value,
|
'grp': gFFI.userModel.groupName.value,
|
||||||
});
|
});
|
||||||
final resp = await http.get(uri, headers: await getHttpHeaders());
|
final resp = await http.get(uri, headers: getHttpHeaders());
|
||||||
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
||||||
Map<String, dynamic> json = jsonDecode(resp.body);
|
Map<String, dynamic> json = jsonDecode(resp.body);
|
||||||
if (json.containsKey('error')) {
|
if (json.containsKey('error')) {
|
||||||
@ -110,7 +110,7 @@ class GroupModel {
|
|||||||
'grp': gFFI.userModel.groupName.value,
|
'grp': gFFI.userModel.groupName.value,
|
||||||
'target_user': username
|
'target_user': username
|
||||||
});
|
});
|
||||||
final resp = await http.get(uri, headers: await getHttpHeaders());
|
final resp = await http.get(uri, headers: getHttpHeaders());
|
||||||
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
||||||
Map<String, dynamic> json = jsonDecode(resp.body);
|
Map<String, dynamic> json = jsonDecode(resp.body);
|
||||||
if (json.containsKey('error')) {
|
if (json.containsKey('error')) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter_hbb/common/hbbs/hbbs.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/peer_tab_page.dart';
|
import 'package:flutter_hbb/common/widgets/peer_tab_page.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
@ -45,7 +46,9 @@ class UserModel {
|
|||||||
if (error != null) {
|
if (error != null) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
await _parseUserInfo(data);
|
|
||||||
|
final user = UserPayload.fromJson(data);
|
||||||
|
await _parseAndUpdateUser(user);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Failed to refreshCurrentUser: $e');
|
print('Failed to refreshCurrentUser: $e');
|
||||||
} finally {
|
} finally {
|
||||||
@ -55,7 +58,6 @@ class UserModel {
|
|||||||
|
|
||||||
Future<void> reset() async {
|
Future<void> reset() async {
|
||||||
await bind.mainSetLocalOption(key: 'access_token', value: '');
|
await bind.mainSetLocalOption(key: 'access_token', value: '');
|
||||||
await bind.mainSetLocalOption(key: 'user_info', value: '');
|
|
||||||
await gFFI.abModel.reset();
|
await gFFI.abModel.reset();
|
||||||
await gFFI.groupModel.reset();
|
await gFFI.groupModel.reset();
|
||||||
userName.value = '';
|
userName.value = '';
|
||||||
@ -63,11 +65,10 @@ class UserModel {
|
|||||||
statePeerTab.check();
|
statePeerTab.check();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _parseUserInfo(dynamic userinfo) async {
|
Future<void> _parseAndUpdateUser(UserPayload user) async {
|
||||||
bind.mainSetLocalOption(key: 'user_info', value: jsonEncode(userinfo));
|
userName.value = user.name;
|
||||||
userName.value = userinfo['name'] ?? '';
|
groupName.value = user.grp;
|
||||||
groupName.value = userinfo['grp'] ?? '';
|
isAdmin.value = user.isAdmin;
|
||||||
isAdmin.value = userinfo['is_admin'] == true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateOtherModels() async {
|
Future<void> _updateOtherModels() async {
|
||||||
@ -85,7 +86,7 @@ class UserModel {
|
|||||||
'id': await bind.mainGetMyId(),
|
'id': await bind.mainGetMyId(),
|
||||||
'uuid': await bind.mainGetUuid(),
|
'uuid': await bind.mainGetUuid(),
|
||||||
},
|
},
|
||||||
headers: await getHttpHeaders())
|
headers: getHttpHeaders())
|
||||||
.timeout(Duration(seconds: 2));
|
.timeout(Duration(seconds: 2));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("request /api/logout failed: err=$e");
|
print("request /api/logout failed: err=$e");
|
||||||
@ -95,26 +96,38 @@ class UserModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> login(String userName, String pass) async {
|
/// throw [RequestException]
|
||||||
|
Future<LoginResponse> login(LoginRequest loginRequest) async {
|
||||||
final url = await bind.mainGetApiServer();
|
final url = await bind.mainGetApiServer();
|
||||||
|
final resp = await http.post(Uri.parse('$url/api/login'),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode(loginRequest.toJson()));
|
||||||
|
|
||||||
|
final Map<String, dynamic> body;
|
||||||
try {
|
try {
|
||||||
final resp = await http.post(Uri.parse('$url/api/login'),
|
body = jsonDecode(resp.body);
|
||||||
headers: {'Content-Type': 'application/json'},
|
} catch (e) {
|
||||||
body: jsonEncode({
|
print("jsonDecode resp body failed: ${e.toString()}");
|
||||||
'username': userName,
|
rethrow;
|
||||||
'password': pass,
|
|
||||||
'id': await bind.mainGetMyId(),
|
|
||||||
'uuid': await bind.mainGetUuid()
|
|
||||||
}));
|
|
||||||
final body = jsonDecode(resp.body);
|
|
||||||
bind.mainSetLocalOption(
|
|
||||||
key: 'access_token', value: body['access_token'] ?? '');
|
|
||||||
await _parseUserInfo(body['user']);
|
|
||||||
return body;
|
|
||||||
} catch (err) {
|
|
||||||
return {'error': '$err'};
|
|
||||||
} finally {
|
|
||||||
await _updateOtherModels();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (resp.statusCode != 200) {
|
||||||
|
throw RequestException(resp.statusCode, body['error'] ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
final LoginResponse loginResponse;
|
||||||
|
try {
|
||||||
|
loginResponse = LoginResponse.fromJson(body);
|
||||||
|
} catch (e) {
|
||||||
|
print("jsonDecode LoginResponse failed: ${e.toString()}");
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loginResponse.user != null) {
|
||||||
|
await _parseAndUpdateUser(loginResponse.user!);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _updateOtherModels();
|
||||||
|
return loginResponse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user