feat(watch): clôture lot #91 - fréquence cardiaque live, notifications de séance et finitions montre

Consolide le lot applicatif watch companion validé :
- télémétrie fréquence cardiaque live remontée montre -> téléphone
  (collecteur watch, adapter Wear Data Layer, persistance Drift,
  propagation aux écrans historique/programme/profil/exécution)
- notifications de séance en arrière-plan côté téléphone (service
  foreground de statut + passerelle applicative)
- finitions montre : chrono d'étape, score d'étape, retrait du bouton
  "lancer une séance", thème, icônes et polices watch_app

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 05:56:12 +02:00
parent c65a5a76a9
commit 65d43b9768
80 changed files with 12292 additions and 892 deletions

View File

@ -89,14 +89,13 @@ final class _ProfileScreenState extends State<ProfileScreen> {
),
),
);
if (connected == true && mounted) {
_reloadSession();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Compte connecté. Synchronisation en arrière-plan.'),
),
);
}
if (connected != true || !mounted || !context.mounted) return;
_reloadSession();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Compte connecté. Synchronisation en arrière-plan.'),
),
);
}
Future<void> _openRegister(BuildContext context) async {
@ -109,14 +108,13 @@ final class _ProfileScreenState extends State<ProfileScreen> {
),
),
);
if (connected == true && mounted) {
_reloadSession();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Compte connecté. Synchronisation en arrière-plan.'),
),
);
}
if (connected != true || !mounted || !context.mounted) return;
_reloadSession();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Compte connecté. Synchronisation en arrière-plan.'),
),
);
}
Future<void> _confirmLogout(BuildContext context) async {
@ -189,13 +187,12 @@ final class _SignedOutProfile extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Compte optionnel',
'Compte GameTime',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8),
const Text(
'GameTime fonctionne entièrement sans compte. Connecte-toi '
'seulement si tu veux sauvegarder tes données en ligne ou '
'Connecte-toi pour sauvegarder tes données en ligne et '
'partager des programmes et séances.',
),
const SizedBox(height: 16),
@ -481,7 +478,8 @@ abstract interface class LocalBackupFileExporter {
});
}
final class SharePlusLocalBackupFileExporter implements LocalBackupFileExporter {
final class SharePlusLocalBackupFileExporter
implements LocalBackupFileExporter {
const SharePlusLocalBackupFileExporter();
@override
@ -950,11 +948,6 @@ final class _LoginScreenState extends State<LoginScreen> {
},
child: const Text('Créer un compte'),
),
const SizedBox(height: 16),
Text(
'Tu peux continuer à utiliser GameTime sans compte.',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
@ -1103,7 +1096,7 @@ final class _RegisterScreenState extends State<RegisterScreen> {
const SizedBox(height: 16),
Text(
'Le compte sert à synchroniser tes données et partager tes '
"contenus. L'app reste utilisable sans compte.",
'contenus.',
style: Theme.of(context).textTheme.bodySmall,
),
],
@ -1211,6 +1204,8 @@ String _loginErrorMessage(RemoteAuthFailure failure) {
RemoteAuthFailure.invalidCredentials => 'Email ou mot de passe incorrect.',
RemoteAuthFailure.network =>
'Connexion impossible pour le moment. Réessaie plus tard.',
RemoteAuthFailure.server =>
'Serveur indisponible pour le moment. Réessaie plus tard.',
_ => 'Connexion impossible pour le moment. Réessaie plus tard.',
};
}
@ -1221,6 +1216,8 @@ String _registerErrorMessage(RemoteAuthFailure failure) {
'Un compte existe déjà avec cet email.',
RemoteAuthFailure.network =>
'Création impossible pour le moment. Réessaie plus tard.',
RemoteAuthFailure.server =>
'Serveur indisponible pour le moment. Réessaie plus tard.',
_ => 'Création impossible pour le moment. Réessaie plus tard.',
};
}