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
+123
View File
@@ -3,7 +3,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:relationship_saver/core/config/app_config.dart';
import 'package:relationship_saver/core/config/app_theme.dart';
import 'package:relationship_saver/features/auth/session_controller.dart';
import 'package:relationship_saver/features/local/local_repository.dart';
import 'package:relationship_saver/features/reminders/scheduling/reminder_scheduler_provider.dart';
import 'package:relationship_saver/features/share_intake/whatsapp_share_parser.dart';
import 'package:relationship_saver/features/shared/frosted_card.dart';
import 'package:relationship_saver/integrations/backend/models/backend_models.dart';
@@ -120,6 +122,12 @@ class SettingsView extends ConsumerWidget {
),
label: const Text('Request Notification Access'),
),
OutlinedButton.icon(
onPressed: () =>
_simulateWhatsAppShare(context, ref),
icon: const Icon(Icons.message_rounded),
label: const Text('Simulate WhatsApp Share'),
),
],
),
],
@@ -169,6 +177,59 @@ class SettingsView extends ConsumerWidget {
),
);
}
Future<void> _simulateWhatsAppShare(
BuildContext context,
WidgetRef ref,
) async {
final _ShareSimulationDraft? draft =
await showDialog<_ShareSimulationDraft>(
context: context,
builder: (BuildContext context) => const _ShareSimulationDialog(),
);
if (draft == null) {
return;
}
final WhatsAppSharePayload parsed = WhatsAppShareParser.parse(
draft.rawText,
);
if (parsed.messageText.trim().isEmpty) {
if (!context.mounted) {
return;
}
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Message text is empty.')));
return;
}
final SharedMessageIngestResult result = await ref
.read(localRepositoryProvider.notifier)
.ingestSharedMessage(
SharedMessageIngestInput(
sourceApp: 'whatsapp',
messageText: parsed.messageText,
sourceDisplayName: parsed.sourceDisplayName,
sourceUserId: parsed.sourceUserId,
sourceThreadId: parsed.sourceThreadId,
sharedAt: DateTime.now(),
),
);
if (!context.mounted) {
return;
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
result.createdProfile
? 'Imported and created ${result.profileName}.'
: 'Imported to ${result.profileName}.',
),
),
);
}
}
class _SettingRow extends StatelessWidget {
@@ -219,3 +280,65 @@ class _SettingRow extends StatelessWidget {
);
}
}
class _ShareSimulationDraft {
const _ShareSimulationDraft({required this.rawText});
final String rawText;
}
class _ShareSimulationDialog extends StatefulWidget {
const _ShareSimulationDialog();
@override
State<_ShareSimulationDialog> createState() => _ShareSimulationDialogState();
}
class _ShareSimulationDialogState extends State<_ShareSimulationDialog> {
late final TextEditingController _controller;
@override
void initState() {
super.initState();
_controller = TextEditingController(
text: '[2/18/26, 20:11] Ava Hart: Can we do coffee this Saturday?',
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Simulate WhatsApp Share'),
content: TextField(
controller: _controller,
minLines: 3,
maxLines: 6,
decoration: const InputDecoration(
hintText: 'Paste shared WhatsApp text payload',
),
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel'),
),
FilledButton(
onPressed: () {
final String rawText = _controller.text.trim();
if (rawText.isEmpty) {
return;
}
Navigator.of(context).pop(_ShareSimulationDraft(rawText: rawText));
},
child: const Text('Import'),
),
],
);
}
}