import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lucide_icons/lucide_icons.dart'; import 'package:url_launcher/url_launcher.dart'; import '../providers/theme_provider.dart'; import '../providers/navigation_provider.dart'; // Navigation import '../utils/translations.dart'; // Translation helper import '../widgets/settings/display_settings_section.dart'; import '../widgets/settings/other_settings_section.dart'; import '../widgets/settings/backup_settings_section.dart'; import '../widgets/gamification/level_title_card.dart'; import '../widgets/gamification/badge_case.dart'; import '../widgets/gamification/activity_stats.dart'; import '../theme/app_colors.dart'; import '../services/mbti_types.dart'; // Needed for type title display // v1.5 class SoulScreen extends ConsumerStatefulWidget { const SoulScreen({super.key}); @override ConsumerState createState() => _SoulScreenState(); } class _SoulScreenState extends ConsumerState { @override Widget build(BuildContext context) { final userProfile = ref.watch(userProfileProvider); final t = Translations(userProfile.locale); // Translation helper final appColors = Theme.of(context).extension()!; return Scaffold( appBar: AppBar( title: Text(t['myPage']), centerTitle: true, ), body: ListView( padding: const EdgeInsets.all(16), children: [ // Gamification Section (v1.3) const LevelTitleCard(), const SizedBox(height: 16), const ActivityStats(), const SizedBox(height: 16), const BadgeCase(), const SizedBox(height: 16), // Identity Section Padding( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4), child: Row( children: [ Icon( LucideIcons.fingerprint, size: 20, color: appColors.iconDefault, ), const SizedBox(width: 8), Text( t['profile'], style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: appColors.textPrimary, ), ), ], ), ), Card( color: appColors.surfaceSubtle, child: Column( children: [ ListTile( leading: Icon(LucideIcons.user, color: appColors.iconDefault), title: Text(t['nickname'], style: TextStyle(color: appColors.textPrimary)), subtitle: Text(userProfile.nickname ?? t['notSet'], style: TextStyle(color: appColors.textSecondary)), trailing: Icon(LucideIcons.chevronRight, color: appColors.iconSubtle), onTap: () => _showNicknameDialog(context, userProfile.nickname, t), ), Divider(height: 1, color: appColors.divider), ListTile( leading: Icon(LucideIcons.personStanding, color: appColors.iconDefault), title: Text(t['gender'], style: TextStyle(color: appColors.textPrimary)), subtitle: Text(_getGenderLabel(userProfile.gender, t), style: TextStyle(color: appColors.textSecondary)), trailing: Icon(LucideIcons.chevronRight, color: appColors.iconSubtle), onTap: () => _showGenderDialog(context, userProfile.gender, t), ), Divider(height: 1, color: appColors.divider), // 1. Real MBTI (User Input) - Core Value for Recommendation ListTile( leading: Icon(LucideIcons.brainCircuit, color: appColors.iconDefault), title: Text("あなたのMBTI", style: TextStyle(color: appColors.textPrimary)), subtitle: Text(userProfile.mbti ?? t['notSet'], style: TextStyle(color: appColors.textSecondary)), trailing: Icon(LucideIcons.chevronRight, color: appColors.iconSubtle), onTap: () => _showRealMbtiDialog(context, userProfile.mbti, t), ), Divider(height: 1, color: appColors.divider), // 2. Sake Persona (AI Diagnosis) - Entertainment Value ListTile( leading: Icon(LucideIcons.sparkles, color: appColors.brandAccent), title: Text(t['mbtiDiagnosis'], style: TextStyle(color: appColors.textPrimary)), subtitle: Text( userProfile.sakePersonaMbti != null ? MBTIType.types[userProfile.sakePersonaMbti]?.title ?? userProfile.sakePersonaMbti! : '未診断(AI分析)', style: TextStyle(color: appColors.textSecondary) ), trailing: Icon(LucideIcons.chevronRight, color: appColors.iconSubtle), onTap: () { // Navigate to Sommelier Tab (Index 2 in BottomNavBar) ref.read(currentTabIndexProvider.notifier).setIndex(2); }, ), ], ), ), const SizedBox(height: 24), // Display Settings (新設 - カラーテーマ + グリッド + フォント + 明るさ) const DisplaySettingsSection(), const SizedBox(height: 24), // other Settings OtherSettingsSection( title: t['otherSettings'], ), const SizedBox(height: 24), BackupSettingsSection(), ], ), ); } // Simplified Dialog for Real MBTI Selection void _showRealMbtiDialog(BuildContext context, String? current, Translations t) { final appColors = Theme.of(context).extension()!; // Standard MBTI Types const typesWithLabels = { 'INTJ': '建築家', 'INTP': '論理学者', 'ENTJ': '指揮官', 'ENTP': '討論者', 'INFJ': '提唱者', 'INFP': '仲介者', 'ENFJ': '主人公', 'ENFP': '広報運動家', 'ISTJ': '管理者', 'ISFJ': '擁護者', 'ESTJ': '幹部', 'ESFJ': '領事官', 'ISTP': '巨匠', 'ISFP': '冒険家', 'ESTP': '起業家', 'ESFP': 'エンターテイナー', }; showDialog( context: context, builder: (context) => SimpleDialog( title: Text(t['selectMbti']), contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 16), children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 8.0), child: Text( "診断済みのMBTIタイプを選択してください。\n性格に合った日本酒をおすすめします。", style: TextStyle(fontSize: 12, color: appColors.textSecondary), ), ), SizedBox( width: double.maxFinite, height: 300, child: ListView( children: typesWithLabels.entries.map((entry) => SimpleDialogOption( onPressed: () { ref.read(userProfileProvider.notifier).setIdentity(mbti: entry.key); Navigator.pop(context); }, child: Row( children: [ Icon( entry.key == current ? Icons.check_circle : Icons.circle_outlined, size: 20, color: entry.key == current ? appColors.brandPrimary : appColors.iconSubtle, ), const SizedBox(width: 16), Expanded( child: RichText( text: TextSpan( style: DefaultTextStyle.of(context).style.copyWith(fontSize: 16), children: [ TextSpan( text: entry.key, style: TextStyle(fontWeight: FontWeight.bold, color: appColors.textPrimary), ), TextSpan( text: ' (${entry.value})', style: TextStyle( fontSize: 14, color: appColors.textSecondary, ), ), ], ), ), ), ], ), )).toList(), ), ), // Link to 16Personalities Padding( padding: const EdgeInsets.all(16.0), child: InkWell( onTap: () async { final uri = Uri.parse('https://www.16personalities.com/ja/無料性格診断テスト'); if (await canLaunchUrl(uri)) { await launchUrl(uri, mode: LaunchMode.externalApplication); } else { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('ブラウザを開けませんでした')), ); } } }, child: Text( '自分のタイプがわからない場合\n(16Personalitiesで診断)', style: TextStyle(fontSize: 10, color: appColors.brandAccent, decoration: TextDecoration.underline), textAlign: TextAlign.center, ), ), ), ], ), ); } void _showNicknameDialog(BuildContext context, String? current, Translations t) { final controller = TextEditingController(text: current); showDialog( context: context, builder: (context) => AlertDialog( title: Text(t['changeNickname']), content: TextField( controller: controller, decoration: InputDecoration(hintText: t['enterName']), autofocus: true, ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text(t['cancel']), ), TextButton( onPressed: () { ref.read(userProfileProvider.notifier).setIdentity(nickname: controller.text); Navigator.pop(context); }, child: Text(t['save']), ), ], ), ); } void _showGenderDialog(BuildContext context, String? current, Translations t) { showDialog( context: context, builder: (context) => SimpleDialog( title: Text(t['selectGender']), children: [ _buildGenderOption(context, 'male', t['male'], current), _buildGenderOption(context, 'female', t['female'], current), _buildGenderOption(context, 'other', t['genderOther'], current), _buildGenderOption(context, '', t['genderNotAnswer'], current), ], ), ); } Widget _buildGenderOption(BuildContext context, String? value, String label, String? current) { final appColors = Theme.of(context).extension()!; return SimpleDialogOption( onPressed: () { ref.read(userProfileProvider.notifier).setIdentity(gender: value); Navigator.pop(context); }, child: Row( children: [ Icon( value == current ? Icons.check_circle : Icons.circle_outlined, color: value == current ? appColors.brandPrimary : appColors.iconSubtle, ), const SizedBox(width: 16), Text(label), ], ), ); } String _getGenderLabel(String? gender, Translations t) { switch (gender) { case 'male': return t['male']; case 'female': return t['female']; case 'other': return t['genderOther']; default: return t['notSet']; } } }