@@ -38,9 +38,74 @@ <h2>Feed</h2>
3838 < h3 > Benchmark Lab</ h3 >
3939 < p > Advanced scenarios, saved/replayed run comparison, and adversarial benchmark packs.</ p >
4040 < button id ="premium-status " disabled > Benchmark Lab: checking…</ button >
41+ < button id ="premium-purchase " type ="button "> Unlock Benchmark Lab</ button >
42+ < button id ="premium-restore " type ="button "> Restore purchase</ button >
43+ < p id ="premium-message " class ="inspect " aria-live ="polite "> </ p >
4144 </ section >
4245 </ aside >
4346 </ main >
4447 < script src ="board.js "> </ script >
48+ < script >
49+ ( ( ) => {
50+ const purchase = document . getElementById ( "premium-purchase" ) ;
51+ const restore = document . getElementById ( "premium-restore" ) ;
52+ const status = document . getElementById ( "premium-status" ) ;
53+ const message = document . getElementById ( "premium-message" ) ;
54+ const nativeBridge = typeof window . ahbg !== "undefined" ? window . ahbg : null ;
55+ if ( ! purchase || ! restore || ! status || ! message ) return ;
56+
57+ const pending = new Map ( ) ;
58+ window . ahbgPremiumResult = ( callbackId , payload ) => {
59+ const resolver = pending . get ( callbackId ) ;
60+ if ( ! resolver ) return ;
61+ pending . delete ( callbackId ) ;
62+ try { resolver . resolve ( JSON . parse ( payload ) ) ; }
63+ catch ( error ) { resolver . reject ( error ) ; }
64+ } ;
65+
66+ function invoke ( method ) {
67+ if ( ! nativeBridge || typeof nativeBridge [ method ] !== "function" ) {
68+ return Promise . reject ( new Error ( "Purchases are available in the Android app" ) ) ;
69+ }
70+ return new Promise ( ( resolve , reject ) => {
71+ const callbackId = `premium_${ Date . now ( ) } _${ Math . floor ( Math . random ( ) * 1e6 ) } ` ;
72+ const timer = setTimeout ( ( ) => {
73+ pending . delete ( callbackId ) ;
74+ reject ( new Error ( "purchase bridge timeout" ) ) ;
75+ } , 30000 ) ;
76+ pending . set ( callbackId , {
77+ resolve : ( result ) => { clearTimeout ( timer ) ; resolve ( result ) ; } ,
78+ reject : ( error ) => { clearTimeout ( timer ) ; reject ( error ) ; } ,
79+ } ) ;
80+ nativeBridge [ method ] ( callbackId ) ;
81+ } ) ;
82+ }
83+
84+ function applyResult ( result ) {
85+ status . textContent = result . unlocked
86+ ? "Benchmark Lab: unlocked"
87+ : "Benchmark Lab: locked (basic play free)" ;
88+ message . textContent = result . message || "Purchase state updated" ;
89+ }
90+
91+ function run ( button , method ) {
92+ button . disabled = true ;
93+ message . textContent = method === "purchaseBenchmarkLab" ? "Opening Google Play…" : "Restoring purchases…" ;
94+ invoke ( method )
95+ . then ( applyResult )
96+ . catch ( ( error ) => { message . textContent = String ( error . message || error ) ; } )
97+ . finally ( ( ) => { button . disabled = false ; } ) ;
98+ }
99+
100+ purchase . addEventListener ( "click" , ( ) => run ( purchase , "purchaseBenchmarkLab" ) ) ;
101+ restore . addEventListener ( "click" , ( ) => run ( restore , "restoreBenchmarkLab" ) ) ;
102+
103+ if ( ! nativeBridge ) {
104+ purchase . disabled = true ;
105+ restore . disabled = true ;
106+ message . textContent = "Purchases are available in the Android app." ;
107+ }
108+ } ) ( ) ;
109+ </ script >
45110 </ body >
46111</ html >
0 commit comments