Merge pull request #2741 from Heap-Hop/master
refactor user login, add two-step verification (email)
This commit is contained in:
commit
20a4550cce
@ -1367,7 +1367,7 @@ connect(BuildContext context, String id,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, String>> getHttpHeaders() async {
|
Map<String, String> getHttpHeaders() {
|
||||||
return {
|
return {
|
||||||
'Authorization': 'Bearer ${bind.mainGetLocalOption(key: 'access_token')}'
|
'Authorization': 'Bearer ${bind.mainGetLocalOption(key: 'access_token')}'
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,22 @@
|
|||||||
import 'package:flutter_hbb/models/peer_model.dart';
|
import 'package:flutter_hbb/models/peer_model.dart';
|
||||||
|
|
||||||
|
class HttpType {
|
||||||
|
static const kAuthReqTypeAccount = "account";
|
||||||
|
static const kAuthReqTypeMobile = "mobile";
|
||||||
|
static const kAuthReqTypeSMSCode = "sms_code";
|
||||||
|
static const kAuthReqTypeEmailCode = "email_code";
|
||||||
|
|
||||||
|
static const kAuthResTypeToken = "access_token";
|
||||||
|
static const kAuthResTypeEmailCheck = "email_check";
|
||||||
|
}
|
||||||
|
|
||||||
class UserPayload {
|
class UserPayload {
|
||||||
String name = '';
|
String name = '';
|
||||||
String email = '';
|
String email = '';
|
||||||
String note = '';
|
String note = '';
|
||||||
int? status;
|
int? status;
|
||||||
String grp = '';
|
String grp = '';
|
||||||
bool is_admin = false;
|
bool isAdmin = false;
|
||||||
|
|
||||||
UserPayload.fromJson(Map<String, dynamic> json)
|
UserPayload.fromJson(Map<String, dynamic> json)
|
||||||
: name = json['name'] ?? '',
|
: name = json['name'] ?? '',
|
||||||
@ -14,7 +24,7 @@ class UserPayload {
|
|||||||
note = json['note'] ?? '',
|
note = json['note'] ?? '',
|
||||||
status = json['status'],
|
status = json['status'],
|
||||||
grp = json['grp'] ?? '',
|
grp = json['grp'] ?? '',
|
||||||
is_admin = json['is_admin'] == true;
|
isAdmin = json['is_admin'] == true;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PeerPayload {
|
class PeerPayload {
|
||||||
@ -37,3 +47,73 @@ class PeerPayload {
|
|||||||
return Peer.fromJson({"id": p.id});
|
return Peer.fromJson({"id": p.id});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LoginRequest {
|
||||||
|
String? username;
|
||||||
|
String? password;
|
||||||
|
String? id;
|
||||||
|
String? uuid;
|
||||||
|
bool? autoLogin;
|
||||||
|
String? type;
|
||||||
|
String? verificationCode;
|
||||||
|
String? deviceInfo;
|
||||||
|
|
||||||
|
LoginRequest(
|
||||||
|
{this.username,
|
||||||
|
this.password,
|
||||||
|
this.id,
|
||||||
|
this.uuid,
|
||||||
|
this.autoLogin,
|
||||||
|
this.type,
|
||||||
|
this.verificationCode,
|
||||||
|
this.deviceInfo});
|
||||||
|
|
||||||
|
LoginRequest.fromJson(Map<String, dynamic> json) {
|
||||||
|
username = json['username'];
|
||||||
|
password = json['password'];
|
||||||
|
id = json['id'];
|
||||||
|
uuid = json['uuid'];
|
||||||
|
autoLogin = json['autoLogin'];
|
||||||
|
type = json['type'];
|
||||||
|
verificationCode = json['verificationCode'];
|
||||||
|
deviceInfo = json['deviceInfo'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
|
data['username'] = username ?? '';
|
||||||
|
data['password'] = password ?? '';
|
||||||
|
data['id'] = id ?? '';
|
||||||
|
data['uuid'] = uuid ?? '';
|
||||||
|
data['autoLogin'] = autoLogin ?? '';
|
||||||
|
data['type'] = type ?? '';
|
||||||
|
data['verificationCode'] = verificationCode ?? '';
|
||||||
|
data['deviceInfo'] = deviceInfo ?? '';
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoginResponse {
|
||||||
|
String? access_token;
|
||||||
|
String? type;
|
||||||
|
UserPayload? user;
|
||||||
|
|
||||||
|
LoginResponse({this.access_token, this.type, this.user});
|
||||||
|
|
||||||
|
LoginResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
access_token = json['access_token'];
|
||||||
|
type = json['type'];
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,14 +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/peer_card.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
|
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/login.dart';
|
|
||||||
import '../../consts.dart';
|
import '../../consts.dart';
|
||||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../desktop/pages/desktop_home_page.dart';
|
import 'login.dart';
|
||||||
import '../../mobile/pages/settings_page.dart';
|
|
||||||
|
|
||||||
class AddressBook extends StatefulWidget {
|
class AddressBook extends StatefulWidget {
|
||||||
final EdgeInsets? menuPadding;
|
final EdgeInsets? menuPadding;
|
||||||
@ -41,21 +39,12 @@ class _AddressBookState extends State<AddressBook> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
handleLogin() {
|
|
||||||
// TODO refactor login dialog for desktop and mobile
|
|
||||||
if (isDesktop) {
|
|
||||||
loginDialog();
|
|
||||||
} else {
|
|
||||||
showLogin(gFFI.dialogManager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Widget> buildBody(BuildContext context) async {
|
Future<Widget> buildBody(BuildContext context) async {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
if (gFFI.userModel.userName.value.isEmpty) {
|
if (gFFI.userModel.userName.value.isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: handleLogin,
|
onTap: loginDialog,
|
||||||
child: Text(
|
child: Text(
|
||||||
translate("Login"),
|
translate("Login"),
|
||||||
style: const TextStyle(decoration: TextDecoration.underline),
|
style: const TextStyle(decoration: TextDecoration.underline),
|
||||||
|
676
flutter/lib/common/widgets/login.dart
Normal file
676
flutter/lib/common/widgets/login.dart
Normal file
@ -0,0 +1,676 @@
|
|||||||
|
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';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
import '../../common.dart';
|
||||||
|
|
||||||
|
class _IconOP extends StatelessWidget {
|
||||||
|
final String icon;
|
||||||
|
final double 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: margin,
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
'assets/$icon.svg',
|
||||||
|
width: iconWidth,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ButtonOP extends StatelessWidget {
|
||||||
|
final String op;
|
||||||
|
final RxString curOP;
|
||||||
|
final double iconWidth;
|
||||||
|
final Color primaryColor;
|
||||||
|
final double height;
|
||||||
|
final Function() onTap;
|
||||||
|
|
||||||
|
const ButtonOP({
|
||||||
|
Key? key,
|
||||||
|
required this.op,
|
||||||
|
required this.curOP,
|
||||||
|
required this.iconWidth,
|
||||||
|
required this.primaryColor,
|
||||||
|
required this.height,
|
||||||
|
required this.onTap,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(children: [
|
||||||
|
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')))),
|
||||||
|
],
|
||||||
|
))),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfigOP {
|
||||||
|
final String op;
|
||||||
|
final double iconWidth;
|
||||||
|
ConfigOP({required this.op, required this.iconWidth});
|
||||||
|
}
|
||||||
|
|
||||||
|
class WidgetOP extends StatefulWidget {
|
||||||
|
final ConfigOP config;
|
||||||
|
final RxString curOP;
|
||||||
|
final Function(String) cbLogin;
|
||||||
|
const WidgetOP({
|
||||||
|
Key? key,
|
||||||
|
required this.config,
|
||||||
|
required this.curOP,
|
||||||
|
required this.cbLogin,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() {
|
||||||
|
return _WidgetOPState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WidgetOPState extends State<WidgetOP> {
|
||||||
|
Timer? _updateTimer;
|
||||||
|
String _stateMsg = '';
|
||||||
|
String _failedMsg = '';
|
||||||
|
String _url = '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_updateTimer?.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
_beginQueryState() {
|
||||||
|
_updateTimer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||||
|
_updateState();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateState() {
|
||||||
|
bind.mainAccountAuthResult().then((result) {
|
||||||
|
if (result.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final resultMap = jsonDecode(result);
|
||||||
|
if (resultMap == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String stateMsg = resultMap['state_msg'];
|
||||||
|
String failedMsg = resultMap['failed_msg'];
|
||||||
|
final String? url = resultMap['url'];
|
||||||
|
final authBody = resultMap['auth_body'];
|
||||||
|
if (_stateMsg != stateMsg || _failedMsg != failedMsg) {
|
||||||
|
if (_url.isEmpty && url != null && url.isNotEmpty) {
|
||||||
|
launchUrl(Uri.parse(url));
|
||||||
|
_url = url;
|
||||||
|
}
|
||||||
|
if (authBody != null) {
|
||||||
|
_updateTimer?.cancel();
|
||||||
|
final String username = authBody['user']['name'];
|
||||||
|
widget.curOP.value = '';
|
||||||
|
widget.cbLogin(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_stateMsg = stateMsg;
|
||||||
|
_failedMsg = failedMsg;
|
||||||
|
if (failedMsg.isNotEmpty) {
|
||||||
|
widget.curOP.value = '';
|
||||||
|
_updateTimer?.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_resetState() {
|
||||||
|
_stateMsg = '';
|
||||||
|
_failedMsg = '';
|
||||||
|
_url = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
ButtonOP(
|
||||||
|
op: widget.config.op,
|
||||||
|
curOP: widget.curOP,
|
||||||
|
iconWidth: widget.config.iconWidth,
|
||||||
|
primaryColor: str2color(widget.config.op, 0x7f),
|
||||||
|
height: 36,
|
||||||
|
onTap: () async {
|
||||||
|
_resetState();
|
||||||
|
widget.curOP.value = widget.config.op;
|
||||||
|
await bind.mainAccountAuth(op: widget.config.op);
|
||||||
|
_beginQueryState();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Obx(() {
|
||||||
|
if (widget.curOP.isNotEmpty &&
|
||||||
|
widget.curOP.value != widget.config.op) {
|
||||||
|
_failedMsg = '';
|
||||||
|
}
|
||||||
|
return Offstage(
|
||||||
|
offstage:
|
||||||
|
_failedMsg.isEmpty && widget.curOP.value != widget.config.op,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
_stateMsg,
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
_failedMsg,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}),
|
||||||
|
Obx(
|
||||||
|
() => Offstage(
|
||||||
|
offstage: widget.curOP.value != widget.config.op,
|
||||||
|
child: const SizedBox(
|
||||||
|
height: 5.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() => Offstage(
|
||||||
|
offstage: widget.curOP.value != widget.config.op,
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(maxHeight: 20),
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
widget.curOP.value = '';
|
||||||
|
_updateTimer?.cancel();
|
||||||
|
_resetState();
|
||||||
|
bind.mainAccountAuthCancel();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
translate('Cancel'),
|
||||||
|
style: TextStyle(fontSize: 15),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoginWidgetOP extends StatelessWidget {
|
||||||
|
final List<ConfigOP> ops;
|
||||||
|
final RxString curOP;
|
||||||
|
final Function(String) cbLogin;
|
||||||
|
|
||||||
|
LoginWidgetOP({
|
||||||
|
Key? key,
|
||||||
|
required this.ops,
|
||||||
|
required this.curOP,
|
||||||
|
required this.cbLogin,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var children = ops
|
||||||
|
.map((op) => [
|
||||||
|
WidgetOP(
|
||||||
|
config: op,
|
||||||
|
curOP: curOP,
|
||||||
|
cbLogin: cbLogin,
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
indent: 5,
|
||||||
|
endIndent: 5,
|
||||||
|
)
|
||||||
|
])
|
||||||
|
.expand((i) => i)
|
||||||
|
.toList();
|
||||||
|
if (children.isNotEmpty) {
|
||||||
|
children.removeLast();
|
||||||
|
}
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Container(
|
||||||
|
width: 200,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: children,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoginWidgetUserPass extends StatelessWidget {
|
||||||
|
final TextEditingController username;
|
||||||
|
final TextEditingController pass;
|
||||||
|
final String? usernameMsg;
|
||||||
|
final String? passMsg;
|
||||||
|
final bool isInProgress;
|
||||||
|
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,
|
||||||
|
required this.passMsg,
|
||||||
|
required this.isInProgress,
|
||||||
|
required this.curOP,
|
||||||
|
required this.autoLogin,
|
||||||
|
required this.onLogin,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.all(0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 8.0),
|
||||||
|
DialogTextField(
|
||||||
|
title: '${translate("Username")}:',
|
||||||
|
controller: username,
|
||||||
|
focusNode: userFocusNode,
|
||||||
|
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),
|
||||||
|
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 obscureText;
|
||||||
|
final String? errorText;
|
||||||
|
final String? helperText;
|
||||||
|
final Widget? prefixIcon;
|
||||||
|
final TextEditingController controller;
|
||||||
|
final FocusNode? focusNode;
|
||||||
|
|
||||||
|
DialogTextField(
|
||||||
|
{Key? key,
|
||||||
|
this.focusNode,
|
||||||
|
this.obscureText = false,
|
||||||
|
this.errorText,
|
||||||
|
this.helperText,
|
||||||
|
this.prefixIcon,
|
||||||
|
required this.title,
|
||||||
|
required this.controller})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: title,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
prefixIcon: prefixIcon,
|
||||||
|
helperText: helperText,
|
||||||
|
helperMaxLines: 8,
|
||||||
|
errorText: errorText),
|
||||||
|
controller: controller,
|
||||||
|
focusNode: focusNode,
|
||||||
|
autofocus: true,
|
||||||
|
obscureText: obscureText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetric(vertical: 4.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// common login dialog for desktop
|
||||||
|
/// call this directly
|
||||||
|
Future<bool?> loginDialog() async {
|
||||||
|
var username = TextEditingController();
|
||||||
|
var password = TextEditingController();
|
||||||
|
final userFocusNode = FocusNode()..requestFocus();
|
||||||
|
Timer(Duration(milliseconds: 100), () => userFocusNode..requestFocus());
|
||||||
|
|
||||||
|
String? usernameMsg;
|
||||||
|
String? passwordMsg;
|
||||||
|
var isInProgress = false;
|
||||||
|
final autoLogin = true.obs;
|
||||||
|
final RxString curOP = ''.obs;
|
||||||
|
|
||||||
|
final res = await gFFI.dialogManager.show<bool>((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 {
|
||||||
|
// validate
|
||||||
|
if (username.text.isEmpty) {
|
||||||
|
setState(() => usernameMsg = translate('Username missed'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (password.text.isEmpty) {
|
||||||
|
setState(() => passwordMsg = translate('Password missed'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
curOP.value = 'rustdesk';
|
||||||
|
setState(() => isInProgress = true);
|
||||||
|
try {
|
||||||
|
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) {
|
||||||
|
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());
|
||||||
|
} catch (err) {
|
||||||
|
passwordMsg = "Unknown Error: $err";
|
||||||
|
debugPrintStack(label: err.toString());
|
||||||
|
}
|
||||||
|
curOP.value = '';
|
||||||
|
setState(() => isInProgress = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CustomAlertDialog(
|
||||||
|
title: Text(translate('Login')),
|
||||||
|
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,
|
||||||
|
userFocusNode: userFocusNode,
|
||||||
|
),
|
||||||
|
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'), onDialogCancel)],
|
||||||
|
onCancel: onDialogCancel,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res != null) {
|
||||||
|
// update ab and group status
|
||||||
|
await gFFI.abModel.pullAb();
|
||||||
|
await gFFI.groupModel.pull();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool?> verificationCodeDialog(UserPayload? user) async {
|
||||||
|
var autoLogin = true;
|
||||||
|
var isInProgress = false;
|
||||||
|
String? errorText;
|
||||||
|
|
||||||
|
final code = TextEditingController();
|
||||||
|
final focusNode = FocusNode()..requestFocus();
|
||||||
|
Timer(Duration(milliseconds: 100), () => focusNode..requestFocus());
|
||||||
|
|
||||||
|
final res = await gFFI.dialogManager.show<bool>((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,
|
||||||
|
errorText: errorText,
|
||||||
|
focusNode: focusNode,
|
||||||
|
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;
|
||||||
|
}
|
@ -8,7 +8,6 @@ import 'package:flutter_hbb/common.dart';
|
|||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_home_page.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/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/platform_model.dart';
|
||||||
import 'package:flutter_hbb/models/server_model.dart';
|
import 'package:flutter_hbb/models/server_model.dart';
|
||||||
import 'package:get/get.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 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
|
||||||
|
|
||||||
import '../../common/widgets/dialog.dart';
|
import '../../common/widgets/dialog.dart';
|
||||||
|
import '../../common/widgets/login.dart';
|
||||||
|
|
||||||
const double _kTabWidth = 235;
|
const double _kTabWidth = 235;
|
||||||
const double _kTabHeight = 42;
|
const double _kTabHeight = 42;
|
||||||
|
@ -1,521 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
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})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 4.0),
|
|
||||||
child: SvgPicture.asset(
|
|
||||||
'assets/$icon.svg',
|
|
||||||
width: iconWidth,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ButtonOP extends StatelessWidget {
|
|
||||||
final String op;
|
|
||||||
final RxString curOP;
|
|
||||||
final double iconWidth;
|
|
||||||
final Color primaryColor;
|
|
||||||
final double height;
|
|
||||||
final Function() onTap;
|
|
||||||
|
|
||||||
const ButtonOP({
|
|
||||||
Key? key,
|
|
||||||
required this.op,
|
|
||||||
required this.curOP,
|
|
||||||
required this.iconWidth,
|
|
||||||
required this.primaryColor,
|
|
||||||
required this.height,
|
|
||||||
required this.onTap,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@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,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConfigOP {
|
|
||||||
final String op;
|
|
||||||
final double iconWidth;
|
|
||||||
ConfigOP({required this.op, required this.iconWidth});
|
|
||||||
}
|
|
||||||
|
|
||||||
class WidgetOP extends StatefulWidget {
|
|
||||||
final ConfigOP config;
|
|
||||||
final RxString curOP;
|
|
||||||
final Function(String) cbLogin;
|
|
||||||
const WidgetOP({
|
|
||||||
Key? key,
|
|
||||||
required this.config,
|
|
||||||
required this.curOP,
|
|
||||||
required this.cbLogin,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() {
|
|
||||||
return _WidgetOPState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _WidgetOPState extends State<WidgetOP> {
|
|
||||||
Timer? _updateTimer;
|
|
||||||
String _stateMsg = '';
|
|
||||||
String _failedMsg = '';
|
|
||||||
String _url = '';
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
_updateTimer?.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
_beginQueryState() {
|
|
||||||
_updateTimer = Timer.periodic(Duration(seconds: 1), (timer) {
|
|
||||||
_updateState();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateState() {
|
|
||||||
bind.mainAccountAuthResult().then((result) {
|
|
||||||
if (result.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final resultMap = jsonDecode(result);
|
|
||||||
if (resultMap == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final String stateMsg = resultMap['state_msg'];
|
|
||||||
String failedMsg = resultMap['failed_msg'];
|
|
||||||
final String? url = resultMap['url'];
|
|
||||||
final authBody = resultMap['auth_body'];
|
|
||||||
if (_stateMsg != stateMsg || _failedMsg != failedMsg) {
|
|
||||||
if (_url.isEmpty && url != null && url.isNotEmpty) {
|
|
||||||
launchUrl(Uri.parse(url));
|
|
||||||
_url = url;
|
|
||||||
}
|
|
||||||
if (authBody != null) {
|
|
||||||
_updateTimer?.cancel();
|
|
||||||
final String username = authBody['user']['name'];
|
|
||||||
widget.curOP.value = '';
|
|
||||||
widget.cbLogin(username);
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_stateMsg = stateMsg;
|
|
||||||
_failedMsg = failedMsg;
|
|
||||||
if (failedMsg.isNotEmpty) {
|
|
||||||
widget.curOP.value = '';
|
|
||||||
_updateTimer?.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_resetState() {
|
|
||||||
_stateMsg = '';
|
|
||||||
_failedMsg = '';
|
|
||||||
_url = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
ButtonOP(
|
|
||||||
op: widget.config.op,
|
|
||||||
curOP: widget.curOP,
|
|
||||||
iconWidth: widget.config.iconWidth,
|
|
||||||
primaryColor: str2color(widget.config.op, 0x7f),
|
|
||||||
height: 36,
|
|
||||||
onTap: () async {
|
|
||||||
_resetState();
|
|
||||||
widget.curOP.value = widget.config.op;
|
|
||||||
await bind.mainAccountAuth(op: widget.config.op);
|
|
||||||
_beginQueryState();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Obx(() {
|
|
||||||
if (widget.curOP.isNotEmpty &&
|
|
||||||
widget.curOP.value != widget.config.op) {
|
|
||||||
_failedMsg = '';
|
|
||||||
}
|
|
||||||
return Offstage(
|
|
||||||
offstage:
|
|
||||||
_failedMsg.isEmpty && widget.curOP.value != widget.config.op,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
_stateMsg,
|
|
||||||
style: TextStyle(fontSize: 12),
|
|
||||||
),
|
|
||||||
SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
_failedMsg,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
));
|
|
||||||
}),
|
|
||||||
Obx(
|
|
||||||
() => Offstage(
|
|
||||||
offstage: widget.curOP.value != widget.config.op,
|
|
||||||
child: const SizedBox(
|
|
||||||
height: 5.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Obx(
|
|
||||||
() => Offstage(
|
|
||||||
offstage: widget.curOP.value != widget.config.op,
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(maxHeight: 20),
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
widget.curOP.value = '';
|
|
||||||
_updateTimer?.cancel();
|
|
||||||
_resetState();
|
|
||||||
bind.mainAccountAuthCancel();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
translate('Cancel'),
|
|
||||||
style: TextStyle(fontSize: 15),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoginWidgetOP extends StatelessWidget {
|
|
||||||
final List<ConfigOP> ops;
|
|
||||||
final RxString curOP;
|
|
||||||
final Function(String) cbLogin;
|
|
||||||
|
|
||||||
LoginWidgetOP({
|
|
||||||
Key? key,
|
|
||||||
required this.ops,
|
|
||||||
required this.curOP,
|
|
||||||
required this.cbLogin,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var children = ops
|
|
||||||
.map((op) => [
|
|
||||||
WidgetOP(
|
|
||||||
config: op,
|
|
||||||
curOP: curOP,
|
|
||||||
cbLogin: cbLogin,
|
|
||||||
),
|
|
||||||
const Divider(
|
|
||||||
indent: 5,
|
|
||||||
endIndent: 5,
|
|
||||||
)
|
|
||||||
])
|
|
||||||
.expand((i) => i)
|
|
||||||
.toList();
|
|
||||||
if (children.isNotEmpty) {
|
|
||||||
children.removeLast();
|
|
||||||
}
|
|
||||||
return SingleChildScrollView(
|
|
||||||
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 bool isInProgress;
|
|
||||||
final RxString curOP;
|
|
||||||
final Function(String, String) onLogin;
|
|
||||||
const LoginWidgetUserPass({
|
|
||||||
Key? key,
|
|
||||||
required this.username,
|
|
||||||
required this.pass,
|
|
||||||
required this.usernameMsg,
|
|
||||||
required this.passMsg,
|
|
||||||
required this.isInProgress,
|
|
||||||
required this.curOP,
|
|
||||||
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,
|
|
||||||
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(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
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,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// common login dialog for desktop
|
|
||||||
/// call this directly
|
|
||||||
Future<bool> loginDialog() async {
|
|
||||||
String username = '';
|
|
||||||
var usernameMsg = '';
|
|
||||||
String pass = '';
|
|
||||||
var passMsg = '';
|
|
||||||
var isInProgress = false;
|
|
||||||
var completer = Completer<bool>();
|
|
||||||
final RxString curOP = ''.obs;
|
|
||||||
|
|
||||||
gFFI.dialogManager.show((setState, close) {
|
|
||||||
cancel() {
|
|
||||||
isInProgress = false;
|
|
||||||
completer.complete(false);
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
onLogin(String username0, String pass0) async {
|
|
||||||
setState(() {
|
|
||||||
usernameMsg = '';
|
|
||||||
passMsg = '';
|
|
||||||
isInProgress = true;
|
|
||||||
});
|
|
||||||
cancel() {
|
|
||||||
curOP.value = '';
|
|
||||||
if (isInProgress) {
|
|
||||||
setState(() {
|
|
||||||
isInProgress = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
curOP.value = 'rustdesk';
|
|
||||||
username = username0;
|
|
||||||
pass = pass0;
|
|
||||||
if (username.isEmpty) {
|
|
||||||
usernameMsg = translate('Username missed');
|
|
||||||
cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (pass.isEmpty) {
|
|
||||||
passMsg = translate('Password missed');
|
|
||||||
cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
final resp = await gFFI.userModel.login(username, pass);
|
|
||||||
if (resp.containsKey('error')) {
|
|
||||||
passMsg = resp['error'];
|
|
||||||
cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// {access_token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJndWlkIjoiMDFkZjQ2ZjgtZjg3OS00MDE0LTk5Y2QtMGMwYzM2MmViZGJlIiwiZXhwIjoxNjYxNDg2NzYwfQ.GZpe1oI8TfM5yTYNrpcwbI599P4Z_-b2GmnwNl2Lr-w,
|
|
||||||
// token_type: Bearer, user: {id: , name: admin, email: null, note: null, status: null, grp: null, is_admin: true}}
|
|
||||||
debugPrint('$resp');
|
|
||||||
completer.complete(true);
|
|
||||||
} catch (err) {
|
|
||||||
debugPrintStack(label: err.toString());
|
|
||||||
cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [msgBoxButton(translate('Close'), cancel)],
|
|
||||||
onCancel: cancel,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return completer.future;
|
|
||||||
}
|
|
@ -7,9 +7,8 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../common/widgets/address_book.dart';
|
import '../../common/widgets/login.dart';
|
||||||
import '../../common/widgets/peer_tab_page.dart';
|
import '../../common/widgets/peer_tab_page.dart';
|
||||||
import '../../common/widgets/peers_view.dart';
|
|
||||||
import '../../consts.dart';
|
import '../../consts.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
@ -258,7 +257,7 @@ class _WebMenuState extends State<WebMenu> {
|
|||||||
}
|
}
|
||||||
if (value == 'login') {
|
if (value == 'login') {
|
||||||
if (gFFI.userModel.userName.value.isEmpty) {
|
if (gFFI.userModel.userName.value.isEmpty) {
|
||||||
showLogin(gFFI.dialogManager);
|
loginDialog();
|
||||||
} else {
|
} else {
|
||||||
gFFI.userModel.logOut();
|
gFFI.userModel.logOut();
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import 'package:url_launcher/url_launcher.dart';
|
|||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../common/widgets/dialog.dart';
|
import '../../common/widgets/dialog.dart';
|
||||||
|
import '../../common/widgets/login.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../widgets/dialog.dart';
|
import '../widgets/dialog.dart';
|
||||||
@ -300,7 +301,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
leading: Icon(Icons.person),
|
leading: Icon(Icons.person),
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
if (gFFI.userModel.userName.value.isEmpty) {
|
if (gFFI.userModel.userName.value.isEmpty) {
|
||||||
showLogin(gFFI.dialogManager);
|
loginDialog();
|
||||||
} else {
|
} else {
|
||||||
gFFI.userModel.logOut();
|
gFFI.userModel.logOut();
|
||||||
}
|
}
|
||||||
@ -397,7 +398,7 @@ void showServerSettings(OverlayDialogManager dialogManager) async {
|
|||||||
void showLanguageSettings(OverlayDialogManager dialogManager) async {
|
void showLanguageSettings(OverlayDialogManager dialogManager) async {
|
||||||
try {
|
try {
|
||||||
final langs = json.decode(await bind.mainGetLangs()) as List<dynamic>;
|
final langs = json.decode(await bind.mainGetLangs()) as List<dynamic>;
|
||||||
var lang = await bind.mainGetLocalOption(key: "lang");
|
var lang = bind.mainGetLocalOption(key: "lang");
|
||||||
dialogManager.show((setState, close) {
|
dialogManager.show((setState, close) {
|
||||||
setLang(v) {
|
setLang(v) {
|
||||||
if (lang != v) {
|
if (lang != v) {
|
||||||
@ -482,77 +483,6 @@ void showAbout(OverlayDialogManager dialogManager) {
|
|||||||
}, clickMaskDismiss: true, backDismiss: true);
|
}, clickMaskDismiss: true, backDismiss: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showLogin(OverlayDialogManager dialogManager) {
|
|
||||||
final passwordController = TextEditingController();
|
|
||||||
final nameController = TextEditingController();
|
|
||||||
var loading = false;
|
|
||||||
var error = '';
|
|
||||||
dialogManager.show((setState, close) {
|
|
||||||
return CustomAlertDialog(
|
|
||||||
title: Text(translate('Login')),
|
|
||||||
content: Column(mainAxisSize: MainAxisSize.min, children: [
|
|
||||||
TextField(
|
|
||||||
autofocus: true,
|
|
||||||
autocorrect: false,
|
|
||||||
enableSuggestions: false,
|
|
||||||
keyboardType: TextInputType.visiblePassword,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: translate('Username'),
|
|
||||||
),
|
|
||||||
controller: nameController,
|
|
||||||
),
|
|
||||||
PasswordWidget(controller: passwordController, autoFocus: false),
|
|
||||||
]),
|
|
||||||
actions: (loading
|
|
||||||
? <Widget>[CircularProgressIndicator()]
|
|
||||||
: (error != ""
|
|
||||||
? <Widget>[
|
|
||||||
Text(translate(error),
|
|
||||||
style: TextStyle(color: Colors.red))
|
|
||||||
]
|
|
||||||
: <Widget>[])) +
|
|
||||||
<Widget>[
|
|
||||||
TextButton(
|
|
||||||
style: flatButtonStyle,
|
|
||||||
onPressed: loading
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
close();
|
|
||||||
setState(() {
|
|
||||||
loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Text(translate('Cancel')),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
style: flatButtonStyle,
|
|
||||||
onPressed: loading
|
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
final name = nameController.text.trim();
|
|
||||||
final pass = passwordController.text.trim();
|
|
||||||
if (name != "" && pass != "") {
|
|
||||||
setState(() {
|
|
||||||
loading = true;
|
|
||||||
});
|
|
||||||
final resp = await gFFI.userModel.login(name, pass);
|
|
||||||
setState(() {
|
|
||||||
loading = false;
|
|
||||||
});
|
|
||||||
if (resp.containsKey('error')) {
|
|
||||||
error = resp['error'];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close();
|
|
||||||
},
|
|
||||||
child: Text(translate('OK')),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScanButton extends StatelessWidget {
|
class ScanButton extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -27,8 +27,7 @@ class AbModel {
|
|||||||
abError.value = "";
|
abError.value = "";
|
||||||
final api = "${await bind.mainGetApiServer()}/api/ab/get";
|
final api = "${await bind.mainGetApiServer()}/api/ab/get";
|
||||||
try {
|
try {
|
||||||
final resp =
|
final resp = await http.post(Uri.parse(api), headers: getHttpHeaders());
|
||||||
await http.post(Uri.parse(api), headers: await getHttpHeaders());
|
|
||||||
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
||||||
Map<String, dynamic> json = jsonDecode(resp.body);
|
Map<String, dynamic> json = jsonDecode(resp.body);
|
||||||
if (json.containsKey('error')) {
|
if (json.containsKey('error')) {
|
||||||
@ -102,7 +101,7 @@ class AbModel {
|
|||||||
Future<void> pushAb() async {
|
Future<void> pushAb() async {
|
||||||
abLoading.value = true;
|
abLoading.value = true;
|
||||||
final api = "${await bind.mainGetApiServer()}/api/ab";
|
final api = "${await bind.mainGetApiServer()}/api/ab";
|
||||||
var authHeaders = await getHttpHeaders();
|
var authHeaders = getHttpHeaders();
|
||||||
authHeaders['Content-Type'] = "application/json";
|
authHeaders['Content-Type'] = "application/json";
|
||||||
final peersJsonData = peers.map((e) => e.toJson()).toList();
|
final peersJsonData = peers.map((e) => e.toJson()).toList();
|
||||||
final body = jsonEncode({
|
final body = jsonEncode({
|
||||||
|
@ -59,7 +59,7 @@ class GroupModel {
|
|||||||
if (gFFI.userModel.isAdmin.isFalse)
|
if (gFFI.userModel.isAdmin.isFalse)
|
||||||
'grp': gFFI.userModel.groupName.value,
|
'grp': gFFI.userModel.groupName.value,
|
||||||
});
|
});
|
||||||
final resp = await http.get(uri, headers: await getHttpHeaders());
|
final resp = await http.get(uri, headers: getHttpHeaders());
|
||||||
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
||||||
Map<String, dynamic> json = jsonDecode(resp.body);
|
Map<String, dynamic> json = jsonDecode(resp.body);
|
||||||
if (json.containsKey('error')) {
|
if (json.containsKey('error')) {
|
||||||
@ -110,7 +110,7 @@ class GroupModel {
|
|||||||
'grp': gFFI.userModel.groupName.value,
|
'grp': gFFI.userModel.groupName.value,
|
||||||
'target_user': username
|
'target_user': username
|
||||||
});
|
});
|
||||||
final resp = await http.get(uri, headers: await getHttpHeaders());
|
final resp = await http.get(uri, headers: getHttpHeaders());
|
||||||
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
||||||
Map<String, dynamic> json = jsonDecode(resp.body);
|
Map<String, dynamic> json = jsonDecode(resp.body);
|
||||||
if (json.containsKey('error')) {
|
if (json.containsKey('error')) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter_hbb/common/hbbs/hbbs.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/peer_tab_page.dart';
|
import 'package:flutter_hbb/common/widgets/peer_tab_page.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
@ -45,7 +46,9 @@ class UserModel {
|
|||||||
if (error != null) {
|
if (error != null) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
await _parseUserInfo(data);
|
|
||||||
|
final user = UserPayload.fromJson(data);
|
||||||
|
await _parseAndUpdateUser(user);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Failed to refreshCurrentUser: $e');
|
print('Failed to refreshCurrentUser: $e');
|
||||||
} finally {
|
} finally {
|
||||||
@ -55,7 +58,6 @@ class UserModel {
|
|||||||
|
|
||||||
Future<void> reset() async {
|
Future<void> reset() async {
|
||||||
await bind.mainSetLocalOption(key: 'access_token', value: '');
|
await bind.mainSetLocalOption(key: 'access_token', value: '');
|
||||||
await bind.mainSetLocalOption(key: 'user_info', value: '');
|
|
||||||
await gFFI.abModel.reset();
|
await gFFI.abModel.reset();
|
||||||
await gFFI.groupModel.reset();
|
await gFFI.groupModel.reset();
|
||||||
userName.value = '';
|
userName.value = '';
|
||||||
@ -63,11 +65,10 @@ class UserModel {
|
|||||||
statePeerTab.check();
|
statePeerTab.check();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _parseUserInfo(dynamic userinfo) async {
|
Future<void> _parseAndUpdateUser(UserPayload user) async {
|
||||||
bind.mainSetLocalOption(key: 'user_info', value: jsonEncode(userinfo));
|
userName.value = user.name;
|
||||||
userName.value = userinfo['name'] ?? '';
|
groupName.value = user.grp;
|
||||||
groupName.value = userinfo['grp'] ?? '';
|
isAdmin.value = user.isAdmin;
|
||||||
isAdmin.value = userinfo['is_admin'] == true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateOtherModels() async {
|
Future<void> _updateOtherModels() async {
|
||||||
@ -85,7 +86,7 @@ class UserModel {
|
|||||||
'id': await bind.mainGetMyId(),
|
'id': await bind.mainGetMyId(),
|
||||||
'uuid': await bind.mainGetUuid(),
|
'uuid': await bind.mainGetUuid(),
|
||||||
},
|
},
|
||||||
headers: await getHttpHeaders())
|
headers: getHttpHeaders())
|
||||||
.timeout(Duration(seconds: 2));
|
.timeout(Duration(seconds: 2));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("request /api/logout failed: err=$e");
|
print("request /api/logout failed: err=$e");
|
||||||
@ -95,26 +96,37 @@ class UserModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> login(String userName, String pass) async {
|
/// throw [RequestException]
|
||||||
|
Future<LoginResponse> login(LoginRequest loginRequest) async {
|
||||||
final url = await bind.mainGetApiServer();
|
final url = await bind.mainGetApiServer();
|
||||||
|
final resp = await http.post(Uri.parse('$url/api/login'),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode(loginRequest.toJson()));
|
||||||
|
|
||||||
|
final Map<String, dynamic> body;
|
||||||
try {
|
try {
|
||||||
final resp = await http.post(Uri.parse('$url/api/login'),
|
body = jsonDecode(resp.body);
|
||||||
headers: {'Content-Type': 'application/json'},
|
} catch (e) {
|
||||||
body: jsonEncode({
|
print("jsonDecode resp body failed: ${e.toString()}");
|
||||||
'username': userName,
|
rethrow;
|
||||||
'password': pass,
|
|
||||||
'id': await bind.mainGetMyId(),
|
|
||||||
'uuid': await bind.mainGetUuid()
|
|
||||||
}));
|
|
||||||
final body = jsonDecode(resp.body);
|
|
||||||
bind.mainSetLocalOption(
|
|
||||||
key: 'access_token', value: body['access_token'] ?? '');
|
|
||||||
await _parseUserInfo(body['user']);
|
|
||||||
return body;
|
|
||||||
} catch (err) {
|
|
||||||
return {'error': '$err'};
|
|
||||||
} finally {
|
|
||||||
await _updateOtherModels();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (resp.statusCode != 200) {
|
||||||
|
throw RequestException(resp.statusCode, body['error'] ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
final LoginResponse loginResponse;
|
||||||
|
try {
|
||||||
|
loginResponse = LoginResponse.fromJson(body);
|
||||||
|
} catch (e) {
|
||||||
|
print("jsonDecode LoginResponse failed: ${e.toString()}");
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loginResponse.user != null) {
|
||||||
|
await _parseAndUpdateUser(loginResponse.user!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return loginResponse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Connecta sempre a través de relay"),
|
("Always connect via relay", "Connecta sempre a través de relay"),
|
||||||
("whitelist_tip", ""),
|
("whitelist_tip", ""),
|
||||||
("Login", "Inicia sessió"),
|
("Login", "Inicia sessió"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Sortir"),
|
("Logout", "Sortir"),
|
||||||
("Tags", ""),
|
("Tags", ""),
|
||||||
("Search ID", "Cerca ID"),
|
("Search ID", "Cerca ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "强制走中继连接"),
|
("Always connect via relay", "强制走中继连接"),
|
||||||
("whitelist_tip", "只有白名单里的ip才能访问我"),
|
("whitelist_tip", "只有白名单里的ip才能访问我"),
|
||||||
("Login", "登录"),
|
("Login", "登录"),
|
||||||
|
("Verify", "验证"),
|
||||||
|
("Remember me", "记住我"),
|
||||||
|
("Trust this device", "信任此设备"),
|
||||||
|
("Verification code", "验证码"),
|
||||||
|
("verification_tip", "检测到新设备登录,已向注册邮箱发送了登录验证码,输入验证码继续登录"),
|
||||||
("Logout", "登出"),
|
("Logout", "登出"),
|
||||||
("Tags", "标签"),
|
("Tags", "标签"),
|
||||||
("Search ID", "查找ID"),
|
("Search ID", "查找ID"),
|
||||||
@ -221,7 +226,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Network error", "网络错误"),
|
("Network error", "网络错误"),
|
||||||
("Username missed", "用户名没有填写"),
|
("Username missed", "用户名没有填写"),
|
||||||
("Password missed", "密码没有填写"),
|
("Password missed", "密码没有填写"),
|
||||||
("Wrong credentials", "用户名或者密码错误"),
|
("Wrong credentials", "提供的登入信息错误"),
|
||||||
("Edit Tag", "修改标签"),
|
("Edit Tag", "修改标签"),
|
||||||
("Unremember Password", "忘掉密码"),
|
("Unremember Password", "忘掉密码"),
|
||||||
("Favorites", "收藏"),
|
("Favorites", "收藏"),
|
||||||
@ -273,7 +278,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Do you accept?", "是否接受?"),
|
("Do you accept?", "是否接受?"),
|
||||||
("Open System Setting", "打开系统设置"),
|
("Open System Setting", "打开系统设置"),
|
||||||
("How to get Android input permission?", "如何获取安卓的输入权限?"),
|
("How to get Android input permission?", "如何获取安卓的输入权限?"),
|
||||||
("android_input_permission_tip1", "為了讓遠程設備通過鼠標或者觸屏控制您的安卓設備,你需要允許 RustDesk 使用\"無障礙\"服務。"),
|
("android_input_permission_tip1", "为了让远程设备通过鼠标或触屏控制您的安卓设备,你需要允許RustDesk使用\"无障碍\"服务。"),
|
||||||
("android_input_permission_tip2", "请在接下来的系统设置页面里,找到并进入 [已安装的服务] 页面,将 [RustDesk Input] 服务开启。"),
|
("android_input_permission_tip2", "请在接下来的系统设置页面里,找到并进入 [已安装的服务] 页面,将 [RustDesk Input] 服务开启。"),
|
||||||
("android_new_connection_tip", "收到新的连接控制请求,对方想要控制你当前的设备。"),
|
("android_new_connection_tip", "收到新的连接控制请求,对方想要控制你当前的设备。"),
|
||||||
("android_service_will_start_tip", "开启录屏权限将自动开启服务,允许其他设备向此设备请求建立连接。"),
|
("android_service_will_start_tip", "开启录屏权限将自动开启服务,允许其他设备向此设备请求建立连接。"),
|
||||||
|
@ -210,6 +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)"),
|
("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"),
|
("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"),
|
("Login", "Přihlásit se"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Odhlásit se"),
|
("Logout", "Odhlásit se"),
|
||||||
("Tags", "Štítky"),
|
("Tags", "Štítky"),
|
||||||
("Search ID", "Hledat identifikátor"),
|
("Search ID", "Hledat identifikátor"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Forbindelse via relæ-server"),
|
("Always connect via relay", "Forbindelse via relæ-server"),
|
||||||
("whitelist_tip", "Kun IP'er på udgivelseslisten kan få adgang til mig"),
|
("whitelist_tip", "Kun IP'er på udgivelseslisten kan få adgang til mig"),
|
||||||
("Login", "Login"),
|
("Login", "Login"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "logger af"),
|
("Logout", "logger af"),
|
||||||
("Tags", "Nøgleord"),
|
("Tags", "Nøgleord"),
|
||||||
("Search ID", "Søg ID"),
|
("Search ID", "Søg ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Immer über Relay-Server verbinden"),
|
("Always connect via relay", "Immer über Relay-Server verbinden"),
|
||||||
("whitelist_tip", "Nur IPs auf der Whitelist können zugreifen."),
|
("whitelist_tip", "Nur IPs auf der Whitelist können zugreifen."),
|
||||||
("Login", "Anmelden"),
|
("Login", "Anmelden"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Abmelden"),
|
("Logout", "Abmelden"),
|
||||||
("Tags", "Schlagworte"),
|
("Tags", "Schlagworte"),
|
||||||
("Search ID", "Suche ID"),
|
("Search ID", "Suche ID"),
|
||||||
|
@ -36,6 +36,7 @@ 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"),
|
("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."),
|
("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!"),
|
("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."),
|
||||||
("software_render_tip", "If you have an Nvidia graphics card and the remote window closes immediately after connecting, installing the nouveau driver and choosing to use software rendering may help. A software restart is required."),
|
("software_render_tip", "If you have an Nvidia graphics card and the remote window closes immediately after connecting, installing the nouveau driver and choosing to use software rendering may help. A software restart is required."),
|
||||||
("config_input", "In order to control remote desktop with keyboard, you need to grant RustDesk \"Input Monitoring\" permissions."),
|
("config_input", "In order to control remote desktop with keyboard, you need to grant RustDesk \"Input Monitoring\" permissions."),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Ĉiam konekti per relajso"),
|
("Always connect via relay", "Ĉiam konekti per relajso"),
|
||||||
("whitelist_tip", "Nur la IP en la blanka listo povas kontroli mian komputilon"),
|
("whitelist_tip", "Nur la IP en la blanka listo povas kontroli mian komputilon"),
|
||||||
("Login", "Konekti"),
|
("Login", "Konekti"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Malkonekti"),
|
("Logout", "Malkonekti"),
|
||||||
("Tags", "Etikedi"),
|
("Tags", "Etikedi"),
|
||||||
("Search ID", "Serĉi ID"),
|
("Search ID", "Serĉi ID"),
|
||||||
|
@ -210,6 +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"),
|
("Always connect via relay", "Conéctese siempre a través de relay"),
|
||||||
("whitelist_tip", "Solo las direcciones IP autorizadas pueden conectarse a este escritorio"),
|
("whitelist_tip", "Solo las direcciones IP autorizadas pueden conectarse a este escritorio"),
|
||||||
("Login", "Iniciar sesión"),
|
("Login", "Iniciar sesión"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Salir"),
|
("Logout", "Salir"),
|
||||||
("Tags", "Tags"),
|
("Tags", "Tags"),
|
||||||
("Search ID", "Buscar ID"),
|
("Search ID", "Buscar ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "برای اتصال استفاده شود Relay از"),
|
("Always connect via relay", "برای اتصال استفاده شود Relay از"),
|
||||||
("whitelist_tip", "های مجاز می توانند به این دسکتاپ متصل شوند IP فقط"),
|
("whitelist_tip", "های مجاز می توانند به این دسکتاپ متصل شوند IP فقط"),
|
||||||
("Login", "ورود"),
|
("Login", "ورود"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "خروج"),
|
("Logout", "خروج"),
|
||||||
("Tags", "برچسب ها"),
|
("Tags", "برچسب ها"),
|
||||||
("Search ID", "جستجوی شناسه"),
|
("Search ID", "جستجوی شناسه"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Forcer la connexion relais"),
|
("Always connect via relay", "Forcer la connexion relais"),
|
||||||
("whitelist_tip", "Seul l'IP dans la liste blanche peut accéder à mon appareil"),
|
("whitelist_tip", "Seul l'IP dans la liste blanche peut accéder à mon appareil"),
|
||||||
("Login", "Connexion"),
|
("Login", "Connexion"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Déconnexion"),
|
("Logout", "Déconnexion"),
|
||||||
("Tags", "Étiqueter"),
|
("Tags", "Étiqueter"),
|
||||||
("Search ID", "Rechercher un ID"),
|
("Search ID", "Rechercher un ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Σύνδεση πάντα μέσω αναμετάδοσης"),
|
("Always connect via relay", "Σύνδεση πάντα μέσω αναμετάδοσης"),
|
||||||
("whitelist_tip", "Μόνο οι IP της λίστας επιτρεπόμενων έχουν πρόσβαση"),
|
("whitelist_tip", "Μόνο οι IP της λίστας επιτρεπόμενων έχουν πρόσβαση"),
|
||||||
("Login", "Σύνδεση"),
|
("Login", "Σύνδεση"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Αποσύνδεση"),
|
("Logout", "Αποσύνδεση"),
|
||||||
("Tags", "Ετικέτες"),
|
("Tags", "Ετικέτες"),
|
||||||
("Search ID", "Αναζήτηση ID"),
|
("Search ID", "Αναζήτηση ID"),
|
||||||
|
@ -210,6 +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"),
|
("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"),
|
("whitelist_tip", "Csak az engedélyezési listán szereplő címek csatlakozhatnak"),
|
||||||
("Login", "Belépés"),
|
("Login", "Belépés"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Kilépés"),
|
("Logout", "Kilépés"),
|
||||||
("Tags", "Tagok"),
|
("Tags", "Tagok"),
|
||||||
("Search ID", "Azonosító keresése..."),
|
("Search ID", "Azonosító keresése..."),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Selalu terhubung melalui relai"),
|
("Always connect via relay", "Selalu terhubung melalui relai"),
|
||||||
("whitelist_tip", "Hanya whitelisted IP yang dapat mengakses saya"),
|
("whitelist_tip", "Hanya whitelisted IP yang dapat mengakses saya"),
|
||||||
("Login", "Masuk"),
|
("Login", "Masuk"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Keluar"),
|
("Logout", "Keluar"),
|
||||||
("Tags", "Tag"),
|
("Tags", "Tag"),
|
||||||
("Search ID", "Cari ID"),
|
("Search ID", "Cari ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Connetti sempre tramite relay"),
|
("Always connect via relay", "Connetti sempre tramite relay"),
|
||||||
("whitelist_tip", "Solo gli indirizzi IP autorizzati possono connettersi a questo desktop"),
|
("whitelist_tip", "Solo gli indirizzi IP autorizzati possono connettersi a questo desktop"),
|
||||||
("Login", "Accedi"),
|
("Login", "Accedi"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Esci"),
|
("Logout", "Esci"),
|
||||||
("Tags", "Tag"),
|
("Tags", "Tag"),
|
||||||
("Search ID", "Cerca ID"),
|
("Search ID", "Cerca ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "常に中継サーバー経由で接続"),
|
("Always connect via relay", "常に中継サーバー経由で接続"),
|
||||||
("whitelist_tip", "ホワイトリストに登録されたIPからのみ接続を許可します"),
|
("whitelist_tip", "ホワイトリストに登録されたIPからのみ接続を許可します"),
|
||||||
("Login", "ログイン"),
|
("Login", "ログイン"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "ログアウト"),
|
("Logout", "ログアウト"),
|
||||||
("Tags", "タグ"),
|
("Tags", "タグ"),
|
||||||
("Search ID", "IDを検索"),
|
("Search ID", "IDを検索"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "항상 relay를 통해 접속하기"),
|
("Always connect via relay", "항상 relay를 통해 접속하기"),
|
||||||
("whitelist_tip", "화이트리스트에 있는 IP만 현 데스크탑에 접속 가능합니다"),
|
("whitelist_tip", "화이트리스트에 있는 IP만 현 데스크탑에 접속 가능합니다"),
|
||||||
("Login", "로그인"),
|
("Login", "로그인"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "로그아웃"),
|
("Logout", "로그아웃"),
|
||||||
("Tags", "태그"),
|
("Tags", "태그"),
|
||||||
("Search ID", "ID 검색"),
|
("Search ID", "ID 검색"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Әрқашан да релай сербері арқылы қосылу"),
|
("Always connect via relay", "Әрқашан да релай сербері арқылы қосылу"),
|
||||||
("whitelist_tip", "Маған тек ақ-тізімделген IP қол жеткізе алады"),
|
("whitelist_tip", "Маған тек ақ-тізімделген IP қол жеткізе алады"),
|
||||||
("Login", "Кіру"),
|
("Login", "Кіру"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Шығу"),
|
("Logout", "Шығу"),
|
||||||
("Tags", "Тақтар"),
|
("Tags", "Тақтар"),
|
||||||
("Search ID", "ID Іздеу"),
|
("Search ID", "ID Іздеу"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Zawsze łącz pośrednio"),
|
("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"),
|
("whitelist_tip", "Zezwlaj na łączenie z tym komputerem tylko z adresów IP znajdujących się na białej liście"),
|
||||||
("Login", "Zaloguj"),
|
("Login", "Zaloguj"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Wyloguj"),
|
("Logout", "Wyloguj"),
|
||||||
("Tags", "Tagi"),
|
("Tags", "Tagi"),
|
||||||
("Search ID", "Szukaj ID"),
|
("Search ID", "Szukaj ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Sempre conectar via relay"),
|
("Always connect via relay", "Sempre conectar via relay"),
|
||||||
("whitelist_tip", "Somente IPs na whitelist podem me acessar"),
|
("whitelist_tip", "Somente IPs na whitelist podem me acessar"),
|
||||||
("Login", "Login"),
|
("Login", "Login"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Sair"),
|
("Logout", "Sair"),
|
||||||
("Tags", "Tags"),
|
("Tags", "Tags"),
|
||||||
("Search ID", "Procurar ID"),
|
("Search ID", "Procurar ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Sempre conectar via relay"),
|
("Always connect via relay", "Sempre conectar via relay"),
|
||||||
("whitelist_tip", "Somente IPs confiáveis podem me acessar"),
|
("whitelist_tip", "Somente IPs confiáveis podem me acessar"),
|
||||||
("Login", "Login"),
|
("Login", "Login"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Sair"),
|
("Logout", "Sair"),
|
||||||
("Tags", "Tags"),
|
("Tags", "Tags"),
|
||||||
("Search ID", "Pesquisar ID"),
|
("Search ID", "Pesquisar ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Всегда подключаться через ретрансляционный сервер"),
|
("Always connect via relay", "Всегда подключаться через ретрансляционный сервер"),
|
||||||
("whitelist_tip", "Только IP-адреса из белого списка могут получить доступ ко мне"),
|
("whitelist_tip", "Только IP-адреса из белого списка могут получить доступ ко мне"),
|
||||||
("Login", "Войти"),
|
("Login", "Войти"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Выйти"),
|
("Logout", "Выйти"),
|
||||||
("Tags", "Метки"),
|
("Tags", "Метки"),
|
||||||
("Search ID", "Поиск по ID"),
|
("Search ID", "Поиск по ID"),
|
||||||
|
@ -210,6 +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"),
|
("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"),
|
("whitelist_tip", "Len vymenované IP adresy majú oprávnenie sa pripojiť k vzdialenej správe"),
|
||||||
("Login", "Prihlásenie"),
|
("Login", "Prihlásenie"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Odhlásenie"),
|
("Logout", "Odhlásenie"),
|
||||||
("Tags", "Štítky"),
|
("Tags", "Štítky"),
|
||||||
("Search ID", "Hľadať ID"),
|
("Search ID", "Hľadať ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Vedno poveži preko posrednika"),
|
("Always connect via relay", "Vedno poveži preko posrednika"),
|
||||||
("whitelist_tip", "Dostop je možen samo iz dovoljenih IPjev"),
|
("whitelist_tip", "Dostop je možen samo iz dovoljenih IPjev"),
|
||||||
("Login", "Prijavi"),
|
("Login", "Prijavi"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Odjavi"),
|
("Logout", "Odjavi"),
|
||||||
("Tags", "Oznake"),
|
("Tags", "Oznake"),
|
||||||
("Search ID", "Išči ID"),
|
("Search ID", "Išči ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Gjithmonë lidheni me transmetues"),
|
("Always connect via relay", "Gjithmonë lidheni me transmetues"),
|
||||||
("whitelist_tip", "Vetëm IP e listës së bardhë mund të më aksesoj."),
|
("whitelist_tip", "Vetëm IP e listës së bardhë mund të më aksesoj."),
|
||||||
("Login", "Hyrje"),
|
("Login", "Hyrje"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Dalje"),
|
("Logout", "Dalje"),
|
||||||
("Tags", "Tage"),
|
("Tags", "Tage"),
|
||||||
("Search ID", "Kerko ID"),
|
("Search ID", "Kerko ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Uvek se spoj preko posrednika"),
|
("Always connect via relay", "Uvek se spoj preko posrednika"),
|
||||||
("whitelist_tip", "Samo dozvoljene IP mi mogu pristupiti"),
|
("whitelist_tip", "Samo dozvoljene IP mi mogu pristupiti"),
|
||||||
("Login", "Prijava"),
|
("Login", "Prijava"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Odjava"),
|
("Logout", "Odjava"),
|
||||||
("Tags", "Oznake"),
|
("Tags", "Oznake"),
|
||||||
("Search ID", "Traži ID"),
|
("Search ID", "Traži ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Anslut alltid via relay"),
|
("Always connect via relay", "Anslut alltid via relay"),
|
||||||
("whitelist_tip", "Bara vitlistade IPs kan koppla upp till mig"),
|
("whitelist_tip", "Bara vitlistade IPs kan koppla upp till mig"),
|
||||||
("Login", "Logga in"),
|
("Login", "Logga in"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Logga ut"),
|
("Logout", "Logga ut"),
|
||||||
("Tags", "Taggar"),
|
("Tags", "Taggar"),
|
||||||
("Search ID", "Sök ID"),
|
("Search ID", "Sök ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", ""),
|
("Always connect via relay", ""),
|
||||||
("whitelist_tip", ""),
|
("whitelist_tip", ""),
|
||||||
("Login", ""),
|
("Login", ""),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", ""),
|
("Logout", ""),
|
||||||
("Tags", ""),
|
("Tags", ""),
|
||||||
("Search ID", ""),
|
("Search ID", ""),
|
||||||
|
828
src/lang/th.rs
828
src/lang/th.rs
@ -1,413 +1,417 @@
|
|||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||||
[
|
[
|
||||||
("Status", "สถานะ"),
|
("Status", "สถานะ"),
|
||||||
("Your Desktop", "หน้าจอของคุณ"),
|
("Your Desktop", "หน้าจอของคุณ"),
|
||||||
("desk_tip", "คุณสามารถเข้าถึงเดสก์ท็อปของคุณได้ด้วย ID และรหัสผ่านต่อไปนี้"),
|
("desk_tip", "คุณสามารถเข้าถึงเดสก์ท็อปของคุณได้ด้วย ID และรหัสผ่านต่อไปนี้"),
|
||||||
("Password", "รหัสผ่าน"),
|
("Password", "รหัสผ่าน"),
|
||||||
("Ready", "พร้อม"),
|
("Ready", "พร้อม"),
|
||||||
("Established", "เชื่อมต่อแล้ว"),
|
("Established", "เชื่อมต่อแล้ว"),
|
||||||
("connecting_status", "กำลังเชื่อมต่อไปยังเครือข่าย RustDesk..."),
|
("connecting_status", "กำลังเชื่อมต่อไปยังเครือข่าย RustDesk..."),
|
||||||
("Enable Service", "เปิดใช้การงานเซอร์วิส"),
|
("Enable Service", "เปิดใช้การงานเซอร์วิส"),
|
||||||
("Start Service", "เริ่มต้นใช้งานเซอร์วิส"),
|
("Start Service", "เริ่มต้นใช้งานเซอร์วิส"),
|
||||||
("Service is running", "เซอร์วิสกำลังทำงาน"),
|
("Service is running", "เซอร์วิสกำลังทำงาน"),
|
||||||
("Service is not running", "เซอร์วิสไม่ทำงาน"),
|
("Service is not running", "เซอร์วิสไม่ทำงาน"),
|
||||||
("not_ready_status", "ไม่พร้อมใช้งาน กรุณาตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณ"),
|
("not_ready_status", "ไม่พร้อมใช้งาน กรุณาตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณ"),
|
||||||
("Control Remote Desktop", "การควบคุมเดสก์ท็อปปลายทาง"),
|
("Control Remote Desktop", "การควบคุมเดสก์ท็อปปลายทาง"),
|
||||||
("Transfer File", "การถ่ายโอนไฟล์"),
|
("Transfer File", "การถ่ายโอนไฟล์"),
|
||||||
("Connect", "เชื่อมต่อ"),
|
("Connect", "เชื่อมต่อ"),
|
||||||
("Recent Sessions", "เซสชันล่าสุด"),
|
("Recent Sessions", "เซสชันล่าสุด"),
|
||||||
("Address Book", "สมุดรายชื่อ"),
|
("Address Book", "สมุดรายชื่อ"),
|
||||||
("Confirmation", "การยืนยัน"),
|
("Confirmation", "การยืนยัน"),
|
||||||
("TCP Tunneling", "อุโมงค์การเชื่อมต่อ TCP"),
|
("TCP Tunneling", "อุโมงค์การเชื่อมต่อ TCP"),
|
||||||
("Remove", "ลบ"),
|
("Remove", "ลบ"),
|
||||||
("Refresh random password", "รีเฟรชรหัสผ่านใหม่แบบสุ่ม"),
|
("Refresh random password", "รีเฟรชรหัสผ่านใหม่แบบสุ่ม"),
|
||||||
("Set your own password", "ตั้งรหัสผ่านของคุณเอง"),
|
("Set your own password", "ตั้งรหัสผ่านของคุณเอง"),
|
||||||
("Enable Keyboard/Mouse", "เปิดการใช้งาน คีย์บอร์ด/เมาส์"),
|
("Enable Keyboard/Mouse", "เปิดการใช้งาน คีย์บอร์ด/เมาส์"),
|
||||||
("Enable Clipboard", "เปิดการใช้งาน คลิปบอร์ด"),
|
("Enable Clipboard", "เปิดการใช้งาน คลิปบอร์ด"),
|
||||||
("Enable File Transfer", "เปิดการใช้งาน การถ่ายโอนไฟล์"),
|
("Enable File Transfer", "เปิดการใช้งาน การถ่ายโอนไฟล์"),
|
||||||
("Enable TCP Tunneling", "เปิดการใช้งาน อุโมงค์การเชื่อมต่อ TCP"),
|
("Enable TCP Tunneling", "เปิดการใช้งาน อุโมงค์การเชื่อมต่อ TCP"),
|
||||||
("IP Whitelisting", "IP ไวท์ลิสต์"),
|
("IP Whitelisting", "IP ไวท์ลิสต์"),
|
||||||
("ID/Relay Server", "เซิร์ฟเวอร์ ID/Relay"),
|
("ID/Relay Server", "เซิร์ฟเวอร์ ID/Relay"),
|
||||||
("Import Server Config", "นำเข้าการตั้งค่าเซิร์ฟเวอร์"),
|
("Import Server Config", "นำเข้าการตั้งค่าเซิร์ฟเวอร์"),
|
||||||
("Export Server Config", "ส่งออกการตั้งค่าเซิร์ฟเวอร์"),
|
("Export Server Config", "ส่งออกการตั้งค่าเซิร์ฟเวอร์"),
|
||||||
("Import server configuration successfully", "นำเข้าการตั้งค่าเซิร์ฟเวอร์เสร็จสมบูรณ์"),
|
("Import server configuration successfully", "นำเข้าการตั้งค่าเซิร์ฟเวอร์เสร็จสมบูรณ์"),
|
||||||
("Export server configuration successfully", "ส่งออกการตั้งค่าเซิร์ฟเวอร์เสร็จสมบูรณ์"),
|
("Export server configuration successfully", "ส่งออกการตั้งค่าเซิร์ฟเวอร์เสร็จสมบูรณ์"),
|
||||||
("Invalid server configuration", "การตั้งค่าของเซิร์ฟเวอร์ไม่ถูกต้อง"),
|
("Invalid server configuration", "การตั้งค่าของเซิร์ฟเวอร์ไม่ถูกต้อง"),
|
||||||
("Clipboard is empty", "คลิปบอร์ดว่างเปล่า"),
|
("Clipboard is empty", "คลิปบอร์ดว่างเปล่า"),
|
||||||
("Stop service", "หยุดการใช้งานเซอร์วิส"),
|
("Stop service", "หยุดการใช้งานเซอร์วิส"),
|
||||||
("Change ID", "เปลี่ยน ID"),
|
("Change ID", "เปลี่ยน ID"),
|
||||||
("Website", "เว็บไซต์"),
|
("Website", "เว็บไซต์"),
|
||||||
("About", "เกี่ยวกับ"),
|
("About", "เกี่ยวกับ"),
|
||||||
("Slogan_tip", "ทำด้วยใจ ในโลกใบนี้ที่ยุ่งเหยิง!"),
|
("Slogan_tip", "ทำด้วยใจ ในโลกใบนี้ที่ยุ่งเหยิง!"),
|
||||||
("Privacy Statement", "คำแถลงเกี่ยวกับความเป็นส่วนตัว"),
|
("Privacy Statement", "คำแถลงเกี่ยวกับความเป็นส่วนตัว"),
|
||||||
("Mute", "ปิดเสียง"),
|
("Mute", "ปิดเสียง"),
|
||||||
("Audio Input", "ออดิโออินพุท"),
|
("Audio Input", "ออดิโออินพุท"),
|
||||||
("Enhancements", "การปรับปรุง"),
|
("Enhancements", "การปรับปรุง"),
|
||||||
("Hardware Codec", "ฮาร์ดแวร์ codec"),
|
("Hardware Codec", "ฮาร์ดแวร์ codec"),
|
||||||
("Adaptive Bitrate", "บิทเรทผันแปร"),
|
("Adaptive Bitrate", "บิทเรทผันแปร"),
|
||||||
("ID Server", "เซิร์ฟเวอร์ ID"),
|
("ID Server", "เซิร์ฟเวอร์ ID"),
|
||||||
("Relay Server", "เซิร์ฟเวอร์ Relay"),
|
("Relay Server", "เซิร์ฟเวอร์ Relay"),
|
||||||
("API Server", "เซิร์ฟเวอร์ API"),
|
("API Server", "เซิร์ฟเวอร์ API"),
|
||||||
("invalid_http", "ต้องขึ้นต้นด้วย http:// หรือ https:// เท่านั้น"),
|
("invalid_http", "ต้องขึ้นต้นด้วย http:// หรือ https:// เท่านั้น"),
|
||||||
("Invalid IP", "IP ไม่ถูกต้อง"),
|
("Invalid IP", "IP ไม่ถูกต้อง"),
|
||||||
("id_change_tip", "อนุญาตเฉพาะตัวอักษร a-z A-Z 0-9 และ _ (ขีดล่าง) เท่านั้น โดยตัวอักษรขึ้นต้นจะต้องเป็น a-z หรือไม่ก็ A-Z และมีความยาวระหว่าง 6 ถึง 16 ตัวอักษร"),
|
("id_change_tip", "อนุญาตเฉพาะตัวอักษร a-z A-Z 0-9 และ _ (ขีดล่าง) เท่านั้น โดยตัวอักษรขึ้นต้นจะต้องเป็น a-z หรือไม่ก็ A-Z และมีความยาวระหว่าง 6 ถึง 16 ตัวอักษร"),
|
||||||
("Invalid format", "รูปแบบไม่ถูกต้อง"),
|
("Invalid format", "รูปแบบไม่ถูกต้อง"),
|
||||||
("server_not_support", "ยังไม่รองรับโดยเซิร์ฟเวอร์"),
|
("server_not_support", "ยังไม่รองรับโดยเซิร์ฟเวอร์"),
|
||||||
("Not available", "ไม่พร้อมใช้งาน"),
|
("Not available", "ไม่พร้อมใช้งาน"),
|
||||||
("Too frequent", "ดำเนินการถี่เกินไป"),
|
("Too frequent", "ดำเนินการถี่เกินไป"),
|
||||||
("Cancel", "ยกเลิก"),
|
("Cancel", "ยกเลิก"),
|
||||||
("Skip", "ข้าม"),
|
("Skip", "ข้าม"),
|
||||||
("Close", "ปิด"),
|
("Close", "ปิด"),
|
||||||
("Retry", "ลองใหม่อีกครั้ง"),
|
("Retry", "ลองใหม่อีกครั้ง"),
|
||||||
("OK", "ตกลง"),
|
("OK", "ตกลง"),
|
||||||
("Password Required", "ต้องใช้รหัสผ่าน"),
|
("Password Required", "ต้องใช้รหัสผ่าน"),
|
||||||
("Please enter your password", "กรุณาใส่รหัสผ่านของคุณ"),
|
("Please enter your password", "กรุณาใส่รหัสผ่านของคุณ"),
|
||||||
("Remember password", "จดจำรหัสผ่าน"),
|
("Remember password", "จดจำรหัสผ่าน"),
|
||||||
("Wrong Password", "รหัสผ่านไม่ถูกต้อง"),
|
("Wrong Password", "รหัสผ่านไม่ถูกต้อง"),
|
||||||
("Do you want to enter again?", "ต้องการใส่ข้อมูลอีกครั้งหรือไม่?"),
|
("Do you want to enter again?", "ต้องการใส่ข้อมูลอีกครั้งหรือไม่?"),
|
||||||
("Connection Error", "การเชื่อมต่อผิดพลาด"),
|
("Connection Error", "การเชื่อมต่อผิดพลาด"),
|
||||||
("Error", "ข้อผิดพลาด"),
|
("Error", "ข้อผิดพลาด"),
|
||||||
("Reset by the peer", "รีเซ็ตโดยอีกฝั่ง"),
|
("Reset by the peer", "รีเซ็ตโดยอีกฝั่ง"),
|
||||||
("Connecting...", "กำลังเชื่อมต่อ..."),
|
("Connecting...", "กำลังเชื่อมต่อ..."),
|
||||||
("Connection in progress. Please wait.", "กำลังดำเนินการเชื่อมต่อ กรุณารอซักครู่"),
|
("Connection in progress. Please wait.", "กำลังดำเนินการเชื่อมต่อ กรุณารอซักครู่"),
|
||||||
("Please try 1 minute later", "กรุณาลองใหม่อีกครั้งใน 1 นาที"),
|
("Please try 1 minute later", "กรุณาลองใหม่อีกครั้งใน 1 นาที"),
|
||||||
("Login Error", "การเข้าสู่ระบบผิดพลาด"),
|
("Login Error", "การเข้าสู่ระบบผิดพลาด"),
|
||||||
("Successful", "สำเร็จ"),
|
("Successful", "สำเร็จ"),
|
||||||
("Connected, waiting for image...", "เชื่อมต่อสำเร็จ กำลังรับข้อมูลภาพ..."),
|
("Connected, waiting for image...", "เชื่อมต่อสำเร็จ กำลังรับข้อมูลภาพ..."),
|
||||||
("Name", "ชื่อ"),
|
("Name", "ชื่อ"),
|
||||||
("Type", "ประเภท"),
|
("Type", "ประเภท"),
|
||||||
("Modified", "แก้ไขล่าสุด"),
|
("Modified", "แก้ไขล่าสุด"),
|
||||||
("Size", "ขนาด"),
|
("Size", "ขนาด"),
|
||||||
("Show Hidden Files", "แสดงไฟล์ที่ถูกซ่อน"),
|
("Show Hidden Files", "แสดงไฟล์ที่ถูกซ่อน"),
|
||||||
("Receive", "รับ"),
|
("Receive", "รับ"),
|
||||||
("Send", "ส่ง"),
|
("Send", "ส่ง"),
|
||||||
("Refresh File", "รีเฟรชไฟล์"),
|
("Refresh File", "รีเฟรชไฟล์"),
|
||||||
("Local", "ต้นทาง"),
|
("Local", "ต้นทาง"),
|
||||||
("Remote", "ปลายทาง"),
|
("Remote", "ปลายทาง"),
|
||||||
("Remote Computer", "คอมพิวเตอร์ปลายทาง"),
|
("Remote Computer", "คอมพิวเตอร์ปลายทาง"),
|
||||||
("Local Computer", "คอมพิวเตอร์ต้นทาง"),
|
("Local Computer", "คอมพิวเตอร์ต้นทาง"),
|
||||||
("Confirm Delete", "ยืนยันการลบ"),
|
("Confirm Delete", "ยืนยันการลบ"),
|
||||||
("Delete", "ลบ"),
|
("Delete", "ลบ"),
|
||||||
("Properties", "ข้อมูล"),
|
("Properties", "ข้อมูล"),
|
||||||
("Multi Select", "เลือกหลายรายการ"),
|
("Multi Select", "เลือกหลายรายการ"),
|
||||||
("Select All", "เลือกทั้งหมด"),
|
("Select All", "เลือกทั้งหมด"),
|
||||||
("Unselect All", "ยกเลิกการเลือกทั้งหมด"),
|
("Unselect All", "ยกเลิกการเลือกทั้งหมด"),
|
||||||
("Empty Directory", "ไดเรกทอรีว่างเปล่า"),
|
("Empty Directory", "ไดเรกทอรีว่างเปล่า"),
|
||||||
("Not an empty directory", "ไม่ใช่ไดเรกทอรีว่างเปล่า"),
|
("Not an empty directory", "ไม่ใช่ไดเรกทอรีว่างเปล่า"),
|
||||||
("Are you sure you want to delete this file?", "คุณแน่ใจหรือไม่ที่จะลบไฟล์นี้?"),
|
("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 this empty directory?", "คุณแน่ใจหรือไม่ที่จะลบไดเรอทอรีว่างเปล่านี้?"),
|
||||||
("Are you sure you want to delete the file of this directory?", "คุณแน่ใจหรือไม่ที่จะลบไฟล์ของไดเรกทอรีนี้?"),
|
("Are you sure you want to delete the file of this directory?", "คุณแน่ใจหรือไม่ที่จะลบไฟล์ของไดเรกทอรีนี้?"),
|
||||||
("Do this for all conflicts", "ดำเนินการแบบเดียวกันสำหรับรายการทั้งหมด"),
|
("Do this for all conflicts", "ดำเนินการแบบเดียวกันสำหรับรายการทั้งหมด"),
|
||||||
("This is irreversible!", "การดำเนินการนี้ไม่สามารถย้อนกลับได้!"),
|
("This is irreversible!", "การดำเนินการนี้ไม่สามารถย้อนกลับได้!"),
|
||||||
("Deleting", "กำลังลบ"),
|
("Deleting", "กำลังลบ"),
|
||||||
("files", "ไฟล์"),
|
("files", "ไฟล์"),
|
||||||
("Waiting", "กำลังรอ"),
|
("Waiting", "กำลังรอ"),
|
||||||
("Finished", "เสร็จแล้ว"),
|
("Finished", "เสร็จแล้ว"),
|
||||||
("Speed", "ความเร็ว"),
|
("Speed", "ความเร็ว"),
|
||||||
("Custom Image Quality", "คุณภาพของภาพแบบกำหนดเอง"),
|
("Custom Image Quality", "คุณภาพของภาพแบบกำหนดเอง"),
|
||||||
("Privacy mode", "โหมดความเป็นส่วนตัว"),
|
("Privacy mode", "โหมดความเป็นส่วนตัว"),
|
||||||
("Block user input", "บล็อคอินพุทจากผู้ใช้งาน"),
|
("Block user input", "บล็อคอินพุทจากผู้ใช้งาน"),
|
||||||
("Unblock user input", "ยกเลิกการบล็อคอินพุทจากผู้ใช้งาน"),
|
("Unblock user input", "ยกเลิกการบล็อคอินพุทจากผู้ใช้งาน"),
|
||||||
("Adjust Window", "ปรับขนาดหน้าต่าง"),
|
("Adjust Window", "ปรับขนาดหน้าต่าง"),
|
||||||
("Original", "ต้นฉบับ"),
|
("Original", "ต้นฉบับ"),
|
||||||
("Shrink", "ย่อ"),
|
("Shrink", "ย่อ"),
|
||||||
("Stretch", "ยืด"),
|
("Stretch", "ยืด"),
|
||||||
("Scrollbar", "แถบเลื่อน"),
|
("Scrollbar", "แถบเลื่อน"),
|
||||||
("ScrollAuto", "เลื่อนอัตโนมัติ"),
|
("ScrollAuto", "เลื่อนอัตโนมัติ"),
|
||||||
("Good image quality", "ภาพคุณภาพดี"),
|
("Good image quality", "ภาพคุณภาพดี"),
|
||||||
("Balanced", "สมดุล"),
|
("Balanced", "สมดุล"),
|
||||||
("Optimize reaction time", "เน้นการตอบสนอง"),
|
("Optimize reaction time", "เน้นการตอบสนอง"),
|
||||||
("Custom", "กำหนดเอง"),
|
("Custom", "กำหนดเอง"),
|
||||||
("Show remote cursor", "แสดงเคอร์เซอร์ปลายทาง"),
|
("Show remote cursor", "แสดงเคอร์เซอร์ปลายทาง"),
|
||||||
("Show quality monitor", "แสดงคุณภาพหน้าจอ"),
|
("Show quality monitor", "แสดงคุณภาพหน้าจอ"),
|
||||||
("Disable clipboard", "ปิดการใช้งานคลิปบอร์ด"),
|
("Disable clipboard", "ปิดการใช้งานคลิปบอร์ด"),
|
||||||
("Lock after session end", "ล็อคหลังจากจบเซสชัน"),
|
("Lock after session end", "ล็อคหลังจากจบเซสชัน"),
|
||||||
("Insert", "แทรก"),
|
("Insert", "แทรก"),
|
||||||
("Insert Lock", "แทรกล็อค"),
|
("Insert Lock", "แทรกล็อค"),
|
||||||
("Refresh", "รีเฟรช"),
|
("Refresh", "รีเฟรช"),
|
||||||
("ID does not exist", "ไม่พอข้อมูล ID"),
|
("ID does not exist", "ไม่พอข้อมูล ID"),
|
||||||
("Failed to connect to rendezvous server", "การเชื่อมต่อไปยังเซิร์ฟเวอร์นัดพบล้มเหลว"),
|
("Failed to connect to rendezvous server", "การเชื่อมต่อไปยังเซิร์ฟเวอร์นัดพบล้มเหลว"),
|
||||||
("Please try later", "กรุณาลองใหม่ในภายหลัง"),
|
("Please try later", "กรุณาลองใหม่ในภายหลัง"),
|
||||||
("Remote desktop is offline", "เดสก์ท็อปปลายทางออฟไลน์"),
|
("Remote desktop is offline", "เดสก์ท็อปปลายทางออฟไลน์"),
|
||||||
("Key mismatch", "คีย์ไม่ถูกต้อง"),
|
("Key mismatch", "คีย์ไม่ถูกต้อง"),
|
||||||
("Timeout", "หมดเวลา"),
|
("Timeout", "หมดเวลา"),
|
||||||
("Failed to connect to relay server", "การเชื่อมต่อไปยังเซิร์ฟเวอร์รีเลย์ล้มเหลว"),
|
("Failed to connect to relay server", "การเชื่อมต่อไปยังเซิร์ฟเวอร์รีเลย์ล้มเหลว"),
|
||||||
("Failed to connect via rendezvous server", "การเชื่อมต่อผ่านเซิร์ฟเวอร์นัดพบล้มเหลว"),
|
("Failed to connect via rendezvous server", "การเชื่อมต่อผ่านเซิร์ฟเวอร์นัดพบล้มเหลว"),
|
||||||
("Failed to connect via relay server", "การเชื่อมต่อผ่านเซิร์ฟเวอร์รีเลย์ล้มเหลว"),
|
("Failed to connect via relay server", "การเชื่อมต่อผ่านเซิร์ฟเวอร์รีเลย์ล้มเหลว"),
|
||||||
("Failed to make direct connection to remote desktop", "การเชื่อมต่อตรงไปยังเดสก์ท็อปปลายทางล้มเหลว"),
|
("Failed to make direct connection to remote desktop", "การเชื่อมต่อตรงไปยังเดสก์ท็อปปลายทางล้มเหลว"),
|
||||||
("Set Password", "ตั้งรหัสผ่าน"),
|
("Set Password", "ตั้งรหัสผ่าน"),
|
||||||
("OS Password", "รหัสผ่านระบบปฏิบัติการ"),
|
("OS Password", "รหัสผ่านระบบปฏิบัติการ"),
|
||||||
("install_tip", "เนื่องด้วยข้อจำกัดของการใช้งาน UAC ทำให้ RustDesk ไม่สามารถทำงานได้ปกติในฝั่งปลายทางในบางครั้ง เพื่อหลีกเลี่ยงข้อจำกัดของ UAC กรุณากดปุ่มด้านล่างเพื่อติดตั้ง RustDesk ไปยังระบบของคุณ"),
|
("install_tip", "เนื่องด้วยข้อจำกัดของการใช้งาน UAC ทำให้ RustDesk ไม่สามารถทำงานได้ปกติในฝั่งปลายทางในบางครั้ง เพื่อหลีกเลี่ยงข้อจำกัดของ UAC กรุณากดปุ่มด้านล่างเพื่อติดตั้ง RustDesk ไปยังระบบของคุณ"),
|
||||||
("Click to upgrade", "คลิกเพื่ออัปเกรด"),
|
("Click to upgrade", "คลิกเพื่ออัปเกรด"),
|
||||||
("Click to download", "คลิกเพื่อดาวน์โหลด"),
|
("Click to download", "คลิกเพื่อดาวน์โหลด"),
|
||||||
("Click to update", "คลิกเพื่ออัปเดต"),
|
("Click to update", "คลิกเพื่ออัปเดต"),
|
||||||
("Configure", "ปรับแต่งค่า"),
|
("Configure", "ปรับแต่งค่า"),
|
||||||
("config_acc", "เพื่อที่จะควบคุมเดสก์ท็อปปลายทางของคุณ คุณจำเป็นจะต้องอนุญาตสิทธิ์ \"การเข้าถึง\" ให้แก่ RustDesk"),
|
("config_acc", "เพื่อที่จะควบคุมเดสก์ท็อปปลายทางของคุณ คุณจำเป็นจะต้องอนุญาตสิทธิ์ \"การเข้าถึง\" ให้แก่ RustDesk"),
|
||||||
("config_screen", "เพื่อที่จะควบคุมเดสก์ท็อปปลายทางของคุณ คุณจำเป็นจะต้องอนุญาตสิทธิ์ \"การบันทึกภาพหน้าจอ\" ให้แก่ RustDesk"),
|
("config_screen", "เพื่อที่จะควบคุมเดสก์ท็อปปลายทางของคุณ คุณจำเป็นจะต้องอนุญาตสิทธิ์ \"การบันทึกภาพหน้าจอ\" ให้แก่ RustDesk"),
|
||||||
("Installing ...", "กำลังติดตั้ง ..."),
|
("Installing ...", "กำลังติดตั้ง ..."),
|
||||||
("Install", "ติดตั้ง"),
|
("Install", "ติดตั้ง"),
|
||||||
("Installation", "การติดตั้ง"),
|
("Installation", "การติดตั้ง"),
|
||||||
("Installation Path", "ตำแหน่งที่ติดตั้ง"),
|
("Installation Path", "ตำแหน่งที่ติดตั้ง"),
|
||||||
("Create start menu shortcuts", "สร้างทางลัดไปยัง Start Menu"),
|
("Create start menu shortcuts", "สร้างทางลัดไปยัง Start Menu"),
|
||||||
("Create desktop icon", "สร้างไอคอนบนเดสก์ท็อป"),
|
("Create desktop icon", "สร้างไอคอนบนเดสก์ท็อป"),
|
||||||
("agreement_tip", "ในการเริ่มต้นการติดตั้ง ถือว่าคุณได้ยอมรับข้อตกลงใบอนุญาตแล้ว"),
|
("agreement_tip", "ในการเริ่มต้นการติดตั้ง ถือว่าคุณได้ยอมรับข้อตกลงใบอนุญาตแล้ว"),
|
||||||
("Accept and Install", "ยอมรับและติดตั้ง"),
|
("Accept and Install", "ยอมรับและติดตั้ง"),
|
||||||
("End-user license agreement", "ข้อตกลงใบอนุญาตผู้ใช้งาน"),
|
("End-user license agreement", "ข้อตกลงใบอนุญาตผู้ใช้งาน"),
|
||||||
("Generating ...", "กำลังสร้าง ..."),
|
("Generating ...", "กำลังสร้าง ..."),
|
||||||
("Your installation is lower version.", "การติดตั้งของคุณเป็นเวอร์ชั่นที่ต่ำกว่า"),
|
("Your installation is lower version.", "การติดตั้งของคุณเป็นเวอร์ชั่นที่ต่ำกว่า"),
|
||||||
("not_close_tcp_tip", "อย่าปิดหน้าต่างนี้ในขณะที่คุณกำลังใช้งานอุโมงค์การเชื่อมต่อ"),
|
("not_close_tcp_tip", "อย่าปิดหน้าต่างนี้ในขณะที่คุณกำลังใช้งานอุโมงค์การเชื่อมต่อ"),
|
||||||
("Listening ...", "กำลังรอรับข้อมูล ..."),
|
("Listening ...", "กำลังรอรับข้อมูล ..."),
|
||||||
("Remote Host", "โฮสต์ปลายทาง"),
|
("Remote Host", "โฮสต์ปลายทาง"),
|
||||||
("Remote Port", "พอร์ทปลายทาง"),
|
("Remote Port", "พอร์ทปลายทาง"),
|
||||||
("Action", "การดำเนินการ"),
|
("Action", "การดำเนินการ"),
|
||||||
("Add", "เพิ่ม"),
|
("Add", "เพิ่ม"),
|
||||||
("Local Port", "พอร์ทต้นทาง"),
|
("Local Port", "พอร์ทต้นทาง"),
|
||||||
("Local Address", "ที่อยู่ต้นทาง"),
|
("Local Address", "ที่อยู่ต้นทาง"),
|
||||||
("Change Local Port", "เปลี่ยนพอร์ทต้นทาง"),
|
("Change Local Port", "เปลี่ยนพอร์ทต้นทาง"),
|
||||||
("setup_server_tip", "เพื่อการเชื่อมต่อที่เร็วขึ้น กรุณาเซ็ตอัปเซิร์ฟเวอร์ของคุณเอง"),
|
("setup_server_tip", "เพื่อการเชื่อมต่อที่เร็วขึ้น กรุณาเซ็ตอัปเซิร์ฟเวอร์ของคุณเอง"),
|
||||||
("Too short, at least 6 characters.", "สั้นเกินไป ต้องไม่ต่ำกว่า 6 ตัวอักษร"),
|
("Too short, at least 6 characters.", "สั้นเกินไป ต้องไม่ต่ำกว่า 6 ตัวอักษร"),
|
||||||
("The confirmation is not identical.", "การยืนยันข้อมูลไม่ถูกต้อง"),
|
("The confirmation is not identical.", "การยืนยันข้อมูลไม่ถูกต้อง"),
|
||||||
("Permissions", "สิทธิ์การใช้งาน"),
|
("Permissions", "สิทธิ์การใช้งาน"),
|
||||||
("Accept", "ยอมรับ"),
|
("Accept", "ยอมรับ"),
|
||||||
("Dismiss", "ปิด"),
|
("Dismiss", "ปิด"),
|
||||||
("Disconnect", "ยกเลิกการเชื่อมต่อ"),
|
("Disconnect", "ยกเลิกการเชื่อมต่อ"),
|
||||||
("Allow using keyboard and mouse", "อนุญาตให้ใช้งานคีย์บอร์ดและเมาส์"),
|
("Allow using keyboard and mouse", "อนุญาตให้ใช้งานคีย์บอร์ดและเมาส์"),
|
||||||
("Allow using clipboard", "อนุญาตให้ใช้คลิปบอร์ด"),
|
("Allow using clipboard", "อนุญาตให้ใช้คลิปบอร์ด"),
|
||||||
("Allow hearing sound", "อนุญาตให้ได้ยินเสียง"),
|
("Allow hearing sound", "อนุญาตให้ได้ยินเสียง"),
|
||||||
("Allow file copy and paste", "อนุญาตให้มีการคัดลอกและวางไฟล์"),
|
("Allow file copy and paste", "อนุญาตให้มีการคัดลอกและวางไฟล์"),
|
||||||
("Connected", "เชื่อมต่อแล้ว"),
|
("Connected", "เชื่อมต่อแล้ว"),
|
||||||
("Direct and encrypted connection", "การเชื่อมต่อตรงที่มีการเข้ารหัส"),
|
("Direct and encrypted connection", "การเชื่อมต่อตรงที่มีการเข้ารหัส"),
|
||||||
("Relayed and encrypted connection", "การเชื่อมต่อแบบรีเลย์ที่มีการเข้ารหัส"),
|
("Relayed and encrypted connection", "การเชื่อมต่อแบบรีเลย์ที่มีการเข้ารหัส"),
|
||||||
("Direct and unencrypted connection", "การเชื่อมต่อตรงที่ไม่มีการเข้ารหัส"),
|
("Direct and unencrypted connection", "การเชื่อมต่อตรงที่ไม่มีการเข้ารหัส"),
|
||||||
("Relayed and unencrypted connection", "การเชื่อมต่อแบบรีเลย์ที่ไม่มีการเข้ารหัส"),
|
("Relayed and unencrypted connection", "การเชื่อมต่อแบบรีเลย์ที่ไม่มีการเข้ารหัส"),
|
||||||
("Enter Remote ID", "กรอก ID ปลายทาง"),
|
("Enter Remote ID", "กรอก ID ปลายทาง"),
|
||||||
("Enter your password", "กรอกรหัสผ่าน"),
|
("Enter your password", "กรอกรหัสผ่าน"),
|
||||||
("Logging in...", "กำลังเข้าสู่ระบบ..."),
|
("Logging in...", "กำลังเข้าสู่ระบบ..."),
|
||||||
("Enable RDP session sharing", "เปิดการใช้งานการแชร์เซสชัน RDP"),
|
("Enable RDP session sharing", "เปิดการใช้งานการแชร์เซสชัน RDP"),
|
||||||
("Auto Login", "เข้าสู่ระบอัตโนมัติ"),
|
("Auto Login", "เข้าสู่ระบอัตโนมัติ"),
|
||||||
("Enable Direct IP Access", "เปิดการใช้งาน IP ตรง"),
|
("Enable Direct IP Access", "เปิดการใช้งาน IP ตรง"),
|
||||||
("Rename", "ปลายทาง"),
|
("Rename", "ปลายทาง"),
|
||||||
("Space", "พื้นที่ว่าง"),
|
("Space", "พื้นที่ว่าง"),
|
||||||
("Create Desktop Shortcut", "สร้างทางลัดบนเดสก์ท็อป"),
|
("Create Desktop Shortcut", "สร้างทางลัดบนเดสก์ท็อป"),
|
||||||
("Change Path", "เปลี่ยนตำแหน่ง"),
|
("Change Path", "เปลี่ยนตำแหน่ง"),
|
||||||
("Create Folder", "สร้างโฟลเดอร์"),
|
("Create Folder", "สร้างโฟลเดอร์"),
|
||||||
("Please enter the folder name", "กรุณาใส่ชื่อโฟลเดอร์"),
|
("Please enter the folder name", "กรุณาใส่ชื่อโฟลเดอร์"),
|
||||||
("Fix it", "แก้ไข"),
|
("Fix it", "แก้ไข"),
|
||||||
("Warning", "คำเตือน"),
|
("Warning", "คำเตือน"),
|
||||||
("Login screen using Wayland is not supported", "หน้าจอการเข้าสู่ระบบโดยใช้ Wayland ยังไม่ถูกรองรับ"),
|
("Login screen using Wayland is not supported", "หน้าจอการเข้าสู่ระบบโดยใช้ Wayland ยังไม่ถูกรองรับ"),
|
||||||
("Reboot required", "จำเป็นต้องเริ่มต้นระบบใหม่"),
|
("Reboot required", "จำเป็นต้องเริ่มต้นระบบใหม่"),
|
||||||
("Unsupported display server ", "เซิร์ฟเวอร์การแสดงผลที่ไม่รองรับ"),
|
("Unsupported display server ", "เซิร์ฟเวอร์การแสดงผลที่ไม่รองรับ"),
|
||||||
("x11 expected", "ต้องใช้งาน x11"),
|
("x11 expected", "ต้องใช้งาน x11"),
|
||||||
("Port", "พอร์ท"),
|
("Port", "พอร์ท"),
|
||||||
("Settings", "ตั้งค่า"),
|
("Settings", "ตั้งค่า"),
|
||||||
("Username", "ชื่อผู้ใช้งาน"),
|
("Username", "ชื่อผู้ใช้งาน"),
|
||||||
("Invalid port", "พอร์ทไม่ถูกต้อง"),
|
("Invalid port", "พอร์ทไม่ถูกต้อง"),
|
||||||
("Closed manually by the peer", "ถูกปิดโดยอีกฝั่งการการเชื่อมต่อ"),
|
("Closed manually by the peer", "ถูกปิดโดยอีกฝั่งการการเชื่อมต่อ"),
|
||||||
("Enable remote configuration modification", "เปิดการใช้งานการแก้ไขการตั้งค่าปลายทาง"),
|
("Enable remote configuration modification", "เปิดการใช้งานการแก้ไขการตั้งค่าปลายทาง"),
|
||||||
("Run without install", "ใช้งานโดยไม่ต้องติดตั้ง"),
|
("Run without install", "ใช้งานโดยไม่ต้องติดตั้ง"),
|
||||||
("Always connected via relay", "เชื่อมต่อผ่านรีเลย์เสมอ"),
|
("Always connected via relay", "เชื่อมต่อผ่านรีเลย์เสมอ"),
|
||||||
("Always connect via relay", "เชื่อมต่อผ่านรีเลย์เสมอ"),
|
("Always connect via relay", "เชื่อมต่อผ่านรีเลย์เสมอ"),
|
||||||
("whitelist_tip", "อนุญาตเฉพาะการเชื่อมต่อจาก IP ที่ไวท์ลิสต์"),
|
("whitelist_tip", "อนุญาตเฉพาะการเชื่อมต่อจาก IP ที่ไวท์ลิสต์"),
|
||||||
("Login", "เข้าสู่ระบบ"),
|
("Login", "เข้าสู่ระบบ"),
|
||||||
("Logout", "ออกจากระบบ"),
|
("Verify", ""),
|
||||||
("Tags", "แท็ก"),
|
("Remember me", ""),
|
||||||
("Search ID", "ค้นหา ID"),
|
("Trust this device", ""),
|
||||||
("Current Wayland display server is not supported", "เซิร์ฟเวอร์การแสดงผล Wayland ปัจจุบันไม่รองรับ"),
|
("Verification code", ""),
|
||||||
("whitelist_sep", "คั่นโดยเครื่องหมาย comma semicolon เว้นวรรค หรือ ขึ้นบรรทัดใหม่"),
|
("verification_tip", ""),
|
||||||
("Add ID", "เพิ่ม ID"),
|
("Logout", "ออกจากระบบ"),
|
||||||
("Add Tag", "เพิ่มแท็ก"),
|
("Tags", "แท็ก"),
|
||||||
("Unselect all tags", "ยกเลิกการเลือกแท็กทั้งหมด"),
|
("Search ID", "ค้นหา ID"),
|
||||||
("Network error", "ข้อผิดพลาดของเครือข่าย"),
|
("Current Wayland display server is not supported", "เซิร์ฟเวอร์การแสดงผล Wayland ปัจจุบันไม่รองรับ"),
|
||||||
("Username missed", "ไม่พบข้อมูลผู้ใช้งาน"),
|
("whitelist_sep", "คั่นโดยเครื่องหมาย comma semicolon เว้นวรรค หรือ ขึ้นบรรทัดใหม่"),
|
||||||
("Password missed", "ไม่พบรหัสผ่าน"),
|
("Add ID", "เพิ่ม ID"),
|
||||||
("Wrong credentials", "ข้อมูลสำหรับเข้าสู่ระบบไม่ถูกต้อง"),
|
("Add Tag", "เพิ่มแท็ก"),
|
||||||
("Edit Tag", "แก้ไขแท็ก"),
|
("Unselect all tags", "ยกเลิกการเลือกแท็กทั้งหมด"),
|
||||||
("Unremember Password", "ยกเลิกการจดจำรหัสผ่าน"),
|
("Network error", "ข้อผิดพลาดของเครือข่าย"),
|
||||||
("Favorites", "รายการโปรด"),
|
("Username missed", "ไม่พบข้อมูลผู้ใช้งาน"),
|
||||||
("Add to Favorites", "เพิ่มไปยังรายการโปรด"),
|
("Password missed", "ไม่พบรหัสผ่าน"),
|
||||||
("Remove from Favorites", "ลบออกจากรายการโปรด"),
|
("Wrong credentials", "ข้อมูลสำหรับเข้าสู่ระบบไม่ถูกต้อง"),
|
||||||
("Empty", "ว่างเปล่า"),
|
("Edit Tag", "แก้ไขแท็ก"),
|
||||||
("Invalid folder name", "ชื่อโฟลเดอร์ไม่ถูกต้อง"),
|
("Unremember Password", "ยกเลิกการจดจำรหัสผ่าน"),
|
||||||
("Socks5 Proxy", "พรอกซี Socks5"),
|
("Favorites", "รายการโปรด"),
|
||||||
("Hostname", "ชื่อโฮสต์"),
|
("Add to Favorites", "เพิ่มไปยังรายการโปรด"),
|
||||||
("Discovered", "ค้นพบ"),
|
("Remove from Favorites", "ลบออกจากรายการโปรด"),
|
||||||
("install_daemon_tip", "หากต้องการใช้งานขณะระบบเริ่มต้น คุณจำเป็นจะต้องติดตั้งเซอร์วิส"),
|
("Empty", "ว่างเปล่า"),
|
||||||
("Remote ID", "ID ปลายทาง"),
|
("Invalid folder name", "ชื่อโฟลเดอร์ไม่ถูกต้อง"),
|
||||||
("Paste", "วาง"),
|
("Socks5 Proxy", "พรอกซี Socks5"),
|
||||||
("Paste here?", "วางที่นี่หรือไม่?"),
|
("Hostname", "ชื่อโฮสต์"),
|
||||||
("Are you sure to close the connection?", "คุณแน่ใจหรือไม่ที่จะปิดการเชื่อมต่อ?"),
|
("Discovered", "ค้นพบ"),
|
||||||
("Download new version", "ดาวน์โหลดเวอร์ชั่นใหม่"),
|
("install_daemon_tip", "หากต้องการใช้งานขณะระบบเริ่มต้น คุณจำเป็นจะต้องติดตั้งเซอร์วิส"),
|
||||||
("Touch mode", "โหมดการสัมผัส"),
|
("Remote ID", "ID ปลายทาง"),
|
||||||
("Mouse mode", "โหมดการใช้เมาส์"),
|
("Paste", "วาง"),
|
||||||
("One-Finger Tap", "แตะนิ้วเดียว"),
|
("Paste here?", "วางที่นี่หรือไม่?"),
|
||||||
("Left Mouse", "เมาส์ซ้าย"),
|
("Are you sure to close the connection?", "คุณแน่ใจหรือไม่ที่จะปิดการเชื่อมต่อ?"),
|
||||||
("One-Long Tap", "แตะยาวหนึ่งครั้ง"),
|
("Download new version", "ดาวน์โหลดเวอร์ชั่นใหม่"),
|
||||||
("Two-Finger Tap", "แตะสองนิ้ว"),
|
("Touch mode", "โหมดการสัมผัส"),
|
||||||
("Right Mouse", "เมาส์ขวา"),
|
("Mouse mode", "โหมดการใช้เมาส์"),
|
||||||
("One-Finger Move", "ลากนิ้วเดียว"),
|
("One-Finger Tap", "แตะนิ้วเดียว"),
|
||||||
("Double Tap & Move", "แตะเบิ้ลและลาก"),
|
("Left Mouse", "เมาส์ซ้าย"),
|
||||||
("Mouse Drag", "ลากเมาส์"),
|
("One-Long Tap", "แตะยาวหนึ่งครั้ง"),
|
||||||
("Three-Finger vertically", "สามนิ้วแนวตั้ง"),
|
("Two-Finger Tap", "แตะสองนิ้ว"),
|
||||||
("Mouse Wheel", "ลูกลิ้งเมาส์"),
|
("Right Mouse", "เมาส์ขวา"),
|
||||||
("Two-Finger Move", "ลากสองนิ้ว"),
|
("One-Finger Move", "ลากนิ้วเดียว"),
|
||||||
("Canvas Move", "ลากแคนวาส"),
|
("Double Tap & Move", "แตะเบิ้ลและลาก"),
|
||||||
("Pinch to Zoom", "ถ่างเพื่อขยาย"),
|
("Mouse Drag", "ลากเมาส์"),
|
||||||
("Canvas Zoom", "ขยายแคนวาส"),
|
("Three-Finger vertically", "สามนิ้วแนวตั้ง"),
|
||||||
("Reset canvas", "รีเซ็ตแคนวาส"),
|
("Mouse Wheel", "ลูกลิ้งเมาส์"),
|
||||||
("No permission of file transfer", "ไม่มีสิทธิ์ในการถ่ายโอนไฟล์"),
|
("Two-Finger Move", "ลากสองนิ้ว"),
|
||||||
("Note", "บันทึกข้อความ"),
|
("Canvas Move", "ลากแคนวาส"),
|
||||||
("Connection", "การเชื่อมต่อ"),
|
("Pinch to Zoom", "ถ่างเพื่อขยาย"),
|
||||||
("Share Screen", "แชร์หน้าจอ"),
|
("Canvas Zoom", "ขยายแคนวาส"),
|
||||||
("CLOSE", "ปิด"),
|
("Reset canvas", "รีเซ็ตแคนวาส"),
|
||||||
("OPEN", "เปิด"),
|
("No permission of file transfer", "ไม่มีสิทธิ์ในการถ่ายโอนไฟล์"),
|
||||||
("Chat", "แชท"),
|
("Note", "บันทึกข้อความ"),
|
||||||
("Total", "รวม"),
|
("Connection", "การเชื่อมต่อ"),
|
||||||
("items", "รายการ"),
|
("Share Screen", "แชร์หน้าจอ"),
|
||||||
("Selected", "ถูกเลือก"),
|
("CLOSE", "ปิด"),
|
||||||
("Screen Capture", "แคปเจอร์หน้าจอ"),
|
("OPEN", "เปิด"),
|
||||||
("Input Control", "ควบคุมอินพุท"),
|
("Chat", "แชท"),
|
||||||
("Audio Capture", "แคปเจอร์เสียง"),
|
("Total", "รวม"),
|
||||||
("File Connection", "การเชื่อมต่อไฟล์"),
|
("items", "รายการ"),
|
||||||
("Screen Connection", "การเชื่อมต่อหน้าจอ"),
|
("Selected", "ถูกเลือก"),
|
||||||
("Do you accept?", "ยอมรับหรือไม่?"),
|
("Screen Capture", "แคปเจอร์หน้าจอ"),
|
||||||
("Open System Setting", "เปิดการตั้งค่าระบบ"),
|
("Input Control", "ควบคุมอินพุท"),
|
||||||
("How to get Android input permission?", "เปิดสิทธิ์การใช้งานอินพุทของแอนดรอยด์ได้อย่างไร?"),
|
("Audio Capture", "แคปเจอร์เสียง"),
|
||||||
("android_input_permission_tip1", "ในการที่จะอนุญาตให้เครื่องปลายทางควบคุมอุปกรณ์แอนดรอยด์ของคุณโดยใช้เมาส์หรือการสัมผัส คุณจำเป็นจะต้องอนุญาตสิทธิ์ \"การเข้าถึง\" ให้แก่เซอร์วิสของ RustDesk"),
|
("File Connection", "การเชื่อมต่อไฟล์"),
|
||||||
("android_input_permission_tip2", "กรุณาไปยังหน้าตั้งค่าถัดไป ค้นหาและเข้าไปยัง [เซอร์วิสที่ถูกติดตั้ง] และเปิดการใช้งานเซอร์วิส [อินพุท RustDesk]"),
|
("Screen Connection", "การเชื่อมต่อหน้าจอ"),
|
||||||
("android_new_connection_tip", "ได้รับคำขอควบคุมใหม่ที่ต้องการควบคุมอุปกรณ์ของคุณ"),
|
("Do you accept?", "ยอมรับหรือไม่?"),
|
||||||
("android_service_will_start_tip", "การเปิดการใช้งาน \"การบันทึกหน้าจอ\" จะเป็นการเริ่มต้นการทำงานของเซอร์วิสโดยอัตโนมัติ ที่จะอนุญาตให้อุปกรณ์อื่นๆ ส่งคำขอเข้าถึงมายังอุปกรณ์ของคุณได้"),
|
("Open System Setting", "เปิดการตั้งค่าระบบ"),
|
||||||
("android_stop_service_tip", "การปิดการใช้งานเซอร์วิสจะปิดการเชื่อมต่อทั้งหมดโดยอัตโนมัติ"),
|
("How to get Android input permission?", "เปิดสิทธิ์การใช้งานอินพุทของแอนดรอยด์ได้อย่างไร?"),
|
||||||
("android_version_audio_tip", "เวอร์ชั่นแอนดรอยด์ปัจจุบันของคุณไม่รองรับการบันทึกข้อมูลเสียง กรุณาอัปเกรดเป็นแอนดรอยด์เวอร์ชั่น 10 หรือสูงกว่า"),
|
("android_input_permission_tip1", "ในการที่จะอนุญาตให้เครื่องปลายทางควบคุมอุปกรณ์แอนดรอยด์ของคุณโดยใช้เมาส์หรือการสัมผัส คุณจำเป็นจะต้องอนุญาตสิทธิ์ \"การเข้าถึง\" ให้แก่เซอร์วิสของ RustDesk"),
|
||||||
("android_start_service_tip", "แตะ [เริ่มต้นใช้งานเซอร์วิส] หรือเปิดสิทธิ์ [การบันทึกหน้าจอ] เพื่อเริ่มเซอร์วิสการแชร์หน้าจอ"),
|
("android_input_permission_tip2", "กรุณาไปยังหน้าตั้งค่าถัดไป ค้นหาและเข้าไปยัง [เซอร์วิสที่ถูกติดตั้ง] และเปิดการใช้งานเซอร์วิส [อินพุท RustDesk]"),
|
||||||
("Overwrite", "เขียนทับ"),
|
("android_new_connection_tip", "ได้รับคำขอควบคุมใหม่ที่ต้องการควบคุมอุปกรณ์ของคุณ"),
|
||||||
("This file exists, skip or overwrite this file?", "พบไฟล์ที่มีอยู่แล้ว ต้องการเขียนทับหรือไม่?"),
|
("android_service_will_start_tip", "การเปิดการใช้งาน \"การบันทึกหน้าจอ\" จะเป็นการเริ่มต้นการทำงานของเซอร์วิสโดยอัตโนมัติ ที่จะอนุญาตให้อุปกรณ์อื่นๆ ส่งคำขอเข้าถึงมายังอุปกรณ์ของคุณได้"),
|
||||||
("Quit", "ออก"),
|
("android_stop_service_tip", "การปิดการใช้งานเซอร์วิสจะปิดการเชื่อมต่อทั้งหมดโดยอัตโนมัติ"),
|
||||||
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
|
("android_version_audio_tip", "เวอร์ชั่นแอนดรอยด์ปัจจุบันของคุณไม่รองรับการบันทึกข้อมูลเสียง กรุณาอัปเกรดเป็นแอนดรอยด์เวอร์ชั่น 10 หรือสูงกว่า"),
|
||||||
("Help", "ช่วยเหลือ"),
|
("android_start_service_tip", "แตะ [เริ่มต้นใช้งานเซอร์วิส] หรือเปิดสิทธิ์ [การบันทึกหน้าจอ] เพื่อเริ่มเซอร์วิสการแชร์หน้าจอ"),
|
||||||
("Failed", "ล้มเหลว"),
|
("Account", "บัญชี"),
|
||||||
("Succeeded", "สำเร็จ"),
|
("Overwrite", "เขียนทับ"),
|
||||||
("Someone turns on privacy mode, exit", "มีใครบางคนเปิดใช้งานโหมดความเป็นส่วนตัว กำลังออก"),
|
("This file exists, skip or overwrite this file?", "พบไฟล์ที่มีอยู่แล้ว ต้องการเขียนทับหรือไม่?"),
|
||||||
("Unsupported", "ไม่รองรับ"),
|
("Quit", "ออก"),
|
||||||
("Peer denied", "ถูกปฏิเสธโดยอีกฝั่ง"),
|
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
|
||||||
("Please install plugins", "กรุณาติดตั้งปลั๊กอิน"),
|
("Help", "ช่วยเหลือ"),
|
||||||
("Peer exit", "อีกฝั่งออก"),
|
("Failed", "ล้มเหลว"),
|
||||||
("Failed to turn off", "การปิดล้มเหลว"),
|
("Succeeded", "สำเร็จ"),
|
||||||
("Turned off", "ปิด"),
|
("Someone turns on privacy mode, exit", "มีใครบางคนเปิดใช้งานโหมดความเป็นส่วนตัว กำลังออก"),
|
||||||
("In privacy mode", "อยู่ในโหมดความเป็นส่วนตัว"),
|
("Unsupported", "ไม่รองรับ"),
|
||||||
("Out privacy mode", "อยู่นอกโหมดความเป็นส่วนตัว"),
|
("Peer denied", "ถูกปฏิเสธโดยอีกฝั่ง"),
|
||||||
("Language", "ภาษา"),
|
("Please install plugins", "กรุณาติดตั้งปลั๊กอิน"),
|
||||||
("Keep RustDesk background service", "คงสถานะการทำงานเบื้องหลังของเซอร์วิส RustDesk"),
|
("Peer exit", "อีกฝั่งออก"),
|
||||||
("Ignore Battery Optimizations", "เพิกเฉยการตั้งค่าการใช้งาน Battery Optimization"),
|
("Failed to turn off", "การปิดล้มเหลว"),
|
||||||
("android_open_battery_optimizations_tip", "หากคุณต้องการปิดการใช้งานฟีเจอร์นี้ กรุณาไปยังหน้าตั้งค่าในแอปพลิเคชัน RustDesk ค้นหาหัวข้อ [Battery] และยกเลิกการเลือกรายการ [Unrestricted]"),
|
("Turned off", "ปิด"),
|
||||||
("Connection not allowed", "การเชื่อมต่อไม่อนุญาต"),
|
("In privacy mode", "อยู่ในโหมดความเป็นส่วนตัว"),
|
||||||
("Legacy mode", ""),
|
("Out privacy mode", "อยู่นอกโหมดความเป็นส่วนตัว"),
|
||||||
("Map mode", ""),
|
("Language", "ภาษา"),
|
||||||
("Translate mode", ""),
|
("Keep RustDesk background service", "คงสถานะการทำงานเบื้องหลังของเซอร์วิส RustDesk"),
|
||||||
("Use permanent password", "ใช้รหัสผ่านถาวร"),
|
("Ignore Battery Optimizations", "เพิกเฉยการตั้งค่าการใช้งาน Battery Optimization"),
|
||||||
("Use both passwords", "ใช้รหัสผ่านทั้งสองแบบ"),
|
("android_open_battery_optimizations_tip", "หากคุณต้องการปิดการใช้งานฟีเจอร์นี้ กรุณาไปยังหน้าตั้งค่าในแอปพลิเคชัน RustDesk ค้นหาหัวข้อ [Battery] และยกเลิกการเลือกรายการ [Unrestricted]"),
|
||||||
("Set permanent password", "ตั้งค่ารหัสผ่านถาวร"),
|
("Connection not allowed", "การเชื่อมต่อไม่อนุญาต"),
|
||||||
("Enable Remote Restart", "เปิดการใช้งานการรีสตาร์ทระบบทางไกล"),
|
("Legacy mode", ""),
|
||||||
("Allow remote restart", "อนุญาตการรีสตาร์ทระบบทางไกล"),
|
("Map mode", ""),
|
||||||
("Restart Remote Device", "รีสตาร์ทอุปกรณ์ปลายทาง"),
|
("Translate mode", ""),
|
||||||
("Are you sure you want to restart", "คุณแน่ใจหรือไม่ที่จะรีสตาร์ท"),
|
("Use permanent password", "ใช้รหัสผ่านถาวร"),
|
||||||
("Restarting Remote Device", "กำลังรีสตาร์ทระบบปลายทาง"),
|
("Use both passwords", "ใช้รหัสผ่านทั้งสองแบบ"),
|
||||||
("remote_restarting_tip", "ระบบปลายทางกำลังรีสตาร์ท กรุณาปิดกล่องข้อความนี้และดำเนินการเขื่อมต่อใหม่อีกครั้งด้วยรหัสผ่านถาวรหลังจากผ่านไปซักครู่"),
|
("Set permanent password", "ตั้งค่ารหัสผ่านถาวร"),
|
||||||
("Copied", "คัดลอกแล้ว"),
|
("Enable Remote Restart", "เปิดการใช้งานการรีสตาร์ทระบบทางไกล"),
|
||||||
("Exit Fullscreen", "ออกจากเต็มหน้าจอ"),
|
("Allow remote restart", "อนุญาตการรีสตาร์ทระบบทางไกล"),
|
||||||
("Fullscreen", "เต็มหน้าจอ"),
|
("Restart Remote Device", "รีสตาร์ทอุปกรณ์ปลายทาง"),
|
||||||
("Mobile Actions", "การดำเนินการบนมือถือ"),
|
("Are you sure you want to restart", "คุณแน่ใจหรือไม่ที่จะรีสตาร์ท"),
|
||||||
("Select Monitor", "เลือกหน้าจอ"),
|
("Restarting Remote Device", "กำลังรีสตาร์ทระบบปลายทาง"),
|
||||||
("Control Actions", "การดำเนินการควบคุม"),
|
("remote_restarting_tip", "ระบบปลายทางกำลังรีสตาร์ท กรุณาปิดกล่องข้อความนี้และดำเนินการเขื่อมต่อใหม่อีกครั้งด้วยรหัสผ่านถาวรหลังจากผ่านไปซักครู่"),
|
||||||
("Display Settings", "การตั้งค่าแสดงผล"),
|
("Copied", "คัดลอกแล้ว"),
|
||||||
("Ratio", "อัตราส่วน"),
|
("Exit Fullscreen", "ออกจากเต็มหน้าจอ"),
|
||||||
("Image Quality", "คุณภาพภาพ"),
|
("Fullscreen", "เต็มหน้าจอ"),
|
||||||
("Scroll Style", "ลักษณะการเลื่อน"),
|
("Mobile Actions", "การดำเนินการบนมือถือ"),
|
||||||
("Show Menubar", "แสดงแถบเมนู"),
|
("Select Monitor", "เลือกหน้าจอ"),
|
||||||
("Hide Menubar", "ซ่อนแถบเมนู"),
|
("Control Actions", "การดำเนินการควบคุม"),
|
||||||
("Direct Connection", "การเชื่อมต่อตรง"),
|
("Display Settings", "การตั้งค่าแสดงผล"),
|
||||||
("Relay Connection", "การเชื่อมต่อแบบรีเลย์"),
|
("Ratio", "อัตราส่วน"),
|
||||||
("Secure Connection", "การเชื่อมต่อที่ปลอดภัย"),
|
("Image Quality", "คุณภาพภาพ"),
|
||||||
("Insecure Connection", "การเชื่อมต่อที่ไม่ปลอดภัย"),
|
("Scroll Style", "ลักษณะการเลื่อน"),
|
||||||
("Scale original", "ขนาดเดิม"),
|
("Show Menubar", "แสดงแถบเมนู"),
|
||||||
("Scale adaptive", "ขนาดยืดหยุ่น"),
|
("Hide Menubar", "ซ่อนแถบเมนู"),
|
||||||
("General", "ทั่วไป"),
|
("Direct Connection", "การเชื่อมต่อตรง"),
|
||||||
("Security", "ความปลอดภัย"),
|
("Relay Connection", "การเชื่อมต่อแบบรีเลย์"),
|
||||||
("Account", "บัญชี"),
|
("Secure Connection", "การเชื่อมต่อที่ปลอดภัย"),
|
||||||
("Theme", "ธีม"),
|
("Insecure Connection", "การเชื่อมต่อที่ไม่ปลอดภัย"),
|
||||||
("Dark Theme", "ธีมมืด"),
|
("Scale original", "ขนาดเดิม"),
|
||||||
("Dark", "มืด"),
|
("Scale adaptive", "ขนาดยืดหยุ่น"),
|
||||||
("Light", "สว่าง"),
|
("General", "ทั่วไป"),
|
||||||
("Follow System", "ตามระบบ"),
|
("Security", "ความปลอดภัย"),
|
||||||
("Enable hardware codec", "เปิดการใช้งานฮาร์ดแวร์ codec"),
|
("Theme", "ธีม"),
|
||||||
("Unlock Security Settings", "ปลดล็อคการตั้งค่าความปลอดภัย"),
|
("Dark Theme", "ธีมมืด"),
|
||||||
("Enable Audio", "เปิดการใช้งานเสียง"),
|
("Dark", "มืด"),
|
||||||
("Unlock Network Settings", "ปลดล็อคการตั้งค่าเครือข่าย"),
|
("Light", "สว่าง"),
|
||||||
("Server", "เซิร์ฟเวอร์"),
|
("Follow System", "ตามระบบ"),
|
||||||
("Direct IP Access", "การเข้าถึง IP ตรง"),
|
("Enable hardware codec", "เปิดการใช้งานฮาร์ดแวร์ codec"),
|
||||||
("Proxy", "พรอกซี"),
|
("Unlock Security Settings", "ปลดล็อคการตั้งค่าความปลอดภัย"),
|
||||||
("Apply", "นำไปใช้"),
|
("Enable Audio", "เปิดการใช้งานเสียง"),
|
||||||
("Disconnect all devices?", "ยกเลิกการเชื่อมต่ออุปกรณ์ทั้งหมด?"),
|
("Unlock Network Settings", "ปลดล็อคการตั้งค่าเครือข่าย"),
|
||||||
("Clear", "ล้างข้อมูล"),
|
("Server", "เซิร์ฟเวอร์"),
|
||||||
("Audio Input Device", "อุปกรณ์รับอินพุทข้อมูลเสียง"),
|
("Direct IP Access", "การเข้าถึง IP ตรง"),
|
||||||
("Deny remote access", "ปฏิเสธการเชื่อมต่อ"),
|
("Proxy", "พรอกซี"),
|
||||||
("Use IP Whitelisting", "ใช้งาน IP ไวท์ลิสต์"),
|
("Apply", "นำไปใช้"),
|
||||||
("Network", "เครือข่าย"),
|
("Disconnect all devices?", "ยกเลิกการเชื่อมต่ออุปกรณ์ทั้งหมด?"),
|
||||||
("Enable RDP", "เปิดการใช้งาน RDP"),
|
("Clear", "ล้างข้อมูล"),
|
||||||
("Pin menubar", "ปักหมุดแถบเมนู"),
|
("Audio Input Device", "อุปกรณ์รับอินพุทข้อมูลเสียง"),
|
||||||
("Unpin menubar", "ยกเลิกการปักหมุดแถบเมนู"),
|
("Deny remote access", "ปฏิเสธการเชื่อมต่อ"),
|
||||||
("Recording", "การบันทึก"),
|
("Use IP Whitelisting", "ใช้งาน IP ไวท์ลิสต์"),
|
||||||
("Directory", "ไดเรกทอรี่"),
|
("Network", "เครือข่าย"),
|
||||||
("Automatically record incoming sessions", "บันทึกเซสชันขาเข้าโดยอัตโนมัติ"),
|
("Enable RDP", "เปิดการใช้งาน RDP"),
|
||||||
("Change", "เปลี่ยน"),
|
("Pin menubar", "ปักหมุดแถบเมนู"),
|
||||||
("Start session recording", "เริ่มต้นการบันทึกเซสชัน"),
|
("Unpin menubar", "ยกเลิกการปักหมุดแถบเมนู"),
|
||||||
("Stop session recording", "หยุดการบันทึกเซสซัน"),
|
("Recording", "การบันทึก"),
|
||||||
("Enable Recording Session", "เปิดใช้งานการบันทึกเซสชัน"),
|
("Directory", "ไดเรกทอรี่"),
|
||||||
("Allow recording session", "อนุญาตการบันทึกเซสชัน"),
|
("Automatically record incoming sessions", "บันทึกเซสชันขาเข้าโดยอัตโนมัติ"),
|
||||||
("Enable LAN Discovery", "เปิดการใช้งานการค้นหาในวง LAN"),
|
("Change", "เปลี่ยน"),
|
||||||
("Deny LAN Discovery", "ปฏิเสธการใช้งานการค้นหาในวง LAN"),
|
("Start session recording", "เริ่มต้นการบันทึกเซสชัน"),
|
||||||
("Write a message", "เขียนข้อความ"),
|
("Stop session recording", "หยุดการบันทึกเซสซัน"),
|
||||||
("Prompt", ""),
|
("Enable Recording Session", "เปิดใช้งานการบันทึกเซสชัน"),
|
||||||
("Please wait for confirmation of UAC...", "กรุณารอการยืนยันจาก UAC..."),
|
("Allow recording session", "อนุญาตการบันทึกเซสชัน"),
|
||||||
("elevated_foreground_window_tip", "หน้าต่างปัจจุบันของเครื่องปลายทางต้องการสิทธิ์การใช้งานที่สูงขึ้นสำหรับการทำงาน ดังนั้นเมาส์และคีย์บอร์ดจะไม่สามารถใช้งานได้ชั่วคราว คุณสามารถขอผู้ใช้งานปลายทางให้ย่อหน้าต่าง หรือคลิกปุ่มให้สิทธิ์การใช้งานในหน้าต่างการจัดการการเชื่อมต่อ เพื่อหลีกเลี่ยงปัญหานี้เราแนะนำให้ดำเนินการติดตั้งซอฟท์แวร์ในเครื่องปลายทาง"),
|
("Enable LAN Discovery", "เปิดการใช้งานการค้นหาในวง LAN"),
|
||||||
("Disconnected", "ยกเลิกการเชื่อมต่อ"),
|
("Deny LAN Discovery", "ปฏิเสธการใช้งานการค้นหาในวง LAN"),
|
||||||
("Other", "อื่นๆ"),
|
("Write a message", "เขียนข้อความ"),
|
||||||
("Confirm before closing multiple tabs", "ยืนยันการปิดหลายแท็บ"),
|
("Prompt", ""),
|
||||||
("Keyboard Settings", "การตั้งค่าคีย์บอร์ด"),
|
("Please wait for confirmation of UAC...", "กรุณารอการยืนยันจาก UAC..."),
|
||||||
("Full Access", "การเข้าถึงทั้งหมด"),
|
("elevated_foreground_window_tip", "หน้าต่างปัจจุบันของเครื่องปลายทางต้องการสิทธิ์การใช้งานที่สูงขึ้นสำหรับการทำงาน ดังนั้นเมาส์และคีย์บอร์ดจะไม่สามารถใช้งานได้ชั่วคราว คุณสามารถขอผู้ใช้งานปลายทางให้ย่อหน้าต่าง หรือคลิกปุ่มให้สิทธิ์การใช้งานในหน้าต่างการจัดการการเชื่อมต่อ เพื่อหลีกเลี่ยงปัญหานี้เราแนะนำให้ดำเนินการติดตั้งซอฟท์แวร์ในเครื่องปลายทาง"),
|
||||||
("Screen Share", "การแชร์จอ"),
|
("Disconnected", "ยกเลิกการเชื่อมต่อ"),
|
||||||
("Wayland requires Ubuntu 21.04 or higher version.", "Wayland ต้องการ Ubuntu เวอร์ชั่น 21.04 หรือสูงกว่า"),
|
("Other", "อื่นๆ"),
|
||||||
("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland ต้องการลินุกซ์เวอร์ชันที่สูงกว่านี้ กรุณาเปลี่ยนไปใช้เดสก์ท็อป X11 หรือเปลี่ยนระบบปฏิบัติการของคุณ"),
|
("Confirm before closing multiple tabs", "ยืนยันการปิดหลายแท็บ"),
|
||||||
("JumpLink", "View"),
|
("Keyboard Settings", "การตั้งค่าคีย์บอร์ด"),
|
||||||
("Please Select the screen to be shared(Operate on the peer side).", "กรุณาเลือกหน้าจอที่ต้องการแชร์ (ใช้งานในอีกฝั่งของการเชื่อมต่อ)"),
|
("Full Access", "การเข้าถึงทั้งหมด"),
|
||||||
("Show RustDesk", "แสดง RustDesk"),
|
("Screen Share", "การแชร์จอ"),
|
||||||
("This PC", ""),
|
("Wayland requires Ubuntu 21.04 or higher version.", "Wayland ต้องการ Ubuntu เวอร์ชั่น 21.04 หรือสูงกว่า"),
|
||||||
("or", "หรือ"),
|
("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland ต้องการลินุกซ์เวอร์ชันที่สูงกว่านี้ กรุณาเปลี่ยนไปใช้เดสก์ท็อป X11 หรือเปลี่ยนระบบปฏิบัติการของคุณ"),
|
||||||
("Continue with", "ทำต่อด้วย"),
|
("JumpLink", "View"),
|
||||||
("Elevate", "ยกระดับ"),
|
("Please Select the screen to be shared(Operate on the peer side).", "กรุณาเลือกหน้าจอที่ต้องการแชร์ (ใช้งานในอีกฝั่งของการเชื่อมต่อ)"),
|
||||||
("Zoom cursor", "ขยายเคอร์เซอร์"),
|
("Show RustDesk", "แสดง RustDesk"),
|
||||||
("Accept sessions via password", "ยอมรับการเชื่อมต่อด้วยรหัสผ่าน"),
|
("This PC", ""),
|
||||||
("Accept sessions via click", "ยอมรับการเชื่อมต่อด้วยการคลิก"),
|
("or", "หรือ"),
|
||||||
("Accept sessions via both", "ยอมรับการเชื่อมต่อด้วยทั้งสองวิธิ"),
|
("Continue with", "ทำต่อด้วย"),
|
||||||
("Please wait for the remote side to accept your session request...", "กรุณารอให้อีกฝั่งยอมรับการเชื่อมต่อของคุณ..."),
|
("Elevate", "ยกระดับ"),
|
||||||
("One-time Password", "รหัสผ่านครั้งเดียว"),
|
("Zoom cursor", "ขยายเคอร์เซอร์"),
|
||||||
("Use one-time password", "ใช้รหัสผ่านครั้งเดียว"),
|
("Accept sessions via password", "ยอมรับการเชื่อมต่อด้วยรหัสผ่าน"),
|
||||||
("One-time password length", "ความยาวรหัสผ่านครั้งเดียว"),
|
("Accept sessions via click", "ยอมรับการเชื่อมต่อด้วยการคลิก"),
|
||||||
("Request access to your device", "คำขอการเข้าถึงอุปกรณ์ของคุณ"),
|
("Accept sessions via both", "ยอมรับการเชื่อมต่อด้วยทั้งสองวิธิ"),
|
||||||
("Hide connection management window", "ซ่อนหน้าต่างการจัดการการเชื่อมต่อ"),
|
("Please wait for the remote side to accept your session request...", "กรุณารอให้อีกฝั่งยอมรับการเชื่อมต่อของคุณ..."),
|
||||||
("hide_cm_tip", "อนุญาตการซ่อนก็ต่อเมื่อยอมรับการเชื่อมต่อด้วยรหัสผ่าน และต้องเป็นรหัสผ่านถาวรเท่านั้น"),
|
("One-time Password", "รหัสผ่านครั้งเดียว"),
|
||||||
("wayland_experiment_tip", "การสนับสนุน Wayland ยังอยู่ในขั้นตอนการทดลอง กรุณาใช้ X11 หากคุณต้องการใช้งานการเข้าถึงแบบไม่มีผู้ดูแล"),
|
("Use one-time password", "ใช้รหัสผ่านครั้งเดียว"),
|
||||||
("Right click to select tabs", "คลิกขวาเพื่อเลือกแท็บ"),
|
("One-time password length", "ความยาวรหัสผ่านครั้งเดียว"),
|
||||||
("Skipped", "ข้าม"),
|
("Request access to your device", "คำขอการเข้าถึงอุปกรณ์ของคุณ"),
|
||||||
("Add to Address Book", "เพิ่มไปยังสมุดรายชื่อ"),
|
("Hide connection management window", "ซ่อนหน้าต่างการจัดการการเชื่อมต่อ"),
|
||||||
("Group", "กลุ่ม"),
|
("hide_cm_tip", "อนุญาตการซ่อนก็ต่อเมื่อยอมรับการเชื่อมต่อด้วยรหัสผ่าน และต้องเป็นรหัสผ่านถาวรเท่านั้น"),
|
||||||
("Search", "ค้นหา"),
|
("wayland_experiment_tip", "การสนับสนุน Wayland ยังอยู่ในขั้นตอนการทดลอง กรุณาใช้ X11 หากคุณต้องการใช้งานการเข้าถึงแบบไม่มีผู้ดูแล"),
|
||||||
("Closed manually by the web console", "ถูกปิดโดยเว็บคอนโซล"),
|
("Right click to select tabs", "คลิกขวาเพื่อเลือกแท็บ"),
|
||||||
("Local keyboard type", "ประเภทคีย์บอร์ด"),
|
("Skipped", "ข้าม"),
|
||||||
("Select local keyboard type", "เลือกประเภทคีย์บอร์ด"),
|
("Add to Address Book", "เพิ่มไปยังสมุดรายชื่อ"),
|
||||||
("software_render_tip", ""),
|
("Group", "กลุ่ม"),
|
||||||
("Always use software rendering", ""),
|
("Search", "ค้นหา"),
|
||||||
("config_input", ""),
|
("Closed manually by the web console", "ถูกปิดโดยเว็บคอนโซล"),
|
||||||
].iter().cloned().collect();
|
("Local keyboard type", "ประเภทคีย์บอร์ด"),
|
||||||
}
|
("Select local keyboard type", "เลือกประเภทคีย์บอร์ด"),
|
||||||
|
("software_render_tip", ""),
|
||||||
|
("Always use software rendering", ""),
|
||||||
|
("config_input", ""),
|
||||||
|
].iter().cloned().collect();
|
||||||
|
}
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Always connect via relay"),
|
("Always connect via relay", "Always connect via relay"),
|
||||||
("whitelist_tip", "Bu masaüstüne yalnızca yetkili IP adresleri bağlanabilir"),
|
("whitelist_tip", "Bu masaüstüne yalnızca yetkili IP adresleri bağlanabilir"),
|
||||||
("Login", "Giriş yap"),
|
("Login", "Giriş yap"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Çıkış yap"),
|
("Logout", "Çıkış yap"),
|
||||||
("Tags", "Etiketler"),
|
("Tags", "Etiketler"),
|
||||||
("Search ID", "ID Arama"),
|
("Search ID", "ID Arama"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "一律透過轉送連線"),
|
("Always connect via relay", "一律透過轉送連線"),
|
||||||
("whitelist_tip", "只有白名單中的 IP 可以存取"),
|
("whitelist_tip", "只有白名單中的 IP 可以存取"),
|
||||||
("Login", "登入"),
|
("Login", "登入"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "登出"),
|
("Logout", "登出"),
|
||||||
("Tags", "標籤"),
|
("Tags", "標籤"),
|
||||||
("Search ID", "搜尋 ID"),
|
("Search ID", "搜尋 ID"),
|
||||||
|
@ -210,6 +210,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Always connect via relay", "Завжди підключатися через ретрансляційний сервер"),
|
("Always connect via relay", "Завжди підключатися через ретрансляційний сервер"),
|
||||||
("whitelist_tip", "Тільки IP-адреси з білого списку можуть отримати доступ до мене"),
|
("whitelist_tip", "Тільки IP-адреси з білого списку можуть отримати доступ до мене"),
|
||||||
("Login", "Увійти"),
|
("Login", "Увійти"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Вийти"),
|
("Logout", "Вийти"),
|
||||||
("Tags", "Ключові слова"),
|
("Tags", "Ключові слова"),
|
||||||
("Search ID", "Пошук за ID"),
|
("Search ID", "Пошук за ID"),
|
||||||
|
@ -210,6 +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"),
|
("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"),
|
("whitelist_tip", "Chỉ có những IP đựoc cho phép mới có thể truy cập"),
|
||||||
("Login", "Đăng nhập"),
|
("Login", "Đăng nhập"),
|
||||||
|
("Verify", ""),
|
||||||
|
("Remember me", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Verification code", ""),
|
||||||
|
("verification_tip", ""),
|
||||||
("Logout", "Đăng xuất"),
|
("Logout", "Đăng xuất"),
|
||||||
("Tags", "Tags"),
|
("Tags", "Tags"),
|
||||||
("Search ID", "Tìm ID"),
|
("Search ID", "Tìm ID"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user