(function () {
'use strict';
var ASSET_BASE = './huwaa/assets/layers/';
var RANK_LIMIT = 5;
var DAY_MS = 24 * 60 * 60 * 1000;
var DESIGN_HEIGHT = { history: 2309, weekly: 2309, rewards: 3200 };
var DESIGN_FRAME = {
history: '818:1999',
weekly: '818:2066',
rewards: '818:2132',
};
var params = currentParams();
var useMock = params.get('mock') === '1';
var page = document.getElementById('weekStarPage');
var stage = document.getElementById('weekStarStage');
var stageWrap = document.getElementById('weekStarStageWrap');
var tabs = all('[data-tab-target]');
var panels = all('[data-panel]');
var state = {
// Once the user switches sections, the hash is the canonical tab state. It
// must win over a stale entry `?tab=` value while the hash-query token stays intact.
tab: normalizeTab(tabFromHash() || params.get('tab')),
current: null,
weeklyEntries: [],
historyCycles: [],
loadingCurrent: true,
loadingWeekly: true,
loadingHistory: true,
currentError: '',
weeklyError: '',
historyError: '',
requestSerial: 0,
};
function all(selector, root) {
return Array.prototype.slice.call(
(root || document).querySelectorAll(selector)
);
}
function currentParams() {
if (
window.HyAppParams &&
window.HyAppParams.current &&
window.HyAppParams.current.raw
) {
return window.HyAppParams.current.raw;
}
var result = new URLSearchParams(window.location.search || '');
var hash = window.location.hash || '';
if (hash.indexOf('?') >= 0) {
new URLSearchParams(hash.split('?').slice(1).join('?')).forEach(
function (value, key) {
if (!result.has(key)) result.set(key, value);
}
);
}
return result;
}
function text(value) {
return String(
value === undefined || value === null ? '' : value
).trim();
}
function number(value, fallback) {
var result = Number(value);
return Number.isFinite(result) ? result : Number(fallback || 0);
}
function first(source, keys) {
source = source || {};
for (var index = 0; index < keys.length; index += 1) {
var value = source[keys[index]];
if (value !== undefined && value !== null && value !== '') {
return value;
}
}
return '';
}
function envelope(payload) {
if (payload && Object.prototype.hasOwnProperty.call(payload, 'data')) {
return payload.data || {};
}
return payload || {};
}
function parseJSON(value) {
if (!value || typeof value !== 'string') return {};
try {
return JSON.parse(value);
} catch (error) {
return {};
}
}
function t(key, fallback, values) {
var translated =
window.HyAppI18n && window.HyAppI18n.t
? window.HyAppI18n.t(key, fallback)
: fallback || key;
Object.keys(values || {}).forEach(function (name) {
translated = translated.replace(
new RegExp('\\{' + name + '\\}', 'g'),
String(values[name])
);
});
return translated;
}
function escapeHTML(value) {
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function escapeAttr(value) {
return escapeHTML(value);
}
function resolveAssetURL(value) {
var raw = text(value);
if (!raw) return '';
if (
/^(https?:)?\/\//i.test(raw) ||
/^(data|blob):/i.test(raw) ||
raw.charAt(0) === '/' ||
raw.indexOf('./') === 0 ||
raw.indexOf('../') === 0
) {
return raw;
}
// Backend resource records may return a path relative to the API host. Resolving
// against the API base avoids silently requesting that path from this H5 directory.
try {
var base =
window.HyAppAPI && window.HyAppAPI.baseURL
? window.HyAppAPI.baseURL()
: window.location.origin + '/';
return new URL(raw, base).toString();
} catch (error) {
return '';
}
}
function formatNumber(value) {
var lang =
window.HyAppI18n && window.HyAppI18n.lang
? window.HyAppI18n.lang()
: 'en';
return new Intl.NumberFormat(lang).format(number(value));
}
function formatScore(value) {
var raw = text(value);
if (!raw) return '0';
var numeric = Number(raw);
return Number.isFinite(numeric) ? String(numeric) : raw;
}
function pad(value) {
return String(value).padStart(2, '0');
}
function timezoneOffsetHours(cycle) {
var explicit = first(cycle, [
'utc_offset_hours',
'utcOffsetHours',
'timezone_offset_hours',
'timezoneOffsetHours',
]);
if (explicit !== '') return number(explicit, 3);
var label = text(first(cycle, ['timezone', 'time_zone', 'timeZone']));
var match = label.match(/(?:utc|gmt)\s*([+-]\d+(?:\.\d+)?)/i);
// The Figma campaign explicitly uses UTC+3. The API can override this through
// its cycle metadata; the design offset is only the fallback display contract.
return match ? number(match[1], 3) : 3;
}
function shiftedDate(value, offsetHours) {
var timestamp = number(value);
if (!timestamp) return null;
return new Date(timestamp + offsetHours * 60 * 60 * 1000);
}
function formatDateTime(value, offsetHours) {
var date = shiftedDate(value, offsetHours);
if (!date) return '—';
return [
date.getUTCFullYear(),
'-',
pad(date.getUTCMonth() + 1),
'-',
pad(date.getUTCDate()),
' ',
pad(date.getUTCHours()),
':',
pad(date.getUTCMinutes()),
].join('');
}
function formatDate(value, offsetHours) {
var date = shiftedDate(value, offsetHours);
if (!date) return '—';
return [
date.getUTCFullYear(),
'/',
pad(date.getUTCMonth() + 1),
'/',
pad(date.getUTCDate()),
].join('');
}
function formatOffset(offsetHours) {
return (offsetHours >= 0 ? '+' : '') + String(offsetHours);
}
function cycleRange(cycle) {
cycle = cycle || {};
var custom = text(
first(cycle, [
'display_time',
'displayTime',
'time_range',
'timeRange',
])
);
if (custom) return custom;
var offset = timezoneOffsetHours(cycle);
return (
formatDateTime(first(cycle, ['start_ms', 'startMs']), offset) +
'-' +
formatDateTime(first(cycle, ['end_ms', 'endMs']), offset) +
'(utc' +
formatOffset(offset) +
')'
);
}
function historyRange(cycle) {
cycle = cycle || {};
var offset = timezoneOffsetHours(cycle);
return (
formatDate(first(cycle, ['start_ms', 'startMs']), offset) +
'-' +
formatDate(first(cycle, ['end_ms', 'endMs']), offset)
);
}
function currentCycle() {
var data = envelope(state.current);
return data.cycle || data.current_cycle || data.currentCycle || null;
}
function errorKey(error) {
var status = number(
first(error, ['status', 'status_code', 'statusCode'])
);
var code = text(
first(error, ['code', 'error_code', 'errorCode'])
).toUpperCase();
var message = text(error && error.message).toLowerCase();
if (
status === 401 ||
code === 'UNAUTHORIZED' ||
message.indexOf('unauthor') >= 0
) {
return 'huwaa_weekly_star.login_required';
}
if (code === 'PROFILE_REQUIRED') {
return 'huwaa_weekly_star.profile_required';
}
return 'huwaa_weekly_star.request_failed';
}
function normalizeGift(item) {
item = item || {};
var resource = item.resource || item.gift || {};
var metadata = parseJSON(
first(item, ['metadata_json', 'metadataJson']) ||
first(resource, ['metadata_json', 'metadataJson'])
);
return {
id: text(
first(item, [
'gift_id',
'giftId',
'resource_id',
'resourceId',
'id',
]) || first(resource, ['id', 'resource_id', 'resourceId'])
),
name: text(
first(item, ['display_name', 'displayName', 'name']) ||
first(resource, ['display_name', 'displayName', 'name'])
),
image: resolveAssetURL(
first(metadata, [
'preview_url',
'previewUrl',
'asset_url',
'assetUrl',
]) ||
first(item, [
'image_url',
'imageUrl',
'cover_url',
'coverUrl',
'preview_url',
'previewUrl',
'icon_url',
'iconUrl',
'asset_url',
'assetUrl',
]) ||
first(resource, [
'preview_url',
'previewUrl',
'cover_url',
'coverUrl',
'asset_url',
'assetUrl',
])
),
};
}
function normalizeEntries(entries) {
if (!Array.isArray(entries)) return [];
return entries.slice(0, RANK_LIMIT).map(function (entry, index) {
entry = entry || {};
var user = entry.user || entry.profile || {};
var rank = number(
first(entry, ['rank_no', 'rankNo', 'rank']),
index + 1
);
var name = text(
first(user, [
'username',
'nickname',
'display_name',
'displayName',
'display_user_id',
'displayUserId',
]) || first(entry, ['username', 'name', 'user_id', 'userId'])
);
return {
rank: rank || index + 1,
userID: text(
first(entry, ['user_id', 'userId']) ||
first(user, ['user_id', 'userId', 'id'])
),
name: name || t('huwaa_weekly_star.unknown_user', 'User'),
score: first(entry, ['score', 'points', 'value']) || 0,
avatar: resolveAssetURL(
first(user, [
'avatar',
'avatar_url',
'avatarUrl',
'profile_image',
'profileImage',
]) || first(entry, ['avatar', 'avatar_url', 'avatarUrl'])
),
};
});
}
function rewardImage(item, resource, metadata) {
return resolveAssetURL(
first(metadata, [
'preview_url',
'previewUrl',
'asset_url',
'assetUrl',
]) ||
first(item, [
'image',
'image_url',
'imageUrl',
'cover_url',
'coverUrl',
'preview_url',
'previewUrl',
'icon_url',
'iconUrl',
'asset_url',
'assetUrl',
]) ||
first(resource, [
'preview_url',
'previewUrl',
'cover_url',
'coverUrl',
'asset_url',
'assetUrl',
'animation_url',
'animationUrl',
])
);
}
function normalizeRewardItem(item, group) {
item = item || {};
group = group || {};
var resource = item.resource || {};
var metadata = parseJSON(
first(item, ['metadata_json', 'metadataJson']) ||
first(resource, ['metadata_json', 'metadataJson'])
);
var walletType = text(
first(item, ['wallet_asset_type', 'walletAssetType']) ||
first(resource, ['wallet_asset_type', 'walletAssetType'])
).replace(/_/g, ' ');
var walletAmount = number(
first(item, [
'wallet_asset_amount',
'walletAssetAmount',
'amount',
]) || first(resource, ['wallet_asset_amount', 'walletAssetAmount'])
);
var quantity = number(first(item, ['quantity']));
var durationMS = number(first(item, ['duration_ms', 'durationMs']));
var durationDays = number(
first(item, [
'duration_days',
'durationDays',
'valid_days',
'validDays',
]) || first(resource, ['duration_days', 'durationDays'])
);
if (!durationDays && durationMS > 0) {
// Gateway resource-group items expose validity as duration_ms. Keep the
// conversion aligned with the backend's whole-day reward contract.
durationDays = Math.floor(durationMS / DAY_MS);
}
var labelKey = text(first(item, ['i18n_key', 'i18nKey']));
var label = labelKey
? t(labelKey, '')
: text(
first(item, [
'display_name',
'displayName',
'label',
'name',
]) ||
first(resource, [
'display_name',
'displayName',
'name',
]) ||
walletType ||
first(group, ['display_name', 'displayName', 'name'])
);
if (durationDays > 0) {
label = t('huwaa_weekly_star.reward_days', '{name} ({days} days)', {
name: label || t('huwaa_weekly_star.reward', 'Reward'),
days: durationDays,
});
}
if (quantity > 1) {
label =
(label || t('huwaa_weekly_star.reward', 'Reward')) +
' ×' +
formatNumber(quantity);
} else if (walletAmount > 0) {
label =
(label ||
walletType ||
t('huwaa_weekly_star.reward', 'Reward')) +
' ×' +
formatNumber(walletAmount);
}
return {
label: label || t('huwaa_weekly_star.reward', 'Reward'),
image: rewardImage(item, resource, metadata),
};
}
function normalizeRewardGroups(cycle) {
cycle = cycle || {};
var source =
cycle.rewards ||
cycle.reward_groups ||
cycle.rewardGroups ||
cycle.rank_rewards ||
cycle.rankRewards ||
[];
if (!Array.isArray(source)) return [];
return source
.map(function (group, index) {
group = group || {};
var nested =
group.resource_group ||
group.resourceGroup ||
group.reward_resource_group ||
group.rewardResourceGroup ||
{};
var items =
group.items ||
group.resources ||
nested.items ||
nested.resources ||
[];
// Some endpoints flatten a single visual resource onto the rank reward.
// It is only promoted when an actual image/name exists; a bare group ID
// must remain an empty state rather than a fabricated usable reward.
if (!Array.isArray(items)) items = [];
if (
!items.length &&
(rewardImage(group, group.resource || {}, {}) ||
first(group, ['display_name', 'displayName', 'name']))
) {
items = [group];
}
return {
rank: number(
first(group, ['rank_no', 'rankNo', 'rank']),
index + 1
),
items: items.slice(0, 3).map(function (item) {
return normalizeRewardItem(item, group);
}),
};
})
.filter(function (group) {
return group.rank >= 1 && group.rank <= 3;
})
.sort(function (left, right) {
return left.rank - right.rank;
});
}
function renderGifts(cycle) {
var source = cycle && Array.isArray(cycle.gifts) ? cycle.gifts : [];
var gifts = source.slice(0, 3).map(normalizeGift);
var lefts = [61, 411, 761];
var root = document.getElementById('weekStarGifts');
root.innerHTML = lefts
.map(function (left, index) {
var gift = gifts[index] || {};
var label =
gift.name ||
t('huwaa_weekly_star.gift_label', 'Activity gift {index}', {
index: index + 1,
});
return [
'
',
'

',
'

',
gift.image
? '
 +
')
'
: '',
'
—',
'
',
].join('');
})
.join('');
bindImageFallbacks(root);
}
function medalForRank(rank) {
if (rank === 1) return ASSET_BASE + 'rank-1.webp';
if (rank === 2) return ASSET_BASE + 'rank-2.webp';
if (rank === 3) return ASSET_BASE + 'rank-3.webp';
return '';
}
function rankInitial(name) {
var characters = Array.from(text(name));
return characters.length ? characters[0] : '?';
}
function stateHTML(top, message, retry) {
return [
'',
'',
escapeHTML(message),
'',
retry
? ''
: '',
'
',
].join('');
}
function isRetryable(error) {
return error === 'huwaa_weekly_star.request_failed';
}
function renderRankList(options) {
var root = document.getElementById(options.rootID);
var entries = normalizeEntries(options.entries);
if (!entries.length) {
var message = options.loading
? t('huwaa_weekly_star.loading', 'Loading…')
: options.error
? t(options.error, 'Unable to load the ranking.')
: t(options.emptyKey, options.emptyFallback);
root.innerHTML = stateHTML(
options.top,
message,
!options.loading && isRetryable(options.error)
);
return;
}
root.innerHTML = entries
.map(function (entry, index) {
var medal = medalForRank(entry.rank);
var tag = entry.userID ? 'button' : 'article';
var className =
index % 2 === (options.firstDark ? 0 : 1)
? 'is-dark'
: 'is-purple';
var aria = t(
'huwaa_weekly_star.rank_label',
'Rank {rank}, {name}, {score}',
{
rank: entry.rank,
name: entry.name,
score: formatScore(entry.score),
}
);
return [
'<',
tag,
tag === 'button' ? ' type="button"' : '',
' class="week-star-rank-row ',
className,
'" style="top:',
options.top + index * 140,
'px"',
entry.userID
? ' data-user-id="' + escapeAttr(entry.userID) + '"'
: '',
' aria-label="',
escapeAttr(aria),
'">',
medal
? '
'
: '' +
escapeHTML(entry.rank) +
'',
'',
entry.avatar
? '
'
: '',
'',
escapeHTML(rankInitial(entry.name)),
'',
'',
'',
escapeHTML(entry.name),
'',
'',
escapeHTML(formatScore(entry.score)),
'',
'',
tag,
'>',
].join('');
})
.join('');
bindImageFallbacks(root);
}
function historyCycleData() {
var record = state.historyCycles[0] || {};
return {
cycle: record.cycle || record,
entries:
record.top_entries ||
record.topEntries ||
record.entries ||
record.leaderboard ||
[],
};
}
function renderHistory() {
var history = historyCycleData();
document.getElementById('weekStarHistoryDate').textContent =
history.cycle &&
first(history.cycle, ['start_ms', 'startMs']) &&
first(history.cycle, ['end_ms', 'endMs'])
? historyRange(history.cycle)
: t(
'huwaa_weekly_star.previous_top_three',
'Previous Top 3 Records'
);
renderRankList({
rootID: 'weekStarHistoryList',
top: 1607,
entries: history.entries,
loading: state.loadingHistory,
error: state.historyError,
emptyKey: 'huwaa_weekly_star.no_history',
emptyFallback: 'No previous ranking records.',
firstDark: false,
});
}
function renderWeekly() {
renderRankList({
rootID: 'weekStarWeeklyList',
top: 1496,
entries: state.weeklyEntries,
loading: state.loadingWeekly,
error: state.weeklyError,
emptyKey: 'huwaa_weekly_star.no_ranking',
emptyFallback: 'No ranking data yet.',
firstDark: true,
});
}
function renderRewardItem(item) {
return [
'',
'
',
'
',
item.image
? '
'
: '',
'—',
'',
escapeHTML(item.label),
'',
'',
].join('');
}
function renderRewards(cycle) {
var root = document.getElementById('weekStarRewardGroups');
if (state.loadingCurrent) {
root.innerHTML = stateHTML(
1705,
t('huwaa_weekly_star.loading', 'Loading…'),
false
);
return;
}
if (state.currentError) {
root.innerHTML = stateHTML(
1705,
t(state.currentError, 'Unable to load rewards.'),
isRetryable(state.currentError)
);
return;
}
var groups = normalizeRewardGroups(cycle);
if (!groups.length) {
root.innerHTML = stateHTML(
1705,
t('huwaa_weekly_star.no_rewards', 'No reward data yet.'),
false
);
return;
}
var positions = {
1: { heading: 1505, items: 1705 },
2: { heading: 2068, items: 2203 },
3: { heading: 2566, items: 2701 },
};
root.innerHTML = groups
.map(function (group) {
var position = positions[group.rank];
var itemsHTML = group.items.length
? group.items.map(renderRewardItem).join('')
: '' +
escapeHTML(
t(
'huwaa_weekly_star.no_rank_reward',
'No reward data.'
)
) +
'
';
return [
'',
'',
'
',
'TOP',
group.rank,
'',
'
',
'',
itemsHTML,
'
',
'',
].join('');
})
.join('');
bindImageFallbacks(root);
}
function renderHeader() {
var cycle = currentCycle();
var date = document.getElementById('weekStarDate');
if (state.loadingCurrent) {
date.textContent = '—';
} else if (state.currentError) {
date.textContent = '—';
} else if (cycle) {
date.textContent = cycleRange(cycle);
} else {
date.textContent = t(
'huwaa_weekly_star.no_active_cycle',
'No active cycle'
);
}
renderGifts(cycle || {});
renderRewards(cycle || {});
}
function bindImageFallbacks(root) {
all('[data-image-fallback]', root).forEach(function (image) {
if (image.dataset.fallbackBound === 'true') return;
image.dataset.fallbackBound = 'true';
function fallback() {
var type = image.getAttribute('data-image-fallback');
var parent = image.parentElement;
if (type === 'avatar' && parent) {
parent.classList.add('is-fallback');
} else if (parent) {
parent.classList.remove('has-image');
}
image.hidden = true;
}
image.addEventListener('error', fallback, { once: true });
if (image.complete && image.naturalWidth === 0) fallback();
});
}
function renderAll() {
renderHeader();
renderHistory();
renderWeekly();
setTab(state.tab, false, false);
}
function tabFromHash() {
var hash = text((window.location.hash || '').replace(/^#/, ''));
if (!hash) return '';
var route = hash.split('?')[0].replace(/^\/+|\/+$/g, '');
var pieces = route.split('/');
return pieces[pieces.length - 1];
}
function normalizeTab(value) {
return value === 'weekly' || value === 'rewards' || value === 'history'
? value
: 'history';
}
function replaceTabHash(tab) {
if (!window.history || !window.history.replaceState) return;
var hash = window.location.hash || '';
var hashQuery =
hash.indexOf('?') >= 0
? '?' + hash.split('?').slice(1).join('?')
: '';
// Native containers sometimes inject the token after `#?...`; preserving that
// suffix is mandatory because replacing the entire hash would log the user out.
var next =
window.location.pathname +
window.location.search +
'#' +
tab +
hashQuery;
window.history.replaceState(null, '', next);
}
function updateStageHeight() {
var viewportWidth = Math.min(window.innerWidth, 430);
var height = DESIGN_HEIGHT[state.tab] || DESIGN_HEIGHT.history;
var scaledHeight = Math.ceil((height * viewportWidth) / 1080);
document.documentElement.style.setProperty(
'--huwaa-week-star-scale',
String(viewportWidth / 1080)
);
stage.style.height = height + 'px';
stageWrap.style.height = scaledHeight + 'px';
page.style.minHeight = scaledHeight + 'px';
}
function setTab(tab, updateURL, announce) {
state.tab = normalizeTab(tab);
page.setAttribute('data-tab', state.tab);
stage.setAttribute('data-node-id', DESIGN_FRAME[state.tab]);
tabs.forEach(function (button) {
var active = button.getAttribute('data-tab-target') === state.tab;
button.classList.toggle('is-active', active);
button.setAttribute('aria-selected', active ? 'true' : 'false');
button.tabIndex = active ? 0 : -1;
});
panels.forEach(function (panel) {
panel.hidden = panel.getAttribute('data-panel') !== state.tab;
});
updateStageHeight();
if (updateURL) replaceTabHash(state.tab);
if (announce) {
var selected = tabs.find(function (button) {
return button.getAttribute('data-tab-target') === state.tab;
});
document.getElementById('weekStarAnnouncer').textContent = selected
? selected.textContent.trim()
: '';
}
}
function mockState() {
var sample = ASSET_BASE + 'sample-gift.webp';
var mockEntries = [1, 2, 3, 4, 5].map(function (rank) {
return {
rank_no: rank,
score: 337700,
user: {
id: 'mock-user-' + rank,
username: rank === 1 ? 'hanneem' : 'star' + rank,
avatar: '',
},
};
});
var rewardGroups = [1, 2, 3].map(function (rank) {
return {
rank_no: rank,
items: [1, 2, 3].map(function () {
return {
i18n_key: 'huwaa_weekly_star.frame',
duration_days: 7,
image_url: sample,
};
}),
};
});
return {
current: {
cycle: {
cycle_id: 'figma-mock',
start_ms: Date.UTC(2025, 11, 27, 21, 0, 0),
end_ms: Date.UTC(2026, 0, 3, 20, 59, 0),
utc_offset_hours: 3,
gifts: [
{ image_url: sample },
{ image_url: sample },
{ image_url: sample },
],
rewards: rewardGroups,
},
},
entries: mockEntries,
history: [
{
cycle: {
start_ms: Date.UTC(2025, 11, 6, 21, 0, 0),
end_ms: Date.UTC(2025, 11, 14, 20, 59, 0),
utc_offset_hours: 3,
},
top_entries: mockEntries,
},
],
};
}
function loadMock() {
var mock = mockState();
state.current = mock.current;
state.weeklyEntries = mock.entries;
state.historyCycles = mock.history;
state.loadingCurrent = false;
state.loadingWeekly = false;
state.loadingHistory = false;
state.currentError = '';
state.weeklyError = '';
state.historyError = '';
renderAll();
bridgeReady();
}
function tokenAvailable() {
return !!(
window.HyAppAPI &&
window.HyAppAPI.getAccessToken &&
window.HyAppAPI.getAccessToken()
);
}
function loadReal() {
var serial = ++state.requestSerial;
state.loadingCurrent = true;
state.loadingWeekly = true;
state.loadingHistory = true;
state.currentError = '';
state.weeklyError = '';
state.historyError = '';
renderAll();
if (!window.HyAppAPI || !window.HyAppAPI.weeklyStar) {
state.loadingCurrent = false;
state.loadingWeekly = false;
state.loadingHistory = false;
state.currentError = 'huwaa_weekly_star.request_failed';
state.weeklyError = state.currentError;
state.historyError = state.currentError;
renderAll();
bridgeReady();
return;
}
if (!tokenAvailable()) {
// Production weekly-star endpoints require the authenticated user context.
// Do not send three doomed requests or silently replace the response with mock data.
state.loadingCurrent = false;
state.loadingWeekly = false;
state.loadingHistory = false;
state.currentError = 'huwaa_weekly_star.login_required';
state.weeklyError = state.currentError;
state.historyError = state.currentError;
renderAll();
bridgeReady();
return;
}
// Current/leaderboard and history are intentionally isolated. A history outage
// must not erase a successfully loaded current leaderboard, and vice versa.
var currentFlow = window.HyAppAPI.weeklyStar
.current()
.then(function (payload) {
if (serial !== state.requestSerial) return;
var data = envelope(payload);
state.current = data;
state.weeklyEntries = data.top_entries || data.topEntries || [];
state.loadingCurrent = false;
state.loadingWeekly = false;
state.currentError = '';
state.weeklyError = '';
renderHeader();
renderWeekly();
})
.catch(function (error) {
if (serial !== state.requestSerial) return;
state.current = null;
state.weeklyEntries = [];
state.loadingCurrent = false;
state.loadingWeekly = false;
state.currentError = errorKey(error);
state.weeklyError = state.currentError;
renderHeader();
renderWeekly();
});
var historyFlow = window.HyAppAPI.weeklyStar
.history(1)
.then(function (payload) {
if (serial !== state.requestSerial) return;
var data = envelope(payload);
state.historyCycles = Array.isArray(data)
? data
: data.cycles || data.items || data.list || [];
state.historyError = '';
})
.catch(function (error) {
if (serial !== state.requestSerial) return;
state.historyCycles = [];
state.historyError = errorKey(error);
})
.then(function () {
if (serial !== state.requestSerial) return;
state.loadingHistory = false;
renderHistory();
});
Promise.all([currentFlow, historyFlow]).then(function () {
if (serial === state.requestSerial) bridgeReady();
});
}
function bridgeReady() {
if (!window.HyAppBridge || !window.HyAppBridge.ready) return;
window.HyAppBridge.ready({
page: 'huwaa_weekly_star',
app_code: 'huwaa',
mock: useMock,
});
}
function activateRelativeTab(currentIndex, direction) {
var nextIndex = (currentIndex + direction + tabs.length) % tabs.length;
var next = tabs[nextIndex];
setTab(next.getAttribute('data-tab-target'), true, true);
next.focus();
}
function bindEvents() {
tabs.forEach(function (button, index) {
button.addEventListener('click', function () {
setTab(button.getAttribute('data-tab-target'), true, true);
});
button.addEventListener('keydown', function (event) {
if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
event.preventDefault();
activateRelativeTab(index, 1);
} else if (
event.key === 'ArrowLeft' ||
event.key === 'ArrowUp'
) {
event.preventDefault();
activateRelativeTab(index, -1);
} else if (event.key === 'Home') {
event.preventDefault();
setTab('history', true, true);
tabs[0].focus();
} else if (event.key === 'End') {
event.preventDefault();
setTab('rewards', true, true);
tabs[tabs.length - 1].focus();
}
});
});
document.addEventListener('click', function (event) {
var retry = event.target.closest('[data-retry]');
if (retry) {
loadReal();
return;
}
var row = event.target.closest('[data-user-id]');
if (row && window.HyAppBridge && window.HyAppBridge.openUser) {
window.HyAppBridge.openUser(row.getAttribute('data-user-id'));
}
});
window.addEventListener('resize', updateStageHeight);
window.addEventListener('hashchange', function () {
setTab(normalizeTab(tabFromHash()), false, false);
});
window.addEventListener('hyapp:i18n-ready', function () {
// Dynamic API rows are not covered by common/i18n.js's static DOM pass.
// Re-rendering keeps empty/error/reward labels synchronized after locale load.
renderAll();
});
}
function init() {
bindEvents();
setTab(state.tab, false, false);
if (useMock) loadMock();
else loadReal();
}
init();
})();