Add local preference signal scaffolding

This commit is contained in:
Rijad Zuzo
2026-02-22 23:19:49 +01:00
parent 058e2c1787
commit 8c79ba1345
4 changed files with 499 additions and 0 deletions
+150
View File
@@ -6,6 +6,10 @@ enum IdeaType { gift, event }
enum ReminderCadence { daily, weekly, monthly }
enum PreferenceSignalPolarity { like, dislike, neutral }
enum PreferenceSignalStatus { inferred, confirmed, dismissed }
@immutable
class PersonProfile {
const PersonProfile({
@@ -596,6 +600,137 @@ class SharedInboxEntry {
}
}
@immutable
class PersonPreferenceSignal {
const PersonPreferenceSignal({
required this.id,
required this.personId,
required this.key,
required this.label,
required this.category,
required this.polarity,
required this.confidence,
required this.status,
required this.firstSeenAt,
required this.lastSeenAt,
required this.occurrenceCount,
this.sourceApps = const <String>[],
this.evidenceMessageIds = const <String>[],
this.evidenceSnippets = const <String>[],
});
final String id;
final String personId;
final String key;
final String label;
final String category;
final PreferenceSignalPolarity polarity;
final double confidence;
final PreferenceSignalStatus status;
final DateTime firstSeenAt;
final DateTime lastSeenAt;
final int occurrenceCount;
final List<String> sourceApps;
final List<String> evidenceMessageIds;
final List<String> evidenceSnippets;
PersonPreferenceSignal copyWith({
String? id,
String? personId,
String? key,
String? label,
String? category,
PreferenceSignalPolarity? polarity,
double? confidence,
PreferenceSignalStatus? status,
DateTime? firstSeenAt,
DateTime? lastSeenAt,
int? occurrenceCount,
List<String>? sourceApps,
List<String>? evidenceMessageIds,
List<String>? evidenceSnippets,
}) {
return PersonPreferenceSignal(
id: id ?? this.id,
personId: personId ?? this.personId,
key: key ?? this.key,
label: label ?? this.label,
category: category ?? this.category,
polarity: polarity ?? this.polarity,
confidence: confidence ?? this.confidence,
status: status ?? this.status,
firstSeenAt: firstSeenAt ?? this.firstSeenAt,
lastSeenAt: lastSeenAt ?? this.lastSeenAt,
occurrenceCount: occurrenceCount ?? this.occurrenceCount,
sourceApps: sourceApps ?? this.sourceApps,
evidenceMessageIds: evidenceMessageIds ?? this.evidenceMessageIds,
evidenceSnippets: evidenceSnippets ?? this.evidenceSnippets,
);
}
Map<String, dynamic> toJson() {
return <String, dynamic>{
'id': id,
'personId': personId,
'key': key,
'label': label,
'category': category,
'polarity': polarity.name,
'confidence': confidence,
'status': status.name,
'firstSeenAt': firstSeenAt.toUtc().toIso8601String(),
'lastSeenAt': lastSeenAt.toUtc().toIso8601String(),
'occurrenceCount': occurrenceCount,
'sourceApps': sourceApps,
'evidenceMessageIds': evidenceMessageIds,
'evidenceSnippets': evidenceSnippets,
};
}
factory PersonPreferenceSignal.fromJson(Map<String, dynamic> json) {
final String polarityName =
json['polarity'] as String? ?? PreferenceSignalPolarity.neutral.name;
final String statusName =
json['status'] as String? ?? PreferenceSignalStatus.inferred.name;
return PersonPreferenceSignal(
id: json['id'] as String,
personId: json['personId'] as String,
key: json['key'] as String,
label: json['label'] as String? ?? '',
category: json['category'] as String? ?? 'general',
polarity: PreferenceSignalPolarity.values.firstWhere(
(PreferenceSignalPolarity item) => item.name == polarityName,
orElse: () => PreferenceSignalPolarity.neutral,
),
confidence: (json['confidence'] as num?)?.toDouble() ?? 0,
status: PreferenceSignalStatus.values.firstWhere(
(PreferenceSignalStatus item) => item.name == statusName,
orElse: () => PreferenceSignalStatus.inferred,
),
firstSeenAt: DateTime.parse(
json['firstSeenAt'] as String? ??
DateTime.now().toUtc().toIso8601String(),
).toLocal(),
lastSeenAt: DateTime.parse(
json['lastSeenAt'] as String? ??
DateTime.now().toUtc().toIso8601String(),
).toLocal(),
occurrenceCount: (json['occurrenceCount'] as num?)?.toInt() ?? 1,
sourceApps: (json['sourceApps'] as List<dynamic>? ?? <dynamic>[])
.map((dynamic item) => '$item')
.toList(growable: false),
evidenceMessageIds:
(json['evidenceMessageIds'] as List<dynamic>? ?? <dynamic>[])
.map((dynamic item) => '$item')
.toList(growable: false),
evidenceSnippets:
(json['evidenceSnippets'] as List<dynamic>? ?? <dynamic>[])
.map((dynamic item) => '$item')
.toList(growable: false),
);
}
}
@immutable
class DashboardSummary {
const DashboardSummary({
@@ -622,6 +757,7 @@ class LocalDataState {
this.sourceLinks = const <SourceProfileLink>[],
this.sharedMessages = const <SharedMessageEntry>[],
this.sharedInbox = const <SharedInboxEntry>[],
this.preferenceSignals = const <PersonPreferenceSignal>[],
});
final List<PersonProfile> people;
@@ -632,6 +768,7 @@ class LocalDataState {
final List<SourceProfileLink> sourceLinks;
final List<SharedMessageEntry> sharedMessages;
final List<SharedInboxEntry> sharedInbox;
final List<PersonPreferenceSignal> preferenceSignals;
LocalDataState copyWith({
List<PersonProfile>? people,
@@ -642,6 +779,7 @@ class LocalDataState {
List<SourceProfileLink>? sourceLinks,
List<SharedMessageEntry>? sharedMessages,
List<SharedInboxEntry>? sharedInbox,
List<PersonPreferenceSignal>? preferenceSignals,
}) {
return LocalDataState(
people: people ?? this.people,
@@ -652,6 +790,7 @@ class LocalDataState {
sourceLinks: sourceLinks ?? this.sourceLinks,
sharedMessages: sharedMessages ?? this.sharedMessages,
sharedInbox: sharedInbox ?? this.sharedInbox,
preferenceSignals: preferenceSignals ?? this.preferenceSignals,
);
}
@@ -697,6 +836,9 @@ class LocalDataState {
'sharedInbox': sharedInbox
.map((SharedInboxEntry entry) => entry.toJson())
.toList(growable: false),
'preferenceSignals': preferenceSignals
.map((PersonPreferenceSignal signal) => signal.toJson())
.toList(growable: false),
};
}
@@ -750,6 +892,14 @@ class LocalDataState {
SharedInboxEntry.fromJson(entry as Map<String, dynamic>),
)
.toList(growable: false),
preferenceSignals:
(json['preferenceSignals'] as List<dynamic>? ?? <dynamic>[])
.map(
(dynamic signal) => PersonPreferenceSignal.fromJson(
signal as Map<String, dynamic>,
),
)
.toList(growable: false),
);
}