Implement WhatsApp share intake and person quick actions

This commit is contained in:
Rijad Zuzo
2026-02-19 00:09:28 +01:00
parent 818c1046f3
commit d2205bd3d9
15 changed files with 1463 additions and 4 deletions
@@ -40,10 +40,12 @@ void main() {
relationship: 'Friend',
notes: 'test',
tags: <String>['alpha'],
location: 'Berlin',
);
final afterAdd = await container.read(localRepositoryProvider.future);
expect(afterAdd.people.length, initialCount + 1);
expect(afterAdd.people.first.location, 'Berlin');
final String insertedId = afterAdd.people.first.id;
await container
@@ -78,6 +80,68 @@ void main() {
expect(afterDelete.moments.length, beforeCount);
});
test(
'ingestSharedMessage auto-creates profile and reuses source link for follow-up messages',
() async {
final ProviderContainer container = _createContainer();
addTearDown(container.dispose);
final LocalDataState before = await container.read(
localRepositoryProvider.future,
);
final SharedMessageIngestResult first = await container
.read(localRepositoryProvider.notifier)
.ingestSharedMessage(
const SharedMessageIngestInput(
sourceApp: 'whatsapp',
sourceDisplayName: 'Taylor Quinn',
sourceUserId: 'wa:taylor quinn',
messageText: 'Can we do coffee this week?',
),
);
final LocalDataState afterFirst = await container.read(
localRepositoryProvider.future,
);
expect(first.createdProfile, isTrue);
expect(afterFirst.people.length, before.people.length + 1);
expect(
afterFirst.sourceLinks.any((link) => link.profileId == first.profileId),
isTrue,
);
expect(
afterFirst.moments.any(
(RelationshipMoment moment) =>
moment.personId == first.profileId && moment.type == 'whatsapp',
),
isTrue,
);
final SharedMessageIngestResult second = await container
.read(localRepositoryProvider.notifier)
.ingestSharedMessage(
const SharedMessageIngestInput(
sourceApp: 'whatsapp',
sourceDisplayName: 'Taylor Quinn',
sourceUserId: 'wa:taylor quinn',
messageText: 'Lets plan Sunday brunch too.',
),
);
final LocalDataState afterSecond = await container.read(
localRepositoryProvider.future,
);
expect(second.createdProfile, isFalse);
expect(second.profileId, first.profileId);
expect(afterSecond.people.length, afterFirst.people.length);
expect(
afterSecond.sharedMessages.length,
afterFirst.sharedMessages.length + 1,
);
},
);
test('adds, archives, edits, and deletes ideas', () async {
final ProviderContainer container = _createContainer();
addTearDown(container.dispose);
@@ -52,7 +52,7 @@ void main() {
final LocalDataRecord? migrated = await hiveStore.read();
expect(migrated, isNotNull);
expect(migrated!.schemaVersion, 2);
expect(migrated!.schemaVersion, 3);
final LocalDataRecord? legacyAfter = await legacyStore.read();
expect(legacyAfter, isNull);
@@ -0,0 +1,33 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:relationship_saver/features/share_intake/whatsapp_share_parser.dart';
void main() {
test('parses sender-prefixed WhatsApp text', () {
final WhatsAppSharePayload payload = WhatsAppShareParser.parse(
'Ava Hart: Lets do coffee on Saturday?',
);
expect(payload.sourceDisplayName, 'Ava Hart');
expect(payload.sourceUserId, 'wa:ava hart');
expect(payload.messageText, 'Lets do coffee on Saturday?');
});
test('parses bracket timestamp style WhatsApp export line', () {
final WhatsAppSharePayload payload = WhatsAppShareParser.parse(
'[2/17/26, 22:41] Jordan Lee: Finished 5k today!',
);
expect(payload.sourceDisplayName, 'Jordan Lee');
expect(payload.sourceUserId, 'wa:jordan lee');
expect(payload.messageText, 'Finished 5k today!');
});
test('falls back to raw message when sender cannot be detected', () {
const String raw = 'Remember to ask about her interview tomorrow.';
final WhatsAppSharePayload payload = WhatsAppShareParser.parse(raw);
expect(payload.sourceDisplayName, isNull);
expect(payload.sourceUserId, isNull);
expect(payload.messageText, raw);
});
}