Conversation
ended_pill_sheet_dialog_history / ended_pill_sheet_dialog_summary を追加し、A/B 各バリアントの paywall 経路を計測できるようにする
A/B バリアント割り当てを Firebase Remote Config / A/B Testing で行う。 default は空文字(実験未参加=非表示)で、history_blur / summary_stats を配信する。 String 取得用の getStringOrDefault 拡張も追加
服用予定日と服用記録日の集合構築を内部ヘルパーに切り出し(missedPillDays の戻り値は不変)、 服用予定日数を返す scheduledPillDays を追加する。 ダイアログ Variant B の x(服用記録日数) = scheduledPillDays - missedPillDays に利用する
endedPillSheetDialogShown(pillSheetGroupID) で終了グループごとに表示済みを保持し、 1つの終了ピルシートグループにつき1回だけダイアログを出す
ピルシート終了時に free ユーザーへ価値ティーザー型ダイアログを表示する。 - Variant A(history_blur): 最新の服用履歴3件を先頭1件は鮮明・残り2件は blur で見せる - Variant B(summary_stats): 服用記録できた日数・記録漏れ日数の集計メッセージを表示 - shown / cta_tapped / dismissed を variant 付きで送出し、CTA から各 source で paywall を開く - 集計の境界値・variant 解決・ダイアログの分岐描画をテスト
HomePageBody の起動時 useEffect に「終了グループ + free + 未表示」分岐を追加し、 Remote Config の variant に応じてダイアログを表示する。既存の月次 paywall とは排他。 バーは残し、ダイアログを追加する(置換しない)
📝 WalkthroughWalkthrough終了ピルシート向けに、集計ロジック、Remote Config の variant、ダイアログ UI、表示導線を追加しました。履歴ぼかし/集計の2種ティーザーと、それをホーム画面から一度だけ表示する処理も含まれます。 Changes終了ピルシートダイアログ
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
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.
Code Review
このプルリクエストでは、ピルシートが終了した無料ユーザー向けに、Firebase Remote Configのバリアント設定(履歴のぼかし表示または服用記録の集計メッセージ)に応じた課金転換ダイアログを表示する機能が追加されました。これに伴い、服用予定日数や記録漏れ日数を集計するロジックの共通化や、関連するUIコンポーネントおよびテストコードが実装されています。レビュー指摘として、endedPillSheetTakenSummary 内で _buildPillTakenDateSets が重複して実行される非効率な実装に対して、1回の呼び出しに最適化する具体的なコード提案が行われています。
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9ab08dc4d3
ℹ️ 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".
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
lib/l10n/app_en.arb (1)
2805-2809: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value新規ローカライズキーの
@メタデータ description が欠落・空です。
endedPillSheetDialogHistoryTitleとendedPillSheetDialogSummaryTitleには@メタデータ(description)が無く、endedPillSheetDialogSummaryMessageの description は空文字です。このファイルの他キーと同様に翻訳意図が伝わる説明を付与しておくと、翻訳品質・保守性が向上します。🤖 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/l10n/app_en.arb` around lines 2805 - 2809, The new localization keys in app_en.arb are missing proper @ metadata descriptions, and endedPillSheetDialogSummaryMessage currently has an empty description. Update the corresponding metadata entries for endedPillSheetDialogHistoryTitle, endedPillSheetDialogSummaryTitle, and endedPillSheetDialogSummaryMessage so each has a clear, non-empty description consistent with the other ARB entries, using the same key names to locate them.
🤖 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/home/page.dart`:
- Around line 174-175: The ended pill sheet “shown” flag is written without
waiting for the result, so failures can be missed and the dialog may reappear
for the same group. Update the flow in the home page logic around
showEndedPillSheetDialog and sharedPreferences.setBool to await the write, check
the returned success value, and handle the failure case explicitly so the saved
state is reliable.
In `@test/features/ended_pill_sheet_dialog/ended_pill_sheet_dialog_test.dart`:
- Line 30: The EndedPillSheetDialog widget test group name should follow the
Widget Test naming convention and start with a # prefix. Update the group
declaration in EndedPillSheetDialog tests to use the widget class name with a
leading # so it matches the path instructions and is easy to identify alongside
other widget test groups.
---
Nitpick comments:
In `@lib/l10n/app_en.arb`:
- Around line 2805-2809: The new localization keys in app_en.arb are missing
proper @ metadata descriptions, and endedPillSheetDialogSummaryMessage currently
has an empty description. Update the corresponding metadata entries for
endedPillSheetDialogHistoryTitle, endedPillSheetDialogSummaryTitle, and
endedPillSheetDialogSummaryMessage so each has a clear, non-empty description
consistent with the other ARB entries, using the same key names to locate them.
🪄 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: 1301f059-a4bd-45dc-95f7-c5b54131e18a
📒 Files selected for processing (22)
lib/entity/pill_sheet_modified_history.codegen.dartlib/entity/remote_config_parameter.codegen.dartlib/entity/remote_config_parameter.codegen.freezed.dartlib/entity/remote_config_parameter.codegen.g.dartlib/features/ended_pill_sheet_dialog/components/history_blur_teaser.dartlib/features/ended_pill_sheet_dialog/components/summary_stats_teaser.dartlib/features/ended_pill_sheet_dialog/ended_pill_sheet_dialog.dartlib/features/ended_pill_sheet_dialog/ended_pill_sheet_dialog_variant.dartlib/features/ended_pill_sheet_dialog/ended_pill_sheet_taken_summary.dartlib/features/home/page.dartlib/features/premium_introduction/paywall_source.dartlib/l10n/app_en.arblib/l10n/app_ja.arblib/provider/remote_config_parameter.dartlib/provider/remote_config_parameter.g.dartlib/utils/remote_config.dartlib/utils/shared_preference/keys.darttest/entity/pill_sheet_modified_history_test.darttest/features/ended_pill_sheet_dialog/ended_pill_sheet_dialog_test.darttest/features/ended_pill_sheet_dialog/ended_pill_sheet_dialog_variant_test.darttest/features/ended_pill_sheet_dialog/ended_pill_sheet_taken_summary_test.darttest/features/premium_introduction/paywall_source_test.dart
- 日付集合をすべて日付のみに正規化(時刻付き履歴で服用日が一致せず recordedDays=0 になる不具合を修正) - 集計期間をシート開始日(beginDate)〜終了日(endDate・両端含む)にし、開始日〜初回記録前の未記録日も記録漏れに含める - endedPillSheetTakenSummary は pillTakenDateSets を1回だけ呼び出す(服用予定日数・記録漏れ日数の二重計算を解消) - scheduledPillDays を削除し、_buildPillTakenDateSets を pillTakenDateSets として公開
CodeRabbit レビュー対応: setBool の結果を待たずにいたため、保存失敗時に同じ終了グループでダイアログが再表示される可能性があった。await して失敗時は debugPrint する。
CodeRabbit レビュー対応: Widget Test のグループは Widget クラス名を # 始まりで命名する規約に合わせる。
There was a problem hiding this comment.
🧹 Nitpick comments (1)
lib/features/ended_pill_sheet_dialog/ended_pill_sheet_taken_summary.dart (1)
18-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value到達不能な null 分岐
beginDateは必須パラメータのため、pillTakenDateSetsが null を返す条件(beginDate == nullかつ履歴が空)には到達しません。この分岐は実質デッドコードです。♻️ 提案
- if (sets == null) { - return (recordedDays: 0, missedDays: 0); - } - final missedDays = sets.scheduledDates.difference(sets.takenDates).length; + final missedDays = sets!.scheduledDates.difference(sets.takenDates).length;または、戻り値の型を non-nullable にできないか
pillTakenDateSets側の実装を確認した上で、呼び出し側のnullチェックの要否を再検討してください。🤖 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/ended_pill_sheet_dialog/ended_pill_sheet_taken_summary.dart` around lines 18 - 20, The null guard in pillTakenDateSummary is unreachable because beginDate is required, so the sets == null branch is dead code. Remove that branch from ended_pill_sheet_taken_summary.dart and then verify pillTakenDateSets and any callers of pillTakenDateSummary so the return type can be kept consistently non-nullable if possible, or adjust the API only if a real null case exists.
🤖 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.
Nitpick comments:
In `@lib/features/ended_pill_sheet_dialog/ended_pill_sheet_taken_summary.dart`:
- Around line 18-20: The null guard in pillTakenDateSummary is unreachable
because beginDate is required, so the sets == null branch is dead code. Remove
that branch from ended_pill_sheet_taken_summary.dart and then verify
pillTakenDateSets and any callers of pillTakenDateSummary so the return type can
be kept consistently non-nullable if possible, or adjust the API only if a real
null case exists.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: aefe2cf1-9674-4ab2-91e5-0d3e8831e586
📒 Files selected for processing (7)
lib/entity/pill_sheet_modified_history.codegen.dartlib/features/ended_pill_sheet_dialog/components/summary_stats_teaser.dartlib/features/ended_pill_sheet_dialog/ended_pill_sheet_taken_summary.dartlib/features/home/page.darttest/entity/pill_sheet_modified_history_test.darttest/features/ended_pill_sheet_dialog/ended_pill_sheet_dialog_test.darttest/features/ended_pill_sheet_dialog/ended_pill_sheet_taken_summary_test.dart
✅ Files skipped from review due to trivial changes (1)
- lib/entity/pill_sheet_modified_history.codegen.dart
🚧 Files skipped from review as they are similar to previous changes (3)
- test/features/ended_pill_sheet_dialog/ended_pill_sheet_dialog_test.dart
- lib/features/home/page.dart
- lib/features/ended_pill_sheet_dialog/components/summary_stats_teaser.dart
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 723748ec86
ℹ️ 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".
| .valueOrNull ?? | ||
| [], |
There was a problem hiding this comment.
summary_stats バリアントで Firestore の pillSheetModifiedHistoriesWithRangeProvider が初回スナップショットを返す前、valueOrNull は null になるためこのフォールバックで空履歴として集計されます。endedPillSheetTakenSummary は begin/end が渡されると空履歴を「全日記録漏れ」と扱うので、ネットワーク遅延やエラー時に 28日シートなら一旦「服用記録0件・記録漏れ28件」のような誤った訴求を表示します。AsyncValue の loading/error を分けて、データ到着後に集計を表示してください。
Useful? React with 👍 / 👎.
| @override | ||
| Widget build(BuildContext context, WidgetRef ref) { | ||
| final beginDate = pillSheetGroup.pillSheets.first.beginDate; | ||
| final endDate = pillSheetGroup.pillSheets.last.estimatedEndTakenDate; |
There was a problem hiding this comment.
pillsheet_21 や pillsheet_24_rest_4 など PillSheetType.dosingPeriod < totalCount のシートでは、estimatedEndTakenDate が休薬/偽薬日を含む一方、endedPillSheetTakenSummary は begin〜end の全日を服用予定日として扱います。正しく21日服用して7日休薬したユーザーでも「記録漏れ7件」と表示されるため、シート種別の dosingPeriod を使って標準の休薬・偽薬日を scheduledDates から除外してください。
Useful? React with 👍 / 👎.
| final missedDays = sets.scheduledDates.difference(sets.takenDates).length; | ||
| return ( | ||
| recordedDays: sets.scheduledDates.length - missedDays, |
There was a problem hiding this comment.
ユーザーが服用記録を取り消した場合、履歴には元の takenPill と後続の revertTakenPill が残りますが、pillTakenDateSets は taken 日付を追加するだけで取り消しを反映しません。そのため終了時サマリでは、誤って記録して取り消した日も takenDates に残り、recordedDays が1日多く missedDays が1日少なく表示されます。revertTakenPill の before/after から取り消された日を除外するか、最終的な PillSheet 状態から集計してください。
Useful? React with 👍 / 👎.
Abstract
ピルシートが終了した free ユーザーに、価値ティーザー型の課金転換ダイアログを表示する A/B 機能を追加します(PilllBackend issue #385 の Flutter 側実装)。
history_blur: 最新の服用履歴3件を「先頭1件は鮮明・残り2件は blur」で見せて課金を訴求summary_stats: 服用記録できた日数・記録漏れ日数の集計メッセージを見せる既存のピルシート終了バーは残し、ダイアログを 追加 します(置換しない)。表示は free ユーザー限定で、終了グループにつき1回です。
Why
PR #383 のリリース後分析で、ペイウォール起動経路別の購入転換に大きな差が判明しました。
ended_pill_sheet_bar(終了バー)pill_sheet_modified_history_card(ぼかし履歴カード)差はファネル最上流(paywall を開いた瞬間の意図)で生まれています。終了バーは「機能タスクの予告」文言で意図ミスマッチ、ぼかし履歴カードは「価値ティーザー+プレミアム明示+本人のオプトイン」で意図一致するため高転換です。この勝ち筋を終了タイミングに移植し、どの提示形式が効くかを A/B 検証します。
変更内容
計測
PaywallSourceにendedPillSheetDialogHistory/endedPillSheetDialogSummaryを追加(value:ended_pill_sheet_dialog_history/ended_pill_sheet_dialog_summary)variant付きで送出:ended_sheet_dialog_shown/ended_sheet_dialog_cta_tapped/ended_sheet_dialog_dismissedshowPremiumIntroductionSheet(source: 各バリアントの source)で既存 paywall ファネル(paywall_viewed→pressed_*_purchase_button→purchase_succeeded)に接続A/B 割り当て
endedPillSheetDialogVariant(default 空文字=非表示)を追加し、history_blur/summary_statsを配信して 50:50 で振り分ける集計(Variant B の x/y)
missedPillDaysを y(記録漏れ日数)に流用missedPillDaysの集合構築を内部ヘルパーに分解(戻り値は不変)し、服用予定日数を返すscheduledPillDaysを追加。x(服用記録日数)=scheduledPillDays − missedPillDays表示トリガー / 頻度制御
home/page.dartの起動時 useEffect に「終了グループ(activePillSheet == null)+ free(!premiumOrTrial)+ 未表示」分岐を追加(既存の月次 paywall と排他)pillSheetGroupIDを key に SharedPreferences で表示済みフラグを保持し、終了グループにつき1回だけ表示動作確認
iOS Simulator で初期設定 → ピルシート登録 → 服用記録の上、ダイアログを表示して確認しました。
history_blursummary_statsテスト
flutter test全件パス(既存 1426 件 + 新規 12 件)ended_pill_sheet_taken_summary_test.dart、#scheduledPillDaysended_pill_sheet_dialog_variant_test.dartended_pill_sheet_dialog_test.dartmissedPillDaysリファクタの回帰(戻り値不変): 既存テストで担保残作業(本PR対象外)
paywall_source_conversion.sqlのknown_sourcesに新2値追加、paywall_source_ended_dialog_ab.sql(A/B集計)を追加(イベント着弾後)endedPillSheetDialogVariantを 50:50 配信)Links
Checked
Summary by CodeRabbit