blockReason method
The reason campaign is currently blocked, or null when it may show.
isAllowed delegates to this so the gate can log why it dropped — one
evaluation, no duplicated logic (mirrors Android's blockReason).
Implementation
FrequencySkipReason? blockReason(CampaignModel campaign) {
final policy = campaign.frequency;
if (policy == null || !policy.hasConstraint) return null;
final result = FrequencyEvaluator.evaluate(
policy,
_load(campaign.campaignKey),
_clock(),
_sessionIdProvider(),
);
return result.allow ? null : result.reason;
}