From 3e357159f3afa10f93669d2dd34f1b5ea4e5e8e1 Mon Sep 17 00:00:00 2001 From: csf Date: Fri, 6 Jan 2023 19:26:19 +0900 Subject: [PATCH 1/5] 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. --- flutter/lib/common.dart | 2 +- flutter/lib/common/hbbs/hbbs.dart | 92 +++- flutter/lib/common/widgets/address_book.dart | 13 +- flutter/lib/desktop/widgets/login.dart | 424 +++++++++--------- flutter/lib/mobile/pages/connection_page.dart | 5 +- flutter/lib/mobile/pages/settings_page.dart | 74 +-- flutter/lib/models/ab_model.dart | 5 +- flutter/lib/models/group_model.dart | 4 +- flutter/lib/models/user_model.dart | 65 +-- 9 files changed, 363 insertions(+), 321 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 8d047dd0d..23847ab52 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1367,7 +1367,7 @@ connect(BuildContext context, String id, } } -Future> getHttpHeaders() async { +Map getHttpHeaders() { return { 'Authorization': 'Bearer ${bind.mainGetLocalOption(key: 'access_token')}' }; diff --git a/flutter/lib/common/hbbs/hbbs.dart b/flutter/lib/common/hbbs/hbbs.dart index 856f88d20..6a80f8269 100644 --- a/flutter/lib/common/hbbs/hbbs.dart +++ b/flutter/lib/common/hbbs/hbbs.dart @@ -1,12 +1,22 @@ 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 { String name = ''; String email = ''; String note = ''; int? status; String grp = ''; - bool is_admin = false; + bool isAdmin = false; UserPayload.fromJson(Map json) : name = json['name'] ?? '', @@ -14,7 +24,7 @@ class UserPayload { note = json['note'] ?? '', status = json['status'], grp = json['grp'] ?? '', - is_admin = json['is_admin'] == true; + isAdmin = json['is_admin'] == true; } class PeerPayload { @@ -37,3 +47,81 @@ class PeerPayload { 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 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 toJson() { + final Map data = {}; + 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 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"; + } +} diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index fbeca25b2..3e7f46814 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -9,8 +9,6 @@ import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu; import 'package:get/get.dart'; import '../../common.dart'; -import '../../desktop/pages/desktop_home_page.dart'; -import '../../mobile/pages/settings_page.dart'; class AddressBook extends StatefulWidget { final EdgeInsets? menuPadding; @@ -41,21 +39,12 @@ class _AddressBookState extends State { } }); - handleLogin() { - // TODO refactor login dialog for desktop and mobile - if (isDesktop) { - loginDialog(); - } else { - showLogin(gFFI.dialogManager); - } - } - Future buildBody(BuildContext context) async { return Obx(() { if (gFFI.userModel.userName.value.isEmpty) { return Center( child: InkWell( - onTap: handleLogin, + onTap: loginDialog, child: Text( translate("Login"), style: const TextStyle(decoration: TextDecoration.underline), diff --git a/flutter/lib/desktop/widgets/login.dart b/flutter/lib/desktop/widgets/login.dart index 62e6ebc53..8092dfed6 100644 --- a/flutter/lib/desktop/widgets/login.dart +++ b/flutter/lib/desktop/widgets/login.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter_hbb/common/hbbs/hbbs.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:get/get.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -9,18 +10,21 @@ import 'package:url_launcher/url_launcher.dart'; import '../../common.dart'; -final _kMidButtonPadding = const EdgeInsets.fromLTRB(15, 0, 15, 0); - class _IconOP extends StatelessWidget { final String icon; 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); @override Widget build(BuildContext context) { return Container( - margin: const EdgeInsets.symmetric(horizontal: 4.0), + margin: margin, child: SvgPicture.asset( 'assets/$icon.svg', width: iconWidth, @@ -50,33 +54,33 @@ class ButtonOP extends StatelessWidget { @override Widget build(BuildContext context) { return Row(children: [ - Expanded( - child: Container( - height: height, - padding: _kMidButtonPadding, - child: Obx(() => ElevatedButton( - style: ElevatedButton.styleFrom( - primary: curOP.value.isEmpty || curOP.value == op - ? primaryColor - : Colors.grey, - ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)), - onPressed: - curOP.value.isEmpty || curOP.value == op ? onTap : null, - child: Stack(children: [ - Center(child: Text('${translate("Continue with")} $op')), - Align( - alignment: Alignment.centerLeft, - child: SizedBox( - width: 120, - child: _IconOP( - icon: op, - iconWidth: iconWidth, - )), - ), - ]), - )), - ), - ) + Container( + height: height, + width: 200, + child: Obx(() => ElevatedButton( + style: ElevatedButton.styleFrom( + primary: curOP.value.isEmpty || curOP.value == op + ? primaryColor + : Colors.grey, + ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)), + onPressed: curOP.value.isEmpty || curOP.value == op ? onTap : null, + child: Row( + children: [ + SizedBox( + width: 30, + child: _IconOP( + icon: op, + iconWidth: iconWidth, + margin: EdgeInsets.only(right: 5), + )), + Expanded( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Center( + child: Text('${translate("Continue with")} $op')))), + ], + ))), + ), ]); } } @@ -277,22 +281,25 @@ class LoginWidgetOP extends StatelessWidget { children.removeLast(); } return SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: children, - )); + child: Container( + width: 200, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: children, + ))); } } class LoginWidgetUserPass extends StatelessWidget { - final String username; - final String pass; - final String usernameMsg; - final String passMsg; + final TextEditingController username; + final TextEditingController pass; + final String? usernameMsg; + final String? passMsg; final bool isInProgress; final RxString curOP; - final Function(String, String) onLogin; + final RxBool autoLogin; + final Function() onLogin; const LoginWidgetUserPass({ Key? key, required this.username, @@ -301,129 +308,135 @@ class LoginWidgetUserPass extends StatelessWidget { required this.passMsg, required this.isInProgress, required this.curOP, + required this.autoLogin, required this.onLogin, }) : super(key: key); @override Widget build(BuildContext context) { - var userController = TextEditingController(text: username); - var pwdController = TextEditingController(text: pass); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, + return Padding( + padding: EdgeInsets.all(0), + child: Column( + 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: [ - const SizedBox( - height: 8.0, - ), - Container( - padding: _kMidButtonPadding, - child: Row( - children: [ - ConstrainedBox( - constraints: const BoxConstraints(minWidth: 100), - child: Text( - '${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(), - ), - ), - ], + Expanded( + child: TextField( + decoration: InputDecoration( + labelText: title, + border: const OutlineInputBorder(), + errorText: errorText), + controller: controller, + focusNode: focusNode, + autofocus: true, + obscureText: obscureText, ), ), - 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 /// call this directly -Future loginDialog() async { - String username = ''; - var usernameMsg = ''; - String pass = ''; - var passMsg = ''; +Future loginDialog() async { + var username = TextEditingController(); + var password = TextEditingController(); + + String? usernameMsg; + String? passwordMsg; var isInProgress = false; - var completer = Completer(); + final autoLogin = true.obs; final RxString curOP = ''.obs; - gFFI.dialogManager.show((setState, close) { + return gFFI.dialogManager.show((setState, close) { cancel() { isInProgress = false; - completer.complete(false); - close(); + close(false); } - onLogin(String username0, String pass0) async { + onLogin() async { setState(() { - usernameMsg = ''; - passMsg = ''; + usernameMsg = null; + passwordMsg = null; isInProgress = true; }); cancel() { @@ -436,30 +449,44 @@ Future loginDialog() async { } curOP.value = 'rustdesk'; - username = username0; - pass = pass0; - if (username.isEmpty) { + if (username.text.isEmpty) { usernameMsg = translate('Username missed'); cancel(); return; } - if (pass.isEmpty) { - passMsg = translate('Password missed'); + if (password.text.isEmpty) { + passwordMsg = translate('Password missed'); cancel(); return; } try { - final resp = await gFFI.userModel.login(username, pass); - if (resp.containsKey('error')) { - passMsg = resp['error']; - cancel(); - return; + final resp = await gFFI.userModel.login(LoginRequest( + username: username.text, + password: password.text, + id: await bind.mainGetMyId(), + 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, - // token_type: Bearer, user: {id: , name: admin, email: null, note: null, status: null, grp: null, is_admin: true}} - debugPrint('$resp'); - completer.complete(true); + } on RequestException catch (err) { + passwordMsg = translate(err.cause); + debugPrintStack(label: err.toString()); + cancel(); + return; } catch (err) { + passwordMsg = "Unknown Error"; debugPrintStack(label: err.toString()); cancel(); return; @@ -469,53 +496,50 @@ Future loginDialog() async { return CustomAlertDialog( title: Text(translate('Login')), - content: ConstrainedBox( - constraints: const BoxConstraints(minWidth: 500), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox( - height: 8.0, - ), - LoginWidgetUserPass( - username: username, - pass: pass, - usernameMsg: usernameMsg, - passMsg: passMsg, - isInProgress: isInProgress, - curOP: curOP, - onLogin: onLogin, - ), - const SizedBox( - height: 8.0, - ), - Center( - child: Text( - translate('or'), - style: TextStyle(fontSize: 16), - )), - const SizedBox( - height: 8.0, - ), - LoginWidgetOP( - ops: [ - ConfigOP(op: 'Github', iconWidth: 20), - ConfigOP(op: 'Google', iconWidth: 20), - ConfigOP(op: 'Okta', iconWidth: 38), - ], - curOP: curOP, - cbLogin: (String username) { - gFFI.userModel.userName.value = username; - completer.complete(true); - close(); - }, - ), - ], - ), + contentBoxConstraints: BoxConstraints(minWidth: 400), + content: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 8.0, + ), + LoginWidgetUserPass( + username: username, + pass: password, + usernameMsg: usernameMsg, + passMsg: passwordMsg, + isInProgress: isInProgress, + curOP: curOP, + autoLogin: autoLogin, + onLogin: onLogin, + ), + const SizedBox( + height: 8.0, + ), + Center( + child: Text( + translate('or'), + style: TextStyle(fontSize: 16), + )), + const SizedBox( + height: 8.0, + ), + LoginWidgetOP( + ops: [ + ConfigOP(op: 'Github', iconWidth: 20), + ConfigOP(op: 'Google', iconWidth: 20), + ConfigOP(op: 'Okta', iconWidth: 38), + ], + curOP: curOP, + cbLogin: (String username) { + gFFI.userModel.userName.value = username; + close(true); + }, + ), + ], ), actions: [msgBoxButton(translate('Close'), cancel)], onCancel: cancel, ); }); - return completer.future; } diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index 957910324..b8104387e 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -7,10 +7,9 @@ import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../common.dart'; -import '../../common/widgets/address_book.dart'; import '../../common/widgets/peer_tab_page.dart'; -import '../../common/widgets/peers_view.dart'; import '../../consts.dart'; +import '../../desktop/widgets/login.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; import 'home_page.dart'; @@ -258,7 +257,7 @@ class _WebMenuState extends State { } if (value == 'login') { if (gFFI.userModel.userName.value.isEmpty) { - showLogin(gFFI.dialogManager); + loginDialog(); } else { gFFI.userModel.logOut(); } diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 9637ecb40..0764f8247 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -9,6 +9,7 @@ import 'package:url_launcher/url_launcher.dart'; import '../../common.dart'; import '../../common/widgets/dialog.dart'; +import '../../desktop/widgets/login.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; import '../widgets/dialog.dart'; @@ -300,7 +301,7 @@ class _SettingsState extends State with WidgetsBindingObserver { leading: Icon(Icons.person), onPressed: (context) { if (gFFI.userModel.userName.value.isEmpty) { - showLogin(gFFI.dialogManager); + loginDialog(); } else { gFFI.userModel.logOut(); } @@ -482,77 +483,6 @@ void showAbout(OverlayDialogManager dialogManager) { }, 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 - ? [CircularProgressIndicator()] - : (error != "" - ? [ - Text(translate(error), - style: TextStyle(color: Colors.red)) - ] - : [])) + - [ - 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 { @override Widget build(BuildContext context) { diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index d8a0e8f99..175c8424b 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -27,8 +27,7 @@ class AbModel { abError.value = ""; final api = "${await bind.mainGetApiServer()}/api/ab/get"; try { - final resp = - await http.post(Uri.parse(api), headers: await getHttpHeaders()); + final resp = await http.post(Uri.parse(api), headers: getHttpHeaders()); if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") { Map json = jsonDecode(resp.body); if (json.containsKey('error')) { @@ -102,7 +101,7 @@ class AbModel { Future pushAb() async { abLoading.value = true; final api = "${await bind.mainGetApiServer()}/api/ab"; - var authHeaders = await getHttpHeaders(); + var authHeaders = getHttpHeaders(); authHeaders['Content-Type'] = "application/json"; final peersJsonData = peers.map((e) => e.toJson()).toList(); final body = jsonEncode({ diff --git a/flutter/lib/models/group_model.dart b/flutter/lib/models/group_model.dart index f220d62f1..4d9fab0e4 100644 --- a/flutter/lib/models/group_model.dart +++ b/flutter/lib/models/group_model.dart @@ -59,7 +59,7 @@ class GroupModel { if (gFFI.userModel.isAdmin.isFalse) '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") { Map json = jsonDecode(resp.body); if (json.containsKey('error')) { @@ -110,7 +110,7 @@ class GroupModel { 'grp': gFFI.userModel.groupName.value, '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") { Map json = jsonDecode(resp.body); if (json.containsKey('error')) { diff --git a/flutter/lib/models/user_model.dart b/flutter/lib/models/user_model.dart index e5d2c9e15..79a9778b0 100644 --- a/flutter/lib/models/user_model.dart +++ b/flutter/lib/models/user_model.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; +import 'package:flutter_hbb/common/hbbs/hbbs.dart'; import 'package:flutter_hbb/common/widgets/peer_tab_page.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; @@ -45,7 +46,9 @@ class UserModel { if (error != null) { throw error; } - await _parseUserInfo(data); + + final user = UserPayload.fromJson(data); + await _parseAndUpdateUser(user); } catch (e) { print('Failed to refreshCurrentUser: $e'); } finally { @@ -55,7 +58,6 @@ class UserModel { Future reset() async { await bind.mainSetLocalOption(key: 'access_token', value: ''); - await bind.mainSetLocalOption(key: 'user_info', value: ''); await gFFI.abModel.reset(); await gFFI.groupModel.reset(); userName.value = ''; @@ -63,11 +65,10 @@ class UserModel { statePeerTab.check(); } - Future _parseUserInfo(dynamic userinfo) async { - bind.mainSetLocalOption(key: 'user_info', value: jsonEncode(userinfo)); - userName.value = userinfo['name'] ?? ''; - groupName.value = userinfo['grp'] ?? ''; - isAdmin.value = userinfo['is_admin'] == true; + Future _parseAndUpdateUser(UserPayload user) async { + userName.value = user.name; + groupName.value = user.grp; + isAdmin.value = user.isAdmin; } Future _updateOtherModels() async { @@ -85,7 +86,7 @@ class UserModel { 'id': await bind.mainGetMyId(), 'uuid': await bind.mainGetUuid(), }, - headers: await getHttpHeaders()) + headers: getHttpHeaders()) .timeout(Duration(seconds: 2)); } catch (e) { print("request /api/logout failed: err=$e"); @@ -95,26 +96,38 @@ class UserModel { } } - Future> login(String userName, String pass) async { + /// throw [RequestException] + Future login(LoginRequest loginRequest) async { 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 body; 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'] ?? ''); - await _parseUserInfo(body['user']); - return body; - } catch (err) { - return {'error': '$err'}; - } finally { - await _updateOtherModels(); + body = jsonDecode(resp.body); + } catch (e) { + print("jsonDecode resp body failed: ${e.toString()}"); + rethrow; } + + 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; } } From a01b87510c0a57c00c45e1ee47aa24d54b75980d Mon Sep 17 00:00:00 2001 From: csf Date: Sun, 8 Jan 2023 23:30:34 +0900 Subject: [PATCH 2/5] move login.dart --- flutter/lib/common/hbbs/hbbs.dart | 8 -------- flutter/lib/common/widgets/address_book.dart | 2 +- flutter/lib/{desktop => common}/widgets/login.dart | 0 flutter/lib/desktop/pages/desktop_setting_page.dart | 2 +- flutter/lib/mobile/pages/connection_page.dart | 2 +- flutter/lib/mobile/pages/settings_page.dart | 4 ++-- 6 files changed, 5 insertions(+), 13 deletions(-) rename flutter/lib/{desktop => common}/widgets/login.dart (100%) diff --git a/flutter/lib/common/hbbs/hbbs.dart b/flutter/lib/common/hbbs/hbbs.dart index 6a80f8269..27238db67 100644 --- a/flutter/lib/common/hbbs/hbbs.dart +++ b/flutter/lib/common/hbbs/hbbs.dart @@ -103,14 +103,6 @@ class LoginResponse { LoginResponse.fromJson(Map 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; } } diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index 3e7f46814..34d5af485 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -3,12 +3,12 @@ import 'package:flutter_hbb/common/formatter/id_formatter.dart'; import 'package:flutter_hbb/common/widgets/peer_card.dart'; import 'package:flutter_hbb/common/widgets/peers_view.dart'; import 'package:flutter_hbb/desktop/widgets/popup_menu.dart'; -import 'package:flutter_hbb/desktop/widgets/login.dart'; import '../../consts.dart'; import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu; import 'package:get/get.dart'; import '../../common.dart'; +import 'login.dart'; class AddressBook extends StatefulWidget { final EdgeInsets? menuPadding; diff --git a/flutter/lib/desktop/widgets/login.dart b/flutter/lib/common/widgets/login.dart similarity index 100% rename from flutter/lib/desktop/widgets/login.dart rename to flutter/lib/common/widgets/login.dart diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 15f78daeb..ca8e47e69 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -8,7 +8,6 @@ import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart'; import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart'; -import 'package:flutter_hbb/desktop/widgets/login.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/server_model.dart'; import 'package:get/get.dart'; @@ -18,6 +17,7 @@ import 'package:url_launcher/url_launcher_string.dart'; import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart'; import '../../common/widgets/dialog.dart'; +import '../../common/widgets/login.dart'; const double _kTabWidth = 235; const double _kTabHeight = 42; diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index b8104387e..6fce887bf 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -7,9 +7,9 @@ import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../common.dart'; +import '../../common/widgets/login.dart'; import '../../common/widgets/peer_tab_page.dart'; import '../../consts.dart'; -import '../../desktop/widgets/login.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; import 'home_page.dart'; diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 0764f8247..b14f3ee65 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -9,7 +9,7 @@ import 'package:url_launcher/url_launcher.dart'; import '../../common.dart'; import '../../common/widgets/dialog.dart'; -import '../../desktop/widgets/login.dart'; +import '../../common/widgets/login.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; import '../widgets/dialog.dart'; @@ -398,7 +398,7 @@ void showServerSettings(OverlayDialogManager dialogManager) async { void showLanguageSettings(OverlayDialogManager dialogManager) async { try { final langs = json.decode(await bind.mainGetLangs()) as List; - var lang = await bind.mainGetLocalOption(key: "lang"); + var lang = bind.mainGetLocalOption(key: "lang"); dialogManager.show((setState, close) { setLang(v) { if (lang != v) { From bb8c50b2c709af831304990d4dfcea6ff3d6a23b Mon Sep 17 00:00:00 2001 From: csf Date: Sun, 8 Jan 2023 23:38:58 +0900 Subject: [PATCH 3/5] update translate --- src/lang/ca.rs | 1 + src/lang/cn.rs | 1 + src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/fa.rs | 1 + src/lang/fr.rs | 1 + src/lang/gr.rs | 1 + src/lang/hu.rs | 1 + src/lang/id.rs | 1 + src/lang/it.rs | 1 + src/lang/ja.rs | 1 + src/lang/ko.rs | 1 + src/lang/kz.rs | 1 + src/lang/pl.rs | 1 + src/lang/pt_PT.rs | 1 + src/lang/ptbr.rs | 1 + src/lang/ru.rs | 1 + src/lang/sk.rs | 1 + src/lang/sq.rs | 1 + src/lang/sr.rs | 1 + src/lang/sv.rs | 1 + src/lang/template.rs | 1 + src/lang/th.rs | 817 ++++++++++++++++++++++--------------------- src/lang/tr.rs | 1 + src/lang/tw.rs | 1 + src/lang/ua.rs | 1 + src/lang/vn.rs | 1 + 30 files changed, 438 insertions(+), 408 deletions(-) diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 093f2572c..ffc5396fd 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Connecta sempre a través de relay"), ("whitelist_tip", ""), ("Login", "Inicia sessió"), + ("Remember me", ""), ("Logout", "Sortir"), ("Tags", ""), ("Search ID", "Cerca ID"), diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 307cbfd9b..6a4feeeec 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "强制走中继连接"), ("whitelist_tip", "只有白名单里的ip才能访问我"), ("Login", "登录"), + ("Remember me", "记住我"), ("Logout", "登出"), ("Tags", "标签"), ("Search ID", "查找ID"), diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 027de13ba..17e2ea68b 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Vždy se spojovat prostřednictvím brány pro předávání (relay)"), ("whitelist_tip", "Přístup je umožněn pouze z IP adres, nacházejících se na seznamu povolených"), ("Login", "Přihlásit se"), + ("Remember me", ""), ("Logout", "Odhlásit se"), ("Tags", "Štítky"), ("Search ID", "Hledat identifikátor"), diff --git a/src/lang/da.rs b/src/lang/da.rs index 3361804e8..c0db3eda0 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Forbindelse via relæ-server"), ("whitelist_tip", "Kun IP'er på udgivelseslisten kan få adgang til mig"), ("Login", "Login"), + ("Remember me", ""), ("Logout", "logger af"), ("Tags", "Nøgleord"), ("Search ID", "Søg ID"), diff --git a/src/lang/de.rs b/src/lang/de.rs index 7226550f5..28b0a51d3 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Immer über Relay-Server verbinden"), ("whitelist_tip", "Nur IPs auf der Whitelist können zugreifen."), ("Login", "Anmelden"), + ("Remember me", ""), ("Logout", "Abmelden"), ("Tags", "Schlagworte"), ("Search ID", "Suche ID"), diff --git a/src/lang/eo.rs b/src/lang/eo.rs index a21a2e91e..e92d7ab31 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Ĉiam konekti per relajso"), ("whitelist_tip", "Nur la IP en la blanka listo povas kontroli mian komputilon"), ("Login", "Konekti"), + ("Remember me", ""), ("Logout", "Malkonekti"), ("Tags", "Etikedi"), ("Search ID", "Serĉi ID"), diff --git a/src/lang/es.rs b/src/lang/es.rs index b3276949a..539627cb9 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Conéctese siempre a través de relay"), ("whitelist_tip", "Solo las direcciones IP autorizadas pueden conectarse a este escritorio"), ("Login", "Iniciar sesión"), + ("Remember me", ""), ("Logout", "Salir"), ("Tags", "Tags"), ("Search ID", "Buscar ID"), diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 3d4579b27..9ca854ecb 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "برای اتصال استفاده شود Relay از"), ("whitelist_tip", "های مجاز می توانند به این دسکتاپ متصل شوند IP فقط"), ("Login", "ورود"), + ("Remember me", ""), ("Logout", "خروج"), ("Tags", "برچسب ها"), ("Search ID", "جستجوی شناسه"), diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 1e3beb2e4..84cd9c4fa 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Forcer la connexion relais"), ("whitelist_tip", "Seul l'IP dans la liste blanche peut accéder à mon appareil"), ("Login", "Connexion"), + ("Remember me", ""), ("Logout", "Déconnexion"), ("Tags", "Étiqueter"), ("Search ID", "Rechercher un ID"), diff --git a/src/lang/gr.rs b/src/lang/gr.rs index 4b2777729..f1ee5be08 100644 --- a/src/lang/gr.rs +++ b/src/lang/gr.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Σύνδεση πάντα μέσω αναμετάδοσης"), ("whitelist_tip", "Μόνο οι IP της λίστας επιτρεπόμενων έχουν πρόσβαση"), ("Login", "Σύνδεση"), + ("Remember me", ""), ("Logout", "Αποσύνδεση"), ("Tags", "Ετικέτες"), ("Search ID", "Αναζήτηση ID"), diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 2f22ab511..92b7c1ba1 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Mindig közvetítőn keresztüli csatlakozás"), ("whitelist_tip", "Csak az engedélyezési listán szereplő címek csatlakozhatnak"), ("Login", "Belépés"), + ("Remember me", ""), ("Logout", "Kilépés"), ("Tags", "Tagok"), ("Search ID", "Azonosító keresése..."), diff --git a/src/lang/id.rs b/src/lang/id.rs index 362a7fb85..2e96785c5 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Selalu terhubung melalui relai"), ("whitelist_tip", "Hanya whitelisted IP yang dapat mengakses saya"), ("Login", "Masuk"), + ("Remember me", ""), ("Logout", "Keluar"), ("Tags", "Tag"), ("Search ID", "Cari ID"), diff --git a/src/lang/it.rs b/src/lang/it.rs index 390670df4..d54887aa4 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Connetti sempre tramite relay"), ("whitelist_tip", "Solo gli indirizzi IP autorizzati possono connettersi a questo desktop"), ("Login", "Accedi"), + ("Remember me", ""), ("Logout", "Esci"), ("Tags", "Tag"), ("Search ID", "Cerca ID"), diff --git a/src/lang/ja.rs b/src/lang/ja.rs index c247a7582..2f3d0b54f 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "常に中継サーバー経由で接続"), ("whitelist_tip", "ホワイトリストに登録されたIPからのみ接続を許可します"), ("Login", "ログイン"), + ("Remember me", ""), ("Logout", "ログアウト"), ("Tags", "タグ"), ("Search ID", "IDを検索"), diff --git a/src/lang/ko.rs b/src/lang/ko.rs index a4f2fde77..21e75baef 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "항상 relay를 통해 접속하기"), ("whitelist_tip", "화이트리스트에 있는 IP만 현 데스크탑에 접속 가능합니다"), ("Login", "로그인"), + ("Remember me", ""), ("Logout", "로그아웃"), ("Tags", "태그"), ("Search ID", "ID 검색"), diff --git a/src/lang/kz.rs b/src/lang/kz.rs index d8037ff62..69caf1b6b 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Әрқашан да релай сербері арқылы қосылу"), ("whitelist_tip", "Маған тек ақ-тізімделген IP қол жеткізе алады"), ("Login", "Кіру"), + ("Remember me", ""), ("Logout", "Шығу"), ("Tags", "Тақтар"), ("Search ID", "ID Іздеу"), diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 2cf91af4a..24674e197 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Zawsze łącz pośrednio"), ("whitelist_tip", "Zezwlaj na łączenie z tym komputerem tylko z adresów IP znajdujących się na białej liście"), ("Login", "Zaloguj"), + ("Remember me", ""), ("Logout", "Wyloguj"), ("Tags", "Tagi"), ("Search ID", "Szukaj ID"), diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index fc95cb548..dcf48eca4 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Sempre conectar via relay"), ("whitelist_tip", "Somente IPs na whitelist podem me acessar"), ("Login", "Login"), + ("Remember me", ""), ("Logout", "Sair"), ("Tags", "Tags"), ("Search ID", "Procurar ID"), diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 5a89efd4b..3ffee4f93 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Sempre conectar via relay"), ("whitelist_tip", "Somente IPs confiáveis podem me acessar"), ("Login", "Login"), + ("Remember me", ""), ("Logout", "Sair"), ("Tags", "Tags"), ("Search ID", "Pesquisar ID"), diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 82d8d357e..8dd6bfbca 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Всегда подключаться через ретрансляционный сервер"), ("whitelist_tip", "Только IP-адреса из белого списка могут получить доступ ко мне"), ("Login", "Войти"), + ("Remember me", ""), ("Logout", "Выйти"), ("Tags", "Метки"), ("Search ID", "Поиск по ID"), diff --git a/src/lang/sk.rs b/src/lang/sk.rs index e61ac9c58..f67eb60cc 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Vždy pripájať cez prepájací server"), ("whitelist_tip", "Len vymenované IP adresy majú oprávnenie sa pripojiť k vzdialenej správe"), ("Login", "Prihlásenie"), + ("Remember me", ""), ("Logout", "Odhlásenie"), ("Tags", "Štítky"), ("Search ID", "Hľadať ID"), diff --git a/src/lang/sq.rs b/src/lang/sq.rs index c55562942..222f16a8e 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Gjithmonë lidheni me transmetues"), ("whitelist_tip", "Vetëm IP e listës së bardhë mund të më aksesoj."), ("Login", "Hyrje"), + ("Remember me", ""), ("Logout", "Dalje"), ("Tags", "Tage"), ("Search ID", "Kerko ID"), diff --git a/src/lang/sr.rs b/src/lang/sr.rs index d74baf8b2..660df7e9e 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Uvek se spoj preko posrednika"), ("whitelist_tip", "Samo dozvoljene IP mi mogu pristupiti"), ("Login", "Prijava"), + ("Remember me", ""), ("Logout", "Odjava"), ("Tags", "Oznake"), ("Search ID", "Traži ID"), diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 6a770c24a..870d92569 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Anslut alltid via relay"), ("whitelist_tip", "Bara vitlistade IPs kan koppla upp till mig"), ("Login", "Logga in"), + ("Remember me", ""), ("Logout", "Logga ut"), ("Tags", "Taggar"), ("Search ID", "Sök ID"), diff --git a/src/lang/template.rs b/src/lang/template.rs index b4113b91a..3824bf60d 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", ""), ("whitelist_tip", ""), ("Login", ""), + ("Remember me", ""), ("Logout", ""), ("Tags", ""), ("Search ID", ""), diff --git a/src/lang/th.rs b/src/lang/th.rs index 792f4f97a..b2ca698e1 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -1,409 +1,410 @@ lazy_static::lazy_static! { - pub static ref T: std::collections::HashMap<&'static str, &'static str> = - [ - ("Status", "สถานะ"), - ("Your Desktop", "หน้าจอของคุณ"), - ("desk_tip", "คุณสามารถเข้าถึงเดสก์ท็อปของคุณได้ด้วย ID และรหัสผ่านต่อไปนี้"), - ("Password", "รหัสผ่าน"), - ("Ready", "พร้อม"), - ("Established", "เชื่อมต่อแล้ว"), - ("connecting_status", "กำลังเชื่อมต่อไปยังเครือข่าย RustDesk..."), - ("Enable Service", "เปิดใช้การงานเซอร์วิส"), - ("Start Service", "เริ่มต้นใช้งานเซอร์วิส"), - ("Service is running", "เซอร์วิสกำลังทำงาน"), - ("Service is not running", "เซอร์วิสไม่ทำงาน"), - ("not_ready_status", "ไม่พร้อมใช้งาน กรุณาตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณ"), - ("Control Remote Desktop", "การควบคุมเดสก์ท็อปปลายทาง"), - ("Transfer File", "การถ่ายโอนไฟล์"), - ("Connect", "เชื่อมต่อ"), - ("Recent Sessions", "เซสชันล่าสุด"), - ("Address Book", "สมุดรายชื่อ"), - ("Confirmation", "การยืนยัน"), - ("TCP Tunneling", "อุโมงค์การเชื่อมต่อ TCP"), - ("Remove", "ลบ"), - ("Refresh random password", "รีเฟรชรหัสผ่านใหม่แบบสุ่ม"), - ("Set your own password", "ตั้งรหัสผ่านของคุณเอง"), - ("Enable Keyboard/Mouse", "เปิดการใช้งาน คีย์บอร์ด/เมาส์"), - ("Enable Clipboard", "เปิดการใช้งาน คลิปบอร์ด"), - ("Enable File Transfer", "เปิดการใช้งาน การถ่ายโอนไฟล์"), - ("Enable TCP Tunneling", "เปิดการใช้งาน อุโมงค์การเชื่อมต่อ TCP"), - ("IP Whitelisting", "IP ไวท์ลิสต์"), - ("ID/Relay Server", "เซิร์ฟเวอร์ ID/Relay"), - ("Import Server Config", "นำเข้าการตั้งค่าเซิร์ฟเวอร์"), - ("Export Server Config", "ส่งออกการตั้งค่าเซิร์ฟเวอร์"), - ("Import server configuration successfully", "นำเข้าการตั้งค่าเซิร์ฟเวอร์เสร็จสมบูรณ์"), - ("Export server configuration successfully", "ส่งออกการตั้งค่าเซิร์ฟเวอร์เสร็จสมบูรณ์"), - ("Invalid server configuration", "การตั้งค่าของเซิร์ฟเวอร์ไม่ถูกต้อง"), - ("Clipboard is empty", "คลิปบอร์ดว่างเปล่า"), - ("Stop service", "หยุดการใช้งานเซอร์วิส"), - ("Change ID", "เปลี่ยน ID"), - ("Website", "เว็บไซต์"), - ("About", "เกี่ยวกับ"), - ("Slogan_tip", "ทำด้วยใจ ในโลกใบนี้ที่ยุ่งเหยิง!"), - ("Privacy Statement", "คำแถลงเกี่ยวกับความเป็นส่วนตัว"), - ("Mute", "ปิดเสียง"), - ("Audio Input", "ออดิโออินพุท"), - ("Enhancements", "การปรับปรุง"), - ("Hardware Codec", "ฮาร์ดแวร์ codec"), - ("Adaptive Bitrate", "บิทเรทผันแปร"), - ("ID Server", "เซิร์ฟเวอร์ ID"), - ("Relay Server", "เซิร์ฟเวอร์ Relay"), - ("API Server", "เซิร์ฟเวอร์ API"), - ("invalid_http", "ต้องขึ้นต้นด้วย http:// หรือ https:// เท่านั้น"), - ("Invalid IP", "IP ไม่ถูกต้อง"), - ("id_change_tip", "อนุญาตเฉพาะตัวอักษร a-z A-Z 0-9 และ _ (ขีดล่าง) เท่านั้น โดยตัวอักษรขึ้นต้นจะต้องเป็น a-z หรือไม่ก็ A-Z และมีความยาวระหว่าง 6 ถึง 16 ตัวอักษร"), - ("Invalid format", "รูปแบบไม่ถูกต้อง"), - ("server_not_support", "ยังไม่รองรับโดยเซิร์ฟเวอร์"), - ("Not available", "ไม่พร้อมใช้งาน"), - ("Too frequent", "ดำเนินการถี่เกินไป"), - ("Cancel", "ยกเลิก"), - ("Skip", "ข้าม"), - ("Close", "ปิด"), - ("Retry", "ลองใหม่อีกครั้ง"), - ("OK", "ตกลง"), - ("Password Required", "ต้องใช้รหัสผ่าน"), - ("Please enter your password", "กรุณาใส่รหัสผ่านของคุณ"), - ("Remember password", "จดจำรหัสผ่าน"), - ("Wrong Password", "รหัสผ่านไม่ถูกต้อง"), - ("Do you want to enter again?", "ต้องการใส่ข้อมูลอีกครั้งหรือไม่?"), - ("Connection Error", "การเชื่อมต่อผิดพลาด"), - ("Error", "ข้อผิดพลาด"), - ("Reset by the peer", "รีเซ็ตโดยอีกฝั่ง"), - ("Connecting...", "กำลังเชื่อมต่อ..."), - ("Connection in progress. Please wait.", "กำลังดำเนินการเชื่อมต่อ กรุณารอซักครู่"), - ("Please try 1 minute later", "กรุณาลองใหม่อีกครั้งใน 1 นาที"), - ("Login Error", "การเข้าสู่ระบบผิดพลาด"), - ("Successful", "สำเร็จ"), - ("Connected, waiting for image...", "เชื่อมต่อสำเร็จ กำลังรับข้อมูลภาพ..."), - ("Name", "ชื่อ"), - ("Type", "ประเภท"), - ("Modified", "แก้ไขล่าสุด"), - ("Size", "ขนาด"), - ("Show Hidden Files", "แสดงไฟล์ที่ถูกซ่อน"), - ("Receive", "รับ"), - ("Send", "ส่ง"), - ("Refresh File", "รีเฟรชไฟล์"), - ("Local", "ต้นทาง"), - ("Remote", "ปลายทาง"), - ("Remote Computer", "คอมพิวเตอร์ปลายทาง"), - ("Local Computer", "คอมพิวเตอร์ต้นทาง"), - ("Confirm Delete", "ยืนยันการลบ"), - ("Delete", "ลบ"), - ("Properties", "ข้อมูล"), - ("Multi Select", "เลือกหลายรายการ"), - ("Select All", "เลือกทั้งหมด"), - ("Unselect All", "ยกเลิกการเลือกทั้งหมด"), - ("Empty Directory", "ไดเรกทอรีว่างเปล่า"), - ("Not an empty directory", "ไม่ใช่ไดเรกทอรีว่างเปล่า"), - ("Are you sure you want to delete this file?", "คุณแน่ใจหรือไม่ที่จะลบไฟล์นี้?"), - ("Are you sure you want to delete this empty directory?", "คุณแน่ใจหรือไม่ที่จะลบไดเรอทอรีว่างเปล่านี้?"), - ("Are you sure you want to delete the file of this directory?", "คุณแน่ใจหรือไม่ที่จะลบไฟล์ของไดเรกทอรีนี้?"), - ("Do this for all conflicts", "ดำเนินการแบบเดียวกันสำหรับรายการทั้งหมด"), - ("This is irreversible!", "การดำเนินการนี้ไม่สามารถย้อนกลับได้!"), - ("Deleting", "กำลังลบ"), - ("files", "ไฟล์"), - ("Waiting", "กำลังรอ"), - ("Finished", "เสร็จแล้ว"), - ("Speed", "ความเร็ว"), - ("Custom Image Quality", "คุณภาพของภาพแบบกำหนดเอง"), - ("Privacy mode", "โหมดความเป็นส่วนตัว"), - ("Block user input", "บล็อคอินพุทจากผู้ใช้งาน"), - ("Unblock user input", "ยกเลิกการบล็อคอินพุทจากผู้ใช้งาน"), - ("Adjust Window", "ปรับขนาดหน้าต่าง"), - ("Original", "ต้นฉบับ"), - ("Shrink", "ย่อ"), - ("Stretch", "ยืด"), - ("Scrollbar", "แถบเลื่อน"), - ("ScrollAuto", "เลื่อนอัตโนมัติ"), - ("Good image quality", "ภาพคุณภาพดี"), - ("Balanced", "สมดุล"), - ("Optimize reaction time", "เน้นการตอบสนอง"), - ("Custom", "กำหนดเอง"), - ("Show remote cursor", "แสดงเคอร์เซอร์ปลายทาง"), - ("Show quality monitor", "แสดงคุณภาพหน้าจอ"), - ("Disable clipboard", "ปิดการใช้งานคลิปบอร์ด"), - ("Lock after session end", "ล็อคหลังจากจบเซสชัน"), - ("Insert", "แทรก"), - ("Insert Lock", "แทรกล็อค"), - ("Refresh", "รีเฟรช"), - ("ID does not exist", "ไม่พอข้อมูล ID"), - ("Failed to connect to rendezvous server", "การเชื่อมต่อไปยังเซิร์ฟเวอร์นัดพบล้มเหลว"), - ("Please try later", "กรุณาลองใหม่ในภายหลัง"), - ("Remote desktop is offline", "เดสก์ท็อปปลายทางออฟไลน์"), - ("Key mismatch", "คีย์ไม่ถูกต้อง"), - ("Timeout", "หมดเวลา"), - ("Failed to connect to relay server", "การเชื่อมต่อไปยังเซิร์ฟเวอร์รีเลย์ล้มเหลว"), - ("Failed to connect via rendezvous server", "การเชื่อมต่อผ่านเซิร์ฟเวอร์นัดพบล้มเหลว"), - ("Failed to connect via relay server", "การเชื่อมต่อผ่านเซิร์ฟเวอร์รีเลย์ล้มเหลว"), - ("Failed to make direct connection to remote desktop", "การเชื่อมต่อตรงไปยังเดสก์ท็อปปลายทางล้มเหลว"), - ("Set Password", "ตั้งรหัสผ่าน"), - ("OS Password", "รหัสผ่านระบบปฏิบัติการ"), - ("install_tip", "เนื่องด้วยข้อจำกัดของการใช้งาน UAC ทำให้ RustDesk ไม่สามารถทำงานได้ปกติในฝั่งปลายทางในบางครั้ง เพื่อหลีกเลี่ยงข้อจำกัดของ UAC กรุณากดปุ่มด้านล่างเพื่อติดตั้ง RustDesk ไปยังระบบของคุณ"), - ("Click to upgrade", "คลิกเพื่ออัปเกรด"), - ("Click to download", "คลิกเพื่อดาวน์โหลด"), - ("Click to update", "คลิกเพื่ออัปเดต"), - ("Configure", "ปรับแต่งค่า"), - ("config_acc", "เพื่อที่จะควบคุมเดสก์ท็อปปลายทางของคุณ คุณจำเป็นจะต้องอนุญาตสิทธิ์ \"การเข้าถึง\" ให้แก่ RustDesk"), - ("config_screen", "เพื่อที่จะควบคุมเดสก์ท็อปปลายทางของคุณ คุณจำเป็นจะต้องอนุญาตสิทธิ์ \"การบันทึกภาพหน้าจอ\" ให้แก่ RustDesk"), - ("Installing ...", "กำลังติดตั้ง ..."), - ("Install", "ติดตั้ง"), - ("Installation", "การติดตั้ง"), - ("Installation Path", "ตำแหน่งที่ติดตั้ง"), - ("Create start menu shortcuts", "สร้างทางลัดไปยัง Start Menu"), - ("Create desktop icon", "สร้างไอคอนบนเดสก์ท็อป"), - ("agreement_tip", "ในการเริ่มต้นการติดตั้ง ถือว่าคุณได้ยอมรับข้อตกลงใบอนุญาตแล้ว"), - ("Accept and Install", "ยอมรับและติดตั้ง"), - ("End-user license agreement", "ข้อตกลงใบอนุญาตผู้ใช้งาน"), - ("Generating ...", "กำลังสร้าง ..."), - ("Your installation is lower version.", "การติดตั้งของคุณเป็นเวอร์ชั่นที่ต่ำกว่า"), - ("not_close_tcp_tip", "อย่าปิดหน้าต่างนี้ในขณะที่คุณกำลังใช้งานอุโมงค์การเชื่อมต่อ"), - ("Listening ...", "กำลังรอรับข้อมูล ..."), - ("Remote Host", "โฮสต์ปลายทาง"), - ("Remote Port", "พอร์ทปลายทาง"), - ("Action", "การดำเนินการ"), - ("Add", "เพิ่ม"), - ("Local Port", "พอร์ทต้นทาง"), - ("Local Address", "ที่อยู่ต้นทาง"), - ("Change Local Port", "เปลี่ยนพอร์ทต้นทาง"), - ("setup_server_tip", "เพื่อการเชื่อมต่อที่เร็วขึ้น กรุณาเซ็ตอัปเซิร์ฟเวอร์ของคุณเอง"), - ("Too short, at least 6 characters.", "สั้นเกินไป ต้องไม่ต่ำกว่า 6 ตัวอักษร"), - ("The confirmation is not identical.", "การยืนยันข้อมูลไม่ถูกต้อง"), - ("Permissions", "สิทธิ์การใช้งาน"), - ("Accept", "ยอมรับ"), - ("Dismiss", "ปิด"), - ("Disconnect", "ยกเลิกการเชื่อมต่อ"), - ("Allow using keyboard and mouse", "อนุญาตให้ใช้งานคีย์บอร์ดและเมาส์"), - ("Allow using clipboard", "อนุญาตให้ใช้คลิปบอร์ด"), - ("Allow hearing sound", "อนุญาตให้ได้ยินเสียง"), - ("Allow file copy and paste", "อนุญาตให้มีการคัดลอกและวางไฟล์"), - ("Connected", "เชื่อมต่อแล้ว"), - ("Direct and encrypted connection", "การเชื่อมต่อตรงที่มีการเข้ารหัส"), - ("Relayed and encrypted connection", "การเชื่อมต่อแบบรีเลย์ที่มีการเข้ารหัส"), - ("Direct and unencrypted connection", "การเชื่อมต่อตรงที่ไม่มีการเข้ารหัส"), - ("Relayed and unencrypted connection", "การเชื่อมต่อแบบรีเลย์ที่ไม่มีการเข้ารหัส"), - ("Enter Remote ID", "กรอก ID ปลายทาง"), - ("Enter your password", "กรอกรหัสผ่าน"), - ("Logging in...", "กำลังเข้าสู่ระบบ..."), - ("Enable RDP session sharing", "เปิดการใช้งานการแชร์เซสชัน RDP"), - ("Auto Login", "เข้าสู่ระบอัตโนมัติ"), - ("Enable Direct IP Access", "เปิดการใช้งาน IP ตรง"), - ("Rename", "ปลายทาง"), - ("Space", "พื้นที่ว่าง"), - ("Create Desktop Shortcut", "สร้างทางลัดบนเดสก์ท็อป"), - ("Change Path", "เปลี่ยนตำแหน่ง"), - ("Create Folder", "สร้างโฟลเดอร์"), - ("Please enter the folder name", "กรุณาใส่ชื่อโฟลเดอร์"), - ("Fix it", "แก้ไข"), - ("Warning", "คำเตือน"), - ("Login screen using Wayland is not supported", "หน้าจอการเข้าสู่ระบบโดยใช้ Wayland ยังไม่ถูกรองรับ"), - ("Reboot required", "จำเป็นต้องเริ่มต้นระบบใหม่"), - ("Unsupported display server ", "เซิร์ฟเวอร์การแสดงผลที่ไม่รองรับ"), - ("x11 expected", "ต้องใช้งาน x11"), - ("Port", "พอร์ท"), - ("Settings", "ตั้งค่า"), - ("Username", "ชื่อผู้ใช้งาน"), - ("Invalid port", "พอร์ทไม่ถูกต้อง"), - ("Closed manually by the peer", "ถูกปิดโดยอีกฝั่งการการเชื่อมต่อ"), - ("Enable remote configuration modification", "เปิดการใช้งานการแก้ไขการตั้งค่าปลายทาง"), - ("Run without install", "ใช้งานโดยไม่ต้องติดตั้ง"), - ("Always connected via relay", "เชื่อมต่อผ่านรีเลย์เสมอ"), - ("Always connect via relay", "เชื่อมต่อผ่านรีเลย์เสมอ"), - ("whitelist_tip", "อนุญาตเฉพาะการเชื่อมต่อจาก IP ที่ไวท์ลิสต์"), - ("Login", "เข้าสู่ระบบ"), - ("Logout", "ออกจากระบบ"), - ("Tags", "แท็ก"), - ("Search ID", "ค้นหา ID"), - ("Current Wayland display server is not supported", "เซิร์ฟเวอร์การแสดงผล Wayland ปัจจุบันไม่รองรับ"), - ("whitelist_sep", "คั่นโดยเครื่องหมาย comma semicolon เว้นวรรค หรือ ขึ้นบรรทัดใหม่"), - ("Add ID", "เพิ่ม ID"), - ("Add Tag", "เพิ่มแท็ก"), - ("Unselect all tags", "ยกเลิกการเลือกแท็กทั้งหมด"), - ("Network error", "ข้อผิดพลาดของเครือข่าย"), - ("Username missed", "ไม่พบข้อมูลผู้ใช้งาน"), - ("Password missed", "ไม่พบรหัสผ่าน"), - ("Wrong credentials", "ข้อมูลสำหรับเข้าสู่ระบบไม่ถูกต้อง"), - ("Edit Tag", "แก้ไขแท็ก"), - ("Unremember Password", "ยกเลิกการจดจำรหัสผ่าน"), - ("Favorites", "รายการโปรด"), - ("Add to Favorites", "เพิ่มไปยังรายการโปรด"), - ("Remove from Favorites", "ลบออกจากรายการโปรด"), - ("Empty", "ว่างเปล่า"), - ("Invalid folder name", "ชื่อโฟลเดอร์ไม่ถูกต้อง"), - ("Socks5 Proxy", "พรอกซี Socks5"), - ("Hostname", "ชื่อโฮสต์"), - ("Discovered", "ค้นพบ"), - ("install_daemon_tip", "หากต้องการใช้งานขณะระบบเริ่มต้น คุณจำเป็นจะต้องติดตั้งเซอร์วิส"), - ("Remote ID", "ID ปลายทาง"), - ("Paste", "วาง"), - ("Paste here?", "วางที่นี่หรือไม่?"), - ("Are you sure to close the connection?", "คุณแน่ใจหรือไม่ที่จะปิดการเชื่อมต่อ?"), - ("Download new version", "ดาวน์โหลดเวอร์ชั่นใหม่"), - ("Touch mode", "โหมดการสัมผัส"), - ("Mouse mode", "โหมดการใช้เมาส์"), - ("One-Finger Tap", "แตะนิ้วเดียว"), - ("Left Mouse", "เมาส์ซ้าย"), - ("One-Long Tap", "แตะยาวหนึ่งครั้ง"), - ("Two-Finger Tap", "แตะสองนิ้ว"), - ("Right Mouse", "เมาส์ขวา"), - ("One-Finger Move", "ลากนิ้วเดียว"), - ("Double Tap & Move", "แตะเบิ้ลและลาก"), - ("Mouse Drag", "ลากเมาส์"), - ("Three-Finger vertically", "สามนิ้วแนวตั้ง"), - ("Mouse Wheel", "ลูกลิ้งเมาส์"), - ("Two-Finger Move", "ลากสองนิ้ว"), - ("Canvas Move", "ลากแคนวาส"), - ("Pinch to Zoom", "ถ่างเพื่อขยาย"), - ("Canvas Zoom", "ขยายแคนวาส"), - ("Reset canvas", "รีเซ็ตแคนวาส"), - ("No permission of file transfer", "ไม่มีสิทธิ์ในการถ่ายโอนไฟล์"), - ("Note", "บันทึกข้อความ"), - ("Connection", "การเชื่อมต่อ"), - ("Share Screen", "แชร์หน้าจอ"), - ("CLOSE", "ปิด"), - ("OPEN", "เปิด"), - ("Chat", "แชท"), - ("Total", "รวม"), - ("items", "รายการ"), - ("Selected", "ถูกเลือก"), - ("Screen Capture", "แคปเจอร์หน้าจอ"), - ("Input Control", "ควบคุมอินพุท"), - ("Audio Capture", "แคปเจอร์เสียง"), - ("File Connection", "การเชื่อมต่อไฟล์"), - ("Screen Connection", "การเชื่อมต่อหน้าจอ"), - ("Do you accept?", "ยอมรับหรือไม่?"), - ("Open System Setting", "เปิดการตั้งค่าระบบ"), - ("How to get Android input permission?", "เปิดสิทธิ์การใช้งานอินพุทของแอนดรอยด์ได้อย่างไร?"), - ("android_input_permission_tip1", "ในการที่จะอนุญาตให้เครื่องปลายทางควบคุมอุปกรณ์แอนดรอยด์ของคุณโดยใช้เมาส์หรือการสัมผัส คุณจำเป็นจะต้องอนุญาตสิทธิ์ \"การเข้าถึง\" ให้แก่เซอร์วิสของ RustDesk"), - ("android_input_permission_tip2", "กรุณาไปยังหน้าตั้งค่าถัดไป ค้นหาและเข้าไปยัง [เซอร์วิสที่ถูกติดตั้ง] และเปิดการใช้งานเซอร์วิส [อินพุท RustDesk]"), - ("android_new_connection_tip", "ได้รับคำขอควบคุมใหม่ที่ต้องการควบคุมอุปกรณ์ของคุณ"), - ("android_service_will_start_tip", "การเปิดการใช้งาน \"การบันทึกหน้าจอ\" จะเป็นการเริ่มต้นการทำงานของเซอร์วิสโดยอัตโนมัติ ที่จะอนุญาตให้อุปกรณ์อื่นๆ ส่งคำขอเข้าถึงมายังอุปกรณ์ของคุณได้"), - ("android_stop_service_tip", "การปิดการใช้งานเซอร์วิสจะปิดการเชื่อมต่อทั้งหมดโดยอัตโนมัติ"), - ("android_version_audio_tip", "เวอร์ชั่นแอนดรอยด์ปัจจุบันของคุณไม่รองรับการบันทึกข้อมูลเสียง กรุณาอัปเกรดเป็นแอนดรอยด์เวอร์ชั่น 10 หรือสูงกว่า"), - ("android_start_service_tip", "แตะ [เริ่มต้นใช้งานเซอร์วิส] หรือเปิดสิทธิ์ [การบันทึกหน้าจอ] เพื่อเริ่มเซอร์วิสการแชร์หน้าจอ"), - ("Account", "บัญชี"), - ("Overwrite", "เขียนทับ"), - ("This file exists, skip or overwrite this file?", "พบไฟล์ที่มีอยู่แล้ว ต้องการเขียนทับหรือไม่?"), - ("Quit", "ออก"), - ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), - ("Help", "ช่วยเหลือ"), - ("Failed", "ล้มเหลว"), - ("Succeeded", "สำเร็จ"), - ("Someone turns on privacy mode, exit", "มีใครบางคนเปิดใช้งานโหมดความเป็นส่วนตัว กำลังออก"), - ("Unsupported", "ไม่รองรับ"), - ("Peer denied", "ถูกปฏิเสธโดยอีกฝั่ง"), - ("Please install plugins", "กรุณาติดตั้งปลั๊กอิน"), - ("Peer exit", "อีกฝั่งออก"), - ("Failed to turn off", "การปิดล้มเหลว"), - ("Turned off", "ปิด"), - ("In privacy mode", "อยู่ในโหมดความเป็นส่วนตัว"), - ("Out privacy mode", "อยู่นอกโหมดความเป็นส่วนตัว"), - ("Language", "ภาษา"), - ("Keep RustDesk background service", "คงสถานะการทำงานเบื้องหลังของเซอร์วิส RustDesk"), - ("Ignore Battery Optimizations", "เพิกเฉยการตั้งค่าการใช้งาน Battery Optimization"), - ("android_open_battery_optimizations_tip", "หากคุณต้องการปิดการใช้งานฟีเจอร์นี้ กรุณาไปยังหน้าตั้งค่าในแอปพลิเคชัน RustDesk ค้นหาหัวข้อ [Battery] และยกเลิกการเลือกรายการ [Unrestricted]"), - ("Connection not allowed", "การเชื่อมต่อไม่อนุญาต"), - ("Legacy mode", ""), - ("Map mode", ""), - ("Translate mode", ""), - ("Use permanent password", "ใช้รหัสผ่านถาวร"), - ("Use both passwords", "ใช้รหัสผ่านทั้งสองแบบ"), - ("Set permanent password", "ตั้งค่ารหัสผ่านถาวร"), - ("Enable Remote Restart", "เปิดการใช้งานการรีสตาร์ทระบบทางไกล"), - ("Allow remote restart", "อนุญาตการรีสตาร์ทระบบทางไกล"), - ("Restart Remote Device", "รีสตาร์ทอุปกรณ์ปลายทาง"), - ("Are you sure you want to restart", "คุณแน่ใจหรือไม่ที่จะรีสตาร์ท"), - ("Restarting Remote Device", "กำลังรีสตาร์ทระบบปลายทาง"), - ("remote_restarting_tip", "ระบบปลายทางกำลังรีสตาร์ท กรุณาปิดกล่องข้อความนี้และดำเนินการเขื่อมต่อใหม่อีกครั้งด้วยรหัสผ่านถาวรหลังจากผ่านไปซักครู่"), - ("Copied", "คัดลอกแล้ว"), - ("Exit Fullscreen", "ออกจากเต็มหน้าจอ"), - ("Fullscreen", "เต็มหน้าจอ"), - ("Mobile Actions", "การดำเนินการบนมือถือ"), - ("Select Monitor", "เลือกหน้าจอ"), - ("Control Actions", "การดำเนินการควบคุม"), - ("Display Settings", "การตั้งค่าแสดงผล"), - ("Ratio", "อัตราส่วน"), - ("Image Quality", "คุณภาพภาพ"), - ("Scroll Style", "ลักษณะการเลื่อน"), - ("Show Menubar", "แสดงแถบเมนู"), - ("Hide Menubar", "ซ่อนแถบเมนู"), - ("Direct Connection", "การเชื่อมต่อตรง"), - ("Relay Connection", "การเชื่อมต่อแบบรีเลย์"), - ("Secure Connection", "การเชื่อมต่อที่ปลอดภัย"), - ("Insecure Connection", "การเชื่อมต่อที่ไม่ปลอดภัย"), - ("Scale original", "ขนาดเดิม"), - ("Scale adaptive", "ขนาดยืดหยุ่น"), - ("General", "ทั่วไป"), - ("Security", "ความปลอดภัย"), - ("Theme", "ธีม"), - ("Dark Theme", "ธีมมืด"), - ("Dark", "มืด"), - ("Light", "สว่าง"), - ("Follow System", "ตามระบบ"), - ("Enable hardware codec", "เปิดการใช้งานฮาร์ดแวร์ codec"), - ("Unlock Security Settings", "ปลดล็อคการตั้งค่าความปลอดภัย"), - ("Enable Audio", "เปิดการใช้งานเสียง"), - ("Unlock Network Settings", "ปลดล็อคการตั้งค่าเครือข่าย"), - ("Server", "เซิร์ฟเวอร์"), - ("Direct IP Access", "การเข้าถึง IP ตรง"), - ("Proxy", "พรอกซี"), - ("Apply", "นำไปใช้"), - ("Disconnect all devices?", "ยกเลิกการเชื่อมต่ออุปกรณ์ทั้งหมด?"), - ("Clear", "ล้างข้อมูล"), - ("Audio Input Device", "อุปกรณ์รับอินพุทข้อมูลเสียง"), - ("Deny remote access", "ปฏิเสธการเชื่อมต่อ"), - ("Use IP Whitelisting", "ใช้งาน IP ไวท์ลิสต์"), - ("Network", "เครือข่าย"), - ("Enable RDP", "เปิดการใช้งาน RDP"), - ("Pin menubar", "ปักหมุดแถบเมนู"), - ("Unpin menubar", "ยกเลิกการปักหมุดแถบเมนู"), - ("Recording", "การบันทึก"), - ("Directory", "ไดเรกทอรี่"), - ("Automatically record incoming sessions", "บันทึกเซสชันขาเข้าโดยอัตโนมัติ"), - ("Change", "เปลี่ยน"), - ("Start session recording", "เริ่มต้นการบันทึกเซสชัน"), - ("Stop session recording", "หยุดการบันทึกเซสซัน"), - ("Enable Recording Session", "เปิดใช้งานการบันทึกเซสชัน"), - ("Allow recording session", "อนุญาตการบันทึกเซสชัน"), - ("Enable LAN Discovery", "เปิดการใช้งานการค้นหาในวง LAN"), - ("Deny LAN Discovery", "ปฏิเสธการใช้งานการค้นหาในวง LAN"), - ("Write a message", "เขียนข้อความ"), - ("Prompt", ""), - ("Please wait for confirmation of UAC...", "กรุณารอการยืนยันจาก UAC..."), - ("elevated_foreground_window_tip", "หน้าต่างปัจจุบันของเครื่องปลายทางต้องการสิทธิ์การใช้งานที่สูงขึ้นสำหรับการทำงาน ดังนั้นเมาส์และคีย์บอร์ดจะไม่สามารถใช้งานได้ชั่วคราว คุณสามารถขอผู้ใช้งานปลายทางให้ย่อหน้าต่าง หรือคลิกปุ่มให้สิทธิ์การใช้งานในหน้าต่างการจัดการการเชื่อมต่อ เพื่อหลีกเลี่ยงปัญหานี้เราแนะนำให้ดำเนินการติดตั้งซอฟท์แวร์ในเครื่องปลายทาง"), - ("Disconnected", "ยกเลิกการเชื่อมต่อ"), - ("Other", "อื่นๆ"), - ("Confirm before closing multiple tabs", "ยืนยันการปิดหลายแท็บ"), - ("Keyboard Settings", "การตั้งค่าคีย์บอร์ด"), - ("Full Access", "การเข้าถึงทั้งหมด"), - ("Screen Share", "การแชร์จอ"), - ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland ต้องการ Ubuntu เวอร์ชั่น 21.04 หรือสูงกว่า"), - ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland ต้องการลินุกซ์เวอร์ชันที่สูงกว่านี้ กรุณาเปลี่ยนไปใช้เดสก์ท็อป X11 หรือเปลี่ยนระบบปฏิบัติการของคุณ"), - ("JumpLink", "View"), - ("Please Select the screen to be shared(Operate on the peer side).", "กรุณาเลือกหน้าจอที่ต้องการแชร์ (ใช้งานในอีกฝั่งของการเชื่อมต่อ)"), - ("Show RustDesk", "แสดง RustDesk"), - ("This PC", ""), - ("or", "หรือ"), - ("Continue with", "ทำต่อด้วย"), - ("Elevate", "ยกระดับ"), - ("Zoom cursor", "ขยายเคอร์เซอร์"), - ("Accept sessions via password", "ยอมรับการเชื่อมต่อด้วยรหัสผ่าน"), - ("Accept sessions via click", "ยอมรับการเชื่อมต่อด้วยการคลิก"), - ("Accept sessions via both", "ยอมรับการเชื่อมต่อด้วยทั้งสองวิธิ"), - ("Please wait for the remote side to accept your session request...", "กรุณารอให้อีกฝั่งยอมรับการเชื่อมต่อของคุณ..."), - ("One-time Password", "รหัสผ่านครั้งเดียว"), - ("Use one-time password", "ใช้รหัสผ่านครั้งเดียว"), - ("One-time password length", "ความยาวรหัสผ่านครั้งเดียว"), - ("Request access to your device", "คำขอการเข้าถึงอุปกรณ์ของคุณ"), - ("Hide connection management window", "ซ่อนหน้าต่างการจัดการการเชื่อมต่อ"), - ("hide_cm_tip", "อนุญาตการซ่อนก็ต่อเมื่อยอมรับการเชื่อมต่อด้วยรหัสผ่าน และต้องเป็นรหัสผ่านถาวรเท่านั้น"), - ("wayland_experiment_tip", "การสนับสนุน Wayland ยังอยู่ในขั้นตอนการทดลอง กรุณาใช้ X11 หากคุณต้องการใช้งานการเข้าถึงแบบไม่มีผู้ดูแล"), - ("Right click to select tabs", "คลิกขวาเพื่อเลือกแท็บ"), - ("Skipped", "ข้าม"), - ("Add to Address Book", "เพิ่มไปยังสมุดรายชื่อ"), - ("Group", "กลุ่ม"), - ("Search", "ค้นหา"), - ("Closed manually by the web console", "ถูกปิดโดยเว็บคอนโซล"), - ("Local keyboard type", "ประเภทคีย์บอร์ด"), - ("Select local keyboard type", "เลือกประเภทคีย์บอร์ด"), - ].iter().cloned().collect(); - } +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "สถานะ"), + ("Your Desktop", "หน้าจอของคุณ"), + ("desk_tip", "คุณสามารถเข้าถึงเดสก์ท็อปของคุณได้ด้วย ID และรหัสผ่านต่อไปนี้"), + ("Password", "รหัสผ่าน"), + ("Ready", "พร้อม"), + ("Established", "เชื่อมต่อแล้ว"), + ("connecting_status", "กำลังเชื่อมต่อไปยังเครือข่าย RustDesk..."), + ("Enable Service", "เปิดใช้การงานเซอร์วิส"), + ("Start Service", "เริ่มต้นใช้งานเซอร์วิส"), + ("Service is running", "เซอร์วิสกำลังทำงาน"), + ("Service is not running", "เซอร์วิสไม่ทำงาน"), + ("not_ready_status", "ไม่พร้อมใช้งาน กรุณาตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณ"), + ("Control Remote Desktop", "การควบคุมเดสก์ท็อปปลายทาง"), + ("Transfer File", "การถ่ายโอนไฟล์"), + ("Connect", "เชื่อมต่อ"), + ("Recent Sessions", "เซสชันล่าสุด"), + ("Address Book", "สมุดรายชื่อ"), + ("Confirmation", "การยืนยัน"), + ("TCP Tunneling", "อุโมงค์การเชื่อมต่อ TCP"), + ("Remove", "ลบ"), + ("Refresh random password", "รีเฟรชรหัสผ่านใหม่แบบสุ่ม"), + ("Set your own password", "ตั้งรหัสผ่านของคุณเอง"), + ("Enable Keyboard/Mouse", "เปิดการใช้งาน คีย์บอร์ด/เมาส์"), + ("Enable Clipboard", "เปิดการใช้งาน คลิปบอร์ด"), + ("Enable File Transfer", "เปิดการใช้งาน การถ่ายโอนไฟล์"), + ("Enable TCP Tunneling", "เปิดการใช้งาน อุโมงค์การเชื่อมต่อ TCP"), + ("IP Whitelisting", "IP ไวท์ลิสต์"), + ("ID/Relay Server", "เซิร์ฟเวอร์ ID/Relay"), + ("Import Server Config", "นำเข้าการตั้งค่าเซิร์ฟเวอร์"), + ("Export Server Config", "ส่งออกการตั้งค่าเซิร์ฟเวอร์"), + ("Import server configuration successfully", "นำเข้าการตั้งค่าเซิร์ฟเวอร์เสร็จสมบูรณ์"), + ("Export server configuration successfully", "ส่งออกการตั้งค่าเซิร์ฟเวอร์เสร็จสมบูรณ์"), + ("Invalid server configuration", "การตั้งค่าของเซิร์ฟเวอร์ไม่ถูกต้อง"), + ("Clipboard is empty", "คลิปบอร์ดว่างเปล่า"), + ("Stop service", "หยุดการใช้งานเซอร์วิส"), + ("Change ID", "เปลี่ยน ID"), + ("Website", "เว็บไซต์"), + ("About", "เกี่ยวกับ"), + ("Slogan_tip", "ทำด้วยใจ ในโลกใบนี้ที่ยุ่งเหยิง!"), + ("Privacy Statement", "คำแถลงเกี่ยวกับความเป็นส่วนตัว"), + ("Mute", "ปิดเสียง"), + ("Audio Input", "ออดิโออินพุท"), + ("Enhancements", "การปรับปรุง"), + ("Hardware Codec", "ฮาร์ดแวร์ codec"), + ("Adaptive Bitrate", "บิทเรทผันแปร"), + ("ID Server", "เซิร์ฟเวอร์ ID"), + ("Relay Server", "เซิร์ฟเวอร์ Relay"), + ("API Server", "เซิร์ฟเวอร์ API"), + ("invalid_http", "ต้องขึ้นต้นด้วย http:// หรือ https:// เท่านั้น"), + ("Invalid IP", "IP ไม่ถูกต้อง"), + ("id_change_tip", "อนุญาตเฉพาะตัวอักษร a-z A-Z 0-9 และ _ (ขีดล่าง) เท่านั้น โดยตัวอักษรขึ้นต้นจะต้องเป็น a-z หรือไม่ก็ A-Z และมีความยาวระหว่าง 6 ถึง 16 ตัวอักษร"), + ("Invalid format", "รูปแบบไม่ถูกต้อง"), + ("server_not_support", "ยังไม่รองรับโดยเซิร์ฟเวอร์"), + ("Not available", "ไม่พร้อมใช้งาน"), + ("Too frequent", "ดำเนินการถี่เกินไป"), + ("Cancel", "ยกเลิก"), + ("Skip", "ข้าม"), + ("Close", "ปิด"), + ("Retry", "ลองใหม่อีกครั้ง"), + ("OK", "ตกลง"), + ("Password Required", "ต้องใช้รหัสผ่าน"), + ("Please enter your password", "กรุณาใส่รหัสผ่านของคุณ"), + ("Remember password", "จดจำรหัสผ่าน"), + ("Wrong Password", "รหัสผ่านไม่ถูกต้อง"), + ("Do you want to enter again?", "ต้องการใส่ข้อมูลอีกครั้งหรือไม่?"), + ("Connection Error", "การเชื่อมต่อผิดพลาด"), + ("Error", "ข้อผิดพลาด"), + ("Reset by the peer", "รีเซ็ตโดยอีกฝั่ง"), + ("Connecting...", "กำลังเชื่อมต่อ..."), + ("Connection in progress. Please wait.", "กำลังดำเนินการเชื่อมต่อ กรุณารอซักครู่"), + ("Please try 1 minute later", "กรุณาลองใหม่อีกครั้งใน 1 นาที"), + ("Login Error", "การเข้าสู่ระบบผิดพลาด"), + ("Successful", "สำเร็จ"), + ("Connected, waiting for image...", "เชื่อมต่อสำเร็จ กำลังรับข้อมูลภาพ..."), + ("Name", "ชื่อ"), + ("Type", "ประเภท"), + ("Modified", "แก้ไขล่าสุด"), + ("Size", "ขนาด"), + ("Show Hidden Files", "แสดงไฟล์ที่ถูกซ่อน"), + ("Receive", "รับ"), + ("Send", "ส่ง"), + ("Refresh File", "รีเฟรชไฟล์"), + ("Local", "ต้นทาง"), + ("Remote", "ปลายทาง"), + ("Remote Computer", "คอมพิวเตอร์ปลายทาง"), + ("Local Computer", "คอมพิวเตอร์ต้นทาง"), + ("Confirm Delete", "ยืนยันการลบ"), + ("Delete", "ลบ"), + ("Properties", "ข้อมูล"), + ("Multi Select", "เลือกหลายรายการ"), + ("Select All", "เลือกทั้งหมด"), + ("Unselect All", "ยกเลิกการเลือกทั้งหมด"), + ("Empty Directory", "ไดเรกทอรีว่างเปล่า"), + ("Not an empty directory", "ไม่ใช่ไดเรกทอรีว่างเปล่า"), + ("Are you sure you want to delete this file?", "คุณแน่ใจหรือไม่ที่จะลบไฟล์นี้?"), + ("Are you sure you want to delete this empty directory?", "คุณแน่ใจหรือไม่ที่จะลบไดเรอทอรีว่างเปล่านี้?"), + ("Are you sure you want to delete the file of this directory?", "คุณแน่ใจหรือไม่ที่จะลบไฟล์ของไดเรกทอรีนี้?"), + ("Do this for all conflicts", "ดำเนินการแบบเดียวกันสำหรับรายการทั้งหมด"), + ("This is irreversible!", "การดำเนินการนี้ไม่สามารถย้อนกลับได้!"), + ("Deleting", "กำลังลบ"), + ("files", "ไฟล์"), + ("Waiting", "กำลังรอ"), + ("Finished", "เสร็จแล้ว"), + ("Speed", "ความเร็ว"), + ("Custom Image Quality", "คุณภาพของภาพแบบกำหนดเอง"), + ("Privacy mode", "โหมดความเป็นส่วนตัว"), + ("Block user input", "บล็อคอินพุทจากผู้ใช้งาน"), + ("Unblock user input", "ยกเลิกการบล็อคอินพุทจากผู้ใช้งาน"), + ("Adjust Window", "ปรับขนาดหน้าต่าง"), + ("Original", "ต้นฉบับ"), + ("Shrink", "ย่อ"), + ("Stretch", "ยืด"), + ("Scrollbar", "แถบเลื่อน"), + ("ScrollAuto", "เลื่อนอัตโนมัติ"), + ("Good image quality", "ภาพคุณภาพดี"), + ("Balanced", "สมดุล"), + ("Optimize reaction time", "เน้นการตอบสนอง"), + ("Custom", "กำหนดเอง"), + ("Show remote cursor", "แสดงเคอร์เซอร์ปลายทาง"), + ("Show quality monitor", "แสดงคุณภาพหน้าจอ"), + ("Disable clipboard", "ปิดการใช้งานคลิปบอร์ด"), + ("Lock after session end", "ล็อคหลังจากจบเซสชัน"), + ("Insert", "แทรก"), + ("Insert Lock", "แทรกล็อค"), + ("Refresh", "รีเฟรช"), + ("ID does not exist", "ไม่พอข้อมูล ID"), + ("Failed to connect to rendezvous server", "การเชื่อมต่อไปยังเซิร์ฟเวอร์นัดพบล้มเหลว"), + ("Please try later", "กรุณาลองใหม่ในภายหลัง"), + ("Remote desktop is offline", "เดสก์ท็อปปลายทางออฟไลน์"), + ("Key mismatch", "คีย์ไม่ถูกต้อง"), + ("Timeout", "หมดเวลา"), + ("Failed to connect to relay server", "การเชื่อมต่อไปยังเซิร์ฟเวอร์รีเลย์ล้มเหลว"), + ("Failed to connect via rendezvous server", "การเชื่อมต่อผ่านเซิร์ฟเวอร์นัดพบล้มเหลว"), + ("Failed to connect via relay server", "การเชื่อมต่อผ่านเซิร์ฟเวอร์รีเลย์ล้มเหลว"), + ("Failed to make direct connection to remote desktop", "การเชื่อมต่อตรงไปยังเดสก์ท็อปปลายทางล้มเหลว"), + ("Set Password", "ตั้งรหัสผ่าน"), + ("OS Password", "รหัสผ่านระบบปฏิบัติการ"), + ("install_tip", "เนื่องด้วยข้อจำกัดของการใช้งาน UAC ทำให้ RustDesk ไม่สามารถทำงานได้ปกติในฝั่งปลายทางในบางครั้ง เพื่อหลีกเลี่ยงข้อจำกัดของ UAC กรุณากดปุ่มด้านล่างเพื่อติดตั้ง RustDesk ไปยังระบบของคุณ"), + ("Click to upgrade", "คลิกเพื่ออัปเกรด"), + ("Click to download", "คลิกเพื่อดาวน์โหลด"), + ("Click to update", "คลิกเพื่ออัปเดต"), + ("Configure", "ปรับแต่งค่า"), + ("config_acc", "เพื่อที่จะควบคุมเดสก์ท็อปปลายทางของคุณ คุณจำเป็นจะต้องอนุญาตสิทธิ์ \"การเข้าถึง\" ให้แก่ RustDesk"), + ("config_screen", "เพื่อที่จะควบคุมเดสก์ท็อปปลายทางของคุณ คุณจำเป็นจะต้องอนุญาตสิทธิ์ \"การบันทึกภาพหน้าจอ\" ให้แก่ RustDesk"), + ("Installing ...", "กำลังติดตั้ง ..."), + ("Install", "ติดตั้ง"), + ("Installation", "การติดตั้ง"), + ("Installation Path", "ตำแหน่งที่ติดตั้ง"), + ("Create start menu shortcuts", "สร้างทางลัดไปยัง Start Menu"), + ("Create desktop icon", "สร้างไอคอนบนเดสก์ท็อป"), + ("agreement_tip", "ในการเริ่มต้นการติดตั้ง ถือว่าคุณได้ยอมรับข้อตกลงใบอนุญาตแล้ว"), + ("Accept and Install", "ยอมรับและติดตั้ง"), + ("End-user license agreement", "ข้อตกลงใบอนุญาตผู้ใช้งาน"), + ("Generating ...", "กำลังสร้าง ..."), + ("Your installation is lower version.", "การติดตั้งของคุณเป็นเวอร์ชั่นที่ต่ำกว่า"), + ("not_close_tcp_tip", "อย่าปิดหน้าต่างนี้ในขณะที่คุณกำลังใช้งานอุโมงค์การเชื่อมต่อ"), + ("Listening ...", "กำลังรอรับข้อมูล ..."), + ("Remote Host", "โฮสต์ปลายทาง"), + ("Remote Port", "พอร์ทปลายทาง"), + ("Action", "การดำเนินการ"), + ("Add", "เพิ่ม"), + ("Local Port", "พอร์ทต้นทาง"), + ("Local Address", "ที่อยู่ต้นทาง"), + ("Change Local Port", "เปลี่ยนพอร์ทต้นทาง"), + ("setup_server_tip", "เพื่อการเชื่อมต่อที่เร็วขึ้น กรุณาเซ็ตอัปเซิร์ฟเวอร์ของคุณเอง"), + ("Too short, at least 6 characters.", "สั้นเกินไป ต้องไม่ต่ำกว่า 6 ตัวอักษร"), + ("The confirmation is not identical.", "การยืนยันข้อมูลไม่ถูกต้อง"), + ("Permissions", "สิทธิ์การใช้งาน"), + ("Accept", "ยอมรับ"), + ("Dismiss", "ปิด"), + ("Disconnect", "ยกเลิกการเชื่อมต่อ"), + ("Allow using keyboard and mouse", "อนุญาตให้ใช้งานคีย์บอร์ดและเมาส์"), + ("Allow using clipboard", "อนุญาตให้ใช้คลิปบอร์ด"), + ("Allow hearing sound", "อนุญาตให้ได้ยินเสียง"), + ("Allow file copy and paste", "อนุญาตให้มีการคัดลอกและวางไฟล์"), + ("Connected", "เชื่อมต่อแล้ว"), + ("Direct and encrypted connection", "การเชื่อมต่อตรงที่มีการเข้ารหัส"), + ("Relayed and encrypted connection", "การเชื่อมต่อแบบรีเลย์ที่มีการเข้ารหัส"), + ("Direct and unencrypted connection", "การเชื่อมต่อตรงที่ไม่มีการเข้ารหัส"), + ("Relayed and unencrypted connection", "การเชื่อมต่อแบบรีเลย์ที่ไม่มีการเข้ารหัส"), + ("Enter Remote ID", "กรอก ID ปลายทาง"), + ("Enter your password", "กรอกรหัสผ่าน"), + ("Logging in...", "กำลังเข้าสู่ระบบ..."), + ("Enable RDP session sharing", "เปิดการใช้งานการแชร์เซสชัน RDP"), + ("Auto Login", "เข้าสู่ระบอัตโนมัติ"), + ("Enable Direct IP Access", "เปิดการใช้งาน IP ตรง"), + ("Rename", "ปลายทาง"), + ("Space", "พื้นที่ว่าง"), + ("Create Desktop Shortcut", "สร้างทางลัดบนเดสก์ท็อป"), + ("Change Path", "เปลี่ยนตำแหน่ง"), + ("Create Folder", "สร้างโฟลเดอร์"), + ("Please enter the folder name", "กรุณาใส่ชื่อโฟลเดอร์"), + ("Fix it", "แก้ไข"), + ("Warning", "คำเตือน"), + ("Login screen using Wayland is not supported", "หน้าจอการเข้าสู่ระบบโดยใช้ Wayland ยังไม่ถูกรองรับ"), + ("Reboot required", "จำเป็นต้องเริ่มต้นระบบใหม่"), + ("Unsupported display server ", "เซิร์ฟเวอร์การแสดงผลที่ไม่รองรับ"), + ("x11 expected", "ต้องใช้งาน x11"), + ("Port", "พอร์ท"), + ("Settings", "ตั้งค่า"), + ("Username", "ชื่อผู้ใช้งาน"), + ("Invalid port", "พอร์ทไม่ถูกต้อง"), + ("Closed manually by the peer", "ถูกปิดโดยอีกฝั่งการการเชื่อมต่อ"), + ("Enable remote configuration modification", "เปิดการใช้งานการแก้ไขการตั้งค่าปลายทาง"), + ("Run without install", "ใช้งานโดยไม่ต้องติดตั้ง"), + ("Always connected via relay", "เชื่อมต่อผ่านรีเลย์เสมอ"), + ("Always connect via relay", "เชื่อมต่อผ่านรีเลย์เสมอ"), + ("whitelist_tip", "อนุญาตเฉพาะการเชื่อมต่อจาก IP ที่ไวท์ลิสต์"), + ("Login", "เข้าสู่ระบบ"), + ("Remember me", ""), + ("Logout", "ออกจากระบบ"), + ("Tags", "แท็ก"), + ("Search ID", "ค้นหา ID"), + ("Current Wayland display server is not supported", "เซิร์ฟเวอร์การแสดงผล Wayland ปัจจุบันไม่รองรับ"), + ("whitelist_sep", "คั่นโดยเครื่องหมาย comma semicolon เว้นวรรค หรือ ขึ้นบรรทัดใหม่"), + ("Add ID", "เพิ่ม ID"), + ("Add Tag", "เพิ่มแท็ก"), + ("Unselect all tags", "ยกเลิกการเลือกแท็กทั้งหมด"), + ("Network error", "ข้อผิดพลาดของเครือข่าย"), + ("Username missed", "ไม่พบข้อมูลผู้ใช้งาน"), + ("Password missed", "ไม่พบรหัสผ่าน"), + ("Wrong credentials", "ข้อมูลสำหรับเข้าสู่ระบบไม่ถูกต้อง"), + ("Edit Tag", "แก้ไขแท็ก"), + ("Unremember Password", "ยกเลิกการจดจำรหัสผ่าน"), + ("Favorites", "รายการโปรด"), + ("Add to Favorites", "เพิ่มไปยังรายการโปรด"), + ("Remove from Favorites", "ลบออกจากรายการโปรด"), + ("Empty", "ว่างเปล่า"), + ("Invalid folder name", "ชื่อโฟลเดอร์ไม่ถูกต้อง"), + ("Socks5 Proxy", "พรอกซี Socks5"), + ("Hostname", "ชื่อโฮสต์"), + ("Discovered", "ค้นพบ"), + ("install_daemon_tip", "หากต้องการใช้งานขณะระบบเริ่มต้น คุณจำเป็นจะต้องติดตั้งเซอร์วิส"), + ("Remote ID", "ID ปลายทาง"), + ("Paste", "วาง"), + ("Paste here?", "วางที่นี่หรือไม่?"), + ("Are you sure to close the connection?", "คุณแน่ใจหรือไม่ที่จะปิดการเชื่อมต่อ?"), + ("Download new version", "ดาวน์โหลดเวอร์ชั่นใหม่"), + ("Touch mode", "โหมดการสัมผัส"), + ("Mouse mode", "โหมดการใช้เมาส์"), + ("One-Finger Tap", "แตะนิ้วเดียว"), + ("Left Mouse", "เมาส์ซ้าย"), + ("One-Long Tap", "แตะยาวหนึ่งครั้ง"), + ("Two-Finger Tap", "แตะสองนิ้ว"), + ("Right Mouse", "เมาส์ขวา"), + ("One-Finger Move", "ลากนิ้วเดียว"), + ("Double Tap & Move", "แตะเบิ้ลและลาก"), + ("Mouse Drag", "ลากเมาส์"), + ("Three-Finger vertically", "สามนิ้วแนวตั้ง"), + ("Mouse Wheel", "ลูกลิ้งเมาส์"), + ("Two-Finger Move", "ลากสองนิ้ว"), + ("Canvas Move", "ลากแคนวาส"), + ("Pinch to Zoom", "ถ่างเพื่อขยาย"), + ("Canvas Zoom", "ขยายแคนวาส"), + ("Reset canvas", "รีเซ็ตแคนวาส"), + ("No permission of file transfer", "ไม่มีสิทธิ์ในการถ่ายโอนไฟล์"), + ("Note", "บันทึกข้อความ"), + ("Connection", "การเชื่อมต่อ"), + ("Share Screen", "แชร์หน้าจอ"), + ("CLOSE", "ปิด"), + ("OPEN", "เปิด"), + ("Chat", "แชท"), + ("Total", "รวม"), + ("items", "รายการ"), + ("Selected", "ถูกเลือก"), + ("Screen Capture", "แคปเจอร์หน้าจอ"), + ("Input Control", "ควบคุมอินพุท"), + ("Audio Capture", "แคปเจอร์เสียง"), + ("File Connection", "การเชื่อมต่อไฟล์"), + ("Screen Connection", "การเชื่อมต่อหน้าจอ"), + ("Do you accept?", "ยอมรับหรือไม่?"), + ("Open System Setting", "เปิดการตั้งค่าระบบ"), + ("How to get Android input permission?", "เปิดสิทธิ์การใช้งานอินพุทของแอนดรอยด์ได้อย่างไร?"), + ("android_input_permission_tip1", "ในการที่จะอนุญาตให้เครื่องปลายทางควบคุมอุปกรณ์แอนดรอยด์ของคุณโดยใช้เมาส์หรือการสัมผัส คุณจำเป็นจะต้องอนุญาตสิทธิ์ \"การเข้าถึง\" ให้แก่เซอร์วิสของ RustDesk"), + ("android_input_permission_tip2", "กรุณาไปยังหน้าตั้งค่าถัดไป ค้นหาและเข้าไปยัง [เซอร์วิสที่ถูกติดตั้ง] และเปิดการใช้งานเซอร์วิส [อินพุท RustDesk]"), + ("android_new_connection_tip", "ได้รับคำขอควบคุมใหม่ที่ต้องการควบคุมอุปกรณ์ของคุณ"), + ("android_service_will_start_tip", "การเปิดการใช้งาน \"การบันทึกหน้าจอ\" จะเป็นการเริ่มต้นการทำงานของเซอร์วิสโดยอัตโนมัติ ที่จะอนุญาตให้อุปกรณ์อื่นๆ ส่งคำขอเข้าถึงมายังอุปกรณ์ของคุณได้"), + ("android_stop_service_tip", "การปิดการใช้งานเซอร์วิสจะปิดการเชื่อมต่อทั้งหมดโดยอัตโนมัติ"), + ("android_version_audio_tip", "เวอร์ชั่นแอนดรอยด์ปัจจุบันของคุณไม่รองรับการบันทึกข้อมูลเสียง กรุณาอัปเกรดเป็นแอนดรอยด์เวอร์ชั่น 10 หรือสูงกว่า"), + ("android_start_service_tip", "แตะ [เริ่มต้นใช้งานเซอร์วิส] หรือเปิดสิทธิ์ [การบันทึกหน้าจอ] เพื่อเริ่มเซอร์วิสการแชร์หน้าจอ"), + ("Account", "บัญชี"), + ("Overwrite", "เขียนทับ"), + ("This file exists, skip or overwrite this file?", "พบไฟล์ที่มีอยู่แล้ว ต้องการเขียนทับหรือไม่?"), + ("Quit", "ออก"), + ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), + ("Help", "ช่วยเหลือ"), + ("Failed", "ล้มเหลว"), + ("Succeeded", "สำเร็จ"), + ("Someone turns on privacy mode, exit", "มีใครบางคนเปิดใช้งานโหมดความเป็นส่วนตัว กำลังออก"), + ("Unsupported", "ไม่รองรับ"), + ("Peer denied", "ถูกปฏิเสธโดยอีกฝั่ง"), + ("Please install plugins", "กรุณาติดตั้งปลั๊กอิน"), + ("Peer exit", "อีกฝั่งออก"), + ("Failed to turn off", "การปิดล้มเหลว"), + ("Turned off", "ปิด"), + ("In privacy mode", "อยู่ในโหมดความเป็นส่วนตัว"), + ("Out privacy mode", "อยู่นอกโหมดความเป็นส่วนตัว"), + ("Language", "ภาษา"), + ("Keep RustDesk background service", "คงสถานะการทำงานเบื้องหลังของเซอร์วิส RustDesk"), + ("Ignore Battery Optimizations", "เพิกเฉยการตั้งค่าการใช้งาน Battery Optimization"), + ("android_open_battery_optimizations_tip", "หากคุณต้องการปิดการใช้งานฟีเจอร์นี้ กรุณาไปยังหน้าตั้งค่าในแอปพลิเคชัน RustDesk ค้นหาหัวข้อ [Battery] และยกเลิกการเลือกรายการ [Unrestricted]"), + ("Connection not allowed", "การเชื่อมต่อไม่อนุญาต"), + ("Legacy mode", ""), + ("Map mode", ""), + ("Translate mode", ""), + ("Use permanent password", "ใช้รหัสผ่านถาวร"), + ("Use both passwords", "ใช้รหัสผ่านทั้งสองแบบ"), + ("Set permanent password", "ตั้งค่ารหัสผ่านถาวร"), + ("Enable Remote Restart", "เปิดการใช้งานการรีสตาร์ทระบบทางไกล"), + ("Allow remote restart", "อนุญาตการรีสตาร์ทระบบทางไกล"), + ("Restart Remote Device", "รีสตาร์ทอุปกรณ์ปลายทาง"), + ("Are you sure you want to restart", "คุณแน่ใจหรือไม่ที่จะรีสตาร์ท"), + ("Restarting Remote Device", "กำลังรีสตาร์ทระบบปลายทาง"), + ("remote_restarting_tip", "ระบบปลายทางกำลังรีสตาร์ท กรุณาปิดกล่องข้อความนี้และดำเนินการเขื่อมต่อใหม่อีกครั้งด้วยรหัสผ่านถาวรหลังจากผ่านไปซักครู่"), + ("Copied", "คัดลอกแล้ว"), + ("Exit Fullscreen", "ออกจากเต็มหน้าจอ"), + ("Fullscreen", "เต็มหน้าจอ"), + ("Mobile Actions", "การดำเนินการบนมือถือ"), + ("Select Monitor", "เลือกหน้าจอ"), + ("Control Actions", "การดำเนินการควบคุม"), + ("Display Settings", "การตั้งค่าแสดงผล"), + ("Ratio", "อัตราส่วน"), + ("Image Quality", "คุณภาพภาพ"), + ("Scroll Style", "ลักษณะการเลื่อน"), + ("Show Menubar", "แสดงแถบเมนู"), + ("Hide Menubar", "ซ่อนแถบเมนู"), + ("Direct Connection", "การเชื่อมต่อตรง"), + ("Relay Connection", "การเชื่อมต่อแบบรีเลย์"), + ("Secure Connection", "การเชื่อมต่อที่ปลอดภัย"), + ("Insecure Connection", "การเชื่อมต่อที่ไม่ปลอดภัย"), + ("Scale original", "ขนาดเดิม"), + ("Scale adaptive", "ขนาดยืดหยุ่น"), + ("General", "ทั่วไป"), + ("Security", "ความปลอดภัย"), + ("Theme", "ธีม"), + ("Dark Theme", "ธีมมืด"), + ("Dark", "มืด"), + ("Light", "สว่าง"), + ("Follow System", "ตามระบบ"), + ("Enable hardware codec", "เปิดการใช้งานฮาร์ดแวร์ codec"), + ("Unlock Security Settings", "ปลดล็อคการตั้งค่าความปลอดภัย"), + ("Enable Audio", "เปิดการใช้งานเสียง"), + ("Unlock Network Settings", "ปลดล็อคการตั้งค่าเครือข่าย"), + ("Server", "เซิร์ฟเวอร์"), + ("Direct IP Access", "การเข้าถึง IP ตรง"), + ("Proxy", "พรอกซี"), + ("Apply", "นำไปใช้"), + ("Disconnect all devices?", "ยกเลิกการเชื่อมต่ออุปกรณ์ทั้งหมด?"), + ("Clear", "ล้างข้อมูล"), + ("Audio Input Device", "อุปกรณ์รับอินพุทข้อมูลเสียง"), + ("Deny remote access", "ปฏิเสธการเชื่อมต่อ"), + ("Use IP Whitelisting", "ใช้งาน IP ไวท์ลิสต์"), + ("Network", "เครือข่าย"), + ("Enable RDP", "เปิดการใช้งาน RDP"), + ("Pin menubar", "ปักหมุดแถบเมนู"), + ("Unpin menubar", "ยกเลิกการปักหมุดแถบเมนู"), + ("Recording", "การบันทึก"), + ("Directory", "ไดเรกทอรี่"), + ("Automatically record incoming sessions", "บันทึกเซสชันขาเข้าโดยอัตโนมัติ"), + ("Change", "เปลี่ยน"), + ("Start session recording", "เริ่มต้นการบันทึกเซสชัน"), + ("Stop session recording", "หยุดการบันทึกเซสซัน"), + ("Enable Recording Session", "เปิดใช้งานการบันทึกเซสชัน"), + ("Allow recording session", "อนุญาตการบันทึกเซสชัน"), + ("Enable LAN Discovery", "เปิดการใช้งานการค้นหาในวง LAN"), + ("Deny LAN Discovery", "ปฏิเสธการใช้งานการค้นหาในวง LAN"), + ("Write a message", "เขียนข้อความ"), + ("Prompt", ""), + ("Please wait for confirmation of UAC...", "กรุณารอการยืนยันจาก UAC..."), + ("elevated_foreground_window_tip", "หน้าต่างปัจจุบันของเครื่องปลายทางต้องการสิทธิ์การใช้งานที่สูงขึ้นสำหรับการทำงาน ดังนั้นเมาส์และคีย์บอร์ดจะไม่สามารถใช้งานได้ชั่วคราว คุณสามารถขอผู้ใช้งานปลายทางให้ย่อหน้าต่าง หรือคลิกปุ่มให้สิทธิ์การใช้งานในหน้าต่างการจัดการการเชื่อมต่อ เพื่อหลีกเลี่ยงปัญหานี้เราแนะนำให้ดำเนินการติดตั้งซอฟท์แวร์ในเครื่องปลายทาง"), + ("Disconnected", "ยกเลิกการเชื่อมต่อ"), + ("Other", "อื่นๆ"), + ("Confirm before closing multiple tabs", "ยืนยันการปิดหลายแท็บ"), + ("Keyboard Settings", "การตั้งค่าคีย์บอร์ด"), + ("Full Access", "การเข้าถึงทั้งหมด"), + ("Screen Share", "การแชร์จอ"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland ต้องการ Ubuntu เวอร์ชั่น 21.04 หรือสูงกว่า"), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland ต้องการลินุกซ์เวอร์ชันที่สูงกว่านี้ กรุณาเปลี่ยนไปใช้เดสก์ท็อป X11 หรือเปลี่ยนระบบปฏิบัติการของคุณ"), + ("JumpLink", "View"), + ("Please Select the screen to be shared(Operate on the peer side).", "กรุณาเลือกหน้าจอที่ต้องการแชร์ (ใช้งานในอีกฝั่งของการเชื่อมต่อ)"), + ("Show RustDesk", "แสดง RustDesk"), + ("This PC", ""), + ("or", "หรือ"), + ("Continue with", "ทำต่อด้วย"), + ("Elevate", "ยกระดับ"), + ("Zoom cursor", "ขยายเคอร์เซอร์"), + ("Accept sessions via password", "ยอมรับการเชื่อมต่อด้วยรหัสผ่าน"), + ("Accept sessions via click", "ยอมรับการเชื่อมต่อด้วยการคลิก"), + ("Accept sessions via both", "ยอมรับการเชื่อมต่อด้วยทั้งสองวิธิ"), + ("Please wait for the remote side to accept your session request...", "กรุณารอให้อีกฝั่งยอมรับการเชื่อมต่อของคุณ..."), + ("One-time Password", "รหัสผ่านครั้งเดียว"), + ("Use one-time password", "ใช้รหัสผ่านครั้งเดียว"), + ("One-time password length", "ความยาวรหัสผ่านครั้งเดียว"), + ("Request access to your device", "คำขอการเข้าถึงอุปกรณ์ของคุณ"), + ("Hide connection management window", "ซ่อนหน้าต่างการจัดการการเชื่อมต่อ"), + ("hide_cm_tip", "อนุญาตการซ่อนก็ต่อเมื่อยอมรับการเชื่อมต่อด้วยรหัสผ่าน และต้องเป็นรหัสผ่านถาวรเท่านั้น"), + ("wayland_experiment_tip", "การสนับสนุน Wayland ยังอยู่ในขั้นตอนการทดลอง กรุณาใช้ X11 หากคุณต้องการใช้งานการเข้าถึงแบบไม่มีผู้ดูแล"), + ("Right click to select tabs", "คลิกขวาเพื่อเลือกแท็บ"), + ("Skipped", "ข้าม"), + ("Add to Address Book", "เพิ่มไปยังสมุดรายชื่อ"), + ("Group", "กลุ่ม"), + ("Search", "ค้นหา"), + ("Closed manually by the web console", "ถูกปิดโดยเว็บคอนโซล"), + ("Local keyboard type", "ประเภทคีย์บอร์ด"), + ("Select local keyboard type", "เลือกประเภทคีย์บอร์ด"), + ].iter().cloned().collect(); +} diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 60bc9dda1..be95d2b7a 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Always connect via relay"), ("whitelist_tip", "Bu masaüstüne yalnızca yetkili IP adresleri bağlanabilir"), ("Login", "Giriş yap"), + ("Remember me", ""), ("Logout", "Çıkış yap"), ("Tags", "Etiketler"), ("Search ID", "ID Arama"), diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 0e08fa508..96e806d06 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "一律透過轉送連線"), ("whitelist_tip", "只有白名單中的 IP 可以存取"), ("Login", "登入"), + ("Remember me", ""), ("Logout", "登出"), ("Tags", "標籤"), ("Search ID", "搜尋 ID"), diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 343b62b4f..53fa879f5 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Завжди підключатися через ретрансляційний сервер"), ("whitelist_tip", "Тільки IP-адреси з білого списку можуть отримати доступ до мене"), ("Login", "Увійти"), + ("Remember me", ""), ("Logout", "Вийти"), ("Tags", "Ключові слова"), ("Search ID", "Пошук за ID"), diff --git a/src/lang/vn.rs b/src/lang/vn.rs index a2fc416f2..bb638c072 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -210,6 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Luôn kết nối qua relay"), ("whitelist_tip", "Chỉ có những IP đựoc cho phép mới có thể truy cập"), ("Login", "Đăng nhập"), + ("Remember me", ""), ("Logout", "Đăng xuất"), ("Tags", "Tags"), ("Search ID", "Tìm ID"), From a8536118c09718f67af9fc1d1ce9383045774dde Mon Sep 17 00:00:00 2001 From: csf Date: Mon, 9 Jan 2023 14:21:16 +0900 Subject: [PATCH 4/5] add verificationCodeDialog, opt loginDialog --- flutter/lib/common/widgets/login.dart | 190 ++++++++++++++++++++++---- flutter/lib/models/user_model.dart | 1 - src/lang/ca.rs | 4 + src/lang/cn.rs | 8 +- src/lang/cs.rs | 4 + src/lang/da.rs | 4 + src/lang/de.rs | 4 + src/lang/en.rs | 1 + src/lang/eo.rs | 4 + src/lang/es.rs | 4 + src/lang/fa.rs | 4 + src/lang/fr.rs | 4 + src/lang/gr.rs | 4 + src/lang/hu.rs | 4 + src/lang/id.rs | 4 + src/lang/it.rs | 4 + src/lang/ja.rs | 4 + src/lang/ko.rs | 4 + src/lang/kz.rs | 4 + src/lang/pl.rs | 4 + src/lang/pt_PT.rs | 4 + src/lang/ptbr.rs | 4 + src/lang/ru.rs | 4 + src/lang/sk.rs | 4 + src/lang/sq.rs | 4 + src/lang/sr.rs | 4 + src/lang/sv.rs | 4 + src/lang/template.rs | 4 + src/lang/th.rs | 4 + src/lang/tr.rs | 4 + src/lang/tw.rs | 4 + src/lang/ua.rs | 4 + src/lang/vn.rs | 4 + 33 files changed, 283 insertions(+), 33 deletions(-) diff --git a/flutter/lib/common/widgets/login.dart b/flutter/lib/common/widgets/login.dart index 8092dfed6..f760132af 100644 --- a/flutter/lib/common/widgets/login.dart +++ b/flutter/lib/common/widgets/login.dart @@ -324,11 +324,13 @@ class LoginWidgetUserPass extends StatelessWidget { title: '${translate("Username")}:', controller: username, autoFocus: true, + prefixIcon: Icon(Icons.account_circle_outlined), errorText: usernameMsg), DialogTextField( title: '${translate("Password")}:', obscureText: true, controller: pass, + prefixIcon: Icon(Icons.lock_outline), errorText: passMsg), Obx(() => CheckboxListTile( contentPadding: const EdgeInsets.all(0), @@ -377,6 +379,8 @@ class DialogTextField extends StatelessWidget { final bool autoFocus; final bool obscureText; final String? errorText; + final String? helperText; + final Widget? prefixIcon; final TextEditingController controller; final FocusNode focusNode = FocusNode(); @@ -385,6 +389,8 @@ class DialogTextField extends StatelessWidget { this.autoFocus = false, this.obscureText = false, this.errorText, + this.helperText, + this.prefixIcon, required this.title, required this.controller}) : super(key: key) { @@ -403,6 +409,9 @@ class DialogTextField extends StatelessWidget { decoration: InputDecoration( labelText: title, border: const OutlineInputBorder(), + prefixIcon: prefixIcon, + helperText: helperText, + helperMaxLines: 8, errorText: errorText), controller: controller, focusNode: focusNode, @@ -427,38 +436,36 @@ Future loginDialog() async { final autoLogin = true.obs; final RxString curOP = ''.obs; - return gFFI.dialogManager.show((setState, close) { - cancel() { + final res = await gFFI.dialogManager.show((setState, close) { + username.addListener(() { + if (usernameMsg != null) { + setState(() => usernameMsg = null); + } + }); + + password.addListener(() { + if (passwordMsg != null) { + setState(() => passwordMsg = null); + } + }); + + onDialogCancel() { isInProgress = false; close(false); } onLogin() async { - setState(() { - usernameMsg = null; - passwordMsg = null; - isInProgress = true; - }); - cancel() { - curOP.value = ''; - if (isInProgress) { - setState(() { - isInProgress = false; - }); - } - } - - curOP.value = 'rustdesk'; + // validate if (username.text.isEmpty) { - usernameMsg = translate('Username missed'); - cancel(); + setState(() => usernameMsg = translate('Username missed')); return; } if (password.text.isEmpty) { - passwordMsg = translate('Password missed'); - cancel(); + setState(() => passwordMsg = translate('Password missed')); return; } + curOP.value = 'rustdesk'; + setState(() => isInProgress = true); try { final resp = await gFFI.userModel.login(LoginRequest( username: username.text, @@ -471,27 +478,33 @@ Future loginDialog() async { switch (resp.type) { case HttpType.kAuthResTypeToken: if (resp.access_token != null) { - bind.mainSetLocalOption( + await bind.mainSetLocalOption( key: 'access_token', value: resp.access_token!); close(true); return; } break; case HttpType.kAuthResTypeEmailCheck: + setState(() => isInProgress = false); + final res = await verificationCodeDialog(resp.user); + if (res == true) { + close(true); + return; + } + break; + default: + passwordMsg = "Failed, bad response from server"; break; } } on RequestException catch (err) { passwordMsg = translate(err.cause); debugPrintStack(label: err.toString()); - cancel(); - return; } catch (err) { - passwordMsg = "Unknown Error"; + passwordMsg = "Unknown Error: $err"; debugPrintStack(label: err.toString()); - cancel(); - return; } - close(); + curOP.value = ''; + setState(() => isInProgress = false); } return CustomAlertDialog( @@ -538,8 +551,125 @@ Future loginDialog() async { ), ], ), - actions: [msgBoxButton(translate('Close'), cancel)], - onCancel: cancel, + actions: [msgBoxButton(translate('Close'), onDialogCancel)], + onCancel: onDialogCancel, ); }); + + if (res != null) { + // update ab and group status + await gFFI.abModel.pullAb(); + await gFFI.groupModel.pull(); + } + + return res; +} + +Future verificationCodeDialog(UserPayload? user) async { + var autoLogin = true; + var isInProgress = false; + String? errorText; + + final code = TextEditingController(); + + final res = await gFFI.dialogManager.show((setState, close) { + bool validate() { + return code.text.length >= 6; + } + + code.addListener(() { + if (errorText != null) { + setState(() => errorText = null); + } + }); + + void onVerify() async { + if (!validate()) { + setState( + () => errorText = translate('Too short, at least 6 characters.')); + return; + } + setState(() => isInProgress = true); + + try { + final resp = await gFFI.userModel.login(LoginRequest( + verificationCode: code.text, + username: user?.name, + id: await bind.mainGetMyId(), + uuid: await bind.mainGetUuid(), + autoLogin: autoLogin, + type: HttpType.kAuthReqTypeEmailCode)); + + switch (resp.type) { + case HttpType.kAuthResTypeToken: + if (resp.access_token != null) { + await bind.mainSetLocalOption( + key: 'access_token', value: resp.access_token!); + close(true); + return; + } + break; + default: + errorText = "Failed, bad response from server"; + break; + } + } on RequestException catch (err) { + errorText = translate(err.cause); + debugPrintStack(label: err.toString()); + } catch (err) { + errorText = "Unknown Error: $err"; + debugPrintStack(label: err.toString()); + } + + setState(() => isInProgress = false); + } + + return CustomAlertDialog( + title: Text(translate("Verification code")), + contentBoxConstraints: BoxConstraints(maxWidth: 300), + content: Column( + children: [ + Offstage( + offstage: user?.email == null, + child: TextField( + decoration: InputDecoration( + labelText: "Email", + prefixIcon: Icon(Icons.email), + border: InputBorder.none), + readOnly: true, + controller: TextEditingController(text: user?.email), + )), + const SizedBox(height: 8), + DialogTextField( + title: '${translate("Verification code")}:', + controller: code, + autoFocus: true, + errorText: errorText, + helperText: translate('verification_tip'), + ), + CheckboxListTile( + contentPadding: const EdgeInsets.all(0), + dense: true, + controlAffinity: ListTileControlAffinity.leading, + title: Row(children: [ + Expanded(child: Text(translate("Trust this device"))) + ]), + value: autoLogin, + onChanged: (v) { + if (v == null) return; + setState(() => autoLogin = !autoLogin); + }, + ), + Offstage( + offstage: !isInProgress, + child: const LinearProgressIndicator()), + ], + ), + actions: [ + TextButton(onPressed: close, child: Text(translate("Cancel"))), + TextButton(onPressed: onVerify, child: Text(translate("Verify"))), + ]); + }); + + return res; } diff --git a/flutter/lib/models/user_model.dart b/flutter/lib/models/user_model.dart index 79a9778b0..b0eebee53 100644 --- a/flutter/lib/models/user_model.dart +++ b/flutter/lib/models/user_model.dart @@ -127,7 +127,6 @@ class UserModel { await _parseAndUpdateUser(loginResponse.user!); } - await _updateOtherModels(); return loginResponse; } } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index ffc5396fd..82052517a 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Connecta sempre a través de relay"), ("whitelist_tip", ""), ("Login", "Inicia sessió"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Sortir"), ("Tags", ""), ("Search ID", "Cerca ID"), diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 6a4feeeec..c8a6e5f57 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "强制走中继连接"), ("whitelist_tip", "只有白名单里的ip才能访问我"), ("Login", "登录"), + ("Verify", "验证"), ("Remember me", "记住我"), + ("Trust this device", "信任此设备"), + ("Verification code", "验证码"), + ("verification_tip", "检测到新设备登录,已向注册邮箱发送了登录验证码,输入验证码继续登录"), ("Logout", "登出"), ("Tags", "标签"), ("Search ID", "查找ID"), @@ -222,7 +226,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Network error", "网络错误"), ("Username missed", "用户名没有填写"), ("Password missed", "密码没有填写"), - ("Wrong credentials", "用户名或者密码错误"), + ("Wrong credentials", "提供的登入信息错误"), ("Edit Tag", "修改标签"), ("Unremember Password", "忘掉密码"), ("Favorites", "收藏"), @@ -274,7 +278,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Do you accept?", "是否接受?"), ("Open System Setting", "打开系统设置"), ("How to get Android input permission?", "如何获取安卓的输入权限?"), - ("android_input_permission_tip1", "為了讓遠程設備通過鼠標或者觸屏控制您的安卓設備,你需要允許RustDesk使用\"無障礙\"服務。"), + ("android_input_permission_tip1", "为了让远程设备通过鼠标或触屏控制您的安卓设备,你需要允許RustDesk使用\"无障碍\"服务。"), ("android_input_permission_tip2", "请在接下来的系统设置页面里,找到并进入 [已安装的服务] 页面,将 [RustDesk Input] 服务开启。"), ("android_new_connection_tip", "收到新的连接控制请求,对方想要控制你当前的设备。"), ("android_service_will_start_tip", "开启录屏权限将自动开启服务,允许其他设备向此设备请求建立连接。"), diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 17e2ea68b..08aa1fd58 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Vždy se spojovat prostřednictvím brány pro předávání (relay)"), ("whitelist_tip", "Přístup je umožněn pouze z IP adres, nacházejících se na seznamu povolených"), ("Login", "Přihlásit se"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Odhlásit se"), ("Tags", "Štítky"), ("Search ID", "Hledat identifikátor"), diff --git a/src/lang/da.rs b/src/lang/da.rs index c0db3eda0..8f1f8a1ff 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Forbindelse via relæ-server"), ("whitelist_tip", "Kun IP'er på udgivelseslisten kan få adgang til mig"), ("Login", "Login"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "logger af"), ("Tags", "Nøgleord"), ("Search ID", "Søg ID"), diff --git a/src/lang/de.rs b/src/lang/de.rs index 28b0a51d3..e76f6b315 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Immer über Relay-Server verbinden"), ("whitelist_tip", "Nur IPs auf der Whitelist können zugreifen."), ("Login", "Anmelden"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Abmelden"), ("Tags", "Schlagworte"), ("Search ID", "Suche ID"), diff --git a/src/lang/en.rs b/src/lang/en.rs index f351b575d..e9754f296 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -36,5 +36,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("hide_cm_tip", "Allow hiding only if accepting sessions via password and using permanent password"), ("wayland_experiment_tip", "Wayland support is in experimental stage, please use X11 if you require unattended access."), ("Slogan_tip", "Made with heart in this chaotic world!"), + ("verification_tip", "A new device has been detected, and a verification code has been sent to the registered email address, enter the verification code to continue logging in."), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index e92d7ab31..38d3c2588 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Ĉiam konekti per relajso"), ("whitelist_tip", "Nur la IP en la blanka listo povas kontroli mian komputilon"), ("Login", "Konekti"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Malkonekti"), ("Tags", "Etikedi"), ("Search ID", "Serĉi ID"), diff --git a/src/lang/es.rs b/src/lang/es.rs index 539627cb9..4932cf6e2 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Conéctese siempre a través de relay"), ("whitelist_tip", "Solo las direcciones IP autorizadas pueden conectarse a este escritorio"), ("Login", "Iniciar sesión"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Salir"), ("Tags", "Tags"), ("Search ID", "Buscar ID"), diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 9ca854ecb..f4c025030 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "برای اتصال استفاده شود Relay از"), ("whitelist_tip", "های مجاز می توانند به این دسکتاپ متصل شوند IP فقط"), ("Login", "ورود"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "خروج"), ("Tags", "برچسب ها"), ("Search ID", "جستجوی شناسه"), diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 84cd9c4fa..69f977c94 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Forcer la connexion relais"), ("whitelist_tip", "Seul l'IP dans la liste blanche peut accéder à mon appareil"), ("Login", "Connexion"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Déconnexion"), ("Tags", "Étiqueter"), ("Search ID", "Rechercher un ID"), diff --git a/src/lang/gr.rs b/src/lang/gr.rs index f1ee5be08..9ea7cbdc0 100644 --- a/src/lang/gr.rs +++ b/src/lang/gr.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Σύνδεση πάντα μέσω αναμετάδοσης"), ("whitelist_tip", "Μόνο οι IP της λίστας επιτρεπόμενων έχουν πρόσβαση"), ("Login", "Σύνδεση"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Αποσύνδεση"), ("Tags", "Ετικέτες"), ("Search ID", "Αναζήτηση ID"), diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 92b7c1ba1..1db137c43 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Mindig közvetítőn keresztüli csatlakozás"), ("whitelist_tip", "Csak az engedélyezési listán szereplő címek csatlakozhatnak"), ("Login", "Belépés"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Kilépés"), ("Tags", "Tagok"), ("Search ID", "Azonosító keresése..."), diff --git a/src/lang/id.rs b/src/lang/id.rs index 2e96785c5..baf3ab144 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Selalu terhubung melalui relai"), ("whitelist_tip", "Hanya whitelisted IP yang dapat mengakses saya"), ("Login", "Masuk"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Keluar"), ("Tags", "Tag"), ("Search ID", "Cari ID"), diff --git a/src/lang/it.rs b/src/lang/it.rs index d54887aa4..1385a286d 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Connetti sempre tramite relay"), ("whitelist_tip", "Solo gli indirizzi IP autorizzati possono connettersi a questo desktop"), ("Login", "Accedi"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Esci"), ("Tags", "Tag"), ("Search ID", "Cerca ID"), diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 2f3d0b54f..6930aae13 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "常に中継サーバー経由で接続"), ("whitelist_tip", "ホワイトリストに登録されたIPからのみ接続を許可します"), ("Login", "ログイン"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "ログアウト"), ("Tags", "タグ"), ("Search ID", "IDを検索"), diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 21e75baef..fee465786 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "항상 relay를 통해 접속하기"), ("whitelist_tip", "화이트리스트에 있는 IP만 현 데스크탑에 접속 가능합니다"), ("Login", "로그인"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "로그아웃"), ("Tags", "태그"), ("Search ID", "ID 검색"), diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 69caf1b6b..3ce1d6db3 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Әрқашан да релай сербері арқылы қосылу"), ("whitelist_tip", "Маған тек ақ-тізімделген IP қол жеткізе алады"), ("Login", "Кіру"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Шығу"), ("Tags", "Тақтар"), ("Search ID", "ID Іздеу"), diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 24674e197..ead9e625f 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Zawsze łącz pośrednio"), ("whitelist_tip", "Zezwlaj na łączenie z tym komputerem tylko z adresów IP znajdujących się na białej liście"), ("Login", "Zaloguj"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Wyloguj"), ("Tags", "Tagi"), ("Search ID", "Szukaj ID"), diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index dcf48eca4..01248e7b9 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Sempre conectar via relay"), ("whitelist_tip", "Somente IPs na whitelist podem me acessar"), ("Login", "Login"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Sair"), ("Tags", "Tags"), ("Search ID", "Procurar ID"), diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 3ffee4f93..b155a08fd 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Sempre conectar via relay"), ("whitelist_tip", "Somente IPs confiáveis podem me acessar"), ("Login", "Login"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Sair"), ("Tags", "Tags"), ("Search ID", "Pesquisar ID"), diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 8dd6bfbca..e055acaa2 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Всегда подключаться через ретрансляционный сервер"), ("whitelist_tip", "Только IP-адреса из белого списка могут получить доступ ко мне"), ("Login", "Войти"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Выйти"), ("Tags", "Метки"), ("Search ID", "Поиск по ID"), diff --git a/src/lang/sk.rs b/src/lang/sk.rs index f67eb60cc..51f58d9b2 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Vždy pripájať cez prepájací server"), ("whitelist_tip", "Len vymenované IP adresy majú oprávnenie sa pripojiť k vzdialenej správe"), ("Login", "Prihlásenie"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Odhlásenie"), ("Tags", "Štítky"), ("Search ID", "Hľadať ID"), diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 222f16a8e..e3952b474 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Gjithmonë lidheni me transmetues"), ("whitelist_tip", "Vetëm IP e listës së bardhë mund të më aksesoj."), ("Login", "Hyrje"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Dalje"), ("Tags", "Tage"), ("Search ID", "Kerko ID"), diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 660df7e9e..5fd822c27 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Uvek se spoj preko posrednika"), ("whitelist_tip", "Samo dozvoljene IP mi mogu pristupiti"), ("Login", "Prijava"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Odjava"), ("Tags", "Oznake"), ("Search ID", "Traži ID"), diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 870d92569..a9ed367ce 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Anslut alltid via relay"), ("whitelist_tip", "Bara vitlistade IPs kan koppla upp till mig"), ("Login", "Logga in"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Logga ut"), ("Tags", "Taggar"), ("Search ID", "Sök ID"), diff --git a/src/lang/template.rs b/src/lang/template.rs index 3824bf60d..e8d0c6c02 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", ""), ("whitelist_tip", ""), ("Login", ""), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", ""), ("Tags", ""), ("Search ID", ""), diff --git a/src/lang/th.rs b/src/lang/th.rs index b2ca698e1..0c1a93bb5 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "เชื่อมต่อผ่านรีเลย์เสมอ"), ("whitelist_tip", "อนุญาตเฉพาะการเชื่อมต่อจาก IP ที่ไวท์ลิสต์"), ("Login", "เข้าสู่ระบบ"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "ออกจากระบบ"), ("Tags", "แท็ก"), ("Search ID", "ค้นหา ID"), diff --git a/src/lang/tr.rs b/src/lang/tr.rs index be95d2b7a..ec0c65a62 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Always connect via relay"), ("whitelist_tip", "Bu masaüstüne yalnızca yetkili IP adresleri bağlanabilir"), ("Login", "Giriş yap"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Çıkış yap"), ("Tags", "Etiketler"), ("Search ID", "ID Arama"), diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 96e806d06..d5fa6ebf0 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "一律透過轉送連線"), ("whitelist_tip", "只有白名單中的 IP 可以存取"), ("Login", "登入"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "登出"), ("Tags", "標籤"), ("Search ID", "搜尋 ID"), diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 53fa879f5..1aeac5263 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Завжди підключатися через ретрансляційний сервер"), ("whitelist_tip", "Тільки IP-адреси з білого списку можуть отримати доступ до мене"), ("Login", "Увійти"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Вийти"), ("Tags", "Ключові слова"), ("Search ID", "Пошук за ID"), diff --git a/src/lang/vn.rs b/src/lang/vn.rs index bb638c072..a5d99a718 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -210,7 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "Luôn kết nối qua relay"), ("whitelist_tip", "Chỉ có những IP đựoc cho phép mới có thể truy cập"), ("Login", "Đăng nhập"), + ("Verify", ""), ("Remember me", ""), + ("Trust this device", ""), + ("Verification code", ""), + ("verification_tip", ""), ("Logout", "Đăng xuất"), ("Tags", "Tags"), ("Search ID", "Tìm ID"), From 87f203db4ace3ceacb2cc7128df261ff85278da2 Mon Sep 17 00:00:00 2001 From: csf Date: Mon, 9 Jan 2023 14:43:05 +0900 Subject: [PATCH 5/5] fix loginDialog focus conflict --- flutter/lib/common/widgets/login.dart | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/flutter/lib/common/widgets/login.dart b/flutter/lib/common/widgets/login.dart index f760132af..ce27ceb2c 100644 --- a/flutter/lib/common/widgets/login.dart +++ b/flutter/lib/common/widgets/login.dart @@ -300,8 +300,10 @@ class LoginWidgetUserPass extends StatelessWidget { final RxString curOP; final RxBool autoLogin; final Function() onLogin; + final FocusNode? userFocusNode; const LoginWidgetUserPass({ Key? key, + this.userFocusNode, required this.username, required this.pass, required this.usernameMsg, @@ -323,7 +325,7 @@ class LoginWidgetUserPass extends StatelessWidget { DialogTextField( title: '${translate("Username")}:', controller: username, - autoFocus: true, + focusNode: userFocusNode, prefixIcon: Icon(Icons.account_circle_outlined), errorText: usernameMsg), DialogTextField( @@ -376,29 +378,23 @@ class LoginWidgetUserPass extends StatelessWidget { class DialogTextField extends StatelessWidget { final String title; - final bool autoFocus; final bool obscureText; final String? errorText; final String? helperText; final Widget? prefixIcon; final TextEditingController controller; - final FocusNode focusNode = FocusNode(); + final FocusNode? focusNode; DialogTextField( {Key? key, - this.autoFocus = false, + this.focusNode, this.obscureText = false, this.errorText, this.helperText, this.prefixIcon, 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()); - } - } + : super(key: key); @override Widget build(BuildContext context) { @@ -429,6 +425,8 @@ class DialogTextField extends StatelessWidget { Future loginDialog() async { var username = TextEditingController(); var password = TextEditingController(); + final userFocusNode = FocusNode()..requestFocus(); + Timer(Duration(milliseconds: 100), () => userFocusNode..requestFocus()); String? usernameMsg; String? passwordMsg; @@ -525,6 +523,7 @@ Future loginDialog() async { curOP: curOP, autoLogin: autoLogin, onLogin: onLogin, + userFocusNode: userFocusNode, ), const SizedBox( height: 8.0, @@ -571,6 +570,8 @@ Future verificationCodeDialog(UserPayload? user) async { String? errorText; final code = TextEditingController(); + final focusNode = FocusNode()..requestFocus(); + Timer(Duration(milliseconds: 100), () => focusNode..requestFocus()); final res = await gFFI.dialogManager.show((setState, close) { bool validate() { @@ -643,8 +644,8 @@ Future verificationCodeDialog(UserPayload? user) async { DialogTextField( title: '${translate("Verification code")}:', controller: code, - autoFocus: true, errorText: errorText, + focusNode: focusNode, helperText: translate('verification_tip'), ), CheckboxListTile(