Implement husband-wife connection dialogue and theme support for learn articles
This commit is contained in:
@@ -175,6 +175,66 @@ class CycleService {
|
||||
);
|
||||
}
|
||||
|
||||
/// Calculates the cycle phase for a specific date (past or future)
|
||||
static CyclePhase? getPhaseForDate(DateTime date, UserProfile? user) {
|
||||
if (user == null || user.lastPeriodStartDate == null) return null;
|
||||
|
||||
final lastPeriodStart = user.lastPeriodStartDate!;
|
||||
|
||||
// Normalize dates
|
||||
final checkDate = DateTime(date.year, date.month, date.day);
|
||||
final startCycle = DateTime(lastPeriodStart.year, lastPeriodStart.month, lastPeriodStart.day);
|
||||
|
||||
final daysDifference = checkDate.difference(startCycle).inDays;
|
||||
|
||||
// If date is before the last known period, we can't reliably predict using this simple logic
|
||||
// (though in reality we could project backwards, but let's stick to forward/current)
|
||||
if (daysDifference < 0) return null;
|
||||
|
||||
final cycleLength = user.averageCycleLength;
|
||||
final dayOfCycle = (daysDifference % cycleLength) + 1;
|
||||
|
||||
if (dayOfCycle <= user.averagePeriodLength) return CyclePhase.menstrual;
|
||||
if (dayOfCycle <= 13) return CyclePhase.follicular;
|
||||
if (dayOfCycle <= 16) return CyclePhase.ovulation;
|
||||
return CyclePhase.luteal;
|
||||
}
|
||||
|
||||
/// Predicts period days for the next [months] months
|
||||
static List<DateTime> predictNextPeriodDays(UserProfile? user, {int months = 12}) {
|
||||
if (user == null || user.lastPeriodStartDate == null) return [];
|
||||
|
||||
final predictedDays = <DateTime>[];
|
||||
final lastPeriodStart = user.lastPeriodStartDate!;
|
||||
final cycleLength = user.averageCycleLength;
|
||||
final periodLength = user.averagePeriodLength;
|
||||
|
||||
// Start predicting from the NEXT cycle if the current one is finished,
|
||||
// or just project out from the last start date.
|
||||
// We want to list all future period days.
|
||||
|
||||
DateTime currentCycleStart = lastPeriodStart;
|
||||
|
||||
// Project forward for roughly 'months' months
|
||||
// A safe upper bound for loop is months * 30 days
|
||||
final limitDate = DateTime.now().add(Duration(days: months * 30));
|
||||
|
||||
while (currentCycleStart.isBefore(limitDate)) {
|
||||
// Add period days for this cycle
|
||||
for (int i = 0; i < periodLength; i++) {
|
||||
final periodDay = currentCycleStart.add(Duration(days: i));
|
||||
if (periodDay.isAfter(DateTime.now())) {
|
||||
predictedDays.add(periodDay);
|
||||
}
|
||||
}
|
||||
|
||||
// Move to next cycle
|
||||
currentCycleStart = currentCycleStart.add(Duration(days: cycleLength));
|
||||
}
|
||||
|
||||
return predictedDays;
|
||||
}
|
||||
|
||||
/// Format cycle day for display
|
||||
static String getDayOfCycleDisplay(int day) => 'Day $day';
|
||||
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
import 'dart:io';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart'; // Ensure share_plus is in dependencies or use printing/share mechanism
|
||||
import '../models/cycle_entry.dart';
|
||||
|
||||
// Since we might not have share_plus in the pubspec explicitly seen earlier (user plan said adding dependencies),
|
||||
// keeping it safe. The pubspec had 'pdf', 'printing', 'path_provider', 'universal_html'.
|
||||
// 'share_plus' was not explicitly in the list I viewed in Step 258, but 'printing' can share PDF.
|
||||
// For ICS, we need a way to share the file. 'printing' relies on pdf.
|
||||
// Wait, Step 258 pubspec content lines 9-48...
|
||||
// I don't see `share_plus`.
|
||||
// I'll check `pubspec.yaml` again to be absolutely sure or add it via `flutter pub add`.
|
||||
// Actually, `printing` has a share method but it's specific to PDF bytes usually? No, `Printing.sharePdf`.
|
||||
// I should use `share_plus` if I want to share a text/ics file.
|
||||
// Or I can just write to file and open it with `open_filex`.
|
||||
|
||||
import 'package:open_filex/open_filex.dart';
|
||||
import '../models/cycle_entry.dart';
|
||||
import '../models/user_profile.dart';
|
||||
import 'cycle_service.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class IcsService {
|
||||
static Future<void> generateCycleCalendar(List<CycleEntry> entries) async {
|
||||
static Future<void> generateCycleCalendar(List<CycleEntry> entries, {UserProfile? user, bool includePredictions = true}) async {
|
||||
final buffer = StringBuffer();
|
||||
buffer.writeln('BEGIN:VCALENDAR');
|
||||
buffer.writeln('VERSION:2.0');
|
||||
@@ -27,6 +17,7 @@ class IcsService {
|
||||
// Sort entries
|
||||
entries.sort((a, b) => a.date.compareTo(b.date));
|
||||
|
||||
// 1. Logged Entries
|
||||
for (var entry in entries) {
|
||||
if (entry.isPeriodDay) {
|
||||
final dateStr = DateFormat('yyyyMMdd').format(entry.date);
|
||||
@@ -41,6 +32,26 @@ class IcsService {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Predicted Entries
|
||||
if (includePredictions && user != null) {
|
||||
final predictedDays = CycleService.predictNextPeriodDays(user);
|
||||
|
||||
for (var date in predictedDays) {
|
||||
final dateStr = DateFormat('yyyyMMdd').format(date);
|
||||
final uuid = const Uuid().v4();
|
||||
|
||||
buffer.writeln('BEGIN:VEVENT');
|
||||
buffer.writeln('UID:$uuid');
|
||||
buffer.writeln('DTSTAMP:${DateFormat('yyyyMMddTHHmmss').format(DateTime.now())}Z');
|
||||
buffer.writeln('DTSTART;VALUE=DATE:$dateStr');
|
||||
buffer.writeln('DTEND;VALUE=DATE:${DateFormat('yyyyMMdd').format(date.add(const Duration(days: 1)))}');
|
||||
buffer.writeln('SUMMARY:Predicted Period');
|
||||
buffer.writeln('DESCRIPTION:Predicted period day based on cycle history.');
|
||||
buffer.writeln('STATUS:TENTATIVE'); // Mark as tentative
|
||||
buffer.writeln('END:VEVENT');
|
||||
}
|
||||
}
|
||||
|
||||
buffer.writeln('END:VCALENDAR');
|
||||
|
||||
// Save to file
|
||||
|
||||
Reference in New Issue
Block a user