feat: synchronisation mode deconnecte fin chat et stats

This commit is contained in:
2025-08-31 18:21:20 +02:00
parent f5bef999df
commit 96af94ad13
129 changed files with 125731 additions and 110375 deletions

View File

@@ -0,0 +1,139 @@
import 'package:hive/hive.dart';
part 'pending_request.g.dart';
/// Modèle pour stocker les requêtes API en attente quand l'application est hors ligne
/// Ces requêtes seront traitées automatiquement au retour de la connexion
@HiveType(typeId: 100)
class PendingRequest extends HiveObject {
/// ID unique de la requête (UUID)
@HiveField(0)
final String id;
/// Méthode HTTP (POST, GET, PUT, DELETE)
@HiveField(1)
final String method;
/// Path de l'API (ex: /chat/rooms/xxx/messages)
@HiveField(2)
final String path;
/// Body de la requête (données JSON)
@HiveField(3)
final Map<String, dynamic>? data;
/// Query parameters
@HiveField(4)
final Map<String, dynamic>? queryParams;
/// Timestamp de création - UTILISÉ POUR L'ORDRE FIFO
@HiveField(5)
final DateTime createdAt;
/// ID temporaire associé (ex: temp_msg_xxx, temp_user_xxx)
@HiveField(6)
final String? tempId;
/// Contexte de la requête (chat, user, operation, passage, etc.)
@HiveField(7)
final String context;
/// Nombre de tentatives de retry
@HiveField(8)
final int retryCount;
/// Message de la dernière erreur
@HiveField(9)
final String? errorMessage;
/// Métadonnées additionnelles (userId, amicaleId, etc.)
@HiveField(10)
final Map<String, dynamic>? metadata;
/// Priorité de la requête (0 = normale, 1 = haute pour passages)
@HiveField(11)
final int priority;
/// Headers spécifiques pour cette requête
@HiveField(12)
final Map<String, String>? headers;
PendingRequest({
required this.id,
required this.method,
required this.path,
this.data,
this.queryParams,
required this.createdAt,
this.tempId,
required this.context,
this.retryCount = 0,
this.errorMessage,
this.metadata,
this.priority = 0,
this.headers,
});
/// Créer une copie avec des modifications
PendingRequest copyWith({
String? id,
String? method,
String? path,
Map<String, dynamic>? data,
Map<String, dynamic>? queryParams,
DateTime? createdAt,
String? tempId,
String? context,
int? retryCount,
String? errorMessage,
Map<String, dynamic>? metadata,
int? priority,
Map<String, String>? headers,
}) {
return PendingRequest(
id: id ?? this.id,
method: method ?? this.method,
path: path ?? this.path,
data: data ?? this.data,
queryParams: queryParams ?? this.queryParams,
createdAt: createdAt ?? this.createdAt,
tempId: tempId ?? this.tempId,
context: context ?? this.context,
retryCount: retryCount ?? this.retryCount,
errorMessage: errorMessage ?? this.errorMessage,
metadata: metadata ?? this.metadata,
priority: priority ?? this.priority,
headers: headers ?? this.headers,
);
}
/// Calculer le prochain délai de retry basé sur le nombre de tentatives
Duration getNextRetryDelay() {
switch (retryCount) {
case 0:
return Duration.zero; // Immédiat
case 1:
return const Duration(seconds: 30);
case 2:
return const Duration(minutes: 2);
default:
return const Duration(minutes: 5);
}
}
/// Vérifier si la requête a expiré (>24h)
bool isExpired() {
final age = DateTime.now().difference(createdAt);
return age.inHours >= 24;
}
/// Obtenir une description lisible pour les logs
String toLogString() {
return '[$context] $method $path (ID: $id, TempID: $tempId, Priority: $priority, Retry: $retryCount)';
}
@override
String toString() {
return 'PendingRequest{id: $id, method: $method, path: $path, context: $context, priority: $priority, retryCount: $retryCount}';
}
}

View File

@@ -0,0 +1,77 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'pending_request.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class PendingRequestAdapter extends TypeAdapter<PendingRequest> {
@override
final int typeId = 100;
@override
PendingRequest read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return PendingRequest(
id: fields[0] as String,
method: fields[1] as String,
path: fields[2] as String,
data: (fields[3] as Map?)?.cast<String, dynamic>(),
queryParams: (fields[4] as Map?)?.cast<String, dynamic>(),
createdAt: fields[5] as DateTime,
tempId: fields[6] as String?,
context: fields[7] as String,
retryCount: fields[8] as int,
errorMessage: fields[9] as String?,
metadata: (fields[10] as Map?)?.cast<String, dynamic>(),
priority: fields[11] as int,
headers: (fields[12] as Map?)?.cast<String, String>(),
);
}
@override
void write(BinaryWriter writer, PendingRequest obj) {
writer
..writeByte(13)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.method)
..writeByte(2)
..write(obj.path)
..writeByte(3)
..write(obj.data)
..writeByte(4)
..write(obj.queryParams)
..writeByte(5)
..write(obj.createdAt)
..writeByte(6)
..write(obj.tempId)
..writeByte(7)
..write(obj.context)
..writeByte(8)
..write(obj.retryCount)
..writeByte(9)
..write(obj.errorMessage)
..writeByte(10)
..write(obj.metadata)
..writeByte(11)
..write(obj.priority)
..writeByte(12)
..write(obj.headers);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PendingRequestAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}