Groups let people keep each other honest: create one or join with a six-letter invite code. Each group has shared goals split into measurable objectives (km, sessions, words) that every member logs toward, a leaderboard of kept rate and streak with nudges, and a group chat where check-ins and nudges appear as cards beside the talk. The open chat polls for new messages. Reached from a new Groups tab (Training moves into the day rail), a Home "Your groups" rail that opens the chat, and a Group tile in the add sheet. Mock mode seeds two groups, a joinable one and members who reply. The water tracker is removed; it did not fit the app. Bottom sheets are now square app-wide via the theme. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KhQh614DMeYoQEH9SoMRUw
65 lines
1.5 KiB
Dart
65 lines
1.5 KiB
Dart
/// Someone in a group, with the numbers the leaderboard ranks on.
|
|
class GroupMember {
|
|
String id;
|
|
|
|
String name;
|
|
|
|
/// "Owner" or "Member".
|
|
String role;
|
|
|
|
/// Share of their own commitments kept over the last seven days, 0..1.
|
|
double keptRate;
|
|
|
|
/// Kept days in a row.
|
|
int streak;
|
|
|
|
/// Their standing name — Good, Warned, Grounded, Lockdown.
|
|
String standing;
|
|
|
|
DateTime? lastCheckIn;
|
|
|
|
/// True for the signed-in user.
|
|
bool me;
|
|
|
|
GroupMember({
|
|
this.id = "",
|
|
this.name = "",
|
|
this.role = "Member",
|
|
this.keptRate = 0,
|
|
this.streak = 0,
|
|
this.standing = "Good",
|
|
this.lastCheckIn,
|
|
this.me = false,
|
|
});
|
|
|
|
bool get owner => role == "Owner";
|
|
|
|
factory GroupMember.fromJson(Map<String, dynamic> json) {
|
|
return GroupMember(
|
|
id: json['id'] ?? "",
|
|
name: json['name'] ?? "",
|
|
role: json['role'] ?? "Member",
|
|
keptRate: (json['keptRate'] ?? 0).toDouble(),
|
|
streak: json['streak'] ?? 0,
|
|
standing: json['standing'] ?? "Good",
|
|
lastCheckIn: json['lastCheckIn'] == null
|
|
? null
|
|
: DateTime.tryParse(json['lastCheckIn']),
|
|
me: json['me'] ?? false,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
final Map<String, dynamic> data = <String, dynamic>{};
|
|
data['id'] = id;
|
|
data['name'] = name;
|
|
data['role'] = role;
|
|
data['keptRate'] = keptRate;
|
|
data['streak'] = streak;
|
|
data['standing'] = standing;
|
|
data['lastCheckIn'] = lastCheckIn?.toIso8601String();
|
|
data['me'] = me;
|
|
return data;
|
|
}
|
|
}
|