feat: add local-first private AI digest workflow

Migrate app code into canonical feature slices, add phone-only AI digest scheduling and review, wire local notification/background task support, and cover the flow with tests.
This commit is contained in:
Rijad Zuzo
2026-05-17 00:17:20 +02:00
parent dab50abf0e
commit f655adfbea
212 changed files with 24178 additions and 15895 deletions
@@ -1,78 +1,2 @@
class WhatsAppSharePayload {
const WhatsAppSharePayload({
required this.messageText,
this.sourceDisplayName,
this.sourceUserId,
this.sourceThreadId,
});
final String messageText;
final String? sourceDisplayName;
final String? sourceUserId;
final String? sourceThreadId;
}
/// Parses raw shared text from WhatsApp into normalized fields.
class WhatsAppShareParser {
const WhatsAppShareParser._();
static final RegExp _datePrefixPattern = RegExp(
r'^\[[^\]]+\]\s*([^:\n]{2,80}):\s*(.+)$',
dotAll: true,
);
static final RegExp _namePrefixPattern = RegExp(
r'^([^:\n]{2,80}):\s*(.+)$',
dotAll: true,
);
static WhatsAppSharePayload parse(String raw) {
final String input = raw.trim();
if (input.isEmpty) {
return const WhatsAppSharePayload(messageText: '');
}
String? senderName;
String messageText = input;
final RegExpMatch? dateMatch = _datePrefixPattern.firstMatch(input);
if (dateMatch != null) {
senderName = _cleanSender(dateMatch.group(1));
messageText = dateMatch.group(2)?.trim() ?? input;
} else {
final RegExpMatch? nameMatch = _namePrefixPattern.firstMatch(input);
if (nameMatch != null) {
senderName = _cleanSender(nameMatch.group(1));
messageText = nameMatch.group(2)?.trim() ?? input;
}
}
final String? sourceUserId = _normalizedKey(senderName);
return WhatsAppSharePayload(
messageText: messageText,
sourceDisplayName: senderName,
sourceUserId: sourceUserId,
sourceThreadId: null,
);
}
static String? _cleanSender(String? input) {
if (input == null) {
return null;
}
final String cleaned = input.trim();
return cleaned.isEmpty ? null : cleaned;
}
static String? _normalizedKey(String? input) {
if (input == null) {
return null;
}
final String key = input
.toLowerCase()
.replaceAll(RegExp(r'[^a-z0-9]+'), ' ')
.trim()
.replaceAll(RegExp(r'\s+'), ' ');
return key.isEmpty ? null : 'wa:$key';
}
}
// Legacy compatibility export for the WhatsApp share parser.
export 'package:relationship_saver/features/share_intake/domain/whatsapp_share_parser.dart';