任务和游戏

This commit is contained in:
zhx 2026-06-11 13:33:10 +08:00
parent a95322b0d2
commit 8120813ab6
20 changed files with 1442 additions and 51 deletions

View File

@ -0,0 +1,9 @@
<svg preserveAspectRatio="none" width="100%" height="100%" overflow="visible" style="display: block;" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icon_20_&#232;&#191;&#148;&#229;&#155;&#158;">
<g id="Vector">
</g>
<g id="Vector_2">
</g>
<path id="Vector_3" d="M16 4L8 12L16 20" stroke="var(--stroke-0, white)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

84
activity/tasks/index.html Normal file
View File

@ -0,0 +1,84 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
/>
<title data-i18n="tasks.pageTitle">Task</title>
<link
rel="stylesheet"
href="../../common/theme.css?v=20260611-task-h5"
/>
<link rel="stylesheet" href="./style.css?v=20260611-task-h5" />
</head>
<body>
<div class="app-viewport" id="appViewport">
<main
class="task-page"
id="page"
aria-label="Task center"
data-i18n-aria="tasks.pageLabel"
>
<nav
class="task-nav"
aria-label="Task navigation"
data-i18n-aria="tasks.navLabel"
>
<button
class="back-button"
id="backButton"
type="button"
aria-label="Back"
data-i18n-aria="tasks.back"
>
<span aria-hidden="true"></span>
</button>
<h1 data-i18n="tasks.title">Task</h1>
</nav>
<section class="task-section" data-section-block="daily">
<header class="section-head">
<h2 data-i18n="tasks.dailyTitle">Daily Tasks</h2>
<p data-refresh-text data-i18n="tasks.dailyReset">
Resets daily at midnight
</p>
</header>
<div
class="task-list"
id="dailyTasks"
aria-live="polite"
></div>
</section>
<section
class="task-section task-section-exclusive"
data-section-block="exclusive"
>
<header class="section-head">
<h2 data-i18n="tasks.exclusiveTitle">
Exclusive For Newcomers
</h2>
<p data-i18n="tasks.exclusiveLimit">
Limited to one time
</p>
</header>
<div
class="task-list"
id="exclusiveTasks"
aria-live="polite"
></div>
</section>
<div class="task-state" id="taskState" hidden></div>
</main>
</div>
<script src="../../common/i18n.js?v=20260611-task-h5"></script>
<script src="../../common/api.js?v=20260611-task-h5"></script>
<script src="../../common/jsbridge.js?v=20260611-task-h5"></script>
<script src="../../common/toast.js?v=20260611-task-h5"></script>
<script src="./script.js?v=20260611-task-h5"></script>
</body>
</html>

594
activity/tasks/script.js Normal file
View File

@ -0,0 +1,594 @@
(function () {
var viewport = document.getElementById('appViewport');
var page = document.getElementById('page');
var dailyRoot = document.getElementById('dailyTasks');
var exclusiveRoot = document.getElementById('exclusiveTasks');
var stateRoot = document.getElementById('taskState');
var backButton = document.getElementById('backButton');
var COMMAND_PREFIX = 'task_claim_command:';
var COIN_IMAGE = './assets/coin.png';
var GAME_IMAGE = './assets/figma-game-icon.png';
var state = {
loading: true,
error: '',
sections: [],
claiming: {},
};
var mockData = {
server_time_ms: Date.now(),
next_refresh_at_ms: Date.now() + 6 * 60 * 60 * 1000,
sections: [
{
section: 'daily',
items: [
mockTask(
'daily-mic',
'daily',
'Use the microphone for 1 min',
'mic_online_minute',
0,
1000000,
1100,
'in_progress',
false,
'mic'
),
mockTask(
'daily-game',
'daily',
'Use the microphone for 1 min',
'game_spend_coin',
1000000,
1000000,
1100,
'completed',
true,
'gamepad'
),
mockTask(
'daily-gift',
'daily',
'Use the microphone for 1 min',
'gift_spend_coin',
1000000,
1000000,
1100,
'claimed',
false,
'gift'
),
],
},
{
section: 'exclusive',
items: [
mockTask(
'exclusive-game-preview',
'exclusive',
'Use the microphone for 1 min',
'game_spend_coin',
0,
1000000,
1100,
'in_progress',
false,
'game_preview'
),
mockTask(
'exclusive-game',
'exclusive',
'Use the microphone for 1 min',
'game_spend_coin',
1000000,
1000000,
1100,
'completed',
true,
'gamepad'
),
mockTask(
'exclusive-gift',
'exclusive',
'Use the microphone for 1 min',
'gift_spend_coin',
1000000,
1000000,
1100,
'claimed',
false,
'gift'
),
],
},
],
};
function t(key, fallback) {
if (window.HyAppI18n && window.HyAppI18n.t) {
return window.HyAppI18n.t(key, fallback);
}
return fallback || key;
}
function mockTask(
id,
type,
title,
metric,
progress,
target,
reward,
status,
claimable,
iconKey
) {
return {
task_id: id,
task_type: type,
task_day:
type === 'daily'
? new Date().toISOString().slice(0, 10)
: 'lifetime',
title: title,
metric_type: metric,
progress_value: progress,
target_value: target,
reward_coin_amount: reward,
status: status,
claimable: claimable,
icon_key: iconKey,
icon_url: iconKey === 'game_preview' ? GAME_IMAGE : '',
action_type:
iconKey === 'gift'
? 'gift_panel'
: iconKey === 'gamepad'
? 'room_random_game'
: 'wallet',
action_param: iconKey === 'gamepad' ? 'dice' : '',
action_payload: iconKey === 'gamepad' ? { game_id: 'dice' } : {},
};
}
function isMockMode() {
var params = new URLSearchParams(window.location.search || '');
return params.get('mock') === '1' || params.get('mock') === 'true';
}
function escapeHTML(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function escapeAttr(value) {
return escapeHTML(value).replace(/`/g, '&#96;');
}
function clampProgress(item) {
var target = Math.max(
0,
Number(item.target_value || item.targetValue || 0)
);
var progress = Math.max(
0,
Number(item.progress_value || item.progressValue || 0)
);
return {
target: target,
progress: target > 0 ? Math.min(progress, target) : progress,
};
}
function formatProgress(item) {
var values = clampProgress(item);
return String(values.progress) + '/' + String(values.target);
}
function formatReward(value) {
var amount = Number(value || 0);
if (!Number.isFinite(amount)) return '0';
if (amount >= 1000000) return trimNumber(amount / 1000000) + 'M';
if (amount >= 1000) return trimNumber(amount / 1000) + 'k';
return String(amount);
}
function trimNumber(value) {
var rounded = Math.round(value * 10) / 10;
return String(rounded).replace(/\.0$/, '');
}
function claimKey(item) {
return [
item.task_id || item.taskId,
item.task_type || item.taskType,
item.task_day || item.taskDay,
].join('|');
}
function commandStorageKey(item) {
return COMMAND_PREFIX + claimKey(item);
}
function commandID(item) {
var storageKey = commandStorageKey(item);
var cached = window.sessionStorage.getItem(storageKey);
if (cached) return cached;
// 领取命令以任务主键和页面时间组成,保存在 sessionStorage如果请求超时但服务端已入账用户重试会继续用同一 command_id 命中后端幂等。
var next =
'h5-task-claim-' +
Date.now() +
'-' +
Math.random().toString(16).slice(2) +
'-' +
String(item.task_id || item.taskId || '');
window.sessionStorage.setItem(storageKey, next);
return next;
}
function clearCommandID(item) {
window.sessionStorage.removeItem(commandStorageKey(item));
}
function normalizeSections(payload) {
var sections =
payload && Array.isArray(payload.sections) ? payload.sections : [];
var byName = sections.reduce(function (map, section) {
map[String(section.section || '')] = Array.isArray(section.items)
? section.items
: [];
return map;
}, {});
return [
{ section: 'daily', items: byName.daily || [] },
{ section: 'exclusive', items: byName.exclusive || [] },
];
}
function iconKind(item) {
var key = String(item.icon_key || item.iconKey || '').toLowerCase();
var metric = String(
item.metric_type || item.metricType || ''
).toLowerCase();
if (key.indexOf('mic') >= 0 || metric.indexOf('mic') >= 0) return 'mic';
if (key.indexOf('gift') >= 0 || metric.indexOf('gift') >= 0)
return 'gift';
if (key.indexOf('game') >= 0 || metric.indexOf('game') >= 0)
return 'gamepad';
if (key.indexOf('cp') >= 0 || metric.indexOf('cp') >= 0) return 'cp';
if (key.indexOf('mood') >= 0 || metric.indexOf('mood') >= 0)
return 'mood';
return 'coin';
}
function iconHTML(item) {
var iconURL = item.icon_url || item.iconUrl || '';
if (iconURL) {
// 后台 icon_url 是可配置资源,可能因为地址过期、跨域或资源未上传而加载失败;失败时用内置图标兜底,不能让任务列表留下空方块。
return [
'<span class="task-icon is-image" data-icon-fallback="',
escapeAttr(iconKind(item)),
'"><img src="',
escapeAttr(iconURL),
'" alt="" /></span>',
].join('');
}
return (
'<span class="task-icon">' + svgForIcon(iconKind(item)) + '</span>'
);
}
function svgForIcon(kind) {
if (kind === 'mic') {
return '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#fff" d="M12 3a4 4 0 0 0-4 4v5a4 4 0 0 0 8 0V7a4 4 0 0 0-4-4Zm-6 8.5a1 1 0 0 1 2 0V12a4 4 0 0 0 8 0v-.5a1 1 0 1 1 2 0V12a6 6 0 0 1-5 5.92V20h3a1 1 0 1 1 0 2H8a1 1 0 1 1 0-2h3v-2.08A6 6 0 0 1 6 12v-.5Z"/></svg>';
}
if (kind === 'gift') {
return '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#fff" d="M8.2 4.2A3 3 0 0 1 12 5a3 3 0 0 1 3.8-.8A3.1 3.1 0 0 1 17 8h2a1 1 0 0 1 1 1v3h-7V8h-2v4H4V9a1 1 0 0 1 1-1h2a3.1 3.1 0 0 1 1.2-3.8ZM9 6a1 1 0 0 0 .2 2H11a2 2 0 0 0-2-2Zm4 2h1.8A1 1 0 1 0 13 6a2 2 0 0 0 0 2ZM5 14h6v6H6a1 1 0 0 1-1-1v-5Zm8 6v-6h6v5a1 1 0 0 1-1 1h-5Z"/></svg>';
}
if (kind === 'cp') {
return '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#fff" d="M7.5 6A3.5 3.5 0 0 1 12 9.35 3.5 3.5 0 1 1 16.5 6c2.45 0 4.5 1.9 4.5 4.35 0 4.25-6.05 8.05-8.35 9.35a1.3 1.3 0 0 1-1.3 0C9.05 18.4 3 14.6 3 10.35 3 7.9 5.05 6 7.5 6Z"/></svg>';
}
if (kind === 'mood') {
return '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#fff" d="M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Zm-3 7.2a1.2 1.2 0 1 1 0-2.4 1.2 1.2 0 0 1 0 2.4Zm6 0a1.2 1.2 0 1 1 0-2.4 1.2 1.2 0 0 1 0 2.4Zm-6.3 3.35a1 1 0 0 1 1.4.1 2.5 2.5 0 0 0 3.8 0 1 1 0 0 1 1.5 1.3 4.5 4.5 0 0 1-6.8 0 1 1 0 0 1 .1-1.4Z"/></svg>';
}
if (kind === 'coin') {
return '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#fff" d="M12 3c4.42 0 8 2.02 8 4.5V16c0 2.76-3.58 5-8 5s-8-2.24-8-5V7.5C4 5.02 7.58 3 12 3Zm0 2C8.14 5 6 6.47 6 7.5S8.14 10 12 10s6-1.47 6-2.5S15.86 5 12 5Zm6 5.3c-1.45 1.05-3.6 1.7-6 1.7s-4.55-.65-6-1.7V12c0 1.2 2.33 3 6 3s6-1.8 6-3v-1.7Zm0 5c-1.45 1.05-3.6 1.7-6 1.7s-4.55-.65-6-1.7V16c0 1.2 2.33 3 6 3s6-1.8 6-3v-.7Z"/></svg>';
}
return '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#fff" d="M8 9h8a4 4 0 0 1 0 8h-.5a2 2 0 0 1-1.6-.8l-.9-1.2h-2l-.9 1.2a2 2 0 0 1-1.6.8H8a4 4 0 0 1 0-8Zm.5 3H7v1.5h1.5V15H10v-1.5h1.5V12H10v-1.5H8.5V12Zm6.5.5a1 1 0 1 0 0-2 1 1 0 0 0 0 2Zm2.5 2.5a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"/></svg>';
}
function actionState(item) {
if (item.status === 'claimed') {
return {
label: t('tasks.claimed', 'Claimed'),
className: 'is-claimed',
disabled: true,
};
}
if (item.claimable) {
return {
label: t('tasks.claim', 'Claim'),
className: 'is-claimable',
disabled: false,
};
}
return {
label: t('tasks.go', 'Go'),
className: 'is-go',
disabled: false,
};
}
function renderSection(root, items) {
if (!items.length) {
root.innerHTML =
'<div class="task-state">' +
escapeHTML(t('tasks.empty', 'No tasks yet.')) +
'</div>';
return;
}
root.innerHTML = items.map(renderTaskRow).join('');
}
function renderTaskRow(item) {
var action = actionState(item);
var key = claimKey(item);
var loading = Boolean(state.claiming[key]);
var label = loading ? t('tasks.claiming', 'Claiming...') : action.label;
return [
'<article class="task-row" data-task-key="',
escapeAttr(key),
'">',
iconHTML(item),
'<div class="task-main">',
'<strong class="task-title">',
escapeHTML(
item.title || item.description || t('tasks.untitled', 'Task')
),
'</strong>',
'<span class="task-progress">',
escapeHTML(formatProgress(item)),
'</span>',
'<span class="task-reward"><img src="',
COIN_IMAGE,
'" alt="" aria-hidden="true" /><span>',
escapeHTML(
formatReward(item.reward_coin_amount || item.rewardCoinAmount)
),
'</span></span>',
'</div>',
'<button class="task-action ',
action.className,
loading ? ' is-loading' : '',
'" type="button" data-task-action="',
escapeAttr(key),
'"',
action.disabled || loading ? ' disabled' : '',
'>',
escapeHTML(label),
'</button>',
'</article>',
].join('');
}
function render() {
var sections = normalizeSections({ sections: state.sections });
var daily = sections[0].items;
var exclusive = sections[1].items;
renderSection(dailyRoot, daily);
renderSection(exclusiveRoot, exclusive);
bindIconFallbacks();
renderState();
updateScale();
}
function bindIconFallbacks() {
Array.prototype.slice
.call(document.querySelectorAll('[data-icon-fallback] img'))
.forEach(function (image) {
image.addEventListener(
'error',
function () {
var wrapper = image.parentElement;
if (!wrapper) return;
wrapper.classList.remove('is-image');
wrapper.innerHTML = svgForIcon(
wrapper.getAttribute('data-icon-fallback') || 'coin'
);
},
{ once: true }
);
});
}
function renderState() {
if (state.loading) {
stateRoot.hidden = false;
stateRoot.textContent = t('tasks.loading', 'Loading...');
return;
}
if (state.error) {
stateRoot.hidden = false;
stateRoot.textContent = state.error;
return;
}
stateRoot.hidden = true;
stateRoot.textContent = '';
}
function loadTasks() {
state.loading = true;
state.error = '';
renderState();
if (isMockMode()) {
applyPayload(mockData);
return;
}
if (!window.HyAppAPI || !window.HyAppAPI.task) {
state.loading = false;
state.error = t('tasks.apiUnavailable', 'Task API unavailable.');
render();
return;
}
window.HyAppAPI.task
.tabs()
.then(applyPayload)
.catch(function (error) {
state.loading = false;
state.error =
error.message || t('tasks.loadFailed', 'Load failed.');
state.sections = [];
render();
});
}
function applyPayload(payload) {
var sections = normalizeSections(payload);
state.sections = sections.map(function (section) {
return {
section: section.section,
items: section.items,
};
});
state.loading = false;
state.error = '';
render();
}
function findTask(key) {
var sections = normalizeSections({ sections: state.sections });
for (
var sectionIndex = 0;
sectionIndex < sections.length;
sectionIndex += 1
) {
var items = sections[sectionIndex].items;
for (var itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
if (claimKey(items[itemIndex]) === key) return items[itemIndex];
}
}
return null;
}
function claimTask(item) {
var key = claimKey(item);
state.claiming[key] = true;
render();
if (isMockMode()) {
// mock 只服务本地视觉和交互验收,不触发真实发奖;真实页面仍必须通过后端 claim 接口刷新钱包和任务状态。
item.status = 'claimed';
item.claimable = false;
state.claiming[key] = false;
showToast(t('tasks.claimSuccess', 'Reward claimed.'));
render();
return;
}
// H5 只提交任务列表返回的 task_day/task_type/task_id避免页面自行推算每日刷新周期服务端负责判断当天是否仍可领取。
window.HyAppAPI.task
.claim({
task_id: item.task_id || item.taskId,
task_type: item.task_type || item.taskType,
task_day: item.task_day || item.taskDay,
command_id: commandID(item),
})
.then(function () {
clearCommandID(item);
showToast(t('tasks.claimSuccess', 'Reward claimed.'));
return window.HyAppAPI.task.tabs();
})
.then(applyPayload)
.catch(function (error) {
state.claiming[key] = false;
showToast(
error.message || t('tasks.claimFailed', 'Claim failed.')
);
render();
});
}
function goTask(item) {
var actionType = item.action_type || item.actionType || 'none';
if (!actionType || actionType === 'none') {
showToast(t('tasks.noAction', 'No action configured.'));
return;
}
var payload = {
task_id: item.task_id || item.taskId || '',
task_type: item.task_type || item.taskType || '',
task_day: item.task_day || item.taskDay || '',
action_type: actionType,
action_param: item.action_param || item.actionParam || '',
action_payload: item.action_payload || item.actionPayload || {},
};
// H5 不猜测原生房间/钱包/礼物页面的具体路由,只把后台配置的跳转元数据原样交给 AppApp 侧可按 action_type 做统一分发。
if (window.HyAppBridge && window.HyAppBridge.openTaskAction) {
window.HyAppBridge.openTaskAction(payload);
return;
}
if (window.HyAppBridge && window.HyAppBridge.post) {
window.HyAppBridge.post('taskAction', payload);
return;
}
window.dispatchEvent(
new CustomEvent('hyapp:task-action', {
detail: payload,
})
);
}
function showToast(message) {
if (window.HyAppToast && window.HyAppToast.show) {
window.HyAppToast.show(message);
return;
}
window.dispatchEvent(
new CustomEvent('hyapp:toast', {
detail: { message: message },
})
);
}
function onTaskAction(event) {
var button = event.target.closest('[data-task-action]');
if (!button) return;
var item = findTask(button.getAttribute('data-task-action'));
if (!item) return;
if (item.claimable) {
claimTask(item);
return;
}
goTask(item);
}
function updateScale() {
document.documentElement.style.setProperty('--app-scale', '1');
if (!viewport || !page) return;
var height = Math.max(page.scrollHeight, window.innerHeight || 0);
viewport.style.minHeight = height + 'px';
document.body.style.minHeight = height + 'px';
}
function bindEvents() {
page.addEventListener('click', onTaskAction);
window.addEventListener('resize', updateScale);
if (backButton) {
backButton.addEventListener('click', function () {
if (window.HyAppBridge && window.HyAppBridge.back) {
window.HyAppBridge.back();
return;
}
window.history.back();
});
}
}
bindEvents();
loadTasks();
if (window.HyAppBridge && window.HyAppBridge.ready) {
window.HyAppBridge.ready({ page: 'tasks' });
}
})();

325
activity/tasks/style.css Normal file
View File

@ -0,0 +1,325 @@
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
:root {
--app-scale: 1;
--task-width: 100%;
--task-bg: #130a2b;
--task-white: #ffffff;
--task-muted: rgba(255, 255, 255, 0.62);
--task-line: rgba(255, 255, 255, 0.16);
--task-purple: rgba(217, 0, 255, 0.32);
--task-gold: #ffd155;
--task-gold-border: #fff09b;
--task-red-shadow: #be1e21;
}
html,
body {
margin: 0;
width: 100%;
min-height: 100%;
overflow-x: hidden;
background: var(--task-bg);
color: var(--task-white);
font-family:
'Source Han Sans SC', Arial, 'Helvetica Neue', Helvetica, sans-serif;
}
button {
border: 0;
padding: 0;
background: transparent;
color: inherit;
font: inherit;
cursor: pointer;
outline: none;
}
.app-viewport {
position: relative;
width: var(--task-width);
min-height: 100vh;
margin: 0;
}
.task-page {
position: relative;
width: 100%;
min-height: 100vh;
overflow-x: hidden;
padding: 44px 0 36px;
background: var(--task-bg);
}
.task-page::before {
content: '';
position: absolute;
inset: 0;
z-index: 0;
pointer-events: none;
background:
linear-gradient(180deg, rgba(19, 10, 43, 0.08), rgba(19, 10, 43, 0.12)),
url('./assets/task-bg.png') center top / 100% 100% no-repeat;
}
.task-nav {
position: relative;
z-index: 2;
width: 100%;
height: 44px;
}
.task-nav h1 {
position: absolute;
left: 50%;
top: 13px;
width: 180px;
margin: 0 0 0 -90px;
color: var(--task-white);
font-size: 18px;
font-weight: 500;
line-height: 18px;
text-align: center;
text-transform: capitalize;
}
.back-button {
position: absolute;
left: 16px;
top: 10px;
width: 24px;
height: 24px;
}
.back-button span {
position: absolute;
left: 7px;
top: 4px;
width: 14px;
height: 14px;
border-left: 2px solid var(--task-white);
border-bottom: 2px solid var(--task-white);
transform: rotate(45deg);
}
.task-section {
position: relative;
z-index: 1;
margin-top: 16px;
}
.task-section-exclusive {
margin-top: 18px;
}
.section-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
min-height: 20px;
padding: 0 16px;
}
.section-head h2 {
margin: 0;
color: var(--task-white);
font-size: 16px;
font-weight: 800;
line-height: 16px;
text-transform: capitalize;
text-shadow:
0 2px 0 var(--task-red-shadow),
0 0 4px rgba(244, 203, 255, 0.55);
}
.section-head p {
margin: 2px 0 0;
color: var(--task-white);
font-size: 12px;
font-weight: 400;
line-height: 12px;
text-align: right;
white-space: nowrap;
}
.task-list {
margin-top: 12px;
}
.task-row {
position: relative;
display: grid;
grid-template-columns: 42px minmax(0, 1fr) 52px;
column-gap: 9px;
align-items: center;
min-height: 65px;
padding: 0 16px;
}
.task-row::after {
content: '';
position: absolute;
left: 12px;
right: 12px;
bottom: 0;
height: 1px;
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0),
var(--task-line) 50%,
rgba(255, 255, 255, 0)
);
}
.task-row:last-child::after {
opacity: 0.55;
}
.task-icon {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 42px;
height: 42px;
overflow: hidden;
border-radius: 50%;
background: var(--task-purple);
}
.task-icon img,
.task-icon svg {
display: block;
width: 29px;
height: 29px;
}
.task-icon.is-image {
border-radius: 8px;
background: rgba(255, 255, 255, 0.1);
}
.task-icon.is-image img {
width: 38px;
height: 38px;
border-radius: 8px;
object-fit: cover;
}
.task-main {
min-width: 0;
padding-top: 1px;
}
.task-title {
display: block;
max-width: 205px;
overflow: hidden;
color: var(--task-white);
font-size: 12px;
font-weight: 400;
line-height: 14px;
text-overflow: ellipsis;
white-space: nowrap;
}
.task-progress {
display: block;
margin-top: 2px;
color: var(--task-muted);
font-size: 10px;
font-weight: 400;
line-height: 10px;
}
.task-reward {
display: inline-flex;
align-items: center;
gap: 2px;
margin-top: 7px;
color: var(--task-gold);
font-size: 12px;
font-weight: 400;
line-height: 12px;
}
.task-reward img {
width: 12px;
height: 12px;
}
.task-action {
position: relative;
width: 52px;
height: 20px;
border: 1px solid var(--task-gold-border);
background:
linear-gradient(
175deg,
rgba(70, 42, 120, 0.72) 0%,
rgba(59, 36, 100, 0) 72%
),
#130828;
color: var(--task-white);
font-size: 12px;
font-weight: 400;
line-height: 18px;
text-align: center;
}
.task-action.is-claimable {
border-color: rgba(255, 240, 155, 0.95);
background:
linear-gradient(
90deg,
rgba(129, 53, 159, 0.15),
rgba(255, 0, 199, 0.62)
),
url('./assets/button-glow.png') center / 98px 28px no-repeat,
#130828;
}
.task-action.is-claimed,
.task-action:disabled {
cursor: default;
}
.task-action.is-claimed {
border-color: rgba(255, 255, 255, 0.72);
background:
linear-gradient(175deg, rgba(70, 42, 120, 0.4), rgba(59, 36, 100, 0)),
#3a3348;
color: rgba(255, 255, 255, 0.86);
}
.task-action.is-loading {
color: rgba(255, 255, 255, 0.72);
}
.task-state {
position: relative;
z-index: 1;
margin: 64px 24px 0;
padding: 14px 16px;
border: 1px solid rgba(255, 255, 255, 0.18);
background: rgba(19, 8, 40, 0.62);
color: rgba(255, 255, 255, 0.86);
font-size: 13px;
line-height: 18px;
text-align: center;
}
.task-list .task-state {
margin: 4px 16px 0;
}
@media (max-width: 374px) {
.app-viewport {
margin-left: 0;
margin-right: 0;
}
}

View File

@ -27,7 +27,9 @@ var default_api = 'https://api.global-interaction.com/';
function decodeBase64URLJSON(value) {
try {
var normalized = String(value || '').replace(/-/g, '+').replace(/_/g, '/');
var normalized = String(value || '')
.replace(/-/g, '+')
.replace(/_/g, '/');
while (normalized.length % 4) normalized += '=';
return JSON.parse(window.atob(normalized));
} catch (_) {
@ -120,7 +122,8 @@ var default_api = 'https://api.global-interaction.com/';
return memoryAccessToken;
if (memoryAccessToken && isExpiredToken(memoryAccessToken)) {
memoryAccessToken = '';
if (shouldPersist()) window.localStorage.removeItem(ACCESS_TOKEN_KEY);
if (shouldPersist())
window.localStorage.removeItem(ACCESS_TOKEN_KEY);
}
var queryToken =
normalizeToken(readQuery('token')) ||
@ -636,6 +639,20 @@ var default_api = 'https://api.global-interaction.com/';
},
};
// 任务页 API 只封装 H5 需要的两个 App 合约登录态、app_code、env 和错误 envelope 仍由 request 统一处理。
// 领取奖励的 command_id 必须来自点击动作本身,页面脚本负责生成并在同一次重试中复用,避免重复点击造成多次发奖。
var taskAPI = {
tabs: function () {
return request('/api/v1/tasks/tabs', { method: 'GET' });
},
claim: function (payload) {
return request('/api/v1/tasks/claim', {
method: 'POST',
body: payload || {},
});
},
};
var weeklyStarAPI = {
current: function () {
return request('/api/v1/activities/weekly-star/current', {
@ -734,6 +751,7 @@ var default_api = 'https://api.global-interaction.com/';
managerCenter: managerCenterAPI,
superadminCenter: superadminCenterAPI,
level: levelAPI,
task: taskAPI,
weeklyStar: weeklyStarAPI,
game: gameAPI,
};

View File

@ -62,6 +62,9 @@
board_type: String(boardType || ''),
});
},
openTaskAction: function (payload) {
return post('taskAction', payload || {});
},
ready: function (payload) {
return post('ready', payload || {});
},

View File

@ -18,7 +18,7 @@
"diceMatch.apiUnavailable": "واجهة لعبة النرد غير متاحة",
"diceMatch.selectStake": "اختر قيمة الرهان",
"diceMatch.requestFailed": "فشل الطلب",
"diceMatch.configFeeNote": "الرسوم {fee}% · المجمع {pool}%",
"diceMatch.configFeeNote": "الرسوم {fee}%",
"roomReward.pageTitle": "فعالية مكافآت الغرفة",
"roomReward.pageLabel": "فعالية مكافآت الغرفة",
"roomReward.currentTarget": "هدف المكافأة الحالي",
@ -458,5 +458,26 @@
"recharge.status.pending": "قيد الانتظار",
"recharge.status.redirected": "بانتظار الدفع",
"recharge.status.credited": "تمت الإضافة",
"recharge.status.failed": "فشل"
"recharge.status.failed": "فشل",
"tasks.pageTitle": "المهام",
"tasks.pageLabel": "مركز المهام",
"tasks.navLabel": "تنقل المهام",
"tasks.back": "رجوع",
"tasks.title": "المهام",
"tasks.dailyTitle": "المهام اليومية",
"tasks.dailyReset": "تتم إعادة التعيين يوميا عند منتصف الليل",
"tasks.exclusiveTitle": "حصري للوافدين الجدد",
"tasks.exclusiveLimit": "مرة واحدة فقط",
"tasks.go": "اذهب",
"tasks.claim": "استلام",
"tasks.claiming": "جار الاستلام...",
"tasks.claimed": "تم الاستلام",
"tasks.empty": "لا توجد مهام بعد.",
"tasks.loading": "جار التحميل...",
"tasks.untitled": "مهمة",
"tasks.apiUnavailable": "واجهة المهام غير متاحة.",
"tasks.loadFailed": "فشل التحميل.",
"tasks.claimSuccess": "تم استلام المكافأة.",
"tasks.claimFailed": "فشل الاستلام.",
"tasks.noAction": "لم يتم إعداد الانتقال."
}

View File

@ -18,7 +18,7 @@
"diceMatch.apiUnavailable": "Dice API unavailable",
"diceMatch.selectStake": "Select a stake",
"diceMatch.requestFailed": "Request failed",
"diceMatch.configFeeNote": "Fee {fee}% · Pool {pool}%",
"diceMatch.configFeeNote": "Fee {fee}%",
"roomReward.pageTitle": "Room Reward Event",
"roomReward.pageLabel": "Room Reward Event",
"roomReward.currentTarget": "Current reward target",
@ -464,5 +464,26 @@
"recharge.status.pending": "Pending",
"recharge.status.redirected": "Waiting for payment",
"recharge.status.credited": "Credited",
"recharge.status.failed": "Failed"
"recharge.status.failed": "Failed",
"tasks.pageTitle": "Task",
"tasks.pageLabel": "Task center",
"tasks.navLabel": "Task navigation",
"tasks.back": "Back",
"tasks.title": "Task",
"tasks.dailyTitle": "Daily Tasks",
"tasks.dailyReset": "Resets daily at midnight",
"tasks.exclusiveTitle": "Exclusive For Newcomers",
"tasks.exclusiveLimit": "Limited to one time",
"tasks.go": "Go",
"tasks.claim": "Claim",
"tasks.claiming": "Claiming...",
"tasks.claimed": "Claimed",
"tasks.empty": "No tasks yet.",
"tasks.loading": "Loading...",
"tasks.untitled": "Task",
"tasks.apiUnavailable": "Task API unavailable.",
"tasks.loadFailed": "Load failed.",
"tasks.claimSuccess": "Reward claimed.",
"tasks.claimFailed": "Claim failed.",
"tasks.noAction": "No action configured."
}

View File

@ -18,7 +18,7 @@
"diceMatch.apiUnavailable": "API del juego de dados no disponible",
"diceMatch.selectStake": "Selecciona una apuesta",
"diceMatch.requestFailed": "Error de solicitud",
"diceMatch.configFeeNote": "Comisión {fee}% · Bolsa {pool}%",
"diceMatch.configFeeNote": "Comisión {fee}%",
"roomReward.pageTitle": "Evento de recompensas de sala",
"roomReward.pageLabel": "Evento de recompensas de sala",
"roomReward.currentTarget": "Objetivo de recompensa actual",
@ -458,5 +458,26 @@
"recharge.status.pending": "Pendiente",
"recharge.status.redirected": "Esperando pago",
"recharge.status.credited": "Acreditado",
"recharge.status.failed": "Fallido"
"recharge.status.failed": "Fallido",
"tasks.pageTitle": "Tareas",
"tasks.pageLabel": "Centro de tareas",
"tasks.navLabel": "Navegación de tareas",
"tasks.back": "Atrás",
"tasks.title": "Tareas",
"tasks.dailyTitle": "Tareas diarias",
"tasks.dailyReset": "Se reinicia cada día a medianoche",
"tasks.exclusiveTitle": "Exclusivo para nuevos usuarios",
"tasks.exclusiveLimit": "Limitado a una vez",
"tasks.go": "Ir",
"tasks.claim": "Reclamar",
"tasks.claiming": "Reclamando...",
"tasks.claimed": "Reclamado",
"tasks.empty": "Aún no hay tareas.",
"tasks.loading": "Cargando...",
"tasks.untitled": "Tarea",
"tasks.apiUnavailable": "API de tareas no disponible.",
"tasks.loadFailed": "Error al cargar.",
"tasks.claimSuccess": "Recompensa reclamada.",
"tasks.claimFailed": "Error al reclamar.",
"tasks.noAction": "No hay acción configurada."
}

View File

@ -447,5 +447,26 @@
"recharge.status.pending": "Menunggu",
"recharge.status.redirected": "Menunggu pembayaran",
"recharge.status.credited": "Masuk",
"recharge.status.failed": "Gagal"
"recharge.status.failed": "Gagal",
"tasks.pageTitle": "Tugas",
"tasks.pageLabel": "Pusat tugas",
"tasks.navLabel": "Navigasi tugas",
"tasks.back": "Kembali",
"tasks.title": "Tugas",
"tasks.dailyTitle": "Tugas Harian",
"tasks.dailyReset": "Reset setiap tengah malam",
"tasks.exclusiveTitle": "Khusus pengguna baru",
"tasks.exclusiveLimit": "Terbatas satu kali",
"tasks.go": "Pergi",
"tasks.claim": "Klaim",
"tasks.claiming": "Mengklaim...",
"tasks.claimed": "Diklaim",
"tasks.empty": "Belum ada tugas.",
"tasks.loading": "Memuat...",
"tasks.untitled": "Tugas",
"tasks.apiUnavailable": "API tugas tidak tersedia.",
"tasks.loadFailed": "Gagal memuat.",
"tasks.claimSuccess": "Hadiah diklaim.",
"tasks.claimFailed": "Gagal klaim.",
"tasks.noAction": "Aksi belum dikonfigurasi."
}

View File

@ -447,5 +447,26 @@
"recharge.status.pending": "Bekliyor",
"recharge.status.redirected": "Ödeme bekleniyor",
"recharge.status.credited": "Yüklendi",
"recharge.status.failed": "Başarısız"
"recharge.status.failed": "Başarısız",
"tasks.pageTitle": "Görev",
"tasks.pageLabel": "Görev merkezi",
"tasks.navLabel": "Görev navigasyonu",
"tasks.back": "Geri",
"tasks.title": "Görev",
"tasks.dailyTitle": "Günlük Görevler",
"tasks.dailyReset": "Her gün gece yarısı yenilenir",
"tasks.exclusiveTitle": "Yeni kullanıcıya özel",
"tasks.exclusiveLimit": "Bir kez ile sınırlı",
"tasks.go": "Git",
"tasks.claim": "Al",
"tasks.claiming": "Alınıyor...",
"tasks.claimed": "Alındı",
"tasks.empty": "Henüz görev yok.",
"tasks.loading": "Yükleniyor...",
"tasks.untitled": "Görev",
"tasks.apiUnavailable": "Görev API kullanılamıyor.",
"tasks.loadFailed": "Yükleme başarısız.",
"tasks.claimSuccess": "Ödül alındı.",
"tasks.claimFailed": "Alma başarısız.",
"tasks.noAction": "Eylem yapılandırılmadı."
}

View File

@ -453,5 +453,26 @@
"recharge.status.pending": "待支付",
"recharge.status.redirected": "等待支付",
"recharge.status.credited": "已到账",
"recharge.status.failed": "失败"
"recharge.status.failed": "失败",
"tasks.pageTitle": "任务",
"tasks.pageLabel": "任务中心",
"tasks.navLabel": "任务导航",
"tasks.back": "返回",
"tasks.title": "任务",
"tasks.dailyTitle": "每日任务",
"tasks.dailyReset": "每日0点重置",
"tasks.exclusiveTitle": "新人专属",
"tasks.exclusiveLimit": "限一次",
"tasks.go": "去",
"tasks.claim": "领取",
"tasks.claiming": "领取中...",
"tasks.claimed": "已领取",
"tasks.empty": "暂无任务",
"tasks.loading": "加载中...",
"tasks.untitled": "任务",
"tasks.apiUnavailable": "任务接口不可用",
"tasks.loadFailed": "加载失败",
"tasks.claimSuccess": "领取成功",
"tasks.claimFailed": "领取失败",
"tasks.noAction": "未配置跳转"
}

View File

@ -77,6 +77,11 @@
animation: screen-impact-shake 520ms ease-out 420ms both;
}
.dice-frame.is-self-win-compare {
animation: you-win-screen-smash 780ms
cubic-bezier(0.14, 0.84, 0.18, 1) 430ms both;
}
.game-content {
position: absolute;
inset: 0;
@ -182,12 +187,14 @@
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 102px;
justify-content: flex-end;
width: max-content;
min-width: 102px;
max-width: 176px;
height: 32px;
flex: 0 0 102px;
flex: 0 1 auto;
gap: 4px;
overflow: hidden;
overflow: visible;
padding: 0 7px;
/* border: 1px solid rgba(108, 113, 194, 0.96);
border-radius: 4px; */
@ -218,16 +225,16 @@
.top-coin span {
display: block;
min-width: 0;
max-width: 48px;
flex: 0 1 auto;
overflow: hidden;
min-width: max-content;
max-width: none;
flex: 0 0 auto;
overflow: visible;
line-height: 20px;
}
.top-coin.is-compact span {
max-width: 38px;
font-size: 16px;
max-width: none;
font-size: 18px;
}
.top-coin img {
@ -933,6 +940,108 @@
opacity: 1;
}
.you-win-smash-layer,
.you-win-impact {
position: absolute;
visibility: hidden;
opacity: 0;
pointer-events: none;
}
.you-win-smash-layer {
inset: 0;
z-index: 23;
overflow: hidden;
}
.you-win-smash-layer::before {
position: absolute;
inset: 0;
content: '';
background:
radial-gradient(
circle at 50% 39%,
rgba(255, 248, 179, 0.72) 0%,
rgba(255, 126, 20, 0.3) 18%,
rgba(255, 73, 0, 0.08) 35%,
rgba(255, 73, 0, 0) 58%
),
rgba(255, 255, 255, 0.2);
opacity: 0;
}
.you-win-smash-floor {
position: absolute;
top: 362px;
left: 50%;
width: 312px;
height: 92px;
border-radius: 50%;
background: radial-gradient(
ellipse at center,
rgba(255, 215, 75, 0.68) 0%,
rgba(255, 103, 15, 0.34) 30%,
rgba(120, 16, 0, 0.28) 54%,
rgba(0, 0, 0, 0) 76%
);
opacity: 0;
filter: blur(1px) drop-shadow(0 0 18px rgba(255, 118, 22, 0.76));
transform: translateX(-50%) scale(0.38, 0.2);
transform-origin: 50% 50%;
}
.dice-frame.is-self-win-compare .you-win-smash-layer {
visibility: visible;
opacity: 1;
}
.dice-frame.is-self-win-compare .you-win-smash-layer::before {
animation: you-win-screen-flash 760ms ease-out 430ms both;
}
.dice-frame.is-self-win-compare .you-win-smash-floor {
animation: you-win-floor-impact 900ms ease-out 430ms both;
}
.you-win-impact {
top: 300px;
left: 0;
z-index: 25;
display: flex;
align-items: center;
justify-content: center;
width: 375px;
height: 92px;
color: transparent;
font-size: 56px;
font-weight: 1000;
line-height: 62px;
text-align: center;
background: linear-gradient(
180deg,
#ffffff 0%,
#fff5b6 19%,
#ffcf44 48%,
#ff7a00 74%,
#9b2d00 100%
);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-stroke: 2px #7d2700;
filter: drop-shadow(0 5px 0 rgba(94, 17, 0, 0.9))
drop-shadow(0 0 22px rgba(255, 205, 46, 0.9))
drop-shadow(0 0 36px rgba(255, 77, 0, 0.78));
transform-origin: 50% 78%;
will-change: opacity, transform, filter;
}
.dice-frame.is-self-win-compare .you-win-impact {
visibility: visible;
opacity: 1;
animation: you-win-drop-impact 980ms
cubic-bezier(0.13, 0.92, 0.18, 1) both;
}
.countdown-number {
min-width: 132px;
color: transparent;
@ -1264,6 +1373,10 @@
.dice-frame.is-matching .vs-badge,
.dice-frame.is-matching .vs-badge::after,
.dice-frame.is-self-win-compare,
.dice-frame.is-self-win-compare .you-win-smash-layer::before,
.dice-frame.is-self-win-compare .you-win-smash-floor,
.dice-frame.is-self-win-compare .you-win-impact,
.dice-frame.is-matching,
.dice-frame.is-searching .center-search-bg,
.dice-frame.is-searching .search-indicator img,
@ -1343,6 +1456,105 @@
}
}
@keyframes you-win-drop-impact {
0% {
opacity: 0;
filter: brightness(1.9) drop-shadow(0 0 36px #ff79ff);
transform: translateY(-650px) scale(3.9) rotate(-7deg);
}
34% {
opacity: 1;
filter: brightness(1.75) drop-shadow(0 0 32px #4cc9ff);
transform: translateY(-122px) scale(2.05) rotate(4deg);
}
58% {
opacity: 1;
filter: brightness(1.55) drop-shadow(0 0 30px #ffd557);
transform: translateY(28px) scale(0.78) rotate(1deg);
}
72% {
filter: brightness(1.42) drop-shadow(0 0 24px #ff79ff);
transform: translateY(-16px) scale(1.16) rotate(-0.8deg);
}
86% {
filter: brightness(1.18) drop-shadow(0 0 16px #4cc9ff);
transform: translateY(4px) scale(0.96) rotate(0.3deg);
}
100% {
opacity: 1;
filter: drop-shadow(0 5px 0 rgba(94, 17, 0, 0.9))
drop-shadow(0 0 22px rgba(255, 205, 46, 0.9))
drop-shadow(0 0 36px rgba(255, 77, 0, 0.78));
transform: translateY(0) scale(1) rotate(0);
}
}
@keyframes you-win-screen-smash {
0%,
100% {
transform: translate3d(0, 0, 0) scale(var(--dice-scale));
filter: none;
}
12% {
transform: translate3d(0, 14px, 0) scale(var(--dice-scale));
filter: brightness(1.22) contrast(1.08);
}
24% {
transform: translate3d(-12px, -8px, 0)
scale(var(--dice-scale));
}
36% {
transform: translate3d(10px, 7px, 0)
scale(var(--dice-scale));
}
50% {
transform: translate3d(-7px, 4px, 0)
scale(var(--dice-scale));
}
66% {
transform: translate3d(5px, -3px, 0)
scale(var(--dice-scale));
filter: brightness(1.08) contrast(1.03);
}
82% {
transform: translate3d(-2px, 1px, 0)
scale(var(--dice-scale));
}
}
@keyframes you-win-screen-flash {
0% {
opacity: 0;
}
8% {
opacity: 1;
}
22% {
opacity: 0.46;
}
100% {
opacity: 0;
}
}
@keyframes you-win-floor-impact {
0% {
opacity: 0;
transform: translateX(-50%) scale(0.38, 0.2);
}
16% {
opacity: 1;
transform: translateX(-50%) scale(1.05, 0.52);
}
58% {
opacity: 0.62;
}
100% {
opacity: 0;
transform: translateX(-50%) scale(1.32, 0.72);
}
}
@keyframes search-orbit {
0% {
transform: rotate(0deg)
@ -2042,6 +2254,20 @@
data-role="phase-text"
aria-live="polite"
></div>
<div
class="you-win-smash-layer"
data-role="you-win-smash"
aria-hidden="true"
>
<span class="you-win-smash-floor"></span>
</div>
<div
class="you-win-impact"
data-role="you-win-impact"
aria-hidden="true"
>
YOU WIN
</div>
<div
class="countdown-layer"
data-role="countdown-layer"
@ -2459,30 +2685,12 @@
return Math.trunc(number).toLocaleString('en-US');
}
function trimCompactDecimal(value) {
return String(value).replace(/\.0$/, '');
}
function formatTopCoin(value) {
var number = Number(value || 0);
if (!Number.isFinite(number)) number = 0;
number = Math.max(0, Math.trunc(number));
if (number < 100000) return formatCoin(number);
if (number < 1000000)
return Math.floor(number / 1000) + 'K';
if (number < 10000000)
return (
trimCompactDecimal(
(Math.floor(number / 100000) / 10).toFixed(1)
) + 'M'
);
if (number < 1000000000)
return Math.floor(number / 1000000) + 'M';
return (
trimCompactDecimal(
(Math.floor(number / 100000000) / 10).toFixed(1)
) + 'B'
);
if (number < 1000000) return formatCoin(number);
return Math.floor(number / 1000) + 'K';
}
function setCoin(value) {
@ -2495,7 +2703,7 @@
if (topCoin) {
topCoin.classList.toggle(
'is-compact',
number >= 100000
number >= 1000000
);
topCoin.setAttribute('title', fullLabel);
topCoin.setAttribute(
@ -2711,16 +2919,17 @@
selectedButton || document.querySelector('.stake')
);
if (config) {
var fee = Number(config.fee_bps || 0) / 100;
var pool = Number(config.pool_bps || 0) / 100;
var totalFee =
(Number(config.fee_bps || 0) +
Number(config.pool_bps || 0)) /
100;
setText(
document.querySelector('.fee-note'),
i18nFormat(
'diceMatch.configFeeNote',
'Fee {fee}% · Pool {pool}%',
'Fee {fee}%',
{
fee: fee,
pool: pool,
fee: totalFee,
}
)
);
@ -2734,6 +2943,7 @@
'is-matching',
'is-countdown',
'is-victory',
'is-self-win-compare',
'has-scores'
);
setCountdownNumber('');
@ -2867,6 +3077,22 @@
);
}
function isSelfWinner(match) {
var self = findSelfParticipant(match);
var winner = findWinnerParticipant(match);
if (!self) return false;
if (winner && sameParticipant(winner, self)) return true;
return self.result === 'win';
}
function setSelfWinCompare(visible) {
frame.classList.remove('is-self-win-compare');
if (!visible) return;
// 每一局 Compare 都要重新播放“砸下”效果,因此先读一次布局强制浏览器提交移除状态。
void frame.offsetWidth;
frame.classList.add('is-self-win-compare');
}
function renderMatchParticipants(match) {
var self = findSelfParticipant(match);
var opponent = findOpponentParticipant(match, self);
@ -3064,12 +3290,18 @@
stopRollEffects();
renderMatchParticipants(match);
frame.classList.add('has-scores');
var drawReroll = isDrawReroll(match);
var selfWinCompare =
!drawReroll && isSelfWinner(match);
setPhaseText(
isDrawReroll(match)
? i18nText('diceMatch.draw', 'Draw')
: i18nText('diceMatch.compare', 'Compare')
selfWinCompare
? ''
: drawReroll
? i18nText('diceMatch.draw', 'Draw')
: i18nText('diceMatch.compare', 'Compare')
);
if (isDrawReroll(match)) {
setSelfWinCompare(selfWinCompare);
if (drawReroll) {
return delay(2000).then(function () {
if (sequence !== roundSequence) return;
rollAndAnimate(sequence);