50 lines
2.2 KiB
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);
|
|
}
|