getReviewSummariesForDomain method

Future<Map<String, ({bool due, int? intervalDays})>> getReviewSummariesForDomain(
  1. String domainId
)

Per-topic review summary for a domain. For each topic with at least one review card, returns the longest interval_days and whether any of its cards are due (next_review_at <= now). Topics with no cards are absent from the returned map.

Implementation

Future<Map<String, ({int? intervalDays, bool due})>>
getReviewSummariesForDomain(String domainId) async {
  final userId = _client.auth.currentUser?.id;
  if (userId == null) return {};

  final allRows = await _client
      .from('review_cards')
      .select('topic_id, interval_days, topics!inner(domain_id)')
      .eq('user_id', userId)
      .eq('topics.domain_id', domainId);

  final now = DateTime.now().toIso8601String();
  final dueRows = await _client
      .from('review_cards')
      .select('topic_id, topics!inner(domain_id)')
      .eq('user_id', userId)
      .eq('topics.domain_id', domainId)
      .lte('next_review_at', now);
  final dueTopicIds = (dueRows as List<dynamic>)
      .map((r) => (r as Map<String, dynamic>)['topic_id'] as String)
      .toSet();

  final summaries = <String, ({int? intervalDays, bool due})>{};
  for (final row in allRows as List<dynamic>) {
    final r = row as Map<String, dynamic>;
    final topicId = r['topic_id'] as String;
    final interval = r['interval_days'] as int?;
    final prev = summaries[topicId];
    final newInterval = prev == null
        ? interval
        : (interval == null
              ? prev.intervalDays
              : (prev.intervalDays == null
                    ? interval
                    : (interval > prev.intervalDays!
                          ? interval
                          : prev.intervalDays)));
    summaries[topicId] = (
      intervalDays: newInterval,
      due: dueTopicIds.contains(topicId),
    );
  }
  return summaries;
}