Potential bug / behavior question
RewardedAdGate grants the reward (starts the workout) even when the rewarded ad fails to load or is never shown. An ad-blocking user can always bypass the gate.
Where
src/components/ads/custom/RewardedAdGate.tsx:
const handleWatchAd = useCallback(() => {
if (!window.ezRewardedAds?.ready) {
setOpen(false);
onRewardGranted(); // (1) SDK not ready → reward granted
return;
}
setLoading(true);
window.ezRewardedAds.requestAndShow((result) => {
setLoading(false);
setOpen(false);
if (result.reward || !result.status) { // (2) ad failed (!status) → reward granted
onRewardGranted();
}
});
}, [onRewardGranted]);
onRewardGranted is wired to onStartWorkout (workout-stepper-footer.tsx:49), so the gate controls whether a non-premium user can start the workout.
Impact
Two paths grant the reward without a completed ad:
- SDK not ready (
!window.ezRewardedAds?.ready) — e.g. the Ezoic script is blocked by an ad blocker, failed to load, or hasn't initialized. The user clicks "watch ad", the SDK isn't there, and the workout starts anyway.
- Ad callback reports failure (
!result.status) — if the ad request errors out, the reward is granted.
Combined with premium users skipping the gate entirely (line 33), the net effect is that the rewarded-ad gate is advisory: anyone using an ad blocker always passes it for free, which defeats the monetization intent.
Question for maintainers
This may be intentional "fail-open" UX (never block a user from working out because of an ad-system failure). If so, feel free to close. If the intent is to actually gate the workout behind a completed ad, the fix is to grant the reward only on a real completion and show a fallback (e.g. "ad unavailable, try again / start anyway with a clear notice") rather than silently succeeding.
I can open a PR for either direction — wanted to confirm intent first.
Potential bug / behavior question
RewardedAdGategrants the reward (starts the workout) even when the rewarded ad fails to load or is never shown. An ad-blocking user can always bypass the gate.Where
src/components/ads/custom/RewardedAdGate.tsx:onRewardGrantedis wired toonStartWorkout(workout-stepper-footer.tsx:49), so the gate controls whether a non-premium user can start the workout.Impact
Two paths grant the reward without a completed ad:
!window.ezRewardedAds?.ready) — e.g. the Ezoic script is blocked by an ad blocker, failed to load, or hasn't initialized. The user clicks "watch ad", the SDK isn't there, and the workout starts anyway.!result.status) — if the ad request errors out, the reward is granted.Combined with premium users skipping the gate entirely (line 33), the net effect is that the rewarded-ad gate is advisory: anyone using an ad blocker always passes it for free, which defeats the monetization intent.
Question for maintainers
This may be intentional "fail-open" UX (never block a user from working out because of an ad-system failure). If so, feel free to close. If the intent is to actually gate the workout behind a completed ad, the fix is to grant the reward only on a real completion and show a fallback (e.g. "ad unavailable, try again / start anyway with a clear notice") rather than silently succeeding.
I can open a PR for either direction — wanted to confirm intent first.