diff --git a/lib/constants/api.dart b/lib/constants/api.dart index 12b0692..01bcaa3 100644 --- a/lib/constants/api.dart +++ b/lib/constants/api.dart @@ -60,6 +60,12 @@ class _App { ///版本添加 String get updateAdd => '/manage/update/add'; + + ///输入密码 + String get psdAdd=>'/app/register'; + + ///密码登录 + String get psdLogin=>'/app/loginByPwd'; } class _Content { diff --git a/lib/model/user_info_model.dart b/lib/model/user_info_model.dart index 0a7d636..c9643e2 100644 --- a/lib/model/user_info_model.dart +++ b/lib/model/user_info_model.dart @@ -8,6 +8,7 @@ class UserInfoModel extends Equatable { final int id; final String name; final String phone; + final int havePwd; final int isVip; final int isTrial; final String tag; @@ -26,6 +27,7 @@ class UserInfoModel extends Equatable { id: 0, name: '', phone: '', + havePwd: 0, isVip: 0, isTrial: 0, tag: '', @@ -40,6 +42,7 @@ class UserInfoModel extends Equatable { required this.id, required this.name, required this.phone, + required this.havePwd, required this.isVip, required this.isTrial, required this.tag, @@ -55,6 +58,7 @@ class UserInfoModel extends Equatable { id, name, phone, + havePwd, isVip, isTrial, tag, diff --git a/lib/model/user_info_model.g.dart b/lib/model/user_info_model.g.dart index f07ef90..71fd105 100644 --- a/lib/model/user_info_model.g.dart +++ b/lib/model/user_info_model.g.dart @@ -11,6 +11,7 @@ UserInfoModel _$UserInfoModelFromJson(Map json) => id: json['id'] as int, name: json['name'] as String, phone: json['phone'] as String, + havePwd: json['havePwd'] as int, isVip: json['is_vip'] as int, isTrial: json['is_trial'] as int, tag: json['tag'] as String, @@ -30,6 +31,7 @@ Map _$UserInfoModelToJson(UserInfoModel instance) => 'id': instance.id, 'name': instance.name, 'phone': instance.phone, + 'havePwd': instance.havePwd, 'is_vip': instance.isVip, 'is_trial': instance.isTrial, 'tag': instance.tag, diff --git a/lib/ui/login/login_page.dart b/lib/ui/login/login_page.dart index c5529c9..4cd92f8 100644 --- a/lib/ui/login/login_page.dart +++ b/lib/ui/login/login_page.dart @@ -6,13 +6,18 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:project_telephony/ui/login/login_psd_page.dart'; +import 'package:project_telephony/ui/user/set_password_page.dart'; import 'package:project_telephony/ui/widget/plone_back_button.dart'; import 'package:project_telephony/utils/headers.dart'; +import 'package:provider/provider.dart'; import '../../base/base_style.dart'; import '../../constants/api.dart'; import '../../constants/environment/environment.dart'; import '../../model/network/api_client.dart'; +import '../../model/network/base_model.dart'; +import '../../providers/user_provider.dart'; import '../../utils/toast/cloud_toast.dart'; import '../../utils/user_tool.dart'; import '../tab_navigator.dart'; @@ -21,8 +26,7 @@ import '../widget/image_scaffold.dart'; import '../widget/plone_bottom.dart'; class LoginPage extends StatefulWidget { - const - LoginPage({Key? key}) : super(key: key); + const LoginPage({Key? key}) : super(key: key); @override _LoginPageState createState() => _LoginPageState(); @@ -40,8 +44,8 @@ class _LoginPageState extends State { int _countDownNum = 59; late TextEditingController _phoneController; late TextEditingController _smsCodeController; + late TextEditingController _pwdCodeController; late FocusNode _phoneFocusNode; - late FocusNode _smsCodeFocusNode; bool _cantSelected = false; DateTime? _lastTap; @@ -53,7 +57,7 @@ class _LoginPageState extends State { _smsCodeFocusNode = FocusNode(); _phoneController = TextEditingController(); _smsCodeController = TextEditingController(); - // final userProvider = Provider.of(context, listen: false); + _pwdCodeController = TextEditingController(); var env = const String.fromEnvironment('ENV', defaultValue: 'dev'); if (kDebugMode) { print('env :$env'); @@ -70,11 +74,13 @@ class _LoginPageState extends State { _smsCodeFocusNode.unfocus(); _phoneController.dispose(); _smsCodeController.dispose(); + _pwdCodeController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { + return CloudScaffold( systemStyle: const SystemUiOverlayStyle( statusBarIconBrightness: Brightness.dark, @@ -116,9 +122,26 @@ class _LoginPageState extends State { style: TextStyle(fontSize: 32.sp, color: const Color(0xFF999999)), ), 80.hb, - _getBox('+86', 36, _phoneTFWidget()), - _getBox('验证码', 32, _codeWidget()), - 112.hb, + LoginBoxWidget(text: '+86', fontSize: 36, plone: _phoneTFWidget()), + LoginBoxWidget(text: '验证码', fontSize: 32, plone: _codeWidget()), + Row( + children: [ + const Spacer(), + TextButton( + onPressed: () { + Get.to(()=>const LoginPsdPage()); + }, + child: const Text( + '密码登录', + style: TextStyle( + fontSize: 15, + decoration: TextDecoration.underline, + ), + ), + ), + ], + ), + 60.hb, PloneBottom( blM: false, border: _phoneController.text.length != 11, @@ -132,8 +155,8 @@ class _LoginPageState extends State { } else if (!_chooseAgreement) { BotToast.showText(text: "请同意并勾选隐私政策"); } else { - - if (_lastTap != null && DateTime.now().difference(_lastTap!).inSeconds < 2) { + if (_lastTap != null && + DateTime.now().difference(_lastTap!).inSeconds < 2) { // BotToast.showText(text: "过快"); return; } @@ -143,8 +166,11 @@ class _LoginPageState extends State { 'code': _smsCodeController.text }); if (base.code == 0) { - await UserTool.userProvider.setToken(base.data['token']); + await UserTool.userProvider.setToken(base.data['token']); Get.offAll(const TabNavigator()); + if (UserTool.userProvider.userInfo.havePwd == 0) { + Get.to(()=>const SetPasswordPage()); + } } else { CloudToast.show(base.msg); } @@ -160,39 +186,6 @@ class _LoginPageState extends State { ); } - _getBox(String text, int fontSize, Widget plone) { - return Container( - height: 144.w, - width: double.infinity, - padding: EdgeInsets.symmetric(vertical: 48.w), - decoration: BoxDecoration( - // color: Colors.black12, - border: Border( - bottom: BorderSide(color: const Color(0xFFE8E8E8), width: 1.w), - ), - ), - child: Row(children: [ - SizedBox( - width: 112.w, - child: Text( - text, - style: TextStyle( - fontSize: fontSize.sp, - color: BaseStyle.color333333, - fontWeight: FontWeight.bold), - ), - ), - Container( - width: 2.w, - height: 48.w, - margin: EdgeInsets.symmetric(horizontal: 32.w), - color: const Color(0xFFE8E8E8), - ), - plone, - ]), - ); - } - // 输入手机号 _phoneTFWidget() { return SizedBox( @@ -366,3 +359,50 @@ class _LoginPageState extends State { ); } } + +class LoginBoxWidget extends StatelessWidget { + final String text; + final int fontSize; + final Widget plone; + + const LoginBoxWidget( + {Key? key, + required this.text, + required this.fontSize, + required this.plone}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: 144.w, + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 48.w), + decoration: BoxDecoration( + // color: Colors.black12, + border: Border( + bottom: BorderSide(color: const Color(0xFFE8E8E8), width: 1.w), + ), + ), + child: Row(children: [ + SizedBox( + width: 112.w, + child: Text( + text, + style: TextStyle( + fontSize: fontSize.sp, + color: BaseStyle.color333333, + fontWeight: FontWeight.bold), + ), + ), + Container( + width: 2.w, + height: 48.w, + margin: EdgeInsets.symmetric(horizontal: 32.w), + color: const Color(0xFFE8E8E8), + ), + plone, + ]), + ); + } +} diff --git a/lib/ui/login/login_psd_page.dart b/lib/ui/login/login_psd_page.dart new file mode 100644 index 0000000..ab21946 --- /dev/null +++ b/lib/ui/login/login_psd_page.dart @@ -0,0 +1,266 @@ +import 'package:bot_toast/bot_toast.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:get/get_core/src/get_main.dart'; +import 'package:project_telephony/extensions/num_ext.dart'; +import 'package:project_telephony/ui/login/login_page.dart'; + +import '../../base/base_style.dart'; +import '../../constants/api.dart'; +import '../../gen/assets.gen.dart'; +import '../../model/network/api_client.dart'; +import '../../utils/toast/cloud_toast.dart'; +import '../../utils/user_tool.dart'; +import '../tab_navigator.dart'; +import '../user/content_authority_page.dart'; +import '../widget/image_scaffold.dart'; +import '../widget/plone_back_button.dart'; +import '../widget/plone_bottom.dart'; + +class LoginPsdPage extends StatefulWidget { + const LoginPsdPage({Key? key}) : super(key: key); + + @override + _LoginPsdPageState createState() => _LoginPsdPageState(); +} + +class _LoginPsdPageState extends State { + bool _chooseAgreement = false; + late TextEditingController _phoneController; + late TextEditingController _pwdController; + DateTime? _lastTap; + late bool _showPwd = true; + + @override + void initState() { + super.initState(); + _phoneController = TextEditingController(); + _pwdController = TextEditingController(); + } + + @override + void dispose() { + _phoneController.dispose(); + _pwdController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + + return CloudScaffold( + systemStyle: const SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.dark, + ), + path: Assets.images.bg.path, + appbar: Row( + children: [ + Padding( + padding: EdgeInsets.only(top: 88.w, left: 8.w), + child: const CloudBackButton(isSpecial: true), + ), + Padding( + padding: EdgeInsets.only(left: 186.w, top: 88.w), + child: Text( + "", + style: TextStyle( + fontSize: BaseStyle.fontSize34, + color: BaseStyle.color333333, + fontWeight: FontWeight.bold), + )) + ], + ), + extendBody: true, + body: Container( + padding: EdgeInsets.only(left: 64.w, right: 64.w, top: 124.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "短信帮手", + style: TextStyle( + fontSize: 64.sp, + fontWeight: FontWeight.bold, + color: Colors.black), + ), + 32.hb, + Text( + "希望能成为您的短信小助手", + style: TextStyle(fontSize: 32.sp, color: const Color(0xFF999999)), + ), + 80.hb, + LoginBoxWidget(text: "+86", fontSize: 36, plone: _phoneTFWidget()), + LoginBoxWidget(text: "密码", fontSize: 32, plone: _pwdWidget()), + Row( + children: [ + const Spacer(), + TextButton( + onPressed: () { + Get.to(() => const LoginPage()); + }, + child: const Text( + '验证码登录', + style: TextStyle( + fontSize: 15, + decoration: TextDecoration.underline, + ), + ), + ), + ], + ), + 60.hb, + PloneBottom( + blM: false, + border: _phoneController.text.length != 11, + opacity: _phoneController.text.length == 11 ? 1 : 0.4, + onTap: () async { + // DateTime? _lastTap; + if (_phoneController.text.length < 11) { + BotToast.showText(text: "请输入手机号"); + } else if (!_chooseAgreement) { + BotToast.showText(text: "请同意并勾选隐私政策"); + } else { + if (_lastTap != null && + DateTime.now().difference(_lastTap!).inSeconds < 2) { + // BotToast.showText(text: "过快"); + return; + } + _lastTap = DateTime.now(); + var base = await apiClient.request(API.app.psdLogin, data: { + 'phone': _phoneController.text, + 'password': _pwdController.text + }); + if (base.code == 0) { + await UserTool.userProvider.setToken(base.data['token']); + Get.offAll(const TabNavigator()); + } else { + CloudToast.show(base.msg); + + } + } + }, + text: "立即登录", + ), + 32.hb, + _getText() + ], + ), + ), + ); + } + + _getText() { + return GestureDetector( + onTap: () { + _chooseAgreement = !_chooseAgreement; + setState(() {}); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 50.w, + height: 50.w, + padding: EdgeInsets.only(top: 6.w, right: 5.w), + child: !_chooseAgreement + ? const Icon(CupertinoIcons.circle, + size: 18, color: Color(0xFFdddddd)) + : const Icon(CupertinoIcons.checkmark_circle, + size: 18, color: Colors.blue), + ), + RichText( + text: TextSpan( + text: "我已阅读并同意", + style: + TextStyle(color: BaseStyle.colorcccccc, fontSize: 12 * 2.sp), + children: [ + TextSpan( + text: '《短信帮手隐私协议》', + style: TextStyle(color: kPrimaryColor, fontSize: 12 * 2.sp), + recognizer: _recognizer(context, 2)), + ], + ), + ), + ], + ), + ); + } + + _recognizer(context, int type) { + final TapGestureRecognizer recognizer = TapGestureRecognizer(); + recognizer.onTap = () { + if (kDebugMode) { + Get.to(() => const ContentAuthorityPage()); + // print("点击协议了"); + } + + ///跳转到用户协议页面 + }; + return recognizer; + } + + _phoneTFWidget() { + return SizedBox( + // alignment: Alignment.centerLeft, + // padding: EdgeInsets.symmetric(horizontal: 72.w), + width: 430.w, + height: 50.w, + child: TextField( + maxLength: 11, + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[0-9]'))], + textCapitalization: TextCapitalization.none, + onChanged: (text) { + _phoneController.text = text; + setState(() {}); + }, + decoration: InputDecoration( + contentPadding: EdgeInsets.only(bottom: 20.w), + border: InputBorder.none, + counterText: "", + //textfield占位语,类似于iOS中的placeholder + hintText: "请输入手机号", + //占位语颜色 + hintStyle: const TextStyle(color: Colors.black12), + ), + ), + ); + } + + _pwdWidget() { + return SizedBox( + width: 430.w, + height: 50.w, + child: TextField( + obscureText: _showPwd, + maxLength: 16, + textCapitalization: TextCapitalization.none, + onChanged: (text) { + _pwdController.text = text; + setState(() {}); + }, + decoration: InputDecoration( + contentPadding: EdgeInsets.only(bottom: 20.w), + border: InputBorder.none, + counterText: "", + hintText: "请输入密码", + //占位语颜色 + hintStyle: const TextStyle(color: Colors.black12), + suffixIcon: IconButton( + padding: EdgeInsets.zero, + onPressed: () { + _showPwd = !_showPwd; + setState((){}); + }, + icon: Icon(Icons.remove_red_eye,color: _showPwd?Colors.black38:Colors.blue,), + ), + ), + ), + ); + } +} diff --git a/lib/ui/user/set_password_page.dart b/lib/ui/user/set_password_page.dart new file mode 100644 index 0000000..dc723ad --- /dev/null +++ b/lib/ui/user/set_password_page.dart @@ -0,0 +1,159 @@ +import 'package:bot_toast/bot_toast.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:project_telephony/utils/headers.dart'; + +import '../../constants/api.dart'; +import '../../model/network/api_client.dart'; +import '../../model/network/base_model.dart'; +import '../widget/scaffold_theme_widget.dart'; + +class SetPasswordPage extends StatefulWidget { + const SetPasswordPage({Key? key}) : super(key: key); + + @override + _SetPasswordPageState createState() => _SetPasswordPageState(); +} + +class _SetPasswordPageState extends State { + late TextEditingController _psdController; + late TextEditingController _psdTwiceController; + final _isOk=false; + + static bool isLoginPassword(String input) { + RegExp mobile = RegExp(r"(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$"); + return mobile.hasMatch(input); + } + + @override + void initState() { + super.initState(); + _psdController = TextEditingController(); + _psdTwiceController = TextEditingController(); + } + + @override + void dispose() { + _psdController.dispose(); + _psdTwiceController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ScaffoldThemeWidget( + title: "设置密码", + bottom: "确认", + onTap: () async { + if(_psdController.text==_psdTwiceController.text){ + BaseModel res = await apiClient.request(API.app.psdAdd, + data: {"password": _psdController.text}); + if(res.code==0){ + BotToast.showText(text: "设置成功"); + }else{ + BotToast.showText(text:"设置密码出错"); + } + Get.back(); + }else{ + BotToast.showText(text: "两次输入的密码不相同"); + } + }, + isOpacity:_psdController.text.isNotEmpty&&isLoginPassword(_psdController.text), + child: Container( + padding: EdgeInsets.all(40.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + '新 密 码 ', + style: TextStyle( + fontSize: 30.sp, + ), + ), + Expanded( + child: Padding( + padding: EdgeInsets.only(left: 10.w), + child: TextField( + maxLength: 16, + onChanged: (value){ + _psdController.text=value; + _psdController.value = TextEditingValue( + text: _psdController.text, + selection: + TextSelection.fromPosition(TextPosition( + affinity: TextAffinity.downstream, + offset: + _psdController.text.length, + ))); + setState((){}); + }, + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[A-Za-z0-9_]'))], + controller: _psdController, + decoration: const InputDecoration( + counterText: '', + hintText: '填写新密码', + hintStyle: TextStyle( + color: Colors.black38, + ), + ), + ), + ) + , + ), + ], + ), + 20.hb, + Row( + children: [ + Text( + '确认密码 ', + style: TextStyle( + fontSize: 30.sp, + ), + ), + Expanded( + child: Padding( + padding: EdgeInsets.only(left: 10.w), + child: TextField( + onChanged: (value){ + _psdTwiceController.text=value; + _psdTwiceController.value = TextEditingValue( + text: _psdTwiceController.text, + selection: + TextSelection.fromPosition(TextPosition( + affinity: TextAffinity.downstream, + offset: + _psdTwiceController.text.length, + ))); + setState((){}); + }, + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[A-Za-z0-9_]'))], + controller: _psdTwiceController, + decoration: const InputDecoration( + hintText: '再次确认密码', + hintStyle: TextStyle( + color: Colors.black38, + ), + ), + ), + ) + , + ), + ], + ), + 20.hb, + Text( + '密码必须是6~16位字母、数字、字符的组合', + style: TextStyle( + fontSize: 26.sp, + color: Colors.black38, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/ui/user/setting_page.dart b/lib/ui/user/setting_page.dart new file mode 100644 index 0000000..5575416 --- /dev/null +++ b/lib/ui/user/setting_page.dart @@ -0,0 +1,78 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:get/get_core/src/get_main.dart'; +import 'package:project_telephony/ui/user/set_password_page.dart'; +import 'package:project_telephony/ui/widget/scaffold_theme_widget.dart'; +import 'package:project_telephony/utils/user_tool.dart'; + +class SettingPage extends StatefulWidget { + const SettingPage({Key? key}) : super(key: key); + + @override + _SettingPageState createState() => _SettingPageState(); +} + +class _SettingPageState extends State { + @override + Widget build(BuildContext context) { + return ScaffoldThemeWidget( + title: "个人信息", + child: Container( + padding: EdgeInsets.all(40.w), + child: Column( + children: [ + Row( + children: [ + const Text( + '手机号', + style: TextStyle( + fontSize: 18, + ), + ), + const Spacer(), + Text( + UserTool.userProvider.userInfo.phone, + style: const TextStyle( + fontSize: 18, + color: Colors.black38, + ), + ), + Icon( + Icons.arrow_forward_ios, + size: 30.w, + ), + ], + ), + const Divider(), + GestureDetector( + + behavior: HitTestBehavior.opaque, + onTap: (){ + Get.to(()=>const SetPasswordPage()); + }, + child: Row( + children: [ + const Text( + '密码', + style: TextStyle( + fontSize: 18, + ), + ), + const Spacer(), + Icon( + Icons.arrow_forward_ios, + size: 30.w, + ), + ], + ), + ), + const Divider(), + ], + ), + ), + onTap: () {}, + ); + } +} diff --git a/lib/ui/user/user_page.dart b/lib/ui/user/user_page.dart index 1fd483c..12698a9 100644 --- a/lib/ui/user/user_page.dart +++ b/lib/ui/user/user_page.dart @@ -12,6 +12,8 @@ import 'package:project_telephony/ui/tab_navigator.dart'; import 'package:project_telephony/ui/user/content_authority_page.dart'; import 'package:project_telephony/ui/user/members_page.dart'; import 'package:project_telephony/ui/user/privacy_rights_page.dart'; +import 'package:project_telephony/ui/user/set_password_page.dart'; +import 'package:project_telephony/ui/user/setting_page.dart'; import 'package:project_telephony/ui/user/user_card_page.dart'; import 'package:project_telephony/ui/widget/image_scaffold.dart'; import 'package:project_telephony/ui/widget/plone_bottom.dart'; @@ -215,6 +217,8 @@ class _UserPageState extends State { onTap: () { if (!userProvider.isLogin) { Get.to(() => const LoginPage()); + } else { + Get.to(() => const SettingPage()); } }, child: Column( @@ -234,9 +238,10 @@ class _UserPageState extends State { Text( UserTool.userProvider.isLogin ? "欢迎您登录短信帮手" : "登录获取更多信息", style: TextStyle( - fontSize: BaseStyle.fontSize28, - color: BaseStyle.color333333), - ) + fontSize: BaseStyle.fontSize28, + color: BaseStyle.color333333, + ), + ), ], ), ), diff --git a/lib/ui/widget/scaffold_theme_widget.dart b/lib/ui/widget/scaffold_theme_widget.dart index 35c2790..649fb51 100644 --- a/lib/ui/widget/scaffold_theme_widget.dart +++ b/lib/ui/widget/scaffold_theme_widget.dart @@ -1,6 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:project_telephony/model/sms_content_model.dart'; +import 'package:project_telephony/utils/user_tool.dart'; +import 'package:provider/provider.dart'; +import 'package:velocity_x/velocity_x.dart'; import '../../base/base_style.dart'; import '../widget/plone_back_button.dart'; @@ -18,7 +22,7 @@ class ScaffoldThemeWidget extends StatefulWidget { Key? key, required this.title, required this.child, - required this.bottom, + this.bottom='', this.isBorder = true, this.isOpacity = true, required this.onTap, @@ -51,13 +55,15 @@ class _ScaffoldThemeWidgetState extends State { backgroundColor: kForeGroundColor), backgroundColor: Colors.white, body: widget.child, - bottomNavigationBar: - PloneBottom( - onTap: widget.onTap, - border: widget.isBorder, - opacity: widget.isOpacity ? 1 : 0.4, - text: widget.bottom, - ).paddingOnly(bottom: 30.w), + bottomNavigationBar:Offstage( + offstage: widget.bottom.isEmpty, + child: PloneBottom( + onTap: widget.onTap, + border: widget.isBorder, + opacity: widget.isOpacity ? 1 : 0.4, + text: widget.bottom, + ).paddingOnly(bottom: 30.w), + ) ); } }