import 'dart:convert'; import 'package:flutter/material.dart'; import '../application/application.dart'; import '../domain/domain.dart'; import 'theme.dart'; final class ShareInboxScreen extends StatefulWidget { const ShareInboxScreen({required this.shareUseCases, super.key}); final ShareUseCases shareUseCases; @override State createState() => _ShareInboxScreenState(); } final class _ShareInboxScreenState extends State { late Future> _items; @override void initState() { super.initState(); _items = widget.shareUseCases.refreshInbox(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Partages reçus')), body: FutureBuilder>( future: _items, builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const Center(child: CircularProgressIndicator()); } final items = snapshot.data ?? const []; if (items.isEmpty) { return const _EmptyInbox(); } return RefreshIndicator( onRefresh: _reload, child: ListView.separated( padding: const EdgeInsets.all(16), itemBuilder: (context, index) { final item = items[index]; return _ShareInboxTile( item: item, onAccept: item.status == ShareInboxStatus.pending ? () => _accept(item) : null, onDecline: item.status == ShareInboxStatus.pending ? () => _decline(item) : null, ); }, separatorBuilder: (context, index) => const SizedBox(height: 12), itemCount: items.length, ), ); }, ), ); } Future _reload() async { setState(() { _items = widget.shareUseCases.refreshInbox(); }); await _items; } Future _accept(ShareInboxItem item) async { await widget.shareUseCases.acceptShare(item.shareId); if (!mounted) return; await _reload(); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(_acceptedMessage(item.resourceType))), ); } Future _decline(ShareInboxItem item) async { await widget.shareUseCases.declineShare(item.shareId); if (!mounted) return; await _reload(); if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('Partage refusé.'))); } } final class _ShareInboxTile extends StatelessWidget { const _ShareInboxTile({ required this.item, required this.onAccept, required this.onDecline, }); final ShareInboxItem item; final VoidCallback? onAccept; final VoidCallback? onDecline; @override Widget build(BuildContext context) { final summary = _summaryFromPayload(item); return CourtBlazerAccentPanel( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon( item.resourceType == ShareResourceType.program ? Icons.list_alt : Icons.event_note, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _resourceTitle(item.resourceType), style: Theme.of(context).textTheme.labelLarge, ), const SizedBox(height: 4), Text( summary.name, style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 4), Text('Envoyé par ${item.senderUserId}'), if (summary.detail.isNotEmpty) ...[ const SizedBox(height: 4), Text(summary.detail), ], ], ), ), Chip( visualDensity: VisualDensity.compact, label: Text(_statusLabel(item.status)), ), ], ), if (item.status == ShareInboxStatus.pending) ...[ const SizedBox(height: 16), Row( children: [ Expanded( child: OutlinedButton( onPressed: onDecline, child: const Text('Refuser'), ), ), const SizedBox(width: 8), Expanded( child: FilledButton( onPressed: onAccept, child: const Text('Accepter'), ), ), ], ), ], ], ), ); } } final class _EmptyInbox extends StatelessWidget { const _EmptyInbox(); @override Widget build(BuildContext context) { return const Center( child: Padding( padding: EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text('Aucun partage reçu', textAlign: TextAlign.center), SizedBox(height: 8), Text( "Les programmes et séances qu'on t'envoie apparaîtront ici.", textAlign: TextAlign.center, ), ], ), ), ); } } final class _InboxSummary { const _InboxSummary({required this.name, required this.detail}); final String name; final String detail; } _InboxSummary _summaryFromPayload(ShareInboxItem item) { try { final payload = jsonDecode(item.payloadJson) as Map; final name = payload['name'] as String? ?? 'Partage GameTime'; return switch (item.resourceType) { ShareResourceType.program => _InboxSummary( name: name, detail: _programDetail(payload), ), ShareResourceType.workoutTemplate => _InboxSummary( name: name, detail: _templateDetail(payload), ), }; } on Object { return const _InboxSummary(name: 'Partage GameTime', detail: ''); } } String _programDetail(Map payload) { final exercises = payload['exercises'] as List? ?? const []; final sets = exercises.fold(0, (total, raw) { final exercise = raw as Map; return total + (exercise['setsCount'] as int? ?? 0); }); return '${exercises.length} exercice${exercises.length > 1 ? 's' : ''} · ' '$sets série${sets > 1 ? 's' : ''}'; } String _templateDetail(Map payload) { final programs = payload['programs'] as List? ?? const []; var exerciseCount = 0; for (final raw in programs) { final program = raw as Map; final snapshotJson = program['programSnapshotJson'] as String?; if (snapshotJson == null) continue; final snapshot = jsonDecode(snapshotJson) as Map; exerciseCount += (snapshot['exercises'] as List? ?? const []).length; } return '${programs.length} programme${programs.length > 1 ? 's' : ''} · ' '$exerciseCount exercice${exerciseCount > 1 ? 's' : ''}'; } String _resourceTitle(ShareResourceType type) { return switch (type) { ShareResourceType.program => 'Programme', ShareResourceType.workoutTemplate => 'Séance', }; } String _statusLabel(ShareInboxStatus status) { return switch (status) { ShareInboxStatus.pending => 'À traiter', ShareInboxStatus.accepted => 'Accepté', ShareInboxStatus.declined => 'Refusé', ShareInboxStatus.revoked => 'Retiré', }; } String _acceptedMessage(ShareResourceType type) { return switch (type) { ShareResourceType.program => 'Programme ajouté.', ShareResourceType.workoutTemplate => 'Séance ajoutée.', }; }