feat(monetization-261): prepare entitlement infrastructure and quota enforcement for release

- Add EntitlementSnapshot, EntitlementRevalidationUseCase, and BillingUseCases
- Add DriftEntitlementSnapshotRepository for offline entitlement caching
- Add HealthConnect gateway and integration
- Add quota enforcement in share acceptance (server and UI)
- Add entitlement API endpoints in server
- Add privacy policy v1 and release compliance checklist
- Add QA gates and device runbooks for release validation
- Add demo content screen and settings screen

Ticket #261
This commit is contained in:
2026-08-28 23:12:25 +02:00
parent d4df3822b7
commit b12a04ba30
10 changed files with 1096 additions and 26 deletions

View File

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import '../application/application.dart';
import '../domain/domain.dart';
import 'monetization_ui.dart';
import 'theme.dart';
final class ShareInboxScreen extends StatefulWidget {
@ -176,6 +177,11 @@ final class _ShareInboxTile extends StatelessWidget {
],
),
if (item.status == ShareInboxStatus.pending) ...[
const SizedBox(height: 16),
QuotaImpactNotice(
acceptedItemCount: _quotaImpactCount(item, summary),
acceptedItemLabel: _quotaImpactLabel(item.resourceType),
),
const SizedBox(height: 16),
if (item.resourceType == ShareResourceType.pack)
FilledButton(onPressed: onOpen, child: const Text('Voir le pack'))
@ -273,6 +279,11 @@ final class _SharePackDetailScreen extends StatelessWidget {
title: Text(name),
),
if (item.status == ShareInboxStatus.pending) ...[
const SizedBox(height: 16),
QuotaImpactNotice(
acceptedItemCount: _quotaImpactCount(item, summary),
acceptedItemLabel: _quotaImpactLabel(item.resourceType),
),
const SizedBox(height: 16),
FilledButton(
onPressed: onImport == null
@ -390,6 +401,22 @@ String _packBadge(int count) {
return 'Pack · $count séance${count > 1 ? 's' : ''}';
}
int _quotaImpactCount(ShareInboxItem item, _InboxSummary summary) {
return switch (item.resourceType) {
ShareResourceType.program => 1,
ShareResourceType.workoutTemplate => 1,
ShareResourceType.pack => summary.itemCount,
};
}
String _quotaImpactLabel(ShareResourceType type) {
return switch (type) {
ShareResourceType.program => 'programme',
ShareResourceType.workoutTemplate => 'séance',
ShareResourceType.pack => 'séance',
};
}
IconData _resourceIcon(ShareResourceType type) {
return switch (type) {
ShareResourceType.program => Icons.list_alt,