import 'package:flutter/material.dart'; import '../application/application.dart'; import '../domain/domain.dart'; import 'profile_screen.dart'; import 'theme.dart'; final class ShareFormScreen extends StatefulWidget { const ShareFormScreen({ required this.shareUseCases, required this.resourceType, required this.localResourceId, required this.resourceName, required this.resourceSummary, this.localResourceIds = const [], this.packName, this.authUseCases, this.syncUseCases, super.key, }); final ShareUseCases shareUseCases; final AuthUseCases? authUseCases; final SyncUseCases? syncUseCases; final ShareResourceType resourceType; final String localResourceId; final List localResourceIds; final String? packName; final String resourceName; final String resourceSummary; @override State createState() => _ShareFormScreenState(); } final class _ShareFormScreenState extends State { final _formKey = GlobalKey(); final _emailsController = TextEditingController(); var _sending = false; String? _message; @override void dispose() { _emailsController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(_shareTitle(widget.resourceType))), body: ListView( padding: const EdgeInsets.all(16), children: [ CourtBlazerAccentPanel( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( '${_resourceTitle(widget.resourceType)} à partager', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 8), Text(widget.resourceName), const SizedBox(height: 4), Text(widget.resourceSummary), const SizedBox(height: 16), Form( key: _formKey, child: TextFormField( controller: _emailsController, decoration: const InputDecoration( labelText: 'Emails destinataires', helperText: 'Sépare plusieurs emails par une virgule.', ), keyboardType: TextInputType.emailAddress, validator: _emailsValidator, ), ), if (_message != null) ...[ const SizedBox(height: 12), Text(_message!), ], const SizedBox(height: 16), FilledButton.icon( onPressed: _sending ? null : _send, icon: _sending ? const SizedBox.square( dimension: 18, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.ios_share), label: const Text('Envoyer le partage'), ), ], ), ), ], ), ); } Future _send() async { if (!_formKey.currentState!.validate()) return; setState(() { _sending = true; _message = null; }); try { final result = await widget.shareUseCases.sendShare( resourceType: widget.resourceType, localResourceId: widget.localResourceId, localResourceIds: widget.localResourceIds, packName: widget.packName, recipientEmails: _parseEmails(_emailsController.text), ); if (!mounted) return; setState(() { _sending = false; _message = _shareResultMessage(result); }); if (result.status == ShareSendStatus.notConnected) { await Navigator.of(context).push( MaterialPageRoute( builder: (context) => ShareAccountRequiredScreen( resourceType: widget.resourceType, shareUseCases: widget.shareUseCases, authUseCases: widget.authUseCases, syncUseCases: widget.syncUseCases, ), ), ); } } on DomainException { if (!mounted) return; setState(() { _sending = false; _message = 'Partage indisponible pour le moment.'; }); } } } final class ShareAccountRequiredScreen extends StatelessWidget { const ShareAccountRequiredScreen({ required this.resourceType, this.shareUseCases, this.authUseCases, this.syncUseCases, super.key, }); final ShareResourceType resourceType; final ShareUseCases? shareUseCases; final AuthUseCases? authUseCases; final SyncUseCases? syncUseCases; @override Widget build(BuildContext context) { final targetLabel = switch (resourceType) { ShareResourceType.program => 'ce programme', ShareResourceType.workoutTemplate => 'cette séance', ShareResourceType.pack => 'ce pack', }; final canOpenAuth = authUseCases != null && syncUseCases != null; return Scaffold( appBar: AppBar(title: const Text('Compte requis pour partager')), body: ListView( padding: const EdgeInsets.all(16), children: [ CourtBlazerAccentPanel( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( 'Compte requis pour partager', style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 8), Text( 'Connecte-toi pour envoyer $targetLabel à un autre compte ' 'GameTime.', ), const SizedBox(height: 16), FilledButton( onPressed: canOpenAuth ? () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => LoginScreen( authUseCases: authUseCases!, syncUseCases: syncUseCases!, shareUseCases: shareUseCases, ), ), ) : null, child: const Text('Se connecter'), ), const SizedBox(height: 8), OutlinedButton( onPressed: canOpenAuth ? () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => RegisterScreen( authUseCases: authUseCases!, syncUseCases: syncUseCases!, shareUseCases: shareUseCases, ), ), ) : null, child: const Text('Créer un compte'), ), const SizedBox(height: 8), TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Annuler'), ), ], ), ), ], ), ); } } String? _emailsValidator(String? value) { final emails = _parseEmails(value ?? ''); if (emails.isEmpty) { return "Saisis l'email du destinataire."; } final invalid = emails.any( (email) => !RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$').hasMatch(email), ); if (invalid) { return "Saisis l'email du destinataire."; } return null; } List _parseEmails(String value) { return value .split(RegExp(r'[,;\n]')) .map((email) => email.trim()) .where((email) => email.isNotEmpty) .toList(growable: false); } String _shareResultMessage(ShareSendResult result) { return switch (result.status) { ShareSendStatus.notConnected => 'Compte requis pour partager.', ShareSendStatus.queued => 'Partage enregistré. Envoi dès que possible.', ShareSendStatus.sent when result.unresolvedEmails.isNotEmpty => 'Partage envoyé. Non trouvés : ${result.unresolvedEmails.join(', ')}.', ShareSendStatus.sent => 'Partage envoyé.', }; } String _shareTitle(ShareResourceType type) { return switch (type) { ShareResourceType.program => 'Partager le programme', ShareResourceType.workoutTemplate => 'Partager la séance', ShareResourceType.pack => 'Partager le pack', }; } String _resourceTitle(ShareResourceType type) { return switch (type) { ShareResourceType.program => 'Programme', ShareResourceType.workoutTemplate => 'Séance', ShareResourceType.pack => 'Pack', }; } final class SharePackSelectionScreen extends StatefulWidget { const SharePackSelectionScreen({ required this.shareUseCases, this.authUseCases, this.syncUseCases, super.key, }); final ShareUseCases shareUseCases; final AuthUseCases? authUseCases; final SyncUseCases? syncUseCases; @override State createState() => _SharePackSelectionScreenState(); } final class _SharePackSelectionScreenState extends State { final _nameController = TextEditingController(); late Future> _templates; final _selectedTemplateIds = {}; @override void initState() { super.initState(); _templates = widget.shareUseCases.listShareableWorkoutTemplates(); } @override void dispose() { _nameController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Composer un pack')), body: FutureBuilder>( future: _templates, builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const Center(child: CircularProgressIndicator()); } final templates = snapshot.data ?? const []; if (templates.isEmpty) { return const Center( child: Padding( padding: EdgeInsets.all(24), child: Text( 'Aucune séance à partager.', textAlign: TextAlign.center, ), ), ); } return ListView( padding: const EdgeInsets.all(16), children: [ CourtBlazerAccentPanel( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( 'Pack à partager', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 12), TextField( controller: _nameController, decoration: const InputDecoration( labelText: 'Nom du pack', ), onChanged: (_) => setState(() {}), ), const SizedBox(height: 16), Text( 'Séances', style: Theme.of(context).textTheme.labelLarge, ), const SizedBox(height: 8), for (final template in templates) Material( type: MaterialType.transparency, child: CheckboxListTile( contentPadding: EdgeInsets.zero, value: _selectedTemplateIds.contains( template.metadata.id, ), title: Text(template.name), subtitle: Text(_templatePackSummary(template)), onChanged: (selected) { setState(() { if (selected == true) { _selectedTemplateIds.add(template.metadata.id); } else { _selectedTemplateIds.remove( template.metadata.id, ); } }); }, ), ), const SizedBox(height: 16), FilledButton.icon( onPressed: _canContinue ? _continueToRecipients : null, icon: const Icon(Icons.inventory_2_outlined), label: const Text('Continuer'), ), ], ), ), ], ); }, ), ); } bool get _canContinue => _nameController.text.trim().isNotEmpty && _selectedTemplateIds.isNotEmpty; Future _continueToRecipients() async { final name = _nameController.text.trim(); final ids = _selectedTemplateIds.toList(growable: false); await Navigator.of(context).push( MaterialPageRoute( builder: (context) => ShareFormScreen( shareUseCases: widget.shareUseCases, authUseCases: widget.authUseCases, syncUseCases: widget.syncUseCases, resourceType: ShareResourceType.pack, localResourceId: '', localResourceIds: ids, packName: name, resourceName: name, resourceSummary: '${ids.length} séance${ids.length > 1 ? 's' : ''}', ), ), ); } } String _templatePackSummary(WorkoutTemplate template) { return '${template.programs.length} programme' '${template.programs.length > 1 ? 's' : ''}'; }