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
+191
View File
@@ -16,6 +16,7 @@ class PersonProfile {
required this.nextMoment,
required this.tags,
required this.notes,
this.location,
});
final String id;
@@ -25,6 +26,7 @@ class PersonProfile {
final DateTime nextMoment;
final List<String> tags;
final String notes;
final String? location;
PersonProfile copyWith({
String? id,
@@ -34,6 +36,7 @@ class PersonProfile {
DateTime? nextMoment,
List<String>? tags,
String? notes,
String? location,
}) {
return PersonProfile(
id: id ?? this.id,
@@ -43,6 +46,7 @@ class PersonProfile {
nextMoment: nextMoment ?? this.nextMoment,
tags: tags ?? this.tags,
notes: notes ?? this.notes,
location: location ?? this.location,
);
}
@@ -55,6 +59,7 @@ class PersonProfile {
'nextMoment': nextMoment.toUtc().toIso8601String(),
'tags': tags,
'notes': notes,
'location': location,
};
}
@@ -69,6 +74,7 @@ class PersonProfile {
.map((dynamic tag) => '$tag')
.toList(growable: false),
notes: json['notes'] as String? ?? '',
location: json['location'] as String?,
);
}
}
@@ -318,6 +324,162 @@ class DashboardTask {
}
}
@immutable
class SourceProfileLink {
const SourceProfileLink({
required this.id,
required this.sourceApp,
required this.normalizedDisplayName,
required this.profileId,
required this.firstSeenAt,
required this.lastSeenAt,
this.sourceUserId,
this.sourceThreadId,
});
final String id;
final String sourceApp;
final String? sourceUserId;
final String? sourceThreadId;
final String normalizedDisplayName;
final String profileId;
final DateTime firstSeenAt;
final DateTime lastSeenAt;
SourceProfileLink copyWith({
String? id,
String? sourceApp,
String? sourceUserId,
String? sourceThreadId,
String? normalizedDisplayName,
String? profileId,
DateTime? firstSeenAt,
DateTime? lastSeenAt,
}) {
return SourceProfileLink(
id: id ?? this.id,
sourceApp: sourceApp ?? this.sourceApp,
sourceUserId: sourceUserId ?? this.sourceUserId,
sourceThreadId: sourceThreadId ?? this.sourceThreadId,
normalizedDisplayName:
normalizedDisplayName ?? this.normalizedDisplayName,
profileId: profileId ?? this.profileId,
firstSeenAt: firstSeenAt ?? this.firstSeenAt,
lastSeenAt: lastSeenAt ?? this.lastSeenAt,
);
}
Map<String, dynamic> toJson() {
return <String, dynamic>{
'id': id,
'sourceApp': sourceApp,
'sourceUserId': sourceUserId,
'sourceThreadId': sourceThreadId,
'normalizedDisplayName': normalizedDisplayName,
'profileId': profileId,
'firstSeenAt': firstSeenAt.toUtc().toIso8601String(),
'lastSeenAt': lastSeenAt.toUtc().toIso8601String(),
};
}
factory SourceProfileLink.fromJson(Map<String, dynamic> json) {
return SourceProfileLink(
id: json['id'] as String,
sourceApp: json['sourceApp'] as String,
sourceUserId: json['sourceUserId'] as String?,
sourceThreadId: json['sourceThreadId'] as String?,
normalizedDisplayName: json['normalizedDisplayName'] as String? ?? '',
profileId: json['profileId'] as String,
firstSeenAt: DateTime.parse(json['firstSeenAt'] as String).toLocal(),
lastSeenAt: DateTime.parse(json['lastSeenAt'] as String).toLocal(),
);
}
}
@immutable
class SharedMessageEntry {
const SharedMessageEntry({
required this.id,
required this.sourceApp,
required this.profileId,
required this.messageText,
required this.sharedAt,
required this.importedAt,
required this.resolvedAutomatically,
this.sourceDisplayName,
this.sourceUserId,
this.sourceThreadId,
});
final String id;
final String sourceApp;
final String profileId;
final String messageText;
final DateTime sharedAt;
final DateTime importedAt;
final bool resolvedAutomatically;
final String? sourceDisplayName;
final String? sourceUserId;
final String? sourceThreadId;
SharedMessageEntry copyWith({
String? id,
String? sourceApp,
String? profileId,
String? messageText,
DateTime? sharedAt,
DateTime? importedAt,
bool? resolvedAutomatically,
String? sourceDisplayName,
String? sourceUserId,
String? sourceThreadId,
}) {
return SharedMessageEntry(
id: id ?? this.id,
sourceApp: sourceApp ?? this.sourceApp,
profileId: profileId ?? this.profileId,
messageText: messageText ?? this.messageText,
sharedAt: sharedAt ?? this.sharedAt,
importedAt: importedAt ?? this.importedAt,
resolvedAutomatically:
resolvedAutomatically ?? this.resolvedAutomatically,
sourceDisplayName: sourceDisplayName ?? this.sourceDisplayName,
sourceUserId: sourceUserId ?? this.sourceUserId,
sourceThreadId: sourceThreadId ?? this.sourceThreadId,
);
}
Map<String, dynamic> toJson() {
return <String, dynamic>{
'id': id,
'sourceApp': sourceApp,
'profileId': profileId,
'messageText': messageText,
'sharedAt': sharedAt.toUtc().toIso8601String(),
'importedAt': importedAt.toUtc().toIso8601String(),
'resolvedAutomatically': resolvedAutomatically,
'sourceDisplayName': sourceDisplayName,
'sourceUserId': sourceUserId,
'sourceThreadId': sourceThreadId,
};
}
factory SharedMessageEntry.fromJson(Map<String, dynamic> json) {
return SharedMessageEntry(
id: json['id'] as String,
sourceApp: json['sourceApp'] as String,
profileId: json['profileId'] as String,
messageText: json['messageText'] as String? ?? '',
sharedAt: DateTime.parse(json['sharedAt'] as String).toLocal(),
importedAt: DateTime.parse(json['importedAt'] as String).toLocal(),
resolvedAutomatically: json['resolvedAutomatically'] as bool? ?? true,
sourceDisplayName: json['sourceDisplayName'] as String?,
sourceUserId: json['sourceUserId'] as String?,
sourceThreadId: json['sourceThreadId'] as String?,
);
}
}
@immutable
class DashboardSummary {
const DashboardSummary({
@@ -341,6 +503,8 @@ class LocalDataState {
required this.ideas,
required this.reminders,
required this.tasks,
this.sourceLinks = const <SourceProfileLink>[],
this.sharedMessages = const <SharedMessageEntry>[],
});
final List<PersonProfile> people;
@@ -348,6 +512,8 @@ class LocalDataState {
final List<RelationshipIdea> ideas;
final List<ReminderRule> reminders;
final List<DashboardTask> tasks;
final List<SourceProfileLink> sourceLinks;
final List<SharedMessageEntry> sharedMessages;
LocalDataState copyWith({
List<PersonProfile>? people,
@@ -355,6 +521,8 @@ class LocalDataState {
List<RelationshipIdea>? ideas,
List<ReminderRule>? reminders,
List<DashboardTask>? tasks,
List<SourceProfileLink>? sourceLinks,
List<SharedMessageEntry>? sharedMessages,
}) {
return LocalDataState(
people: people ?? this.people,
@@ -362,6 +530,8 @@ class LocalDataState {
ideas: ideas ?? this.ideas,
reminders: reminders ?? this.reminders,
tasks: tasks ?? this.tasks,
sourceLinks: sourceLinks ?? this.sourceLinks,
sharedMessages: sharedMessages ?? this.sharedMessages,
);
}
@@ -398,6 +568,12 @@ class LocalDataState {
'tasks': tasks
.map((DashboardTask task) => task.toJson())
.toList(growable: false),
'sourceLinks': sourceLinks
.map((SourceProfileLink link) => link.toJson())
.toList(growable: false),
'sharedMessages': sharedMessages
.map((SharedMessageEntry entry) => entry.toJson())
.toList(growable: false),
};
}
@@ -433,6 +609,18 @@ class LocalDataState {
DashboardTask.fromJson(task as Map<String, dynamic>),
)
.toList(growable: false),
sourceLinks: (json['sourceLinks'] as List<dynamic>? ?? <dynamic>[])
.map(
(dynamic link) =>
SourceProfileLink.fromJson(link as Map<String, dynamic>),
)
.toList(growable: false),
sharedMessages: (json['sharedMessages'] as List<dynamic>? ?? <dynamic>[])
.map(
(dynamic entry) =>
SharedMessageEntry.fromJson(entry as Map<String, dynamic>),
)
.toList(growable: false),
);
}
@@ -447,6 +635,7 @@ class LocalDataState {
nextMoment: DateTime(2026, 2, 16, 19, 30),
tags: <String>['coffee', 'books', 'slow mornings'],
notes: 'Prefers thoughtful plans over expensive plans.',
location: 'Austin, TX',
),
PersonProfile(
id: 'p-jordan',
@@ -456,6 +645,7 @@ class LocalDataState {
nextMoment: DateTime(2026, 2, 18, 18, 0),
tags: <String>['running', 'street food'],
notes: 'Has a race on Sunday; ask how training is going.',
location: 'Chicago, IL',
),
PersonProfile(
id: 'p-mila',
@@ -465,6 +655,7 @@ class LocalDataState {
nextMoment: DateTime(2026, 2, 20, 12, 0),
tags: <String>['plants', 'retro music'],
notes: 'Birthday prep should start this week.',
location: 'Seattle, WA',
),
],
moments: <RelationshipMoment>[