1- $ ( window ) . on ( 'load' , function ( ) {
2- var frameSpeed = 1000 ,
3- frameContainer = $ ( '#frame-container' ) ,
4- frames = $ ( '.frame' , frameContainer ) ,
5- frameCount = frames . length ,
6- messageContainer = $ ( '#message-container' ) ,
7- messages = $ ( '.message' , messageContainer )
8- messageCount = messages . length ,
9- t = null ,
10- start = $ ( '#start' ) ,
11- showFrame = function ( n ) {
12- if ( n != frameCount ) {
13- return frames . hide ( ) . eq ( n ) . show ( ) && messages . hide ( ) . eq ( n ) . show ( ) ;
14-
15- }
16- return frames . eq ( frameCount ) . show ( ) && messages . eq ( messageCount ) . show ( ) ;
17-
18- } ,
19- nextFrame = function ( ) {
20- if ( index == frameCount ) {
21- stopFrames ( ) ;
22- showFrame ( frameCount - 1 ) ;
23- }
24- else {
25- showFrame ( ++ index ) ;
26- t = setTimeout ( nextFrame , frameSpeed ) ;
27- }
28-
29- } ,
30- stopFrames = function ( ) {
31- clearInterval ( t ) ;
32- index = 0 ;
33- } ;
34- frameContainer
35- start . on ( 'click' , nextFrame )
36- stopFrames ( ) ;
37- showFrame ( 0 ) ;
38- } ) ;
1+ function uniqueRollPool ( tasks ) {
2+ const seen = new Set ( ) ;
3+ const output = [ ] ;
4+ ( tasks || [ ] ) . forEach ( ( task ) => {
5+ if ( ! task || ! task . name || ! task . image ) {
6+ return ;
7+ }
8+ const key = `${ task . name } |${ task . image } |${ task . link || '' } ` ;
9+ if ( ! seen . has ( key ) ) {
10+ seen . add ( key ) ;
11+ output . push ( task ) ;
12+ }
13+ } ) ;
14+ return output ;
15+ }
16+
17+ function shufflePool ( tasks ) {
18+ const shuffled = [ ...tasks ] ;
19+ for ( let i = shuffled . length - 1 ; i > 0 ; i -= 1 ) {
20+ const j = Math . floor ( Math . random ( ) * ( i + 1 ) ) ;
21+ [ shuffled [ i ] , shuffled [ j ] ] = [ shuffled [ j ] , shuffled [ i ] ] ;
22+ }
23+ return shuffled ;
24+ }
25+
26+ function buildRollSequence ( pool , finalTask ) {
27+ const safePool = uniqueRollPool ( pool ) ;
28+ if ( ! safePool . length ) {
29+ return [ finalTask , finalTask , finalTask , finalTask , finalTask ] . filter ( Boolean ) ;
30+ }
31+
32+ const sequence = [ ] ;
33+ const spins = Math . max ( 10 , Math . floor ( safePool . length * 1.35 ) ) ;
34+ while ( sequence . length < spins ) {
35+ const chunk = shufflePool ( safePool ) ;
36+ for ( let i = 0 ; i < chunk . length && sequence . length < spins ; i += 1 ) {
37+ sequence . push ( chunk [ i ] ) ;
38+ }
39+ }
40+ sequence . push ( finalTask ) ;
41+ return sequence ;
42+ }
43+
44+ function runSlotRollAnimation ( sequence , renderTask , onComplete ) {
45+ let index = 0 ;
46+
47+ const tick = ( ) => {
48+ renderTask ( sequence [ index ] , index , sequence . length ) ;
49+ index += 1 ;
50+
51+ if ( index >= sequence . length ) {
52+ if ( typeof onComplete === 'function' ) {
53+ onComplete ( sequence [ sequence . length - 1 ] ) ;
54+ }
55+ return ;
56+ }
57+
58+ const progress = index / sequence . length ;
59+ const delay = 30 + Math . floor ( Math . pow ( progress , 2 ) * 150 ) ;
60+ window . setTimeout ( tick , delay ) ;
61+ } ;
62+
63+ tick ( ) ;
64+ }
65+
66+ function ensureTaskRollModal ( ) {
67+ let dialog = document . getElementById ( 'taskRollModal' ) ;
68+ if ( dialog ) {
69+ return dialog ;
70+ }
71+
72+ dialog = document . createElement ( 'dialog' ) ;
73+ dialog . id = 'taskRollModal' ;
74+ dialog . className = 'task-action-modal task-roll-modal rsText' ;
75+ dialog . innerHTML = `
76+ <div class="rect task-action-shell task-roll-shell">
77+ <div class="task-action-header task-roll-header">
78+ <h3 id="taskRollTitle" class="task-roll-title">Rolling New Task</h3>
79+ </div>
80+ <hr class="task-action-divider task-roll-divider">
81+ <p id="taskRollSubtitle" class="task-roll-subtitle">Welcome to your next grind!</p>
82+ <div class="task-roll-reel">
83+ <img id="taskRollImage" class="task-roll-image" src="/static/assets/Cake_of_guidance_detail.png" alt="Rolling task image" />
84+ </div>
85+ <div id="taskRollName" class="task-roll-name">Rolling...</div>
86+ <div class="task-action-progress-wrap task-roll-progress-wrap">
87+ <div class="task-action-progress-bar task-roll-progress-bar">
88+ <div id="taskRollProgressFill" class="task-roll-progress-fill"></div>
89+ </div>
90+ </div>
91+ <div class="task-action-footer">
92+ <button id="taskRollConfirm" class="button-style task-roll-confirm" type="button" disabled>Rolling...</button>
93+ </div>
94+ </div>
95+ ` ;
96+
97+ document . body . appendChild ( dialog ) ;
98+ return dialog ;
99+ }
100+
101+ function showTaskRollModal ( options ) {
102+ const {
103+ title,
104+ subtitle,
105+ sequence,
106+ onRender,
107+ onComplete,
108+ } = options ;
109+
110+ const modal = ensureTaskRollModal ( ) ;
111+ const titleNode = modal . querySelector ( '#taskRollTitle' ) ;
112+ const subtitleNode = modal . querySelector ( '#taskRollSubtitle' ) ;
113+ const imageNode = modal . querySelector ( '#taskRollImage' ) ;
114+ const nameNode = modal . querySelector ( '#taskRollName' ) ;
115+ const progressFill = modal . querySelector ( '#taskRollProgressFill' ) ;
116+ const confirmButton = modal . querySelector ( '#taskRollConfirm' ) ;
117+
118+ titleNode . textContent = title || 'Rolling New Task' ;
119+ subtitleNode . textContent = subtitle || 'This could be your next 100-hour grind.' ;
120+ confirmButton . disabled = true ;
121+ confirmButton . textContent = 'Rolling...' ;
122+
123+ const setVisualTask = ( task , index , total ) => {
124+ if ( ! task ) {
125+ return ;
126+ }
127+ imageNode . src = task . image ;
128+ nameNode . textContent = task . name ;
129+ const progress = total > 1 ? Math . min ( 100 , Math . floor ( ( index / ( total - 1 ) ) * 100 ) ) : 100 ;
130+ progressFill . style . width = `${ progress } %` ;
131+ if ( typeof onRender === 'function' ) {
132+ onRender ( task ) ;
133+ }
134+ } ;
135+
136+ confirmButton . onclick = ( ) => {
137+ if ( typeof modal . close === 'function' ) {
138+ modal . close ( ) ;
139+ }
140+ } ;
141+
142+ modal . addEventListener ( 'cancel' , ( event ) => {
143+ if ( confirmButton . disabled ) {
144+ event . preventDefault ( ) ;
145+ }
146+ } , { once : true } ) ;
147+
148+ if ( typeof modal . showModal === 'function' ) {
149+ modal . showModal ( ) ;
150+ }
151+
152+ runSlotRollAnimation ( sequence , setVisualTask , ( finalTask ) => {
153+ progressFill . style . width = '100%' ;
154+ confirmButton . disabled = false ;
155+ confirmButton . textContent = 'Begin Grind' ;
156+ if ( typeof onComplete === 'function' ) {
157+ onComplete ( finalTask ) ;
158+ }
159+ } ) ;
160+ }
161+
162+ function loadAvailableRollTasks ( tier ) {
163+ const payload = tier ? { tier : `${ tier } Tasks` } : { } ;
164+ return $ . ajax ( {
165+ url : '/available_roll_tasks/' ,
166+ type : 'POST' ,
167+ data : payload ,
168+ } ) ;
169+ }
170+
171+ function getCurrentUsername ( ) {
172+ const profileLink = document . querySelector ( 'a.profile' ) ;
173+ if ( ! profileLink ) {
174+ return '' ;
175+ }
176+ return ( profileLink . textContent || '' ) . trim ( ) ;
177+ }
178+
179+ function isSpecialInstantRollUser ( ) {
180+ const username = getCurrentUsername ( ) . toLowerCase ( ) ;
181+ return username === 'shadukat' || username === 'gerni shadu' ;
182+ }
39183
40184$ ( document ) . on ( 'click' , '#start' , function ( ) {
41- req = $ . ajax ( {
185+ const startButton = document . getElementById ( 'start' ) ;
186+ const completeButton = document . getElementById ( 'complete' ) ;
187+ startButton . disabled = true ;
188+
189+ const generateRequest = $ . ajax ( {
42190 url : '/generate/' ,
43191 type : 'POST'
44- } )
45-
46- req . done ( function ( data ) {
47- delay ( function ( ) {
48- const message = document . getElementById ( "message_target" ) ;
49- const image = document . getElementById ( "image_target" ) ;
50- const imageLink = document . getElementById ( "taskImage" ) ;
51- imageLink . href = data . link ;
52- imageLink . setAttribute ( 'data-tip' , data . tip ) ;
53- message . innerHTML = data . name ;
54- image . src = data . image ;
55- document . getElementById ( "start" ) . disabled = true ;
56- document . getElementById ( "complete" ) . disabled = false ;
57- } , 6000 ) ;
58192 } ) ;
193+ const poolRequest = loadAvailableRollTasks ( null ) ;
194+
195+ $ . when ( generateRequest , poolRequest )
196+ . done ( function ( generateResponse , poolResponse ) {
197+ const generatedTask = generateResponse [ 0 ] ;
198+ const availableTasks = ( poolResponse [ 0 ] && poolResponse [ 0 ] . tasks ) ? poolResponse [ 0 ] . tasks : [ ] ;
199+
200+ const renderTask = function ( task ) {
201+ const message = document . getElementById ( 'message_target' ) ;
202+ const image = document . getElementById ( 'image_target' ) ;
203+ const imageLink = document . getElementById ( 'taskImage' ) ;
204+ imageLink . href = task . link ;
205+ imageLink . setAttribute ( 'data-tip' , task . tip || '' ) ;
206+ message . innerHTML = task . name ;
207+ image . src = task . image ;
208+ } ;
209+
210+ const sequence = buildRollSequence ( availableTasks , generatedTask ) ;
211+ const instantRoll = isSpecialInstantRollUser ( ) ;
212+ showTaskRollModal ( {
213+ title : 'Official Task Roll' ,
214+ subtitle : instantRoll ? 'Hi Youtube <3 Gerni Task' : 'This could be your next 100-hour grind.' ,
215+ sequence : instantRoll ? [ generatedTask ] : sequence ,
216+ onRender : null ,
217+ onComplete : function ( ) {
218+ renderTask ( generatedTask ) ;
219+ startButton . disabled = true ;
220+ completeButton . disabled = false ;
221+ }
222+ } ) ;
223+ } )
224+ . fail ( function ( ) {
225+ startButton . disabled = false ;
226+ } ) ;
59227} ) ;
60228
61229$ ( document ) . on ( 'click' , '#complete' , function ( ) {
@@ -69,57 +237,57 @@ $(document).on('click', '#complete', function(){
69237 } )
70238} ) ;
71239
72-
73- var delay = ( function ( ) {
74- var timer = 0 ;
75- return function ( callback , ms ) {
76- clearTimeout ( timer ) ;
77- timer = setTimeout ( callback , ms ) ;
78- } ;
79- } ) ( ) ;
80-
81- // $(document).on('click', '#easy_generate', function(){
82- // $('form').submit(false);
83- // req = $.ajax({
84- // url : '/generate_unofficial_easy/',
85- // type : 'POST'
86- // });
87- // req.done(function(data){
88- // const task = document.getElementById("easy_task")
89- // const image = document.getElementById("easy_image")
90- // const imagePreview = document.getElementById("easy_image_preview")
91- // task.innerHTML = data.name
92- // image.src = "/static/assets/" + data.image
93- // imagePreview.src = "/static/assets/" + data.image
94-
95- // });
96- // });
97-
98240$ ( document ) . on ( 'click' , '#generate_unofficial' , function ( ) {
99241 $ ( 'form' ) . submit ( false ) ;
100- let tier = this . name
101- req = $ . ajax ( {
242+ let tier = this . name ;
243+ const generateButton = this ;
244+ generateButton . disabled = true ;
245+
246+ const generateRequest = $ . ajax ( {
102247 url : '/generate_unofficial/' ,
103248 type : 'POST' ,
104249 data : { tier : tier + 'Tasks' }
105250 } ) ;
106- req . done ( function ( data ) {
107- const task = document . getElementById ( tier + "_task" ) ;
108- const image = document . getElementById ( tier + "_image" ) ;
109- const imagePreview = document . getElementById ( tier + "_image_preview" ) ;
110- var imagePlaceholder = document . getElementById ( tier + "_placeholder" ) ;
111- if ( ! imagePlaceholder ) {
112- imagePlaceholder = document . getElementById ( tier + '_imageTask' )
113- }
114- imagePlaceholder . setAttribute ( 'data-tip' , data . tip )
115- imagePlaceholder . href = data . link
116- imagePreview . name = data . name ;
117- task . innerHTML = data . name ;
118- image . src = data . image ;
119- imagePreview . src = data . image ;
120- } ) ;
121- } ) ;
251+ const poolRequest = loadAvailableRollTasks ( tier ) ;
122252
253+ $ . when ( generateRequest , poolRequest )
254+ . done ( function ( generateResponse , poolResponse ) {
255+ const generatedTask = generateResponse [ 0 ] ;
256+ const availableTasks = ( poolResponse [ 0 ] && poolResponse [ 0 ] . tasks ) ? poolResponse [ 0 ] . tasks : [ ] ;
257+
258+ const renderTask = function ( taskData ) {
259+ const task = document . getElementById ( tier + '_task' ) ;
260+ const image = document . getElementById ( tier + '_image' ) ;
261+ const imagePreview = document . getElementById ( tier + '_image_preview' ) ;
262+ let imagePlaceholder = document . getElementById ( tier + '_placeholder' ) ;
263+ if ( ! imagePlaceholder ) {
264+ imagePlaceholder = document . getElementById ( tier + '_imageTask' ) ;
265+ }
266+ imagePlaceholder . setAttribute ( 'data-tip' , taskData . tip || '' ) ;
267+ imagePlaceholder . href = taskData . link || '#' ;
268+ imagePreview . name = taskData . name ;
269+ task . innerHTML = taskData . name ;
270+ image . src = taskData . image ;
271+ imagePreview . src = taskData . image ;
272+ } ;
273+
274+ const sequence = buildRollSequence ( availableTasks , generatedTask ) ;
275+ const instantRoll = isSpecialInstantRollUser ( ) ;
276+ showTaskRollModal ( {
277+ title : `${ tier . charAt ( 0 ) . toUpperCase ( ) + tier . slice ( 1 ) } Task Roll` ,
278+ subtitle : instantRoll ? 'Hi Youtube <3 Gerni Task' : 'This could be your next 100-hour grind.' ,
279+ sequence : instantRoll ? [ generatedTask ] : sequence ,
280+ onRender : null ,
281+ onComplete : function ( ) {
282+ renderTask ( generatedTask ) ;
283+ generateButton . disabled = false ;
284+ }
285+ } ) ;
286+ } )
287+ . fail ( function ( ) {
288+ generateButton . disabled = false ;
289+ } ) ;
290+ } ) ;
123291
124292$ ( document ) . on ( 'click' , '#complete_unofficial' , function ( ) {
125293 $ ( 'form' ) . submit ( false ) ;
0 commit comments