import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:relationship_saver/core/config/app_theme.dart'; import 'package:relationship_saver/features/local/local_models.dart'; import 'package:relationship_saver/features/local/local_repository.dart'; import 'package:relationship_saver/features/shared/frosted_card.dart'; class MomentsView extends ConsumerStatefulWidget { const MomentsView({super.key}); @override ConsumerState createState() => _MomentsViewState(); } class _MomentsViewState extends ConsumerState { final TextEditingController _captureController = TextEditingController(); final TextEditingController _searchController = TextEditingController(); String? _selectedPersonId; @override void dispose() { _captureController.dispose(); _searchController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final AsyncValue localData = ref.watch( localRepositoryProvider, ); return localData.when( data: (LocalDataState data) { final List people = data.people; List moments = data.moments; final Map peopleById = { for (final PersonProfile person in people) person.id: person, }; if (_searchController.text.isNotEmpty) { final query = _searchController.text.toLowerCase(); moments = moments.where((m) { final person = peopleById[m.personId]; return m.title.toLowerCase().contains(query) || m.summary.toLowerCase().contains(query) || (person?.name.toLowerCase().contains(query) ?? false); }).toList(); } final String? fallbackPersonId = people.isEmpty ? null : people.first.id; final String? activePerson = people.any((PersonProfile p) => p.id == _selectedPersonId) ? _selectedPersonId : fallbackPersonId; return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { final bool compact = constraints.maxWidth < 760; return Padding( padding: EdgeInsets.fromLTRB( compact ? 16 : 28, compact ? 16 : 24, compact ? 16 : 28, compact ? 20 : 28, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Moments', style: Theme.of(context).textTheme.headlineMedium, ), const SizedBox(height: 6), Text( 'Capture wins and signals from everyday interactions.', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: AppTheme.textSecondary, ), ), const SizedBox(height: 16), TextField( controller: _searchController, decoration: InputDecoration( hintText: 'Search moments...', prefixIcon: const Icon(Icons.search), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), ), onChanged: (_) => setState(() {}), ), const SizedBox(height: 16), FrostedCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( controller: _captureController, minLines: 1, maxLines: compact ? 4 : 2, decoration: InputDecoration( hintText: 'Quick capture: what happened, how it felt, what to repeat...', hintStyle: Theme.of(context).textTheme.bodyMedium ?.copyWith(color: AppTheme.textSecondary), border: InputBorder.none, ), ), const SizedBox(height: 8), if (compact) Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (people.isNotEmpty) DropdownButtonFormField( initialValue: activePerson, items: people .map( (PersonProfile person) => DropdownMenuItem( value: person.id, child: Text(person.name), ), ) .toList(growable: false), onChanged: (String? value) { setState(() { _selectedPersonId = value; }); }, decoration: const InputDecoration( labelText: 'Person', ), ), const SizedBox(height: 10), FilledButton.icon( onPressed: people.isEmpty || activePerson == null ? null : () => _addCapture(activePerson), icon: const Icon(Icons.add_rounded), label: const Text('Add Capture'), ), ], ) else Row( children: [ if (people.isNotEmpty) DropdownButton( value: activePerson, hint: const Text('Person'), items: people .map( (PersonProfile person) => DropdownMenuItem( value: person.id, child: Text(person.name), ), ) .toList(growable: false), onChanged: (String? value) { setState(() { _selectedPersonId = value; }); }, ), const SizedBox(width: 8), FilledButton.icon( onPressed: people.isEmpty || activePerson == null ? null : () => _addCapture(activePerson), icon: const Icon(Icons.add_rounded), label: const Text('Add Capture'), ), ], ), if (people.isEmpty) Padding( padding: const EdgeInsets.only(top: 10), child: Text( 'Add people first before capturing moments.', style: Theme.of(context).textTheme.bodyMedium ?.copyWith(color: AppTheme.textSecondary), ), ), ], ), ), const SizedBox(height: 16), Expanded( child: ListView.separated( itemCount: moments.length, separatorBuilder: (_, _) => const SizedBox(height: 12), itemBuilder: (BuildContext context, int index) { final RelationshipMoment moment = moments[index]; final PersonProfile? person = peopleById[moment.personId]; return FrostedCard( child: compact ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 10, height: 10, decoration: const BoxDecoration( color: Color(0xFF1AB6C8), shape: BoxShape.circle, ), ), const SizedBox(width: 10), Expanded( child: Text( moment.title, style: Theme.of( context, ).textTheme.titleMedium, maxLines: 1, overflow: TextOverflow.ellipsis, ), ), IconButton( onPressed: () { ref .read( localRepositoryProvider .notifier, ) .deleteMoment(moment.id); }, icon: const Icon( Icons.delete_outline_rounded, ), tooltip: 'Delete capture', ), ], ), Text( '${person?.name ?? 'Unknown'} • ${moment.type.toUpperCase()} • ${moment.at.month}/${moment.at.day}', style: Theme.of(context) .textTheme .labelLarge ?.copyWith( color: AppTheme.textSecondary, ), ), const SizedBox(height: 8), Text( moment.summary, style: Theme.of( context, ).textTheme.bodyLarge, ), ], ) : Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 12, height: 12, margin: const EdgeInsets.only(top: 6), decoration: const BoxDecoration( color: Color(0xFF1AB6C8), shape: BoxShape.circle, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( moment.title, style: Theme.of( context, ).textTheme.titleMedium, ), const SizedBox(height: 4), Text( '${person?.name ?? 'Unknown'} • ${moment.type.toUpperCase()}', style: Theme.of(context) .textTheme .labelLarge ?.copyWith( color: AppTheme.textSecondary, ), ), const SizedBox(height: 8), Text( moment.summary, style: Theme.of( context, ).textTheme.bodyLarge, ), ], ), ), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( '${moment.at.month}/${moment.at.day}', style: Theme.of(context) .textTheme .labelLarge ?.copyWith( color: AppTheme.textSecondary, ), ), const SizedBox(height: 8), IconButton( onPressed: () { ref .read( localRepositoryProvider .notifier, ) .deleteMoment(moment.id); }, icon: const Icon( Icons.delete_outline_rounded, ), tooltip: 'Delete capture', ), ], ), ], ), ); }, ), ), ], ), ); }, ); }, loading: () => const Center(child: CircularProgressIndicator()), error: (Object error, StackTrace stackTrace) { return const Center(child: Text('Unable to load moments')); }, ); } Future _addCapture(String personId) async { final String summary = _captureController.text.trim(); if (summary.isEmpty) { return; } await ref .read(localRepositoryProvider.notifier) .addMoment(personId: personId, summary: summary); _captureController.clear(); } }