Skip to content

Commit a4e2111

Browse files
committed
updated rolling animation
1 parent 789894a commit a4e2111

5 files changed

Lines changed: 390 additions & 243 deletions

File tree

static/js/task.js

Lines changed: 265 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,229 @@
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

Comments
 (0)