-
Notifications
You must be signed in to change notification settings - Fork 562
rewarded ad test #2091
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v25
Are you sure you want to change the base?
rewarded ad test #2091
Conversation
WalkthroughAdds rewarded ad support. Extends Window.ramp with rewarded-video APIs and ad request. Integrates rewarded ad loading and UI into WinModal, including event listeners, button state, and reward flow. Adjusts hash navigation after lobby join. Leaves commented spaNewPage lines unchanged. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant WinModal
participant RAMP as window.ramp
participant Ad as Rewarded Ad
rect rgb(245,248,255)
note over WinModal: Initialization on show()
WinModal->>RAMP: queue rewarded_ad_video into container
RAMP-->>WinModal: rewarded ad ready (event)
WinModal->>WinModal: enable "Rewarded Ad" button
end
User->>WinModal: Click "Rewarded Ad"
WinModal->>RAMP: showRewardedVideoModal(cta, confirm)
RAMP->>Ad: display modal / play video
Ad-->>RAMP: completed
RAMP-->>WinModal: reward granted (event)
WinModal->>WinModal: grantReward()
RAMP-->>WinModal: closed (event)
WinModal->>WinModal: tidy up / re-enable UI
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
Pre-merge checks❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/client/graphics/layers/WinModal.ts (1)
303-313
: Enable the button on “ad ready” event, not on timer.Rely on the RAMP “ready” event to avoid false enables and errors on click.
async show() { - this.loadAds(); + this.loadAds(); await this.loadPatternContent(); - this.initializeRewardedAd(); + this.initializeRewardedAd();And update initializeRewardedAd below (see next comment).
🧹 Nitpick comments (5)
src/client/graphics/layers/WinModal.ts (3)
336-351
: Ad unit lifecycle — destroy on detach/hide to avoid duplicates.Call destroyUnits('rewarded_ad_video') on hide/disconnect.
Add this outside the hunk (two places):
hide() { this.isVisible = false; this.showButtons = false; this.requestUpdate(); try { window.ramp?.destroyUnits?.("rewarded_ad_video"); } catch {/* no-op */} } disconnectedCallback() { super.disconnectedCallback(); try { window.ramp?.destroyUnits?.("rewarded_ad_video"); } catch {/* no-op */} }
399-422
: Avoid re-enabling here; handled in finally. Also keep one confirmation path.Prevent double confirmation and duplicated state toggles.
- // Show Playwire confirmation modal if available - try { - if (window.ramp.showRewardedVideoConfirmationModal) { - window.ramp.showRewardedVideoConfirmationModal(); - } else if (window.ramp.showRewardedVideoConfirmModal) { - window.ramp.showRewardedVideoConfirmModal(); - } - } catch (e) { - // no-op - } - - // Re-enable button after flow - this.rewardedAdAvailable = true; - this.requestUpdate(); + // Show Playwire confirmation modal if available + try { + window.ramp?.showRewardedVideoConfirmationModal?.() ?? + window.ramp?.showRewardedVideoConfirmModal?.(); + } catch {/* no-op */}
81-110
: Type safety for custom events.Augment WindowEventMap with a typed union for the Playwire events to keep handlers type-safe.
Add a .d.ts ambient declaration:
declare global { interface WindowEventMap { rewardedAdVideoRewardReady: Event; userAcceptsRewardedAd: Event; rewardedAdCompleted: Event; rewardedAdRewardGranted: Event; rewardedCloseButtonTriggered: Event; userClosedWithRewardCanResolve: Event; rejectAdCloseCta: Event; } } export {};src/client/Main.ts (2)
62-95
: Shape Window.ramp as a reusable type (in .d.ts), and narrow string types.
- Move this to a shared ambient .d.ts so all modules see it.
- Use narrower types: adType as a union like "rewarded_ad_video".
- Consider making window.ramp optional to match runtime.
Example:
// global-ramp.d.ts declare global { type RampAdType = "rewarded_ad_video"; interface RampApi { que: Array<() => void>; passiveMode: boolean; spaAddAds: (ads: Array<{ type: RampAdType; selectorId: string }>) => void; destroyUnits: (adType: RampAdType) => void; settings?: { slots?: unknown }; spaNewPage: (url: string) => void; manuallyCreateRewardUi(config: { skipConfirmation?: boolean; watchAdId?: string; closeId?: string }): Promise<void>; showRewardedVideoModal( ctaSettings?: { logoSrc?: string; nameLogoSrc?: string; title?: string; backgroundOverlay?: boolean; backgroundColor?: string; titleColor?: string; confirmButtonText?: string; confirmButtonColor?: string; }, confirmSettings?: { logoSrc?: string; title?: string; subTitle?: string; closeButtonText?: string; backgroundOverlay?: boolean; backgroundColor?: string; buttonColor?: string; subTitleTextColor?: string; titleColor?: string; }, ): Promise<void>; showRewardedVideoConfirmationModal(): void; showRewardedVideoConfirmModal?: () => void; requestAd: (adType: RampAdType) => Promise<void>; } interface Window { ramp?: RampApi; } } export {};
622-622
: Hash changed to #google_sample_tag=1 — verify flow impact.
- Does this break back/forward behavior or deep links (e.g., rejoining lobbies)?
- If only tagging, prefer replaceState to avoid extra history entries.
- history.pushState(null, "", `#google_sample_tag=1`); + history.replaceState(null, "", `#google_sample_tag=1`);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/client/Main.ts
(3 hunks)src/client/graphics/layers/WinModal.ts
(6 hunks)
🔇 Additional comments (4)
src/client/graphics/layers/WinModal.ts (3)
34-36
: State flag added — OKGood name and default. No concerns.
46-48
: Initialize listeners in constructor — OK (but see cleanup fix below)Setup is fine. Cleanup currently won’t work due to bound handlers; see below.
207-211
: Verify unique container ID.Confirm only one win-modal exists at a time to avoid duplicate #rewarded-ad-container IDs in DOM.
src/client/Main.ts (1)
609-609
: Restore SPA pageview call on virtual navigations (confirm Playwire API)SPA page-change APIs should be called once for every in-app navigation after the new view/DOM is rendered; skipping it can reduce ad fill and break tracking. I couldn't find public Playwire docs for spaNewPage specifically — confirm the exact API/name/timing with Playwire and re-enable the commented pageview call in src/client/Main.ts (around line 609).
private setupRewardedAdEventListeners() { | ||
// Listen for rewarded ad lifecycle events | ||
window.addEventListener( | ||
"rewardedAdVideoRewardReady", | ||
this.onRewardedAdReady.bind(this), | ||
); | ||
window.addEventListener( | ||
"userAcceptsRewardedAd", | ||
this.onUserAcceptsRewardedAd.bind(this), | ||
); | ||
window.addEventListener( | ||
"rewardedAdCompleted", | ||
this.onRewardedAdCompleted.bind(this), | ||
); | ||
window.addEventListener( | ||
"rewardedAdRewardGranted", | ||
this.onRewardedAdRewardGranted.bind(this), | ||
); | ||
window.addEventListener( | ||
"rewardedCloseButtonTriggered", | ||
this.onRewardedAdClosed.bind(this), | ||
); | ||
window.addEventListener( | ||
"userClosedWithRewardCanResolve", | ||
this.onUserClosedWithReward.bind(this), | ||
); | ||
window.addEventListener( | ||
"rejectAdCloseCta", | ||
this.onRejectAdClose.bind(this), | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Event listeners won’t be removed (bind creates new functions). Store handler refs.
Using .bind(this)
in both add/remove creates different function identities. removeEventListener
will not detach, causing leaks and duplicate handlers after re‑attach.
Apply this diff to use cached handler refs:
- window.addEventListener(
- "rewardedAdVideoRewardReady",
- this.onRewardedAdReady.bind(this),
- );
+ window.addEventListener(
+ "rewardedAdVideoRewardReady",
+ this.boundOnRewardedAdReady,
+ );
- window.addEventListener(
- "userAcceptsRewardedAd",
- this.onUserAcceptsRewardedAd.bind(this),
- );
+ window.addEventListener(
+ "userAcceptsRewardedAd",
+ this.boundOnUserAcceptsRewardedAd,
+ );
- window.addEventListener(
- "rewardedAdCompleted",
- this.onRewardedAdCompleted.bind(this),
- );
+ window.addEventListener(
+ "rewardedAdCompleted",
+ this.boundOnRewardedAdCompleted,
+ );
- window.addEventListener(
- "rewardedAdRewardGranted",
- this.onRewardedAdRewardGranted.bind(this),
- );
+ window.addEventListener(
+ "rewardedAdRewardGranted",
+ this.boundOnRewardedAdRewardGranted,
+ );
- window.addEventListener(
- "rewardedCloseButtonTriggered",
- this.onRewardedAdClosed.bind(this),
- );
+ window.addEventListener(
+ "rewardedCloseButtonTriggered",
+ this.boundOnRewardedAdClosed,
+ );
- window.addEventListener(
- "userClosedWithRewardCanResolve",
- this.onUserClosedWithReward.bind(this),
- );
+ window.addEventListener(
+ "userClosedWithRewardCanResolve",
+ this.boundOnUserClosedWithReward,
+ );
- window.addEventListener(
- "rejectAdCloseCta",
- this.onRejectAdClose.bind(this),
- );
+ window.addEventListener(
+ "rejectAdCloseCta",
+ this.boundOnRejectAdClose,
+ );
Add these fields on the class (outside this hunk):
private boundOnRewardedAdReady = this.onRewardedAdReady.bind(this);
private boundOnUserAcceptsRewardedAd = this.onUserAcceptsRewardedAd.bind(this);
private boundOnRewardedAdCompleted = this.onRewardedAdCompleted.bind(this);
private boundOnRewardedAdRewardGranted = this.onRewardedAdRewardGranted.bind(this);
private boundOnRewardedAdClosed = this.onRewardedAdClosed.bind(this);
private boundOnUserClosedWithReward = this.onUserClosedWithReward.bind(this);
private boundOnRejectAdClose = this.onRejectAdClose.bind(this);
🤖 Prompt for AI Agents
In src/client/graphics/layers/WinModal.ts around lines 49 to 79, the event
listeners are added using .bind(this) which creates new function identities
preventing removeEventListener from detaching them; fix by adding class fields
that cache bound handler references (one per handler: boundOnRewardedAdReady,
boundOnUserAcceptsRewardedAd, boundOnRewardedAdCompleted,
boundOnRewardedAdRewardGranted, boundOnRewardedAdClosed,
boundOnUserClosedWithReward, boundOnRejectAdClose) initialized with
this.<handler>.bind(this), then update setupRewardedAdEventListeners to pass
those cached bound handlers to addEventListener (and ensure any teardown/remove
routine uses the same cached fields for removeEventListener).
private onRewardedAdRewardGranted() { | ||
console.log("🎁 User watched enough to earn a reward"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reward is never granted — call grantReward() on reward events.
You log but don’t grant. Hook the reward grant points.
private onRewardedAdRewardGranted() {
- console.log("🎁 User watched enough to earn a reward");
+ console.log("🎁 User watched enough to earn a reward");
+ this.grantReward();
}
private onUserClosedWithReward() {
- console.log("🎁 User closed the ad after qualifying for the reward");
+ console.log("🎁 User closed the ad after qualifying for the reward");
+ this.grantReward();
}
Also applies to: 103-105
🤖 Prompt for AI Agents
In src/client/graphics/layers/WinModal.ts around lines 95-97 and 103-105, the
handlers only log that a rewarded ad condition was met but never actually grant
the reward; update both event handlers to call the existing grantReward() method
(e.g., this.grantReward() or grantReward() depending on scope) after the
console.log, ensuring the method is accessible from the handler context and
preserving any existing side effects/state updates.
disconnectedCallback() { | ||
super.disconnectedCallback(); | ||
// Clean up event listeners | ||
window.removeEventListener( | ||
"rewardedAdVideoRewardReady", | ||
this.onRewardedAdReady.bind(this), | ||
); | ||
window.removeEventListener( | ||
"userAcceptsRewardedAd", | ||
this.onUserAcceptsRewardedAd.bind(this), | ||
); | ||
window.removeEventListener( | ||
"rewardedAdCompleted", | ||
this.onRewardedAdCompleted.bind(this), | ||
); | ||
window.removeEventListener( | ||
"rewardedAdRewardGranted", | ||
this.onRewardedAdRewardGranted.bind(this), | ||
); | ||
window.removeEventListener( | ||
"rewardedCloseButtonTriggered", | ||
this.onRewardedAdClosed.bind(this), | ||
); | ||
window.removeEventListener( | ||
"userClosedWithRewardCanResolve", | ||
this.onUserClosedWithReward.bind(this), | ||
); | ||
window.removeEventListener( | ||
"rejectAdCloseCta", | ||
this.onRejectAdClose.bind(this), | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Listener cleanup still broken — use cached refs in removeEventListener.
Mirror the add side with the same cached handler refs.
- window.removeEventListener(
- "rewardedAdVideoRewardReady",
- this.onRewardedAdReady.bind(this),
- );
+ window.removeEventListener(
+ "rewardedAdVideoRewardReady",
+ this.boundOnRewardedAdReady,
+ );
- window.removeEventListener(
- "userAcceptsRewardedAd",
- this.onUserAcceptsRewardedAd.bind(this),
- );
+ window.removeEventListener(
+ "userAcceptsRewardedAd",
+ this.boundOnUserAcceptsRewardedAd,
+ );
- window.removeEventListener(
- "rewardedAdCompleted",
- this.onRewardedAdCompleted.bind(this),
- );
+ window.removeEventListener(
+ "rewardedAdCompleted",
+ this.boundOnRewardedAdCompleted,
+ );
- window.removeEventListener(
- "rewardedAdRewardGranted",
- this.onRewardedAdRewardGranted.bind(this),
- );
+ window.removeEventListener(
+ "rewardedAdRewardGranted",
+ this.boundOnRewardedAdRewardGranted,
+ );
- window.removeEventListener(
- "rewardedCloseButtonTriggered",
- this.onRewardedAdClosed.bind(this),
- );
+ window.removeEventListener(
+ "rewardedCloseButtonTriggered",
+ this.boundOnRewardedAdClosed,
+ );
- window.removeEventListener(
- "userClosedWithRewardCanResolve",
- this.onUserClosedWithReward.bind(this),
- );
+ window.removeEventListener(
+ "userClosedWithRewardCanResolve",
+ this.boundOnUserClosedWithReward,
+ );
- window.removeEventListener(
- "rejectAdCloseCta",
- this.onRejectAdClose.bind(this),
- );
+ window.removeEventListener(
+ "rejectAdCloseCta",
+ this.boundOnRejectAdClose,
+ );
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
disconnectedCallback() { | |
super.disconnectedCallback(); | |
// Clean up event listeners | |
window.removeEventListener( | |
"rewardedAdVideoRewardReady", | |
this.onRewardedAdReady.bind(this), | |
); | |
window.removeEventListener( | |
"userAcceptsRewardedAd", | |
this.onUserAcceptsRewardedAd.bind(this), | |
); | |
window.removeEventListener( | |
"rewardedAdCompleted", | |
this.onRewardedAdCompleted.bind(this), | |
); | |
window.removeEventListener( | |
"rewardedAdRewardGranted", | |
this.onRewardedAdRewardGranted.bind(this), | |
); | |
window.removeEventListener( | |
"rewardedCloseButtonTriggered", | |
this.onRewardedAdClosed.bind(this), | |
); | |
window.removeEventListener( | |
"userClosedWithRewardCanResolve", | |
this.onUserClosedWithReward.bind(this), | |
); | |
window.removeEventListener( | |
"rejectAdCloseCta", | |
this.onRejectAdClose.bind(this), | |
); | |
} | |
disconnectedCallback() { | |
super.disconnectedCallback(); | |
// Clean up event listeners | |
window.removeEventListener( | |
"rewardedAdVideoRewardReady", | |
this.boundOnRewardedAdReady, | |
); | |
window.removeEventListener( | |
"userAcceptsRewardedAd", | |
this.boundOnUserAcceptsRewardedAd, | |
); | |
window.removeEventListener( | |
"rewardedAdCompleted", | |
this.boundOnRewardedAdCompleted, | |
); | |
window.removeEventListener( | |
"rewardedAdRewardGranted", | |
this.boundOnRewardedAdRewardGranted, | |
); | |
window.removeEventListener( | |
"rewardedCloseButtonTriggered", | |
this.boundOnRewardedAdClosed, | |
); | |
window.removeEventListener( | |
"userClosedWithRewardCanResolve", | |
this.boundOnUserClosedWithReward, | |
); | |
window.removeEventListener( | |
"rejectAdCloseCta", | |
this.boundOnRejectAdClose, | |
); | |
} |
<div class="mb-4"> | ||
<button | ||
@click=${this._handleRewardedAd} | ||
class="w-full px-4 py-3 text-base cursor-pointer ${this | ||
.rewardedAdAvailable | ||
? "bg-gradient-to-r from-purple-500/80 to-pink-500/80 hover:from-purple-500 hover:to-pink-500" | ||
: "bg-gray-500/60 cursor-not-allowed"} text-white border-0 rounded transition-all duration-200 hover:-translate-y-px active:translate-y-px font-semibold" | ||
?disabled=${!this.rewardedAdAvailable} | ||
> | ||
${this.rewardedAdAvailable ? "Rewarded Ad" : "Loading Ad..."} | ||
</button> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick
Localize button copy.
Run through translateText() and add keys to en.json.
- ${this.rewardedAdAvailable ? "Rewarded Ad" : "Loading Ad..."}
+ ${this.rewardedAdAvailable
+ ? translateText("win_modal.rewarded_ad_btn")
+ : translateText("win_modal.loading_ad")}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
<div class="mb-4"> | |
<button | |
@click=${this._handleRewardedAd} | |
class="w-full px-4 py-3 text-base cursor-pointer ${this | |
.rewardedAdAvailable | |
? "bg-gradient-to-r from-purple-500/80 to-pink-500/80 hover:from-purple-500 hover:to-pink-500" | |
: "bg-gray-500/60 cursor-not-allowed"} text-white border-0 rounded transition-all duration-200 hover:-translate-y-px active:translate-y-px font-semibold" | |
?disabled=${!this.rewardedAdAvailable} | |
> | |
${this.rewardedAdAvailable ? "Rewarded Ad" : "Loading Ad..."} | |
</button> | |
</div> | |
<div class="mb-4"> | |
<button | |
@click=${this._handleRewardedAd} | |
class="w-full px-4 py-3 text-base cursor-pointer ${this | |
.rewardedAdAvailable | |
? "bg-gradient-to-r from-purple-500/80 to-pink-500/80 hover:from-purple-500 hover:to-pink-500" | |
: "bg-gray-500/60 cursor-not-allowed"} text-white border-0 rounded transition-all duration-200 hover:-translate-y-px active:translate-y-px font-semibold" | |
?disabled=${!this.rewardedAdAvailable} | |
> | |
${this.rewardedAdAvailable | |
? translateText("win_modal.rewarded_ad_btn") | |
: translateText("win_modal.loading_ad")} | |
</button> | |
</div> |
🤖 Prompt for AI Agents
In src/client/graphics/layers/WinModal.ts around lines 155 to 166, the button
text is hard-coded; replace the literal labels "Rewarded Ad" and "Loading Ad..."
with calls to translateText() using new keys (e.g. "winModal.rewardedAd" and
"winModal.loadingAd") and keep the conditional logic and disabled binding
intact; then add those keys with appropriate English strings to en.json so
translations load correctly.
private initializeRewardedAd() { | ||
if (!window.ramp) { | ||
console.warn("Playwire RAMP not available for rewarded ads"); | ||
return; | ||
} | ||
|
||
// For out-of-the-box approach, we don't need complex initialization | ||
// Just enable the button after a short delay to allow RAMP to load | ||
setTimeout(() => { | ||
console.log("🎬 Enabling rewarded ad button"); | ||
this.rewardedAdAvailable = true; | ||
this.requestUpdate(); | ||
}, 2000); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initialize by requesting the ad and wait for readiness; remove setTimeout.
Use window.ramp.requestAd and let the ready event flip the flag.
private initializeRewardedAd() {
if (!window.ramp) {
console.warn("Playwire RAMP not available for rewarded ads");
return;
}
-
- // For out-of-the-box approach, we don't need complex initialization
- // Just enable the button after a short delay to allow RAMP to load
- setTimeout(() => {
- console.log("🎬 Enabling rewarded ad button");
- this.rewardedAdAvailable = true;
- this.requestUpdate();
- }, 2000);
+ try {
+ // Let the rewardedAdVideoRewardReady event set rewardedAdAvailable=true
+ void window.ramp.requestAd("rewarded_ad_video");
+ } catch (e) {
+ console.warn("Failed to request rewarded ad", e);
+ }
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private initializeRewardedAd() { | |
if (!window.ramp) { | |
console.warn("Playwire RAMP not available for rewarded ads"); | |
return; | |
} | |
// For out-of-the-box approach, we don't need complex initialization | |
// Just enable the button after a short delay to allow RAMP to load | |
setTimeout(() => { | |
console.log("🎬 Enabling rewarded ad button"); | |
this.rewardedAdAvailable = true; | |
this.requestUpdate(); | |
}, 2000); | |
} | |
private initializeRewardedAd() { | |
if (!window.ramp) { | |
console.warn("Playwire RAMP not available for rewarded ads"); | |
return; | |
} | |
try { | |
// Let the rewardedAdVideoRewardReady event set rewardedAdAvailable=true | |
void window.ramp.requestAd("rewarded_ad_video"); | |
} catch (e) { | |
console.warn("Failed to request rewarded ad", e); | |
} | |
} |
🤖 Prompt for AI Agents
In src/client/graphics/layers/WinModal.ts around lines 315 to 329, replace the
setTimeout-based enable flow with a proper RAMP ad request: call
window.ramp.requestAd() (or the SDK-equivalent request method), attach a
one-time listener for the ad-ready/ready event or promise resolution to set
this.rewardedAdAvailable = true and call this.requestUpdate(), remove the
listener after it fires, and log or handle errors if the request fails; keep the
early return if window.ramp is missing.
private _handleRewardedAd() { | ||
console.log("🎯 Rewarded Ad button clicked"); | ||
|
||
if (!window.ramp) { | ||
console.warn("Playwire RAMP not available"); | ||
return; | ||
} | ||
|
||
// Disable button during playback | ||
this.rewardedAdAvailable = false; | ||
this.requestUpdate(); | ||
|
||
const modalConfig = { | ||
title: "Earn a reward!", | ||
confirmButtonText: "Watch the Video!", | ||
backgroundOverlay: true, | ||
backgroundColor: "", | ||
confirmButtonColor: undefined, | ||
titleColor: undefined, | ||
logoSrc: "", | ||
nameLogoSrc: "", | ||
}; | ||
|
||
const confirmModalConfig = { | ||
title: "Thank you for watching", | ||
subTitle: "You have earned 2 coins!", | ||
closeButtonText: "", | ||
backgroundOverlay: false, | ||
backgroundColor: "", | ||
subTitleTextColor: "#FF0000", | ||
buttonColor: undefined, | ||
titleColor: "#FF0000", | ||
logoSrc: "", | ||
}; | ||
|
||
console.log( | ||
"window.ramp methods:", | ||
Object.getOwnPropertyNames(window.ramp), | ||
); | ||
window.ramp | ||
.showRewardedVideoModal(modalConfig, confirmModalConfig) | ||
.then(() => console.log("✅ Reward granted")) | ||
.then(() => window.ramp.showRewardedVideoConfirmationModal()) | ||
.catch((error) => console.error("❌ Rewarded video error:", error)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Harden the playback flow: i18n, single confirmation, and re-enable in finally.
- Localize all UI text.
- Remove double confirmation.
- Always re-enable button in finally.
private _handleRewardedAd() {
console.log("🎯 Rewarded Ad button clicked");
if (!window.ramp) {
console.warn("Playwire RAMP not available");
return;
}
// Disable button during playback
this.rewardedAdAvailable = false;
this.requestUpdate();
const modalConfig = {
- title: "Earn a reward!",
- confirmButtonText: "Watch the Video!",
+ title: translateText("win_modal.reward_title"),
+ confirmButtonText: translateText("win_modal.reward_cta"),
backgroundOverlay: true,
backgroundColor: "",
confirmButtonColor: undefined,
titleColor: undefined,
logoSrc: "",
nameLogoSrc: "",
};
const confirmModalConfig = {
- title: "Thank you for watching",
- subTitle: "You have earned 2 coins!",
+ title: translateText("win_modal.reward_thanks_title"),
+ subTitle: translateText("win_modal.reward_thanks_subtitle"),
closeButtonText: "",
backgroundOverlay: false,
backgroundColor: "",
- subTitleTextColor: "#FF0000",
+ subTitleTextColor: "",
buttonColor: undefined,
- titleColor: "#FF0000",
+ titleColor: "",
logoSrc: "",
};
- console.log(
- "window.ramp methods:",
- Object.getOwnPropertyNames(window.ramp),
- );
- window.ramp
- .showRewardedVideoModal(modalConfig, confirmModalConfig)
- .then(() => console.log("✅ Reward granted"))
- .then(() => window.ramp.showRewardedVideoConfirmationModal())
- .catch((error) => console.error("❌ Rewarded video error:", error));
+ window.ramp
+ .showRewardedVideoModal(modalConfig, confirmModalConfig)
+ .then(() => {
+ console.log("✅ Reward completed");
+ // In case Playwire doesn’t emit reward event in some flows
+ this.grantReward();
+ })
+ .catch((error) => {
+ console.error("❌ Rewarded video error:", error);
+ this.showRewardError();
+ })
+ .finally(() => {
+ this.rewardedAdAvailable = true;
+ this.requestUpdate();
+ });
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private _handleRewardedAd() { | |
console.log("🎯 Rewarded Ad button clicked"); | |
if (!window.ramp) { | |
console.warn("Playwire RAMP not available"); | |
return; | |
} | |
// Disable button during playback | |
this.rewardedAdAvailable = false; | |
this.requestUpdate(); | |
const modalConfig = { | |
title: "Earn a reward!", | |
confirmButtonText: "Watch the Video!", | |
backgroundOverlay: true, | |
backgroundColor: "", | |
confirmButtonColor: undefined, | |
titleColor: undefined, | |
logoSrc: "", | |
nameLogoSrc: "", | |
}; | |
const confirmModalConfig = { | |
title: "Thank you for watching", | |
subTitle: "You have earned 2 coins!", | |
closeButtonText: "", | |
backgroundOverlay: false, | |
backgroundColor: "", | |
subTitleTextColor: "#FF0000", | |
buttonColor: undefined, | |
titleColor: "#FF0000", | |
logoSrc: "", | |
}; | |
console.log( | |
"window.ramp methods:", | |
Object.getOwnPropertyNames(window.ramp), | |
); | |
window.ramp | |
.showRewardedVideoModal(modalConfig, confirmModalConfig) | |
.then(() => console.log("✅ Reward granted")) | |
.then(() => window.ramp.showRewardedVideoConfirmationModal()) | |
.catch((error) => console.error("❌ Rewarded video error:", error)); | |
} | |
private _handleRewardedAd() { | |
console.log("🎯 Rewarded Ad button clicked"); | |
if (!window.ramp) { | |
console.warn("Playwire RAMP not available"); | |
return; | |
} | |
// Disable button during playback | |
this.rewardedAdAvailable = false; | |
this.requestUpdate(); | |
const modalConfig = { | |
title: translateText("win_modal.reward_title"), | |
confirmButtonText: translateText("win_modal.reward_cta"), | |
backgroundOverlay: true, | |
backgroundColor: "", | |
confirmButtonColor: undefined, | |
titleColor: undefined, | |
logoSrc: "", | |
nameLogoSrc: "", | |
}; | |
const confirmModalConfig = { | |
title: translateText("win_modal.reward_thanks_title"), | |
subTitle: translateText("win_modal.reward_thanks_subtitle"), | |
closeButtonText: "", | |
backgroundOverlay: false, | |
backgroundColor: "", | |
subTitleTextColor: "", | |
buttonColor: undefined, | |
titleColor: "", | |
logoSrc: "", | |
}; | |
window.ramp | |
.showRewardedVideoModal(modalConfig, confirmModalConfig) | |
.then(() => { | |
console.log("✅ Reward completed"); | |
// In case Playwire doesn't emit reward event in some flows | |
this.grantReward(); | |
}) | |
.catch((error) => { | |
console.error("❌ Rewarded video error:", error); | |
this.showRewardError(); | |
}) | |
.finally(() => { | |
this.rewardedAdAvailable = true; | |
this.requestUpdate(); | |
}); | |
} |
🤖 Prompt for AI Agents
In src/client/graphics/layers/WinModal.ts around lines 353 to 397, replace
hard-coded UI strings with i18n lookups (use the component/app i18n utility for
title, button text, confirm modal title/subTitle/subTitleTextColor etc.), remove
the extra .then(() => window.ramp.showRewardedVideoConfirmationModal()) so only
the single confirmation from showRewardedVideoModal(confirmModalConfig) is used,
and ensure rewardedAdAvailable is re-enabled and requestUpdate() is called in a
.finally() handler on the promise chain so the button is always re-enabled
regardless of success or error; keep logging and error handling but move the
state reset into finally.
private showRewardError() { | ||
// Show user-friendly error message | ||
console.log("❌ Unable to show ad at this time. Please try again later."); | ||
// You could show a toast notification or modal here | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick
Localize the error message; prefer UI toast.
Run through translateText and route to your toast/modal helper.
- console.log("❌ Unable to show ad at this time. Please try again later.");
+ console.log("❌", translateText("win_modal.reward_error"));
+ // TODO: toast.show(translateText("win_modal.reward_error"));
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private showRewardError() { | |
// Show user-friendly error message | |
console.log("❌ Unable to show ad at this time. Please try again later."); | |
// You could show a toast notification or modal here | |
} | |
private showRewardError() { | |
// Show user-friendly error message | |
console.log("❌", translateText("win_modal.reward_error")); | |
// TODO: toast.show(translateText("win_modal.reward_error")); | |
// You could show a toast notification or modal here | |
} |
🤖 Prompt for AI Agents
In src/client/graphics/layers/WinModal.ts around lines 424 to 428, the
showRewardError function currently logs a hard-coded English message to console;
update it to use the app's localization and UI notification path by calling
translateText with the appropriate key/message and then routing the translated
string into the existing toast/modal helper (e.g., showToast or showModal)
instead of console.log; ensure you import any missing helpers/types and keep
console logging only for debug if necessary.
Description:
Describe the PR.
Please complete the following:
Please put your Discord username so you can be contacted if a bug or regression is found:
DISCORD_USERNAME