Refactor: Implement multi-item inventory for Pad Tracker and dynamic navigation
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../models/user_profile.dart';
|
||||
import '../models/cycle_entry.dart';
|
||||
|
||||
@@ -34,32 +35,129 @@ class CycleInfo {
|
||||
|
||||
class CycleService {
|
||||
/// Calculates the current cycle information based on user profile
|
||||
static CycleInfo calculateCycleInfo(UserProfile? user) {
|
||||
if (user?.lastPeriodStartDate == null) {
|
||||
return CycleInfo(
|
||||
/// Calculates the current cycle information based on user profile and cycle entries
|
||||
static CycleInfo calculateCycleInfo(UserProfile? user, List<CycleEntry> entries) {
|
||||
if (user == null) {
|
||||
return CycleInfo(
|
||||
phase: CyclePhase.follicular,
|
||||
dayOfCycle: 1,
|
||||
daysUntilPeriod: user?.averageCycleLength ?? 28,
|
||||
daysUntilPeriod: 28,
|
||||
isPeriodExpected: false,
|
||||
);
|
||||
}
|
||||
|
||||
final lastPeriod = user!.lastPeriodStartDate!;
|
||||
DateTime? lastPeriodStart = user.lastPeriodStartDate;
|
||||
|
||||
// Find the most recent period start from entries if available and more recent
|
||||
// We look for a sequence of period days and take the first one
|
||||
if (entries.isNotEmpty) {
|
||||
final sortedEntries = List<CycleEntry>.from(entries)..sort((a, b) => b.date.compareTo(a.date));
|
||||
|
||||
for (var entry in sortedEntries) {
|
||||
if (entry.isPeriodDay) {
|
||||
// Check if this is a "start" of a period (previous day was not period or no entry)
|
||||
// Simplified logic: Just take the most recent period day found and assume it's part of the current/last period
|
||||
// A better approach for "Day 1" is to find the First day of the contiguous block.
|
||||
|
||||
// However, for basic calculation, if we find a period day at Date X,
|
||||
// and today is Date Y.
|
||||
// If X is very recent, we are in the period.
|
||||
|
||||
// Correct logic: Identify the START DATE of the last period group.
|
||||
// 1. Find the latest period entry.
|
||||
// 2. Look backwards from there as long as there are consecutive period days.
|
||||
|
||||
DateTime potentialStart = entry.date;
|
||||
|
||||
// Check if we have a period day "tomorrow" relative to this entry? No, we are iterating backwards (descending).
|
||||
// So if we found a period day, we need to check if the NEXT entry (which is earlier in time) is also a period day.
|
||||
// If so, that earlier day is the better candidate for "Start".
|
||||
|
||||
// Let's iterate linearly.
|
||||
// Since we sorted DESC, `entry` is the LATEST period day.
|
||||
// We need to see if there are consecutive period days before it.
|
||||
|
||||
// But wait, the user might have logged Day 1, Day 2, Day 3.
|
||||
// `entry` will be Day 3.
|
||||
// We want Day 1.
|
||||
|
||||
// Let's try a different approach:
|
||||
// Get all period days sorted DESC.
|
||||
final periodDays = sortedEntries.where((e) => e.isPeriodDay).toList();
|
||||
|
||||
if (periodDays.isNotEmpty) {
|
||||
// Take the latest block
|
||||
DateTime latestParams = periodDays.first.date;
|
||||
|
||||
// Now find the "start" of this block
|
||||
// We iterate backwards from the *latest* date found
|
||||
|
||||
DateTime currentSearch = latestParams;
|
||||
DateTime startOfBlock = latestParams;
|
||||
|
||||
// Check if we have an entry for the day before
|
||||
bool foundPrevious = true;
|
||||
while(foundPrevious) {
|
||||
final dayBefore = currentSearch.subtract(const Duration(days: 1));
|
||||
final hasDayBefore = periodDays.any((e) => DateUtils.isSameDay(e.date, dayBefore));
|
||||
if (hasDayBefore) {
|
||||
currentSearch = dayBefore;
|
||||
startOfBlock = dayBefore;
|
||||
} else {
|
||||
foundPrevious = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If this calculated start is more recent than the user profile one, use it
|
||||
if (lastPeriodStart == null || startOfBlock.isAfter(lastPeriodStart)) {
|
||||
lastPeriodStart = startOfBlock;
|
||||
}
|
||||
}
|
||||
break; // We only care about the most recent period block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lastPeriodStart == null) {
|
||||
return CycleInfo(
|
||||
phase: CyclePhase.follicular,
|
||||
dayOfCycle: 1,
|
||||
daysUntilPeriod: user.averageCycleLength,
|
||||
isPeriodExpected: false,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the calculated last period is in the future (invalid state validation)
|
||||
if (lastPeriodStart.isAfter(DateTime.now())) {
|
||||
// Fallback to today if data is weird, or just use it (maybe user logged future?)
|
||||
// Let's stick to standard logic:
|
||||
}
|
||||
|
||||
final cycleLength = user.averageCycleLength;
|
||||
final now = DateTime.now();
|
||||
|
||||
// Normalize dates to midnight for accurate day counting
|
||||
final startOfToday = DateTime(now.year, now.month, now.day);
|
||||
final startOfLastPeriod = DateTime(lastPeriod.year, lastPeriod.month, lastPeriod.day);
|
||||
final startOfCycle = DateTime(lastPeriodStart.year, lastPeriodStart.month, lastPeriodStart.day);
|
||||
|
||||
final daysSinceLastPeriod = startOfToday.difference(startOfLastPeriod).inDays + 1;
|
||||
final daysSinceLastPeriod = startOfToday.difference(startOfCycle).inDays + 1;
|
||||
|
||||
// If negative (future date), handle gracefully
|
||||
if (daysSinceLastPeriod < 1) {
|
||||
return CycleInfo(
|
||||
phase: CyclePhase.follicular,
|
||||
dayOfCycle: 1,
|
||||
daysUntilPeriod: cycleLength,
|
||||
isPeriodExpected: false,
|
||||
);
|
||||
}
|
||||
|
||||
// Handle cases where last period was long ago (more than one cycle)
|
||||
final dayOfCycle = ((daysSinceLastPeriod - 1) % cycleLength) + 1;
|
||||
final daysUntilPeriod = cycleLength - dayOfCycle;
|
||||
|
||||
CyclePhase phase;
|
||||
if (dayOfCycle <= 5) {
|
||||
if (dayOfCycle <= user.averagePeriodLength) { // Use variable period length
|
||||
phase = CyclePhase.menstrual;
|
||||
} else if (dayOfCycle <= 13) {
|
||||
phase = CyclePhase.follicular;
|
||||
|
||||
Reference in New Issue
Block a user