深夜(0:00-2:00)の服用記録に当日分まで記録された旨の注意ダイアログを表示#1821
Conversation
日を跨いだことに気づかず服用記録をすると当日分まで記録され、 当日の通知が届かなくなる問い合わせへの対策。 - 表示は30日に1回まで。「閉じる」押下時にSharedPreferencesへ記録 - 2回目以降の表示では「二度と表示しない」ボタンを出す - 当日分まで記録された場合のみ対象(過去のピル番号タップは対象外) - 本文の通知時刻は記録時点でまだ届いていない当日の通知のみ列挙し、 1件もなければ表示しない(通知時刻が0:00-2:00内のユーザーを考慮) - 服用取り消し方法のFAQリンク付き
- 飲んだボタン: 服用記録の完了(await)を待たずに表示する - ピルシートの番号タップ: 服用記録されないケースがあるため、 実際に記録された場合のみ表示判定する - ダイアログに必要なsettingはRecordPageBody -> RecordPageButton -> TakenButton とconstructorで受け渡す
- 表示判定: 記録操作時刻(0:00/1:59/2:00/23:59)の境界値、 当日分まで記録されたかどうか、通知時刻の未到達判定、 30日間隔(前日/29日後/30日後/年跨ぎ)の境界値 - Widget: 初回と2回目以降の「二度と表示しない」ボタンの出し分け、 本文の日付・通知時刻表示 - 既存のRecordPageButtonテストにsetting引数を追加
📝 WalkthroughWalkthrough深夜の服用記録に対する注意ダイアログを追加し、通知時刻・表示履歴・非表示設定に基づく表示判定、記録ボタンへの統合、ローカライズ、関連テストを実装した。 Changes深夜服用記録の注意表示
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant RecordPageBody
participant RecordPageButton
participant TakenButton
participant takePill
participant showMidnightTakenWarningDialogIfNeeded
participant SharedPreferences
RecordPageBody->>RecordPageButton: Setting を渡して構築
RecordPageButton->>TakenButton: Setting を渡す
TakenButton->>takePill: 服用記録を開始
TakenButton->>showMidnightTakenWarningDialogIfNeeded: 記録日時と服用日を渡す
showMidnightTakenWarningDialogIfNeeded->>SharedPreferences: 表示履歴と非表示設定を参照
showMidnightTakenWarningDialogIfNeeded-->>TakenButton: 条件を満たす場合に表示
TakenButton->>takePill: 記録処理の完了を待機
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
test/features/record/components/button/midnight_taken_warning_dialog_test.dart (1)
273-342: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winWidgetテストのgroup名を規約に合わせてください。
MidnightTakenWarningDialogのgroup名は#MidnightTakenWarningDialogのように#で始めてください。コーディングガイドラインの「Widget TestsはWidget classごとに整理し、group名を#で始める」に基づく指摘です。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/features/record/components/button/midnight_taken_warning_dialog_test.dart` around lines 273 - 342, Update the widget test group declaration for MidnightTakenWarningDialog to use the required “#MidnightTakenWarningDialog” naming convention, leaving the contained tests unchanged.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/features/record/components/button/midnight_taken_warning_dialog.dart`:
- Around line 192-200: Update the midnight warning dialog flow so the timestamp
keyed by DoubleKey.midnightTakenWarningDialogLastShownDateTime is persisted when
the dialog is shown, rather than only in the close button’s onPressed callback.
Remove the close-time-only persistence while preserving analytics logging and
navigation behavior.
- Around line 87-100:
直列化されていない判定から表示開始までの処理を、対象のダイアログ表示関数で共有ロックまたは表示中フラグを使って保護してください。MidnightTakenWarningDialogDisplayMode.none
の判定後から showDialog
の開始までを同時実行できないようにし、重複呼び出し時は後続処理を中止してください。表示開始後は必ずガードを解放し、context.mounted が false
の場合も解除されるようにしてください。
- Around line 45-60: Update midnightTakenWarningDialogDisplayMode to accept and
check the reminder-enabled flag before evaluating remaining reminder times.
Return MidnightTakenWarningDialogDisplayMode.none when reminders are disabled,
while preserving the existing behavior for enabled reminders.
- Around line 179-185: Update the FAQ button’s onPressed handler to await the
Future<bool> returned by launchUrl in the midnight warning dialog, and notify
the user when it returns false because the FAQ cannot be opened. Preserve the
existing analytics event and URL.
In `@lib/features/record/components/button/taken_button.dart`:
- Around line 58-72: Update the takePill flow in the button handler so it awaits
successful completion before calling showMidnightTakenWarningDialogIfNeeded.
Remove the fire-and-forget invocation, preserve the existing takenDate and
arguments, and ensure the dialog is not shown when takePill fails.
In
`@test/features/record/components/button/midnight_taken_warning_dialog_test.dart`:
- Around line 175-270: Extend the SharedPreferences tests for
midnightTakenWarningDialogDisplayMode with leap-year boundary cases covering
dates around February 29, 2024, where 29 elapsed days returns none and 30
elapsed days returns repeated. Add equivalent 29-day and 30-day boundary cases
in non-leap-year 2025, preserving the existing setup and assertions.
---
Nitpick comments:
In
`@test/features/record/components/button/midnight_taken_warning_dialog_test.dart`:
- Around line 273-342: Update the widget test group declaration for
MidnightTakenWarningDialog to use the required “#MidnightTakenWarningDialog”
naming convention, leaving the contained tests unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4fdc837f-bb74-4e09-bfea-4a375cab7289
📒 Files selected for processing (11)
lib/features/record/components/button/midnight_taken_warning_dialog.dartlib/features/record/components/button/record_page_button.dartlib/features/record/components/button/taken_button.dartlib/features/record/components/pill_sheet/record_page_pill_sheet.dartlib/features/record/page.dartlib/l10n/app_en.arblib/l10n/app_ja.arblib/utils/shared_preference/keys.darttest/features/record/components/button/midnight_taken_warning_dialog_test.darttest/features/record/components/button/record_page_button_test.darttest/features/record/record_page_button_state_test.dart
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f1b96e5546
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| import 'package:url_launcher/url_launcher.dart'; | ||
|
|
||
| /// 深夜(0:00-2:00)服用記録の注意ダイアログの表示要否 | ||
| enum MidnightTakenWarningDialogDisplayMode { |
There was a problem hiding this comment.
わざわざenumを用意する必要なさそう。不要な中間表現は使わない
TakenButton / RecordPageButton / RecordPageBody はhooksを使用していないため、 hooks-and-consumer-widget.md の規約に従い ConsumerWidget にする
- hooksを使用していないため ConsumerWidget にする - 1回しか参照しない updatedPillSheetGroup を条件式にインライン化
- 1回しか参照しない sharedPreferences をインライン化 - getBool の ?? false に「未保存は未押下として扱う」根拠コメントを追加
表示要否と「二度と表示しない」ボタンの出し分けを1つのenumに まとめる中間表現は不要なため、それぞれ直接導出する形にする。 - 表示要否: shouldShowMidnightTakenWarningDialog (bool) - ボタンの出し分け: 表示日時キー(double)の保存有無をそのまま参照
There was a problem hiding this comment.
♻️ Duplicate comments (2)
lib/features/record/components/button/midnight_taken_warning_dialog.dart (1)
74-94: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win通知OFF時はこの警告を出さないように考慮してください。
以前のレビューでも指摘されていましたが、
setting.isOnReminderがfalseの場合でも、保存済みの通知時刻がsetting.reminderTimesに残っていると「通知が届かない」と誤って案内されてしまいます。通知が無効な場合は警告をスキップするよう、isOnReminderを判定に含めてください。💡 修正案
void showMidnightTakenWarningDialogIfNeeded({ required BuildContext context, required DateTime takenDate, required DateTime recordedAt, required Setting setting, }) async { final sharedPreferences = await SharedPreferences.getInstance(); + final activeReminderTimes = setting.isOnReminder ? setting.reminderTimes : <ReminderTime>[]; if (!shouldShowMidnightTakenWarningDialog( sharedPreferences: sharedPreferences, takenDate: takenDate, recordedAt: recordedAt, - reminderTimes: setting.reminderTimes, + reminderTimes: activeReminderTimes, )) { return; } if (context.mounted) { analytics.logScreenView(screenName: 'MidnightTakenWarningDialog'); showDialog( context: context, // 「閉じる」でSharedPreferencesに表示記録を残すため、ボタン以外で閉じられないようにする barrierDismissible: false, builder: (context) => MidnightTakenWarningDialog( takenDate: takenDate, - reminderTimes: midnightTakenWarningRemainingReminderTimes(reminderTimes: setting.reminderTimes, recordedAt: recordedAt), + reminderTimes: midnightTakenWarningRemainingReminderTimes(reminderTimes: activeReminderTimes, recordedAt: recordedAt), // 2回目以降(過去に「閉じる」で表示日時が保存済み)の表示でのみ「二度と表示しない」を出す showsNeverShowAgainButton: sharedPreferences.containsKey(DoubleKey.midnightTakenWarningDialogLastShownDateTime), ), ); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/features/record/components/button/midnight_taken_warning_dialog.dart` around lines 74 - 94, 通知が無効な場合に警告ダイアログを表示しないよう、MidnightTakenWarningDialog の表示判定で setting.isOnReminder を確認してください。setting.isOnReminder が false の場合は shouldShowMidnightTakenWarningDialog の呼び出し前に return し、既存の有効時の判定と表示処理は維持してください。test/features/record/components/button/midnight_taken_warning_dialog_test.dart (1)
255-270: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winうるう年の境界値テストを追加してください。
以前のレビューでも指摘されていましたが、30日経過の判定に対して、うるう年(2024年2月29日を含む)と平年(2025年)の境界値テストが不足しています。コーディングガイドラインに従い、日付の境界値テストとしてうるう年のケースを網羅してください。
💡 テスト追加案
test("年をまたいでも前回表示から30日未満の場合は表示しない", () async { SharedPreferences.setMockInitialValues({ DoubleKey.midnightTakenWarningDialogLastShownDateTime: DateTime(2026, 12, 31, 0, 30).millisecondsSinceEpoch.toDouble(), }); expect( shouldShowMidnightTakenWarningDialog( sharedPreferences: await SharedPreferences.getInstance(), takenDate: DateTime(2027, 1, 1, 0, 10), recordedAt: DateTime(2027, 1, 1, 0, 10), reminderTimes: reminderTimes, ), false, ); }); + + test("うるう年(2月29日を含む)で前回表示から29日後の場合は表示しない", () async { + SharedPreferences.setMockInitialValues({ + DoubleKey.midnightTakenWarningDialogLastShownDateTime: + DateTime(2024, 2, 1, 0, 30).millisecondsSinceEpoch.toDouble(), + }); + expect( + shouldShowMidnightTakenWarningDialog( + sharedPreferences: await SharedPreferences.getInstance(), + takenDate: DateTime(2024, 3, 1, 0, 10), // 2月29日があるので29日後 + recordedAt: DateTime(2024, 3, 1, 0, 10), + reminderTimes: reminderTimes, + ), + false, + ); + }); + + test("うるう年(2月29日を含む)で前回表示から30日後の場合は表示する", () async { + SharedPreferences.setMockInitialValues({ + DoubleKey.midnightTakenWarningDialogLastShownDateTime: + DateTime(2024, 1, 31, 0, 30).millisecondsSinceEpoch.toDouble(), + }); + expect( + shouldShowMidnightTakenWarningDialog( + sharedPreferences: await SharedPreferences.getInstance(), + takenDate: DateTime(2024, 3, 1, 0, 10), // 2月29日があるので30日後 + recordedAt: DateTime(2024, 3, 1, 0, 10), + reminderTimes: reminderTimes, + ), + true, + ); + }); + + test("平年(2月28日まで)で前回表示から29日後の場合は表示しない", () async { + SharedPreferences.setMockInitialValues({ + DoubleKey.midnightTakenWarningDialogLastShownDateTime: + DateTime(2025, 2, 1, 0, 30).millisecondsSinceEpoch.toDouble(), + }); + expect( + shouldShowMidnightTakenWarningDialog( + sharedPreferences: await SharedPreferences.getInstance(), + takenDate: DateTime(2025, 3, 2, 0, 10), // 2月は28日までなので29日後 + recordedAt: DateTime(2025, 3, 2, 0, 10), + reminderTimes: reminderTimes, + ), + false, + ); + }); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/features/record/components/button/midnight_taken_warning_dialog_test.dart` around lines 255 - 270, Extend the tests around shouldShowMidnightTakenWarningDialog with leap-year boundary cases covering February 29, 2024 and the corresponding non-leap-year boundary in 2025. Verify both sides of the 30-day threshold, including dates just before and at/after the threshold, while preserving the existing cross-year coverage and expected display behavior.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@lib/features/record/components/button/midnight_taken_warning_dialog.dart`:
- Around line 74-94: 通知が無効な場合に警告ダイアログを表示しないよう、MidnightTakenWarningDialog の表示判定で
setting.isOnReminder を確認してください。setting.isOnReminder が false の場合は
shouldShowMidnightTakenWarningDialog の呼び出し前に return し、既存の有効時の判定と表示処理は維持してください。
In
`@test/features/record/components/button/midnight_taken_warning_dialog_test.dart`:
- Around line 255-270: Extend the tests around
shouldShowMidnightTakenWarningDialog with leap-year boundary cases covering
February 29, 2024 and the corresponding non-leap-year boundary in 2025. Verify
both sides of the 30-day threshold, including dates just before and at/after the
threshold, while preserving the existing cross-year coverage and expected
display behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 21c63fac-9deb-4f6c-9d68-274a9e9696ab
📒 Files selected for processing (6)
lib/features/record/components/button/midnight_taken_warning_dialog.dartlib/features/record/components/button/record_page_button.dartlib/features/record/components/button/taken_button.dartlib/features/record/components/pill_sheet/record_page_pill_sheet.dartlib/features/record/page.darttest/features/record/components/button/midnight_taken_warning_dialog_test.dart
🚧 Files skipped from review as they are similar to previous changes (2)
- lib/features/record/components/button/taken_button.dart
- lib/features/record/components/button/record_page_button.dart
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 808d267393
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
複数の記録経路(飲んだボタン・ピルシートの番号タップ)が短時間に呼んだ場合、 表示記録の保存前に両方が表示可能と判定して二重表示され得るため、 表示中フラグでガードする。フラグはダイアログが閉じたら必ず解除し、 解除後の再表示は「閉じる」で保存される表示記録の30日条件で防がれる
setting.isOnReminderがfalseの場合、LocalNotificationService.runが早期returnして 当日の通知は元々送信されないため、「通知が届きません」という注意自体が成立しない。 判定関数にはreminderTimesではなくSettingを渡す形に変更
await式をif条件に直接埋め込むとインデントが崩れて読みづらいため、 一時変数に代入してから判定する
setting.isOnNotifyInNotTakenDurationがfalseで当日が偽薬/休薬期間 (ピル番号がdosingPeriod超)の場合、RegisterReminderLocalNotification.runは その日の通知登録をスキップするため当日の通知は元々予定されていない。 判定にactivePillSheetを渡し、該当日は表示対象から外す
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/features/record/components/button/midnight_taken_warning_dialog.dart`:
- Around line 91-113: Update the midnight warning dialog flow around showDialog
to persist DoubleKey.midnightTakenWarningDialogLastShownDateTime when the dialog
is displayed, before awaiting its dismissal. In the FAQ link handler, await
launchUrl and show the existing error guidance when it returns false instead of
ignoring the result.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fdaec9f1-e8f0-4ea4-a459-c38c6f6250fc
📒 Files selected for processing (4)
lib/features/record/components/button/midnight_taken_warning_dialog.dartlib/features/record/components/button/taken_button.dartlib/features/record/components/pill_sheet/record_page_pill_sheet.darttest/features/record/components/button/midnight_taken_warning_dialog_test.dart
🚧 Files skipped from review as they are similar to previous changes (3)
- lib/features/record/components/button/taken_button.dart
- lib/features/record/components/pill_sheet/record_page_pill_sheet.dart
- test/features/record/components/button/midnight_taken_warning_dialog_test.dart


Abstract
深夜0:00〜2:00にアプリ上で服用記録(「飲んだ」ボタン・ピルシートの番号タップ)をした場合に、当日分まで服用記録されたことを知らせる注意ダイアログを表示する。
barrierDismissible: false)。「閉じる」で確実に表示記録を残すため表示条件の詳細
midnightTakenWarningDialogDisplayModeで判定:なお通知時刻の未設定は
ReminderTime.minimumCount = 1が保存時にバリデーションされるため通常発生しないが、条件3が空リストも自然に吸収する。実装
SettingはRecordPageBody→RecordPageButton→TakenButtonとconstructorで受け渡すonTap: () {}で服用記録できないため対象外Why
明朝0時〜2時に服用するユーザーが、日を跨いでいないと勘違いして「飲んだ」を押すと当日分まで服用記録される。例えば7/11 0:10の記録でユーザーは7/10分のつもりだが、実際は7/11分まで記録され、7/11当日の通知が届かず「設定した時刻に通知が来ない」という問い合わせが発生している。その対策として、記録直後に注意を促し服用取消のFAQへ誘導する。
Links
https://pilll.notion.site/63c3436e63ea4ba8a1407be40cb5f0e5
スクリーンショット
シミュレータでの動作確認(検証用に時刻・表示頻度条件を一時無効化したビルドで撮影。コードは復元済み)。
Checked
midnight_taken_warning_faq/midnight_taken_warning_close/midnight_taken_warning_never_show_again、表示時のlogScreenView)セッション再開
cd /Users/bannzai/worktrees/bannzai/Pilll/fix-issue-dialog claude --resume 9b6ce426-d0fa-4947-a03f-cfded3be9b7cSummary by CodeRabbit