iykyka/mobile/lib/services/snooze_service.dart

50 lines
2.2 KiB
Dart

/// 답장 마감 알림(roadmap.md §2.7-F, PRD.md §3.2 P1 "내가 '이따 답장' 누르면 나에게만
/// 리마인드") 순수 로직. `DateTime.now()`에 의존하지 않고 `now`를 인자로 받는 함수들이라
/// 테스트에서 고정된 시각으로 결정적으로 검증할 수 있다.
library;
/// "이따 답장"을 그냥 눌렀을 때(빠른 선택 없이) 적용되는 기본 스누즈 길이.
const Duration kDefaultSnoozeDuration = Duration(hours: 2);
/// 채팅방/대화 목록에 노출하는 빠른 선택지.
enum SnoozeQuickPick { oneHour, thisEvening, tomorrowMorning }
extension SnoozeQuickPickLabel on SnoozeQuickPick {
String get label => switch (this) {
SnoozeQuickPick.oneHour => '1시간 후',
SnoozeQuickPick.thisEvening => '저녁에',
SnoozeQuickPick.tomorrowMorning => '내일',
};
}
/// [now] 기준으로 빠른 선택지가 가리키는 실제 리마인드 시각을 계산한다.
///
/// - 1시간 후: 지금부터 정확히 1시간
/// - 저녁에: 오늘 19시(이미 지났으면 내일 19시)
/// - 내일: 내일 오전 9시
DateTime resolveSnoozeQuickPick(SnoozeQuickPick pick, DateTime now) {
switch (pick) {
case SnoozeQuickPick.oneHour:
return now.add(const Duration(hours: 1));
case SnoozeQuickPick.thisEvening:
final evening = DateTime(now.year, now.month, now.day, 19);
return now.isBefore(evening) ? evening : evening.add(const Duration(days: 1));
case SnoozeQuickPick.tomorrowMorning:
final tomorrow = now.add(const Duration(days: 1));
return DateTime(tomorrow.year, tomorrow.month, tomorrow.day, 9);
}
}
/// 스누즈 마감이 [now] 기준으로 이미 지났는지 — 대화 목록 배지/채팅방 배너 표시 여부를
/// 결정하는 순수 함수. `snoozedUntil`이 없으면 항상 false.
bool isSnoozePastDue(DateTime now, DateTime? snoozedUntil) {
if (snoozedUntil == null) return false;
return !now.isBefore(snoozedUntil);
}
/// 스누즈가 걸려 있고 아직 마감 전인지(= 활성 스누즈).
bool isSnoozeActive(DateTime now, DateTime? snoozedUntil) {
if (snoozedUntil == null) return false;
return now.isBefore(snoozedUntil);
}