feat: user login/logout with UserModel

Signed-off-by: Kingtous <kingtous@qq.com>
This commit is contained in:
Kingtous 2022-07-27 14:29:47 +08:00
parent 98a01aefa6
commit 06cb05f796
7 changed files with 424 additions and 144 deletions

View File

@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:contextmenu/contextmenu.dart'; import 'package:contextmenu/contextmenu.dart';
import 'package:flutter/material.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:flutter_hbb/utils/multi_window_manager.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -588,7 +589,13 @@ class _ConnectionPageState extends State<ConnectionPage> {
svcIsUsingPublicServer.value = await gFFI.bind.mainIsUsingPublicServer(); svcIsUsingPublicServer.value = await gFFI.bind.mainIsUsingPublicServer();
} }
handleLogin() {} handleLogin() {
loginDialog().then((success) {
if (success) {
setState(() {});
}
});
}
Future<Widget> buildAddressBook(BuildContext context) async { Future<Widget> buildAddressBook(BuildContext context) async {
final token = await gFFI.getLocalOption('access_token'); 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 { class WebMenu extends StatefulWidget {
@override @override
_WebMenuState createState() => _WebMenuState(); _WebMenuState createState() => _WebMenuState();

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
@ -110,68 +111,18 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
style: TextStyle( style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w500), fontSize: 18, fontWeight: FontWeight.w500),
), ),
PopupMenuButton( FutureBuilder<Widget>(
padding: EdgeInsets.all(4.0), future: buildPopupMenu(context),
itemBuilder: (context) => [ builder: (context, snapshot) {
genEnablePopupMenuItem( if (snapshot.hasError) {
translate("Enable Keyboard/Mouse"), print("${snapshot.error}");
'enable-keyboard', }
), if (snapshot.hasData) {
genEnablePopupMenuItem( return snapshot.data!;
translate("Enable Clipboard"), } else {
'enable-clipboard', return Offstage();
), }
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,
)
], ],
), ),
TextFormField( 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) { buildPasswordBoard(BuildContext context) {
final model = gFFI.serverModel; final model = gFFI.serverModel;
return Container( return Container(
@ -259,15 +295,15 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
Text(translate("Control Remote Desktop")), Text(translate("Control Remote Desktop")),
Form( Form(
child: Column( child: Column(
children: [ children: [
TextFormField( TextFormField(
controller: TextEditingController(), controller: TextEditingController(),
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r"[0-9]")) FilteringTextInputFormatter.allow(RegExp(r"[0-9]"))
],
)
], ],
) ))
],
))
], ],
), ),
); );
@ -284,7 +320,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
case "quit": case "quit":
exit(0); exit(0);
case "show": case "show":
// windowManager.show(); // windowManager.show();
break; break;
default: default:
break; break;
@ -323,6 +359,10 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
changeSocks5Proxy(); changeSocks5Proxy();
} else if (value == "about") { } else if (value == "about") {
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 return isPositive
? TextStyle() ? TextStyle()
: TextStyle( : TextStyle(
color: Colors.redAccent, decoration: TextDecoration.lineThrough); color: Colors.redAccent, decoration: TextDecoration.lineThrough);
} }
PopupMenuItem<String> genAudioInputPopupMenuItem() { PopupMenuItem<String> genAudioInputPopupMenuItem() {
@ -366,23 +406,23 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
} }
var inputList = inputs var inputList = inputs
.map((e) => PopupMenuItem( .map((e) => PopupMenuItem(
child: Row( child: Row(
children: [ children: [
Obx(() => Offstage( Obx(() => Offstage(
offstage: defaultInput.value != e, offstage: defaultInput.value != e,
child: Icon(Icons.check))), child: Icon(Icons.check))),
Expanded( Expanded(
child: Tooltip( child: Tooltip(
message: e, message: e,
child: Text( child: Text(
"$e", "$e",
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
))), ))),
], ],
), ),
value: e, value: e,
)) ))
.toList(); .toList();
inputList.insert( inputList.insert(
0, 0,
@ -503,7 +543,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
void changeServer() async { void changeServer() async {
Map<String, dynamic> oldOptions = Map<String, dynamic> oldOptions =
jsonDecode(await gFFI.bind.mainGetOptions()); jsonDecode(await gFFI.bind.mainGetOptions());
print("${oldOptions}"); print("${oldOptions}");
String idServer = oldOptions['custom-rendezvous-server'] ?? ""; String idServer = oldOptions['custom-rendezvous-server'] ?? "";
var idServerMsg = ""; var idServerMsg = "";
@ -542,7 +582,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: OutlineInputBorder(),
errorText: errorText:
idServerMsg.isNotEmpty ? idServerMsg : null), idServerMsg.isNotEmpty ? idServerMsg : null),
controller: TextEditingController(text: idServer), controller: TextEditingController(text: idServer),
), ),
), ),
@ -595,7 +635,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: OutlineInputBorder(),
errorText: errorText:
apiServerMsg.isNotEmpty ? apiServerMsg : null), apiServerMsg.isNotEmpty ? apiServerMsg : null),
controller: TextEditingController(text: apiServer), controller: TextEditingController(text: apiServer),
), ),
), ),
@ -711,7 +751,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
void changeWhiteList() async { void changeWhiteList() async {
Map<String, dynamic> oldOptions = Map<String, dynamic> oldOptions =
jsonDecode(await gFFI.bind.mainGetOptions()); jsonDecode(await gFFI.bind.mainGetOptions());
var newWhiteList = ((oldOptions['whitelist'] ?? "") as String).split(','); var newWhiteList = ((oldOptions['whitelist'] ?? "") as String).split(',');
var newWhiteListField = newWhiteList.join('\n'); var newWhiteListField = newWhiteList.join('\n');
var msg = ""; var msg = "";
@ -767,7 +807,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
// pass // pass
} else { } else {
final ips = final ips =
newWhiteListField.trim().split(RegExp(r"[\s,;\n]+")); newWhiteListField.trim().split(RegExp(r"[\s,;\n]+"));
// test ip // test ip
final ipMatch = RegExp(r"^\d+\.\d+\.\d+\.\d+$"); final ipMatch = RegExp(r"^\d+\.\d+\.\d+\.\d+$");
for (final ip in ips) { for (final ip in ips) {
@ -832,8 +872,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
}, },
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: OutlineInputBorder(),
errorText: errorText: proxyMsg.isNotEmpty ? proxyMsg : null),
proxyMsg.isNotEmpty ? proxyMsg : null),
controller: TextEditingController(text: proxy), controller: TextEditingController(text: proxy),
), ),
), ),
@ -857,8 +896,8 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
username = s; username = s;
}, },
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: OutlineInputBorder(),
), ),
controller: TextEditingController(text: username), controller: TextEditingController(text: username),
), ),
), ),
@ -882,8 +921,8 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
password = s; password = s;
}, },
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: OutlineInputBorder(),
), ),
controller: TextEditingController(text: password), controller: TextEditingController(text: password),
), ),
), ),
@ -941,9 +980,7 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
final appName = await gFFI.bind.mainGetAppName(); final appName = await gFFI.bind.mainGetAppName();
final license = await gFFI.bind.mainGetLicense(); final license = await gFFI.bind.mainGetLicense();
final version = await gFFI.bind.mainGetVersion(); final version = await gFFI.bind.mainGetVersion();
final linkStyle = TextStyle( final linkStyle = TextStyle(decoration: TextDecoration.underline);
decoration: TextDecoration.underline
);
DialogManager.show((setState, close) { DialogManager.show((setState, close) {
return CustomAlertDialog( return CustomAlertDialog(
title: Text("About $appName"), title: Text("About $appName"),
@ -960,16 +997,20 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
onTap: () { onTap: () {
launchUrlString("https://rustdesk.com/privacy"); 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( InkWell(
onTap: () { onTap: () {
launchUrlString("https://rustdesk.com"); launchUrlString("https://rustdesk.com");
} },
,child: Text("Website",style: linkStyle,).marginSymmetric(vertical: 4.0)), child: Text(
"Website",
style: linkStyle,
).marginSymmetric(vertical: 4.0)),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(color: Color(0xFF2c8cff)),
color: Color(0xFF2c8cff)
),
padding: EdgeInsets.symmetric(vertical: 24, horizontal: 8), padding: EdgeInsets.symmetric(vertical: 24, horizontal: 8),
child: Row( child: Row(
children: [ children: [
@ -977,13 +1018,16 @@ class _DesktopHomePageState extends State<DesktopHomePage> with TrayListener {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text("Copyright &copy; 2022 Purslane Ltd.\n$license", style: TextStyle( Text(
color: Colors.white "Copyright &copy; 2022 Purslane Ltd.\n$license",
),), style: TextStyle(color: Colors.white),
Text("Made with heart in this chaotic world!", style: TextStyle( ),
fontWeight: FontWeight.w800, Text(
color: Colors.white "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;
} }

View File

@ -8,6 +8,7 @@ import 'package:flutter_hbb/utils/multi_window_manager.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/route_manager.dart'; import 'package:get/route_manager.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
// import 'package:window_manager/window_manager.dart'; // import 'package:window_manager/window_manager.dart';
import 'common.dart'; import 'common.dart';
@ -77,6 +78,8 @@ class App extends StatelessWidget {
ChangeNotifierProvider.value(value: gFFI.imageModel), ChangeNotifierProvider.value(value: gFFI.imageModel),
ChangeNotifierProvider.value(value: gFFI.cursorModel), ChangeNotifierProvider.value(value: gFFI.cursorModel),
ChangeNotifierProvider.value(value: gFFI.canvasModel), ChangeNotifierProvider.value(value: gFFI.canvasModel),
ChangeNotifierProvider.value(value: gFFI.abModel),
ChangeNotifierProvider.value(value: gFFI.userModel),
], ],
child: GetMaterialApp( child: GetMaterialApp(
navigatorKey: globalKey, navigatorKey: globalKey,

View File

@ -140,4 +140,10 @@ class AbModel with ChangeNotifier {
return it.first['tags'] ?? []; return it.first['tags'] ?? [];
} }
} }
void clear() {
peers.clear();
tags.clear();
notifyListeners();
}
} }

View File

@ -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/chat_model.dart';
import 'package:flutter_hbb/models/file_model.dart'; import 'package:flutter_hbb/models/file_model.dart';
import 'package:flutter_hbb/models/server_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:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
@ -811,6 +812,7 @@ class FFI {
late final ChatModel chatModel; late final ChatModel chatModel;
late final FileModel fileModel; late final FileModel fileModel;
late final AbModel abModel; late final AbModel abModel;
late final UserModel userModel;
FFI() { FFI() {
this.imageModel = ImageModel(WeakReference(this)); this.imageModel = ImageModel(WeakReference(this));
@ -821,6 +823,7 @@ class FFI {
this.chatModel = ChatModel(WeakReference(this)); this.chatModel = ChatModel(WeakReference(this));
this.fileModel = FileModel(WeakReference(this)); this.fileModel = FileModel(WeakReference(this));
this.abModel = AbModel(WeakReference(this)); this.abModel = AbModel(WeakReference(this));
this.userModel = UserModel(WeakReference(this));
} }
static FFI newFFI() { static FFI newFFI() {

View 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"};
}
}
}

View File

@ -21,10 +21,10 @@ use crate::start_server;
use crate::ui_interface; use crate::ui_interface;
use crate::ui_interface::{ use crate::ui_interface::{
change_id, check_connect_status, get_api_server, get_app_name, get_async_job_status, 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_connect_status, get_fav, get_id, 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, get_peer, get_socks, get_sound_inputs, get_uuid, get_version, has_rendezvous_service,
post_request, set_local_option, set_options, set_socks, store_fav, test_if_valid_server, is_ok_change_id, post_request, set_local_option, set_options, set_socks, store_fav,
using_public_server, test_if_valid_server, using_public_server,
}; };
fn initialize(app_dir: &str) { fn initialize(app_dir: &str) {
@ -488,6 +488,14 @@ pub fn main_set_local_option(key: String, value: String) {
set_local_option(key, value) 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. /// FFI for **get** commands which are idempotent.
/// Return result in c string. /// Return result in c string.
/// ///