feat: user login/logout with UserModel
Signed-off-by: Kingtous <kingtous@qq.com>
This commit is contained in:
parent
98a01aefa6
commit
06cb05f796
@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
|
||||
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:provider/provider.dart';
|
||||
@ -588,7 +589,13 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
||||
svcIsUsingPublicServer.value = await gFFI.bind.mainIsUsingPublicServer();
|
||||
}
|
||||
|
||||
handleLogin() {}
|
||||
handleLogin() {
|
||||
loginDialog().then((success) {
|
||||
if (success) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<Widget> buildAddressBook(BuildContext context) async {
|
||||
final token = await gFFI.getLocalOption('access_token');
|
||||
@ -975,27 +982,6 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
||||
}
|
||||
}
|
||||
|
||||
class AddressBookPage extends StatefulWidget {
|
||||
const AddressBookPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<AddressBookPage> createState() => _AddressBookPageState();
|
||||
}
|
||||
|
||||
class _AddressBookPageState extends State<AddressBookPage> {
|
||||
@override
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
final ab = gFFI.abModel.getAb();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
|
||||
class WebMenu extends StatefulWidget {
|
||||
@override
|
||||
_WebMenuState createState() => _WebMenuState();
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
@ -110,68 +111,18 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
style: TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.w500),
|
||||
),
|
||||
PopupMenuButton(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
itemBuilder: (context) => [
|
||||
genEnablePopupMenuItem(
|
||||
translate("Enable Keyboard/Mouse"),
|
||||
'enable-keyboard',
|
||||
),
|
||||
genEnablePopupMenuItem(
|
||||
translate("Enable Clipboard"),
|
||||
'enable-clipboard',
|
||||
),
|
||||
genEnablePopupMenuItem(
|
||||
translate("Enable File Transfer"),
|
||||
'enable-file-transfer',
|
||||
),
|
||||
genEnablePopupMenuItem(
|
||||
translate("Enable TCP Tunneling"),
|
||||
'enable-tunnel',
|
||||
),
|
||||
genAudioInputPopupMenuItem(),
|
||||
// TODO: Audio Input
|
||||
PopupMenuItem(
|
||||
child: Text(translate("ID/Relay Server")),
|
||||
value: 'custom-server',
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("IP Whitelisting")),
|
||||
value: 'whitelist',
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("Socks5 Proxy")),
|
||||
value: 'socks5-proxy',
|
||||
),
|
||||
// sep
|
||||
genEnablePopupMenuItem(
|
||||
translate("Enable Service"),
|
||||
'stop-service',
|
||||
),
|
||||
// TODO: direct server
|
||||
genEnablePopupMenuItem(
|
||||
translate("Always connected via relay"),
|
||||
'allow-always-relay',
|
||||
),
|
||||
genEnablePopupMenuItem(
|
||||
translate("Start ID/relay service"),
|
||||
'stop-rendezvous-service',
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("Change ID")),
|
||||
value: 'change-id',
|
||||
),
|
||||
genEnablePopupMenuItem(
|
||||
translate("Dark Theme"),
|
||||
'allow-darktheme',
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("About")),
|
||||
value: 'about',
|
||||
),
|
||||
],
|
||||
onSelected: onSelectMenu,
|
||||
)
|
||||
FutureBuilder<Widget>(
|
||||
future: buildPopupMenu(context),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
print("${snapshot.error}");
|
||||
}
|
||||
if (snapshot.hasData) {
|
||||
return snapshot.data!;
|
||||
} else {
|
||||
return Offstage();
|
||||
}
|
||||
})
|
||||
],
|
||||
),
|
||||
TextFormField(
|
||||
@ -189,6 +140,91 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
);
|
||||
}
|
||||
|
||||
Future<Widget> buildPopupMenu(BuildContext context) async {
|
||||
var position;
|
||||
return GestureDetector(
|
||||
onTapDown: (detail) {
|
||||
final x = detail.globalPosition.dx;
|
||||
final y = detail.globalPosition.dy;
|
||||
position = RelativeRect.fromLTRB(x, y, x, y);
|
||||
},
|
||||
onTap: () async {
|
||||
final userName = await gFFI.userModel.getUserName();
|
||||
var menu = [
|
||||
genEnablePopupMenuItem(
|
||||
translate("Enable Keyboard/Mouse"),
|
||||
'enable-keyboard',
|
||||
),
|
||||
genEnablePopupMenuItem(
|
||||
translate("Enable Clipboard"),
|
||||
'enable-clipboard',
|
||||
),
|
||||
genEnablePopupMenuItem(
|
||||
translate("Enable File Transfer"),
|
||||
'enable-file-transfer',
|
||||
),
|
||||
genEnablePopupMenuItem(
|
||||
translate("Enable TCP Tunneling"),
|
||||
'enable-tunnel',
|
||||
),
|
||||
genAudioInputPopupMenuItem(),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("ID/Relay Server")),
|
||||
value: 'custom-server',
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("IP Whitelisting")),
|
||||
value: 'whitelist',
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("Socks5 Proxy")),
|
||||
value: 'socks5-proxy',
|
||||
),
|
||||
// sep
|
||||
genEnablePopupMenuItem(
|
||||
translate("Enable Service"),
|
||||
'stop-service',
|
||||
),
|
||||
// TODO: direct server
|
||||
genEnablePopupMenuItem(
|
||||
translate("Always connected via relay"),
|
||||
'allow-always-relay',
|
||||
),
|
||||
genEnablePopupMenuItem(
|
||||
translate("Start ID/relay service"),
|
||||
'stop-rendezvous-service',
|
||||
),
|
||||
userName.isEmpty
|
||||
? PopupMenuItem(
|
||||
child: Text(translate("Login")),
|
||||
value: 'login',
|
||||
)
|
||||
: PopupMenuItem(
|
||||
child: Text("${translate("Logout")} $userName"),
|
||||
value: 'logout',
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("Change ID")),
|
||||
value: 'change-id',
|
||||
),
|
||||
genEnablePopupMenuItem(
|
||||
translate("Dark Theme"),
|
||||
'allow-darktheme',
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("About")),
|
||||
value: 'about',
|
||||
),
|
||||
];
|
||||
final v =
|
||||
await showMenu(context: context, position: position, items: menu);
|
||||
if (v != null) {
|
||||
onSelectMenu(v);
|
||||
}
|
||||
},
|
||||
child: Icon(Icons.more_vert_outlined));
|
||||
}
|
||||
|
||||
buildPasswordBoard(BuildContext context) {
|
||||
final model = gFFI.serverModel;
|
||||
return Container(
|
||||
@ -259,15 +295,15 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
Text(translate("Control Remote Desktop")),
|
||||
Form(
|
||||
child: Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: TextEditingController(),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r"[0-9]"))
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: TextEditingController(),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r"[0-9]"))
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
))
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -284,7 +320,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
case "quit":
|
||||
exit(0);
|
||||
case "show":
|
||||
// windowManager.show();
|
||||
// windowManager.show();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -323,6 +359,10 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
changeSocks5Proxy();
|
||||
} else if (value == "about") {
|
||||
about();
|
||||
} else if (value == "logout") {
|
||||
logOut();
|
||||
} else if (value == "login") {
|
||||
login();
|
||||
}
|
||||
}
|
||||
|
||||
@ -348,7 +388,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
return isPositive
|
||||
? TextStyle()
|
||||
: TextStyle(
|
||||
color: Colors.redAccent, decoration: TextDecoration.lineThrough);
|
||||
color: Colors.redAccent, decoration: TextDecoration.lineThrough);
|
||||
}
|
||||
|
||||
PopupMenuItem<String> genAudioInputPopupMenuItem() {
|
||||
@ -366,23 +406,23 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
}
|
||||
var inputList = inputs
|
||||
.map((e) => PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
Obx(() => Offstage(
|
||||
offstage: defaultInput.value != e,
|
||||
child: Icon(Icons.check))),
|
||||
Expanded(
|
||||
child: Tooltip(
|
||||
message: e,
|
||||
child: Text(
|
||||
"$e",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
))),
|
||||
],
|
||||
),
|
||||
value: e,
|
||||
))
|
||||
child: Row(
|
||||
children: [
|
||||
Obx(() => Offstage(
|
||||
offstage: defaultInput.value != e,
|
||||
child: Icon(Icons.check))),
|
||||
Expanded(
|
||||
child: Tooltip(
|
||||
message: e,
|
||||
child: Text(
|
||||
"$e",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
))),
|
||||
],
|
||||
),
|
||||
value: e,
|
||||
))
|
||||
.toList();
|
||||
inputList.insert(
|
||||
0,
|
||||
@ -503,7 +543,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
|
||||
void changeServer() async {
|
||||
Map<String, dynamic> oldOptions =
|
||||
jsonDecode(await gFFI.bind.mainGetOptions());
|
||||
jsonDecode(await gFFI.bind.mainGetOptions());
|
||||
print("${oldOptions}");
|
||||
String idServer = oldOptions['custom-rendezvous-server'] ?? "";
|
||||
var idServerMsg = "";
|
||||
@ -542,7 +582,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
errorText:
|
||||
idServerMsg.isNotEmpty ? idServerMsg : null),
|
||||
idServerMsg.isNotEmpty ? idServerMsg : null),
|
||||
controller: TextEditingController(text: idServer),
|
||||
),
|
||||
),
|
||||
@ -595,7 +635,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
errorText:
|
||||
apiServerMsg.isNotEmpty ? apiServerMsg : null),
|
||||
apiServerMsg.isNotEmpty ? apiServerMsg : null),
|
||||
controller: TextEditingController(text: apiServer),
|
||||
),
|
||||
),
|
||||
@ -711,7 +751,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
|
||||
void changeWhiteList() async {
|
||||
Map<String, dynamic> oldOptions =
|
||||
jsonDecode(await gFFI.bind.mainGetOptions());
|
||||
jsonDecode(await gFFI.bind.mainGetOptions());
|
||||
var newWhiteList = ((oldOptions['whitelist'] ?? "") as String).split(',');
|
||||
var newWhiteListField = newWhiteList.join('\n');
|
||||
var msg = "";
|
||||
@ -767,7 +807,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
// pass
|
||||
} else {
|
||||
final ips =
|
||||
newWhiteListField.trim().split(RegExp(r"[\s,;\n]+"));
|
||||
newWhiteListField.trim().split(RegExp(r"[\s,;\n]+"));
|
||||
// test ip
|
||||
final ipMatch = RegExp(r"^\d+\.\d+\.\d+\.\d+$");
|
||||
for (final ip in ips) {
|
||||
@ -832,8 +872,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
errorText:
|
||||
proxyMsg.isNotEmpty ? proxyMsg : null),
|
||||
errorText: proxyMsg.isNotEmpty ? proxyMsg : null),
|
||||
controller: TextEditingController(text: proxy),
|
||||
),
|
||||
),
|
||||
@ -857,8 +896,8 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
username = s;
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
controller: TextEditingController(text: username),
|
||||
),
|
||||
),
|
||||
@ -882,8 +921,8 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
password = s;
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
controller: TextEditingController(text: password),
|
||||
),
|
||||
),
|
||||
@ -941,9 +980,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
final appName = await gFFI.bind.mainGetAppName();
|
||||
final license = await gFFI.bind.mainGetLicense();
|
||||
final version = await gFFI.bind.mainGetVersion();
|
||||
final linkStyle = TextStyle(
|
||||
decoration: TextDecoration.underline
|
||||
);
|
||||
final linkStyle = TextStyle(decoration: TextDecoration.underline);
|
||||
DialogManager.show((setState, close) {
|
||||
return CustomAlertDialog(
|
||||
title: Text("About $appName"),
|
||||
@ -960,16 +997,20 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
onTap: () {
|
||||
launchUrlString("https://rustdesk.com/privacy");
|
||||
},
|
||||
child: Text("Privacy Statement", style: linkStyle,).marginSymmetric(vertical: 4.0)),
|
||||
child: Text(
|
||||
"Privacy Statement",
|
||||
style: linkStyle,
|
||||
).marginSymmetric(vertical: 4.0)),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
launchUrlString("https://rustdesk.com");
|
||||
}
|
||||
,child: Text("Website",style: linkStyle,).marginSymmetric(vertical: 4.0)),
|
||||
onTap: () {
|
||||
launchUrlString("https://rustdesk.com");
|
||||
},
|
||||
child: Text(
|
||||
"Website",
|
||||
style: linkStyle,
|
||||
).marginSymmetric(vertical: 4.0)),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFF2c8cff)
|
||||
),
|
||||
decoration: BoxDecoration(color: Color(0xFF2c8cff)),
|
||||
padding: EdgeInsets.symmetric(vertical: 24, horizontal: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
@ -977,13 +1018,16 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Copyright © 2022 Purslane Ltd.\n$license", style: TextStyle(
|
||||
color: Colors.white
|
||||
),),
|
||||
Text("Made with heart in this chaotic world!", style: TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
color: Colors.white
|
||||
),)
|
||||
Text(
|
||||
"Copyright © 2022 Purslane Ltd.\n$license",
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
Text(
|
||||
"Made with heart in this chaotic world!",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
color: Colors.white),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1003,4 +1047,151 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void login() {
|
||||
loginDialog().then((success) {
|
||||
if (success) {
|
||||
// refresh frame
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void logOut() {
|
||||
gFFI.userModel.logOut().then((_) => {setState(() {})});
|
||||
}
|
||||
}
|
||||
|
||||
/// common login dialog for desktop
|
||||
/// call this directly
|
||||
Future<bool> loginDialog() async {
|
||||
String userName = "";
|
||||
var userNameMsg = "";
|
||||
String pass = "";
|
||||
var passMsg = "";
|
||||
|
||||
var isInProgress = false;
|
||||
var completer = Completer<bool>();
|
||||
DialogManager.show((setState, close) {
|
||||
return CustomAlertDialog(
|
||||
title: Text(translate("Login")),
|
||||
content: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: 500),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: 100),
|
||||
child: Text(
|
||||
"${translate('Username')}:",
|
||||
textAlign: TextAlign.start,
|
||||
).marginOnly(bottom: 16.0)),
|
||||
SizedBox(
|
||||
width: 24.0,
|
||||
),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
onChanged: (s) {
|
||||
userName = s;
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
errorText: userNameMsg.isNotEmpty ? userNameMsg : null),
|
||||
controller: TextEditingController(text: userName),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: 100),
|
||||
child: Text("${translate('Password')}:")
|
||||
.marginOnly(bottom: 16.0)),
|
||||
SizedBox(
|
||||
width: 24.0,
|
||||
),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
obscureText: true,
|
||||
onChanged: (s) {
|
||||
pass = s;
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
errorText: passMsg.isNotEmpty ? passMsg : null),
|
||||
controller: TextEditingController(text: pass),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 4.0,
|
||||
),
|
||||
Offstage(offstage: !isInProgress, child: LinearProgressIndicator())
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
completer.complete(false);
|
||||
close();
|
||||
},
|
||||
child: Text(translate("Cancel"))),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
setState(() {
|
||||
userNameMsg = "";
|
||||
passMsg = "";
|
||||
isInProgress = true;
|
||||
});
|
||||
final cancel = () {
|
||||
setState(() {
|
||||
isInProgress = false;
|
||||
});
|
||||
};
|
||||
userName = userName;
|
||||
pass = pass;
|
||||
if (userName.isEmpty) {
|
||||
userNameMsg = translate("Username missed");
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
if (pass.isEmpty) {
|
||||
passMsg = translate("Password missed");
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final resp = await gFFI.userModel.login(userName, pass);
|
||||
if (resp.containsKey('error')) {
|
||||
passMsg = resp['error'];
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
// {access_token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJndWlkIjoiMDFkZjQ2ZjgtZjg3OS00MDE0LTk5Y2QtMGMwYzM2MmViZGJlIiwiZXhwIjoxNjYxNDg2NzYwfQ.GZpe1oI8TfM5yTYNrpcwbI599P4Z_-b2GmnwNl2Lr-w,
|
||||
// token_type: Bearer, user: {id: , name: admin, email: null, note: null, status: null, grp: null, is_admin: true}}
|
||||
debugPrint("$resp");
|
||||
completer.complete(true);
|
||||
} catch (err) {
|
||||
print(err.toString());
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
close();
|
||||
},
|
||||
child: Text(translate("OK"))),
|
||||
],
|
||||
);
|
||||
});
|
||||
return completer.future;
|
||||
}
|
@ -8,6 +8,7 @@ import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/route_manager.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
// import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import 'common.dart';
|
||||
@ -77,6 +78,8 @@ class App extends StatelessWidget {
|
||||
ChangeNotifierProvider.value(value: gFFI.imageModel),
|
||||
ChangeNotifierProvider.value(value: gFFI.cursorModel),
|
||||
ChangeNotifierProvider.value(value: gFFI.canvasModel),
|
||||
ChangeNotifierProvider.value(value: gFFI.abModel),
|
||||
ChangeNotifierProvider.value(value: gFFI.userModel),
|
||||
],
|
||||
child: GetMaterialApp(
|
||||
navigatorKey: globalKey,
|
||||
|
@ -140,4 +140,10 @@ class AbModel with ChangeNotifier {
|
||||
return it.first['tags'] ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
peers.clear();
|
||||
tags.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import 'package:flutter_hbb/models/ab_model.dart';
|
||||
import 'package:flutter_hbb/models/chat_model.dart';
|
||||
import 'package:flutter_hbb/models/file_model.dart';
|
||||
import 'package:flutter_hbb/models/server_model.dart';
|
||||
import 'package:flutter_hbb/models/user_model.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
@ -811,6 +812,7 @@ class FFI {
|
||||
late final ChatModel chatModel;
|
||||
late final FileModel fileModel;
|
||||
late final AbModel abModel;
|
||||
late final UserModel userModel;
|
||||
|
||||
FFI() {
|
||||
this.imageModel = ImageModel(WeakReference(this));
|
||||
@ -821,6 +823,7 @@ class FFI {
|
||||
this.chatModel = ChatModel(WeakReference(this));
|
||||
this.fileModel = FileModel(WeakReference(this));
|
||||
this.abModel = AbModel(WeakReference(this));
|
||||
this.userModel = UserModel(WeakReference(this));
|
||||
}
|
||||
|
||||
static FFI newFFI() {
|
||||
|
83
flutter/lib/models/user_model.dart
Normal file
83
flutter/lib/models/user_model.dart
Normal file
@ -0,0 +1,83 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'model.dart';
|
||||
|
||||
class UserModel extends ChangeNotifier {
|
||||
var userName = "".obs;
|
||||
WeakReference<FFI> parent;
|
||||
|
||||
UserModel(this.parent);
|
||||
|
||||
Future<String> getUserName() async {
|
||||
if (userName.isNotEmpty) {
|
||||
return userName.value;
|
||||
}
|
||||
final userInfo =
|
||||
await parent.target?.bind.mainGetLocalOption(key: 'user_info') ?? "{}";
|
||||
if (userInfo.trim().isEmpty) {
|
||||
return "";
|
||||
}
|
||||
final m = jsonDecode(userInfo);
|
||||
userName.value = m['name'] ?? '';
|
||||
return userName.value;
|
||||
}
|
||||
|
||||
Future<void> logOut() async {
|
||||
debugPrint("start logout");
|
||||
final bind = parent.target?.bind;
|
||||
if (bind == null) {
|
||||
return;
|
||||
}
|
||||
final url = await bind.mainGetApiServer();
|
||||
final _ = await http.post(Uri.parse("$url/api/logout"),
|
||||
body: {
|
||||
"id": await bind.mainGetMyId(),
|
||||
"uuid": await bind.mainGetUuid(),
|
||||
},
|
||||
headers: await _getHeaders());
|
||||
await Future.wait([
|
||||
bind.mainSetLocalOption(key: 'access_token', value: ''),
|
||||
bind.mainSetLocalOption(key: 'user_info', value: ''),
|
||||
bind.mainSetLocalOption(key: 'selected-tags', value: ''),
|
||||
]);
|
||||
parent.target?.abModel.clear();
|
||||
userName.value = "";
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<Map<String, String>>? _getHeaders() {
|
||||
return parent.target?.getHttpHeaders();
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> login(String userName, String pass) async {
|
||||
final bind = parent.target?.bind;
|
||||
if (bind == null) {
|
||||
return {"error": "no context"};
|
||||
}
|
||||
final url = await bind.mainGetApiServer();
|
||||
try {
|
||||
final resp = await http.post(Uri.parse("$url/api/login"),
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: jsonEncode({
|
||||
"username": userName,
|
||||
"password": pass,
|
||||
"id": await bind.mainGetMyId(),
|
||||
"uuid": await bind.mainGetUuid()
|
||||
}));
|
||||
final body = jsonDecode(resp.body);
|
||||
bind.mainSetLocalOption(
|
||||
key: "access_token", value: body['access_token'] ?? "");
|
||||
bind.mainSetLocalOption(
|
||||
key: "user_info", value: jsonEncode(body['user']));
|
||||
this.userName.value = body['user']?['name'] ?? "";
|
||||
return body;
|
||||
} catch (err) {
|
||||
return {"error": "$err"};
|
||||
}
|
||||
}
|
||||
}
|
@ -21,10 +21,10 @@ 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_lan_peers, get_license, get_local_option, get_options,
|
||||
get_peer, get_socks, get_sound_inputs, 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_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,
|
||||
};
|
||||
|
||||
fn initialize(app_dir: &str) {
|
||||
@ -488,6 +488,14 @@ pub fn main_set_local_option(key: String, value: String) {
|
||||
set_local_option(key, value)
|
||||
}
|
||||
|
||||
pub fn main_get_my_id() -> String {
|
||||
get_id()
|
||||
}
|
||||
|
||||
pub fn main_get_uuid() -> String {
|
||||
get_uuid()
|
||||
}
|
||||
|
||||
/// FFI for **get** commands which are idempotent.
|
||||
/// Return result in c string.
|
||||
///
|
||||
|
Loading…
x
Reference in New Issue
Block a user