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:
@@ -0,0 +1,72 @@
|
||||
class SignalSharePayload {
|
||||
const SignalSharePayload({
|
||||
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 Signal into normalized sender/message fields.
|
||||
class SignalShareParser {
|
||||
const SignalShareParser._();
|
||||
|
||||
static final RegExp _namePrefixPattern = RegExp(
|
||||
r'^([^:\n]{2,80}):\s*(.+)$',
|
||||
dotAll: true,
|
||||
);
|
||||
|
||||
static SignalSharePayload parse(String raw) {
|
||||
final String input = raw.trim();
|
||||
if (input.isEmpty) {
|
||||
return const SignalSharePayload(messageText: '');
|
||||
}
|
||||
|
||||
final RegExpMatch? match = _namePrefixPattern.firstMatch(input);
|
||||
if (match == null) {
|
||||
return SignalSharePayload(messageText: input);
|
||||
}
|
||||
|
||||
final String? senderName = _cleanSender(match.group(1));
|
||||
final String messageText = match.group(2)?.trim() ?? input;
|
||||
return SignalSharePayload(
|
||||
messageText: messageText,
|
||||
sourceDisplayName: senderName,
|
||||
sourceUserId: _normalizedKey(senderName),
|
||||
sourceThreadId: null,
|
||||
);
|
||||
}
|
||||
|
||||
static bool looksLikeSignalMessage(String raw) {
|
||||
final String input = raw.trim();
|
||||
if (input.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return _namePrefixPattern.hasMatch(input);
|
||||
}
|
||||
|
||||
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 : 'signal:$key';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user