import 'package:flutter/material.dart'; import '../models/app_user.dart'; import '../models/incoming_question.dart'; import '../models/sync_result.dart'; import '../models/user_profile.dart'; import '../repositories/user_profile_repository.dart'; import '../services/auth_service.dart'; import '../services/questions_hub_service.dart'; import '../theme/app_theme.dart'; import '../widgets/swipe_question_tile.dart'; class HomeScreen extends StatelessWidget { const HomeScreen({ super.key, required this.user, this.profile, this.syncStatus = ProfileSyncStatus.idle, }); final AppUser user; final UserProfile? profile; final ProfileSyncStatus syncStatus; @override Widget build(BuildContext context) { final String? photoUrl = profile?.photoUrl ?? user.photoUrl; final String displayName = profile?.displayName ?? user.displayName ?? 'there'; final String? email = profile?.email ?? user.email; final Color syncIconColor = switch (syncStatus) { ProfileSyncStatus.syncing => Colors.white, ProfileSyncStatus.synced => AppColors.success, ProfileSyncStatus.error => Colors.redAccent, ProfileSyncStatus.offline => Colors.orange, ProfileSyncStatus.idle => AppColors.textSecondary, }; return Scaffold( appBar: AppBar( backgroundColor: Colors.transparent, centerTitle: true, title: ListenableBuilder( listenable: Listenable.merge([ QuestionsHubService.instance.hasPendingQuestion, QuestionsHubService.instance.pendingQuestion, QuestionsHubService.instance.pendingQuestionCount, ]), builder: (BuildContext context, Widget? child) { final int count = QuestionsHubService.instance.pendingQuestionCount.value; final bool hasPending = QuestionsHubService.instance.hasPendingQuestion.value; if (!hasPending || count < 1) { return const SizedBox.shrink(); } final int displayCount = count; return _QuestionEnvelopeButton( count: displayCount, onPressed: () => QuestionsHubService.instance.openQuestionPanel(), ); }, ), actions: [ IconButton( onPressed: () => UserProfileRepository.instance.sync(), tooltip: 'Sync profile', icon: Icon(Icons.sync, color: syncIconColor), ), IconButton( onPressed: () => AuthService.instance.signOut(), tooltip: 'Sign out', icon: const Icon(Icons.logout), ), ], ), body: DecoratedBox( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [AppColors.background, AppColors.surface], ), ), child: SafeArea( child: ListenableBuilder( listenable: Listenable.merge([ QuestionsHubService.instance.questionPanelOpen, QuestionsHubService.instance.hasPendingQuestion, QuestionsHubService.instance.questionQueue, QuestionsHubService.instance.pendingQuestion, QuestionsHubService.instance.questionActionBusy, QuestionsHubService.instance.pendingQuestionCount, ]), builder: (BuildContext context, Widget? child) { final QuestionsHubService hub = QuestionsHubService.instance; final bool panelOpen = hub.questionPanelOpen.value; final bool hasPending = hub.hasPendingQuestion.value; final IncomingQuestion? question = hub.currentQuestion; final bool showQuestionPanel = panelOpen && hasPending && question != null; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (showQuestionPanel) Expanded( child: Padding( padding: const EdgeInsets.fromLTRB(8, 4, 8, 8), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( children: [ const Spacer(), IconButton( onPressed: hub.closeQuestionPanel, tooltip: 'Close', icon: const Icon(Icons.close), ), ], ), const SizedBox(height: 4), Expanded( child: SwipeQuestionTile( key: ValueKey(question.id), questionId: question.id, busy: hub.questionActionBusy.value, onSwipeRight: (num answer) => hub.submitCurrentAnswer(answer: answer), onSwipeLeft: hub.deferCurrentQuestion, ), ), ], ), ), ), if (!showQuestionPanel) Expanded( child: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: AppColors.surfaceElevated, borderRadius: BorderRadius.circular(16), border: Border.all( color: AppColors.accent.withValues(alpha: 0.2), ), ), child: Column( children: [ if (photoUrl != null) CircleAvatar( radius: 36, backgroundImage: NetworkImage(photoUrl), ) else const CircleAvatar( radius: 36, child: Icon(Icons.person, size: 36), ), const SizedBox(height: 16), Text( 'Welcome, $displayName', style: Theme.of(context) .textTheme .headlineMedium, textAlign: TextAlign.center, ), if (email != null) ...[ const SizedBox(height: 8), Text( email, style: Theme.of(context).textTheme.bodyLarge, textAlign: TextAlign.center, ), ], const SizedBox(height: 12), _SyncStatusChip(status: syncStatus), const SizedBox(height: 20), const Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.check_circle, color: AppColors.success, size: 20, ), SizedBox(width: 8), Text( 'You\'re signed in', style: TextStyle( color: AppColors.success, fontWeight: FontWeight.w600, ), ), ], ), ], ), ), const SizedBox(height: 24), Text( profile?.dirty == true ? 'Profile saved locally. Will sync when online.' : 'Profile synced with your account.', style: Theme.of(context).textTheme.bodyLarge, textAlign: TextAlign.center, ), if (UserProfileRepository.instance.usesLocalStore) ...[ const SizedBox(height: 16), OutlinedButton.icon( onPressed: profile == null ? null : () async { final UserProfile current = profile!; await UserProfileRepository.instance .updateProfile( current.copyWith( onboardingCompleted: !current .onboardingCompleted, ), ); }, icon: const Icon(Icons.toggle_on_outlined), label: Text( profile?.onboardingCompleted == true ? 'Mark onboarding incomplete' : 'Mark onboarding complete', ), ), ], ], ), ), ), ], ); }, ), ), ), ); } } class _QuestionEnvelopeButton extends StatelessWidget { const _QuestionEnvelopeButton({ required this.count, required this.onPressed, }); final int count; final VoidCallback onPressed; @override Widget build(BuildContext context) { return Stack( clipBehavior: Clip.none, alignment: Alignment.center, children: [ IconButton( onPressed: onPressed, tooltip: count > 1 ? '$count questions' : 'New question', icon: const Icon(Icons.mail_outline, size: 22), style: IconButton.styleFrom( backgroundColor: AppColors.accent.withValues(alpha: 0.15), foregroundColor: AppColors.accent, padding: const EdgeInsets.all(8), minimumSize: const Size(36, 36), tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), ), if (count >= 2) Positioned( top: 4, right: 0, child: Container( padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2), decoration: BoxDecoration( color: AppColors.accent, borderRadius: BorderRadius.circular(10), border: Border.all(color: AppColors.surface, width: 1.5), ), constraints: const BoxConstraints(minWidth: 18, minHeight: 18), child: Text( '$count', textAlign: TextAlign.center, style: const TextStyle( color: AppColors.background, fontSize: 11, fontWeight: FontWeight.w700, height: 1.1, ), ), ), ), ], ); } } class _SyncStatusChip extends StatelessWidget { const _SyncStatusChip({required this.status}); final ProfileSyncStatus status; @override Widget build(BuildContext context) { final ({String label, Color color, IconData icon}) style = switch (status) { ProfileSyncStatus.syncing => ( label: 'Syncing…', color: AppColors.accent, icon: Icons.sync, ), ProfileSyncStatus.synced => ( label: 'Synced', color: AppColors.success, icon: Icons.cloud_done_outlined, ), ProfileSyncStatus.offline => ( label: 'Offline', color: Colors.orange, icon: Icons.cloud_off_outlined, ), ProfileSyncStatus.error => ( label: 'Sync error', color: Colors.redAccent, icon: Icons.error_outline, ), ProfileSyncStatus.idle => ( label: 'Ready', color: AppColors.accent, icon: Icons.cloud_queue_outlined, ), }; return Chip( avatar: Icon(style.icon, size: 18, color: style.color), label: Text(style.label), side: BorderSide(color: style.color.withValues(alpha: 0.4)), ); } }