Add Progress over time and a targets and reminders setup
Progress gives a Day/Week/Month view of routines held against a weekly target, compared with the same point of the previous period, with active time and the daily streak. Targets & reminders sets routines and minutes per week and schedules a heads-up before each routine. The runner's summary now shows today's held count and streak. Finished runs are mirrored to a device run log that feeds both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KhQh614DMeYoQEH9SoMRUw
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
/// The span the Progress screen measures.
|
||||
enum ProgressPeriod { Day, Week, Month }
|
||||
|
||||
String periodLabel(ProgressPeriod value) {
|
||||
switch (value) {
|
||||
case ProgressPeriod.Day:
|
||||
return "Day";
|
||||
case ProgressPeriod.Week:
|
||||
return "Week";
|
||||
case ProgressPeriod.Month:
|
||||
return "Month";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/// What a good week looks like, set by the user on the Targets screen.
|
||||
/// Progress measures against these; reminders fire before scheduled routines.
|
||||
class ProgressTargets {
|
||||
/// Routines held (every step done) per week.
|
||||
int routinesPerWeek;
|
||||
|
||||
/// Minutes spent inside routines per week, held or not.
|
||||
int minutesPerWeek;
|
||||
|
||||
bool reminders;
|
||||
|
||||
/// How long before a routine's start the reminder fires.
|
||||
int reminderLead;
|
||||
|
||||
/// False until the user has saved the Targets screen once, so Progress can
|
||||
/// ask for a target instead of silently using the defaults.
|
||||
bool set;
|
||||
|
||||
ProgressTargets({
|
||||
this.routinesPerWeek = 5,
|
||||
this.minutesPerWeek = 150,
|
||||
this.reminders = false,
|
||||
this.reminderLead = 10,
|
||||
this.set = false,
|
||||
});
|
||||
|
||||
factory ProgressTargets.fromJson(Map<String, dynamic> json) {
|
||||
return ProgressTargets(
|
||||
routinesPerWeek: json['routinesPerWeek'] ?? 5,
|
||||
minutesPerWeek: json['minutesPerWeek'] ?? 150,
|
||||
reminders: json['reminders'] ?? false,
|
||||
reminderLead: json['reminderLead'] ?? 10,
|
||||
set: json['set'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['routinesPerWeek'] = routinesPerWeek;
|
||||
data['minutesPerWeek'] = minutesPerWeek;
|
||||
data['reminders'] = reminders;
|
||||
data['reminderLead'] = reminderLead;
|
||||
data['set'] = set;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/// One finished run of a routine, kept on the device so Progress can draw the
|
||||
/// days and weeks without waiting on the network. The server keeps its own
|
||||
/// log through `Routine/Log`; this is the local mirror.
|
||||
class RoutineRunRecord {
|
||||
String routineId;
|
||||
|
||||
String title;
|
||||
|
||||
/// [RoutineKind] name.
|
||||
String kind;
|
||||
|
||||
DateTime startedAt;
|
||||
|
||||
int seconds;
|
||||
|
||||
int completedSteps;
|
||||
|
||||
int totalSteps;
|
||||
|
||||
RoutineRunRecord({
|
||||
required this.routineId,
|
||||
required this.title,
|
||||
required this.kind,
|
||||
required this.startedAt,
|
||||
this.seconds = 0,
|
||||
this.completedSteps = 0,
|
||||
this.totalSteps = 0,
|
||||
});
|
||||
|
||||
/// Every step, in order. Partial runs still count toward active time.
|
||||
bool get held => totalSteps > 0 && completedSteps >= totalSteps;
|
||||
|
||||
factory RoutineRunRecord.fromJson(Map<String, dynamic> json) {
|
||||
return RoutineRunRecord(
|
||||
routineId: json['routineId'] ?? '',
|
||||
title: json['title'] ?? '',
|
||||
kind: json['kind'] ?? '',
|
||||
startedAt: DateTime.tryParse(json['startedAt'] ?? '') ?? DateTime.now(),
|
||||
seconds: json['seconds'] ?? 0,
|
||||
completedSteps: json['completedSteps'] ?? 0,
|
||||
totalSteps: json['totalSteps'] ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['routineId'] = routineId;
|
||||
data['title'] = title;
|
||||
data['kind'] = kind;
|
||||
data['startedAt'] = startedAt.toIso8601String();
|
||||
data['seconds'] = seconds;
|
||||
data['completedSteps'] = completedSteps;
|
||||
data['totalSteps'] = totalSteps;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user