import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:nomo/app/base/base_view.dart'; import 'package:nomo/app/widgets/click_opacity.dart'; import 'package:nomo/app/widgets/ix_app_bar.dart'; import 'package:nomo/config/theme/theme_extensions/theme_extension.dart'; import 'package:pinput/pinput.dart'; import '../controllers/deviceauth_controller.dart'; class DeviceauthView extends BaseView { const DeviceauthView({super.key}); @override PreferredSizeWidget? get appBar => IXAppBar(title: 'Device Authorization'); @override Widget buildContent(BuildContext context) { return Obx(() { final isPremium = controller.isPremium.value; return SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 20.h), child: Column( children: [ if (isPremium) ...[ // VIP用户界面 _buildVIPUserInterface(), ] else ...[ // 免费用户界面 _buildFreeUserInterface(), ], ], ), ); }); } /// 免费用户界面 - 显示授权码和等待授权 Widget _buildFreeUserInterface() { return Column( children: [ // 授权码显示区域 _buildAuthCodeDisplay(), 30.verticalSpaceFromWidth, // 说明卡片 _buildInstructionCards(), ], ); } /// VIP用户界面 - 输入授权码和管理设备 Widget _buildVIPUserInterface() { return Column( children: [ // 6个输入框在顶部 _buildTopCodeInput(), 20.verticalSpaceFromWidth, // 设备管理区域 _buildDeviceManagementSection(), 20.verticalSpaceFromWidth, // 授权步骤说明 _buildAuthorizationSteps(), ], ); } /// 授权码显示区域 Widget _buildAuthCodeDisplay() { return Container( padding: EdgeInsets.all(24.w), decoration: BoxDecoration( color: Get.reactiveTheme.highlightColor, borderRadius: BorderRadius.circular(16.r), ), child: Column( children: [ // 6位授权码 Obx( () => Text( controller.authCode.value, style: TextStyle( fontSize: 48.sp, fontWeight: FontWeight.bold, color: Colors.white, letterSpacing: 4.w, ), ), ), 20.verticalSpaceFromWidth, // 倒计时和复制按钮 Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.access_time, size: 16.w, color: Get.reactiveTheme.hintColor, ), SizedBox(width: 8.w), Obx( () => Text( controller.countdownText, style: TextStyle( fontSize: 16.sp, color: Get.reactiveTheme.hintColor, ), ), ), SizedBox(width: 16.w), Container( width: 1, height: 20.h, color: Get.reactiveTheme.hintColor.withOpacity(0.3), ), SizedBox(width: 16.w), ClickOpacity( onTap: () { Clipboard.setData( ClipboardData(text: controller.authCode.value), ); controller.copyAuthCode(); }, child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( 'Copy', style: TextStyle( fontSize: 16.sp, color: Get.reactiveTheme.hintColor, ), ), SizedBox(width: 4.w), Icon( Icons.copy, size: 16.w, color: Get.reactiveTheme.hintColor, ), ], ), ), ], ), 20.verticalSpaceFromWidth, // 提示文字 Text( 'Please keep this page open.', style: TextStyle( fontSize: 16.sp, color: const Color(0xFFFFB800), fontWeight: FontWeight.w500, ), ), ], ), ); } /// 说明卡片 Widget _buildInstructionCards() { return Container( padding: EdgeInsets.all(20.w), decoration: BoxDecoration( color: Get.reactiveTheme.hintColor.withOpacity(0.1), borderRadius: BorderRadius.circular(16.r), border: Border.all( color: const Color(0xFF00A8E8).withOpacity(0.3), width: 1, ), ), child: Column( children: [ _buildInstructionItem( icon: Icons.vpn_key, title: 'Authorization Code', description: 'This 6-digit code allows a VIP user to link your device. It refreshes every 15 minutes.', ), 20.verticalSpaceFromWidth, _buildInstructionItem( icon: Icons.workspace_premium, title: 'Share with Pre User', description: 'Tell the VIP user this code so they can enter it on their device to authorize you.', ), 20.verticalSpaceFromWidth, _buildInstructionItem( icon: Icons.access_time, title: 'Waiting for Authorization', description: 'Please keep this page open.\nOnce approved, your account will automatically upgrade and reconnect.', highlightText: 'Please keep this page open.', ), ], ), ); } /// 说明条目 Widget _buildInstructionItem({ required IconData icon, required String title, required String description, String? highlightText, }) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 40.w, height: 40.w, decoration: BoxDecoration( color: const Color(0xFF00A8E8).withOpacity(0.2), borderRadius: BorderRadius.circular(8.r), ), child: Icon(icon, size: 20.w, color: const Color(0xFF00A8E8)), ), SizedBox(width: 16.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: Colors.white, ), ), SizedBox(height: 8.h), highlightText != null ? RichText( text: TextSpan( children: [ TextSpan( text: highlightText, style: TextStyle( fontSize: 14.sp, color: const Color(0xFF00A8E8), ), ), TextSpan( text: '\n${description.substring(highlightText.length + 1)}', style: TextStyle( fontSize: 14.sp, color: Get.reactiveTheme.hintColor, ), ), ], ), ) : Text( description, style: TextStyle( fontSize: 14.sp, color: Get.reactiveTheme.hintColor, height: 1.4, ), ), ], ), ), ], ); } /// 顶部6个输入框 Widget _buildTopCodeInput() { return Container( padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h), decoration: BoxDecoration( color: Get.reactiveTheme.highlightColor, borderRadius: BorderRadius.circular(16.r), ), child: Obx( () => Pinput( length: 6, defaultPinTheme: PinTheme( width: 40.w, height: 50.h, textStyle: TextStyle( fontSize: 24.sp, fontWeight: FontWeight.bold, color: Colors.white, ), decoration: BoxDecoration( color: Get.reactiveTheme.hintColor.withOpacity(0.1), borderRadius: BorderRadius.circular(8.r), border: Border.all(color: Colors.transparent, width: 2), ), ), focusedPinTheme: PinTheme( width: 40.w, height: 50.h, textStyle: TextStyle( fontSize: 24.sp, fontWeight: FontWeight.bold, color: Colors.white, ), decoration: BoxDecoration( color: const Color(0xFF00A8E8).withOpacity(0.2), borderRadius: BorderRadius.circular(8.r), border: Border.all(color: const Color(0xFF00A8E8), width: 2), ), ), submittedPinTheme: PinTheme( width: 40.w, height: 50.h, textStyle: TextStyle( fontSize: 24.sp, fontWeight: FontWeight.bold, color: Colors.white, ), decoration: BoxDecoration( color: const Color(0xFF00A8E8).withOpacity(0.2), borderRadius: BorderRadius.circular(8.r), border: Border.all(color: Colors.transparent, width: 2), ), ), onChanged: (value) { controller.setInputCode(value); }, onCompleted: (pin) { controller.submitAuthCode(); }, inputFormatters: [FilteringTextInputFormatter.digitsOnly], keyboardType: TextInputType.number, showCursor: true, cursor: Container( width: 2, height: 20.h, color: const Color(0xFF00A8E8), ), ), ), ); } /// 设备管理区域 Widget _buildDeviceManagementSection() { return Container( padding: EdgeInsets.all(20.w), decoration: BoxDecoration( color: Get.reactiveTheme.highlightColor, borderRadius: BorderRadius.circular(16.r), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 标题 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Manage Devices', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, color: Colors.white, ), ), Obx( () => Text( controller.deviceCountText, style: TextStyle( fontSize: 16.sp, color: Get.reactiveTheme.hintColor, ), ), ), ], ), 20.verticalSpaceFromWidth, // 设备列表 Obx( () => Column( children: [ ...controller.currentDevices.map((device) { return _buildDeviceCard(device); }).toList(), // 等待激活的设备 if (controller.inputCode.value.isEmpty) _buildAwaitingDeviceCard(), ], ), ), 20.verticalSpaceFromWidth, // 授权限制说明 Text( 'Authorize up to 4 devices as Premium (${controller.deviceCountText})', style: TextStyle( fontSize: 14.sp, color: Get.reactiveTheme.hintColor, ), ), ], ), ); } /// 等待激活的设备卡片 Widget _buildAwaitingDeviceCard() { return Container( margin: EdgeInsets.only(bottom: 12.h), padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: Get.reactiveTheme.hintColor.withOpacity(0.1), borderRadius: BorderRadius.circular(12.r), ), child: Row( children: [ // 设备图标 Container( width: 40.w, height: 40.w, decoration: BoxDecoration( color: Get.reactiveTheme.hintColor.withOpacity(0.2), borderRadius: BorderRadius.circular(8.r), ), child: Icon( Icons.phone_iphone, size: 20.w, color: Get.reactiveTheme.hintColor, ), ), SizedBox(width: 12.w), // 设备信息 Expanded( child: Text( 'Awaiting Activation', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, color: Colors.white, ), ), ), ], ), ); } /// 设备卡片 Widget _buildDeviceCard(DeviceInfo device) { return Container( margin: EdgeInsets.only(bottom: 12.h), padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: Get.reactiveTheme.hintColor.withOpacity(0.1), borderRadius: BorderRadius.circular(12.r), ), child: Row( children: [ // 设备图标 Container( width: 40.w, height: 40.w, decoration: BoxDecoration( color: Get.reactiveTheme.hintColor.withOpacity(0.2), borderRadius: BorderRadius.circular(8.r), ), child: Icon( _getDeviceIcon(device.type), size: 20.w, color: Get.reactiveTheme.hintColor, ), ), SizedBox(width: 12.w), // 设备信息 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( device.name, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, color: Colors.white, ), ), if (device.uid != null) ...[ SizedBox(height: 4.h), Text( device.uid!, style: TextStyle( fontSize: 14.sp, color: Get.reactiveTheme.hintColor, ), ), ], if (device.date != null) ...[ SizedBox(height: 4.h), Text( device.date!, style: TextStyle( fontSize: 12.sp, color: Get.reactiveTheme.hintColor, ), ), ], ], ), ), // 操作按钮 if (device.isCurrent) ClickOpacity( onTap: () => controller.removeDevice(device.id), child: Container( padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h), decoration: BoxDecoration( color: Get.reactiveTheme.hintColor.withOpacity(0.2), borderRadius: BorderRadius.circular(6.r), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.delete, size: 16.w, color: Get.reactiveTheme.hintColor, ), SizedBox(width: 4.w), Text( 'Relieve', style: TextStyle( fontSize: 12.sp, color: Get.reactiveTheme.hintColor, ), ), ], ), ), ) else ClickOpacity( onTap: () => controller.removeDevice(device.id), child: Container( padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h), decoration: BoxDecoration( color: const Color(0xFF00A8E8), borderRadius: BorderRadius.circular(6.r), ), child: Text( 'Relieve', style: TextStyle( fontSize: 12.sp, color: Colors.white, fontWeight: FontWeight.w500, ), ), ), ), ], ), ); } /// 获取设备图标 IconData _getDeviceIcon(DeviceTypeEnum type) { switch (type) { case DeviceTypeEnum.ios: return Icons.phone_iphone; case DeviceTypeEnum.android: return Icons.android; case DeviceTypeEnum.windows: return Icons.laptop_windows; case DeviceTypeEnum.mac: return Icons.laptop_mac; } } /// 授权步骤说明 Widget _buildAuthorizationSteps() { return Container( padding: EdgeInsets.all(20.w), decoration: BoxDecoration( color: Get.reactiveTheme.highlightColor, borderRadius: BorderRadius.circular(16.r), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Authorization Steps', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, color: Colors.white, ), ), 20.verticalSpaceFromWidth, _buildStepItem( stepNumber: 1, title: 'Enter Code', description: 'Input the 6-digit code shown on the other device (free user). This code refreshes every 15 minutes.', icon: Icons.input, isCompleted: controller.inputCode.value.isNotEmpty, ), 16.verticalSpaceFromWidth, _buildStepItem( stepNumber: 2, title: 'Verify Device', description: 'We\'ll check if the entered code matches an active device waiting for authorization.', icon: Icons.verified_user, isCompleted: controller.isCodeComplete.value, ), 16.verticalSpaceFromWidth, _buildStepItem( stepNumber: 3, title: 'Authorization Successful', description: 'Once confirmed, the device will automatically upgrade and link to your account.', icon: Icons.check_circle, isCompleted: controller.authorizationStatus.value == AuthorizationStatus.success, ), ], ), ); } /// 步骤条目 Widget _buildStepItem({ required int stepNumber, required String title, required String description, required IconData icon, required bool isCompleted, }) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 32.w, height: 32.w, decoration: BoxDecoration( color: isCompleted ? const Color(0xFF00D9A3) : Get.reactiveTheme.hintColor.withOpacity(0.2), borderRadius: BorderRadius.circular(8.r), ), child: Center( child: isCompleted ? Icon(Icons.check, size: 16.w, color: Colors.white) : Icon(icon, size: 16.w, color: Get.reactiveTheme.hintColor), ), ), SizedBox(width: 12.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, color: Colors.white, ), ), SizedBox(height: 4.h), Text( description, style: TextStyle( fontSize: 14.sp, color: Get.reactiveTheme.hintColor, height: 1.4, ), ), ], ), ), ], ); } }