358 lines
9.3 KiB
Dart
358 lines
9.3 KiB
Dart
// ignore_for_file: sort_constructors_first
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
@immutable
|
|
class PersonProfile {
|
|
const PersonProfile({
|
|
required this.id,
|
|
required this.name,
|
|
required this.relationship,
|
|
required this.affinityScore,
|
|
required this.nextMoment,
|
|
required this.tags,
|
|
required this.notes,
|
|
});
|
|
|
|
final String id;
|
|
final String name;
|
|
final String relationship;
|
|
final int affinityScore;
|
|
final DateTime nextMoment;
|
|
final List<String> tags;
|
|
final String notes;
|
|
|
|
PersonProfile copyWith({
|
|
String? id,
|
|
String? name,
|
|
String? relationship,
|
|
int? affinityScore,
|
|
DateTime? nextMoment,
|
|
List<String>? tags,
|
|
String? notes,
|
|
}) {
|
|
return PersonProfile(
|
|
id: id ?? this.id,
|
|
name: name ?? this.name,
|
|
relationship: relationship ?? this.relationship,
|
|
affinityScore: affinityScore ?? this.affinityScore,
|
|
nextMoment: nextMoment ?? this.nextMoment,
|
|
tags: tags ?? this.tags,
|
|
notes: notes ?? this.notes,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return <String, dynamic>{
|
|
'id': id,
|
|
'name': name,
|
|
'relationship': relationship,
|
|
'affinityScore': affinityScore,
|
|
'nextMoment': nextMoment.toUtc().toIso8601String(),
|
|
'tags': tags,
|
|
'notes': notes,
|
|
};
|
|
}
|
|
|
|
factory PersonProfile.fromJson(Map<String, dynamic> json) {
|
|
return PersonProfile(
|
|
id: json['id'] as String,
|
|
name: json['name'] as String,
|
|
relationship: json['relationship'] as String,
|
|
affinityScore: (json['affinityScore'] as num).toInt(),
|
|
nextMoment: DateTime.parse(json['nextMoment'] as String).toLocal(),
|
|
tags: (json['tags'] as List<dynamic>)
|
|
.map((dynamic tag) => '$tag')
|
|
.toList(),
|
|
notes: json['notes'] as String,
|
|
);
|
|
}
|
|
}
|
|
|
|
@immutable
|
|
class RelationshipMoment {
|
|
const RelationshipMoment({
|
|
required this.id,
|
|
required this.personId,
|
|
required this.title,
|
|
required this.summary,
|
|
required this.at,
|
|
required this.type,
|
|
});
|
|
|
|
final String id;
|
|
final String personId;
|
|
final String title;
|
|
final String summary;
|
|
final DateTime at;
|
|
final String type;
|
|
|
|
RelationshipMoment copyWith({
|
|
String? id,
|
|
String? personId,
|
|
String? title,
|
|
String? summary,
|
|
DateTime? at,
|
|
String? type,
|
|
}) {
|
|
return RelationshipMoment(
|
|
id: id ?? this.id,
|
|
personId: personId ?? this.personId,
|
|
title: title ?? this.title,
|
|
summary: summary ?? this.summary,
|
|
at: at ?? this.at,
|
|
type: type ?? this.type,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return <String, dynamic>{
|
|
'id': id,
|
|
'personId': personId,
|
|
'title': title,
|
|
'summary': summary,
|
|
'at': at.toUtc().toIso8601String(),
|
|
'type': type,
|
|
};
|
|
}
|
|
|
|
factory RelationshipMoment.fromJson(Map<String, dynamic> json) {
|
|
return RelationshipMoment(
|
|
id: json['id'] as String,
|
|
personId: json['personId'] as String,
|
|
title: json['title'] as String,
|
|
summary: json['summary'] as String,
|
|
at: DateTime.parse(json['at'] as String).toLocal(),
|
|
type: json['type'] as String,
|
|
);
|
|
}
|
|
}
|
|
|
|
@immutable
|
|
class DashboardTask {
|
|
const DashboardTask({
|
|
required this.id,
|
|
required this.title,
|
|
required this.description,
|
|
required this.when,
|
|
this.done = false,
|
|
});
|
|
|
|
final String id;
|
|
final String title;
|
|
final String description;
|
|
final DateTime when;
|
|
final bool done;
|
|
|
|
DashboardTask copyWith({
|
|
String? id,
|
|
String? title,
|
|
String? description,
|
|
DateTime? when,
|
|
bool? done,
|
|
}) {
|
|
return DashboardTask(
|
|
id: id ?? this.id,
|
|
title: title ?? this.title,
|
|
description: description ?? this.description,
|
|
when: when ?? this.when,
|
|
done: done ?? this.done,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return <String, dynamic>{
|
|
'id': id,
|
|
'title': title,
|
|
'description': description,
|
|
'when': when.toUtc().toIso8601String(),
|
|
'done': done,
|
|
};
|
|
}
|
|
|
|
factory DashboardTask.fromJson(Map<String, dynamic> json) {
|
|
return DashboardTask(
|
|
id: json['id'] as String,
|
|
title: json['title'] as String,
|
|
description: json['description'] as String,
|
|
when: DateTime.parse(json['when'] as String).toLocal(),
|
|
done: json['done'] as bool? ?? false,
|
|
);
|
|
}
|
|
}
|
|
|
|
@immutable
|
|
class DashboardSummary {
|
|
const DashboardSummary({
|
|
required this.activePeople,
|
|
required this.upcomingPlans,
|
|
required this.pendingIdeas,
|
|
required this.weeklyConsistency,
|
|
});
|
|
|
|
final int activePeople;
|
|
final int upcomingPlans;
|
|
final int pendingIdeas;
|
|
final int weeklyConsistency;
|
|
}
|
|
|
|
@immutable
|
|
class LocalDataState {
|
|
const LocalDataState({
|
|
required this.people,
|
|
required this.moments,
|
|
required this.tasks,
|
|
});
|
|
|
|
final List<PersonProfile> people;
|
|
final List<RelationshipMoment> moments;
|
|
final List<DashboardTask> tasks;
|
|
|
|
LocalDataState copyWith({
|
|
List<PersonProfile>? people,
|
|
List<RelationshipMoment>? moments,
|
|
List<DashboardTask>? tasks,
|
|
}) {
|
|
return LocalDataState(
|
|
people: people ?? this.people,
|
|
moments: moments ?? this.moments,
|
|
tasks: tasks ?? this.tasks,
|
|
);
|
|
}
|
|
|
|
DashboardSummary summary({DateTime? now}) {
|
|
final DateTime baseline = now ?? DateTime.now();
|
|
return DashboardSummary(
|
|
activePeople: people.length,
|
|
upcomingPlans: people
|
|
.where((PersonProfile p) => p.nextMoment.isAfter(baseline))
|
|
.length,
|
|
pendingIdeas: people.fold<int>(
|
|
0,
|
|
(int acc, PersonProfile person) =>
|
|
acc + (person.tags.isNotEmpty ? 1 : 0),
|
|
),
|
|
weeklyConsistency: moments.isEmpty
|
|
? 0
|
|
: (70 + moments.length * 4).clamp(0, 100),
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return <String, dynamic>{
|
|
'people': people
|
|
.map((PersonProfile person) => person.toJson())
|
|
.toList(growable: false),
|
|
'moments': moments
|
|
.map((RelationshipMoment moment) => moment.toJson())
|
|
.toList(growable: false),
|
|
'tasks': tasks
|
|
.map((DashboardTask task) => task.toJson())
|
|
.toList(growable: false),
|
|
};
|
|
}
|
|
|
|
factory LocalDataState.fromJson(Map<String, dynamic> json) {
|
|
return LocalDataState(
|
|
people: (json['people'] as List<dynamic>)
|
|
.map(
|
|
(dynamic person) =>
|
|
PersonProfile.fromJson(person as Map<String, dynamic>),
|
|
)
|
|
.toList(growable: false),
|
|
moments: (json['moments'] as List<dynamic>)
|
|
.map(
|
|
(dynamic moment) =>
|
|
RelationshipMoment.fromJson(moment as Map<String, dynamic>),
|
|
)
|
|
.toList(growable: false),
|
|
tasks: (json['tasks'] as List<dynamic>)
|
|
.map(
|
|
(dynamic task) =>
|
|
DashboardTask.fromJson(task as Map<String, dynamic>),
|
|
)
|
|
.toList(growable: false),
|
|
);
|
|
}
|
|
|
|
static LocalDataState seed() {
|
|
return LocalDataState(
|
|
people: <PersonProfile>[
|
|
PersonProfile(
|
|
id: 'p-ava',
|
|
name: 'Ava Hart',
|
|
relationship: 'Partner',
|
|
affinityScore: 92,
|
|
nextMoment: DateTime(2026, 2, 16, 19, 30),
|
|
tags: <String>['coffee', 'books', 'slow mornings'],
|
|
notes: 'Prefers thoughtful plans over expensive plans.',
|
|
),
|
|
PersonProfile(
|
|
id: 'p-jordan',
|
|
name: 'Jordan Lee',
|
|
relationship: 'Close Friend',
|
|
affinityScore: 78,
|
|
nextMoment: DateTime(2026, 2, 18, 18, 0),
|
|
tags: <String>['running', 'street food'],
|
|
notes: 'Has a race on Sunday; ask how training is going.',
|
|
),
|
|
PersonProfile(
|
|
id: 'p-mila',
|
|
name: 'Mila Stone',
|
|
relationship: 'Sister',
|
|
affinityScore: 84,
|
|
nextMoment: DateTime(2026, 2, 20, 12, 0),
|
|
tags: <String>['plants', 'retro music'],
|
|
notes: 'Birthday prep should start this week.',
|
|
),
|
|
],
|
|
moments: <RelationshipMoment>[
|
|
RelationshipMoment(
|
|
id: 'm-1',
|
|
personId: 'p-ava',
|
|
title: 'Sunday Walk Tradition',
|
|
summary: 'Short walk + no-phone hour worked really well.',
|
|
at: DateTime(2026, 2, 9, 9, 30),
|
|
type: 'ritual',
|
|
),
|
|
RelationshipMoment(
|
|
id: 'm-2',
|
|
personId: 'p-jordan',
|
|
title: 'Post-workout Check-in',
|
|
summary: 'Shared training playlist and grabbed smoothies.',
|
|
at: DateTime(2026, 2, 11, 19, 0),
|
|
type: 'support',
|
|
),
|
|
RelationshipMoment(
|
|
id: 'm-3',
|
|
personId: 'p-mila',
|
|
title: 'Gift Idea Captured',
|
|
summary: 'Found a vintage lamp from her saved style board.',
|
|
at: DateTime(2026, 2, 12, 14, 20),
|
|
type: 'gift',
|
|
),
|
|
],
|
|
tasks: <DashboardTask>[
|
|
DashboardTask(
|
|
id: 't-1',
|
|
title: 'Plan Friday dinner with Ava',
|
|
description: 'Keep it low-key: ramen + bookstore stop.',
|
|
when: DateTime(2026, 2, 16, 17, 0),
|
|
),
|
|
DashboardTask(
|
|
id: 't-2',
|
|
title: 'Send race-day encouragement to Jordan',
|
|
description: 'Voice note before 8:00 AM.',
|
|
when: DateTime(2026, 2, 17, 7, 30),
|
|
),
|
|
DashboardTask(
|
|
id: 't-3',
|
|
title: 'Finalize Mila birthday shortlist',
|
|
description: 'Pick 1 meaningful gift and 1 backup.',
|
|
when: DateTime(2026, 2, 18, 20, 0),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|