feat(online): partage sortant et boîte de réception (ticket #69)

Ajoute l'écran de partage sortant (presentation/share_screen.dart) et
la boîte de réception des partages (presentation/share_inbox_screen.dart),
avec les points d'entrée sur profile_screen.dart, home_screen.dart,
program_screen.dart et workout_template_screen.dart. flutter analyze
propre (mêmes infos préexistantes), 132/132 tests verts, build APK
debug validé. Dernier ticket de développement du chantier "Ajouter
les features serveur au client" (#63) — il ne reste que la QA finale
(#70).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 07:23:14 +02:00
parent eee4348b52
commit d16a017743
11 changed files with 1598 additions and 22 deletions

View File

@ -5,16 +5,23 @@ import 'package:flutter/material.dart';
import '../application/application.dart';
import '../domain/domain.dart';
import 'exercise_library_screen.dart';
import 'share_screen.dart';
final class ProgramListScreen extends StatefulWidget {
const ProgramListScreen({
required this.programUseCases,
required this.exerciseUseCases,
this.shareUseCases,
this.authUseCases,
this.syncUseCases,
super.key,
});
final ProgramUseCases programUseCases;
final ExerciseUseCases exerciseUseCases;
final ShareUseCases? shareUseCases;
final AuthUseCases? authUseCases;
final SyncUseCases? syncUseCases;
@override
State<ProgramListScreen> createState() => _ProgramListScreenState();
@ -101,6 +108,9 @@ final class _ProgramListScreenState extends State<ProgramListScreen> {
builder: (context) => ProgramFormScreen(
programUseCases: widget.programUseCases,
exerciseUseCases: widget.exerciseUseCases,
shareUseCases: widget.shareUseCases,
authUseCases: widget.authUseCases,
syncUseCases: widget.syncUseCases,
program: program,
),
),
@ -161,12 +171,18 @@ final class ProgramFormScreen extends StatefulWidget {
const ProgramFormScreen({
required this.programUseCases,
required this.exerciseUseCases,
this.shareUseCases,
this.authUseCases,
this.syncUseCases,
this.program,
super.key,
});
final ProgramUseCases programUseCases;
final ExerciseUseCases exerciseUseCases;
final ShareUseCases? shareUseCases;
final AuthUseCases? authUseCases;
final SyncUseCases? syncUseCases;
final Program? program;
@override
@ -216,6 +232,14 @@ final class _ProgramFormScreenState extends State<ProgramFormScreen> {
title: Text(
_isEditing ? 'Modifier le programme' : 'Créer un programme',
),
actions: [
if (_isEditing && widget.shareUseCases != null)
IconButton(
tooltip: 'Partager',
icon: const Icon(Icons.ios_share),
onPressed: _shareProgram,
),
],
),
bottomNavigationBar: SafeArea(
minimum: const EdgeInsets.all(16),
@ -395,6 +419,25 @@ final class _ProgramFormScreenState extends State<ProgramFormScreen> {
}
}
Future<void> _shareProgram() async {
final program = widget.program;
final shareUseCases = widget.shareUseCases;
if (program == null || shareUseCases == null) return;
await Navigator.of(context).push<void>(
MaterialPageRoute(
builder: (context) => ShareFormScreen(
shareUseCases: shareUseCases,
authUseCases: widget.authUseCases,
syncUseCases: widget.syncUseCases,
resourceType: ShareResourceType.program,
localResourceId: program.metadata.id,
resourceName: program.name,
resourceSummary: _programSummary(program),
),
),
);
}
String? _nonNegativeIntValidator(String? value) {
final number = int.tryParse(value?.trim() ?? '');
if (number == null || number < 0) {