1231 lines
40 KiB
JavaScript
1231 lines
40 KiB
JavaScript
(function () {
|
||
var HOST_SALARY_USD = 'HOST_SALARY_USD';
|
||
|
||
var state = emptyHostState();
|
||
var policyAutoScrollPending = false;
|
||
|
||
function $(id) {
|
||
return document.getElementById(id);
|
||
}
|
||
|
||
function emptyHostState() {
|
||
return {
|
||
profile: {
|
||
loading: true,
|
||
name: '',
|
||
uid: '',
|
||
avatar: '',
|
||
},
|
||
agency: {
|
||
loading: true,
|
||
shortId: '',
|
||
name: '',
|
||
avatar: '',
|
||
},
|
||
salary: {
|
||
loading: true,
|
||
available: 0,
|
||
monthKey: '',
|
||
month: '',
|
||
currencyKey: '',
|
||
currency: '',
|
||
useCurrentMonth: false,
|
||
},
|
||
level: {
|
||
loading: true,
|
||
current: 0,
|
||
nextLevelNo: 0,
|
||
currentRequired: 0,
|
||
currentValue: 0,
|
||
nextValue: 0,
|
||
needed: 0,
|
||
},
|
||
policy: {
|
||
loading: true,
|
||
loaded: false,
|
||
found: false,
|
||
policy: null,
|
||
},
|
||
messages: [],
|
||
processingMessages: {},
|
||
};
|
||
}
|
||
|
||
function setSkeleton(element, loading, text) {
|
||
if (!element) return;
|
||
element.classList.toggle('skeleton-line', !!loading);
|
||
if (loading) {
|
||
element.textContent = '';
|
||
return;
|
||
}
|
||
element.textContent = text || '';
|
||
}
|
||
|
||
function avatarData(text, background, foreground) {
|
||
var value =
|
||
String(text || 'Y')
|
||
.trim()
|
||
.charAt(0) || 'Y';
|
||
var letter = value.replace(/[<>&"']/g, '');
|
||
var svg =
|
||
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">' +
|
||
'<rect width="96" height="96" rx="48" fill="' +
|
||
background +
|
||
'"/>' +
|
||
'<text x="50%" y="54%" text-anchor="middle" dominant-baseline="middle" fill="' +
|
||
foreground +
|
||
'" font-family="Arial, sans-serif" font-size="38" font-weight="800">' +
|
||
letter +
|
||
'</text>' +
|
||
'</svg>';
|
||
return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
|
||
}
|
||
|
||
function t(key, fallback) {
|
||
return window.HyAppI18n && window.HyAppI18n.t
|
||
? window.HyAppI18n.t(key, fallback)
|
||
: fallback || key;
|
||
}
|
||
|
||
function money(value) {
|
||
var number = Number(value || 0);
|
||
return number.toLocaleString('en-US', {
|
||
minimumFractionDigits: 2,
|
||
maximumFractionDigits: 2,
|
||
});
|
||
}
|
||
|
||
function integer(value) {
|
||
var number = Number(value || 0);
|
||
return number.toLocaleString('en-US', {
|
||
maximumFractionDigits: 0,
|
||
});
|
||
}
|
||
|
||
function initial(text) {
|
||
return String(text || '')
|
||
.trim()
|
||
.charAt(0)
|
||
.toUpperCase();
|
||
}
|
||
|
||
function hasAccessToken() {
|
||
return (
|
||
window.HyAppAPI &&
|
||
window.HyAppAPI.getAccessToken &&
|
||
!!window.HyAppAPI.getAccessToken()
|
||
);
|
||
}
|
||
|
||
function unwrapData(payload) {
|
||
if (!payload || typeof payload !== 'object') return payload;
|
||
return Object.prototype.hasOwnProperty.call(payload, 'data')
|
||
? payload.data
|
||
: payload;
|
||
}
|
||
|
||
function firstValue(source, keys) {
|
||
if (!source) return '';
|
||
for (var i = 0; i < keys.length; i += 1) {
|
||
var value = source[keys[i]];
|
||
if (value !== undefined && value !== null && value !== '') {
|
||
return value;
|
||
}
|
||
}
|
||
return '';
|
||
}
|
||
|
||
function toNumber(value) {
|
||
var number = Number(value);
|
||
return Number.isFinite(number) ? number : 0;
|
||
}
|
||
|
||
function safeRequest(factory) {
|
||
return factory().catch(function (error) {
|
||
if (window.console && window.console.warn) {
|
||
window.console.warn('[host-center] api failed', error);
|
||
}
|
||
return null;
|
||
});
|
||
}
|
||
|
||
function currentLocale() {
|
||
var lang =
|
||
window.HyAppI18n && window.HyAppI18n.lang
|
||
? window.HyAppI18n.lang()
|
||
: 'en';
|
||
var map = {
|
||
en: 'en-US',
|
||
ar: 'ar',
|
||
tr: 'tr-TR',
|
||
es: 'es-ES',
|
||
zh: 'zh-CN',
|
||
id: 'id-ID',
|
||
};
|
||
return map[lang] || 'en-US';
|
||
}
|
||
|
||
function currentMonthName() {
|
||
try {
|
||
return new Date().toLocaleString(currentLocale(), {
|
||
month: 'long',
|
||
});
|
||
} catch (_) {
|
||
return 'June';
|
||
}
|
||
}
|
||
|
||
function salaryMonthLabel(salary) {
|
||
if (salary.monthKey) return t(salary.monthKey, salary.month);
|
||
if (salary.useCurrentMonth) return currentMonthName();
|
||
return salary.month || currentMonthName();
|
||
}
|
||
|
||
function salaryCurrencyLabel(salary) {
|
||
if (salary.currencyKey) return t(salary.currencyKey, salary.currency);
|
||
return salary.currency || t('host_center.currency_usd', 'USD');
|
||
}
|
||
|
||
function formatMode(value) {
|
||
var text = String(value || '')
|
||
.replace(/[_-]+/g, ' ')
|
||
.trim();
|
||
if (!text) return '-';
|
||
return text.replace(/\b\w/g, function (letter) {
|
||
return letter.toUpperCase();
|
||
});
|
||
}
|
||
|
||
function formatDate(ms) {
|
||
var value = toNumber(ms);
|
||
if (!value) return '';
|
||
try {
|
||
return new Date(value).toLocaleDateString(currentLocale(), {
|
||
year: 'numeric',
|
||
month: 'short',
|
||
day: 'numeric',
|
||
});
|
||
} catch (_) {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
function commandID(prefix, id) {
|
||
return [
|
||
prefix,
|
||
id || '0',
|
||
Date.now(),
|
||
Math.random().toString(16).slice(2),
|
||
].join('-');
|
||
}
|
||
|
||
function normalizeInvitationMessage(item) {
|
||
var invitation = (item && item.invitation) || item || {};
|
||
var inviter = (item && item.inviter) || {};
|
||
var id = String(
|
||
invitation.invitation_id || invitation.invitationId || ''
|
||
);
|
||
var type = firstValue(invitation, [
|
||
'invitation_type',
|
||
'invitationType',
|
||
]);
|
||
var agencyName = firstValue(invitation, ['agency_name', 'agencyName']);
|
||
var inviterName =
|
||
firstValue(inviter, ['username', 'name', 'display_user_id']) ||
|
||
firstValue(invitation, ['inviter_user_id', 'inviterUserId']) ||
|
||
t('host_center.inviter', 'Inviter');
|
||
return {
|
||
id: id,
|
||
type: type,
|
||
name: inviterName,
|
||
account:
|
||
firstValue(inviter, ['display_user_id', 'displayUserID']) ||
|
||
firstValue(inviter, ['user_id', 'userId']) ||
|
||
firstValue(invitation, ['inviter_user_id', 'inviterUserId']),
|
||
avatar: firstValue(inviter, ['avatar']),
|
||
title: invitationMessageTitle(type, inviterName, agencyName),
|
||
};
|
||
}
|
||
|
||
function invitationMessageTitle(type, inviterName, agencyName) {
|
||
if (type === 'host') {
|
||
return t(
|
||
'host_center.host_invitation',
|
||
'{name} invited you to join {agency}'
|
||
)
|
||
.replace('{name}', inviterName)
|
||
.replace(
|
||
'{agency}',
|
||
agencyName || t('host_center.agency', 'Agency')
|
||
);
|
||
}
|
||
if (type === 'agency') {
|
||
return t(
|
||
'host_center.agency_invitation',
|
||
'{name} invited you to create agency {agency}'
|
||
)
|
||
.replace('{name}', inviterName)
|
||
.replace(
|
||
'{agency}',
|
||
agencyName || t('host_center.agency', 'Agency')
|
||
);
|
||
}
|
||
if (type === 'bd') {
|
||
return t(
|
||
'host_center.bd_invitation',
|
||
'{name} invited you to become BD'
|
||
).replace('{name}', inviterName);
|
||
}
|
||
return t(
|
||
'host_center.role_invitation',
|
||
'{name} sent you an invitation'
|
||
).replace('{name}', inviterName);
|
||
}
|
||
|
||
function renderUnread() {
|
||
var dot = $('messageUnreadDot');
|
||
if (dot) dot.hidden = state.messages.length <= 0;
|
||
}
|
||
|
||
function createMessageAvatar(message) {
|
||
var avatar = document.createElement('div');
|
||
avatar.className = 'message-avatar';
|
||
if (!message.avatar) {
|
||
avatar.textContent = initial(message.name || 'M') || 'M';
|
||
return avatar;
|
||
}
|
||
var img = document.createElement('img');
|
||
img.src = message.avatar;
|
||
img.alt = '';
|
||
img.addEventListener('error', function () {
|
||
avatar.textContent = initial(message.name || 'M') || 'M';
|
||
});
|
||
avatar.appendChild(img);
|
||
return avatar;
|
||
}
|
||
|
||
function renderMessages() {
|
||
var list = $('messageList');
|
||
var empty = $('messageEmpty');
|
||
if (!list || !empty) return;
|
||
list.textContent = '';
|
||
empty.hidden = state.messages.length > 0;
|
||
empty.textContent = t('host_center.no_messages', 'No messages');
|
||
state.messages.forEach(function (message) {
|
||
var card = document.createElement('article');
|
||
var user = document.createElement('div');
|
||
var copy = document.createElement('div');
|
||
var title = document.createElement('div');
|
||
var meta = document.createElement('div');
|
||
var actions = document.createElement('div');
|
||
var agree = document.createElement('button');
|
||
var refuse = document.createElement('button');
|
||
card.className = 'message-card';
|
||
user.className = 'message-user';
|
||
copy.className = 'message-copy';
|
||
title.className = 'message-name';
|
||
meta.className = 'message-id';
|
||
actions.className = 'message-actions';
|
||
agree.className = 'message-action agree';
|
||
refuse.className = 'message-action refuse';
|
||
agree.type = 'button';
|
||
refuse.type = 'button';
|
||
agree.disabled = Boolean(state.processingMessages[message.id]);
|
||
refuse.disabled = Boolean(state.processingMessages[message.id]);
|
||
title.textContent = message.title;
|
||
meta.textContent = message.account ? 'ID: ' + message.account : '';
|
||
agree.textContent = t('host_center.agree', 'Agree');
|
||
refuse.textContent = t('host_center.refuse', 'Refuse');
|
||
agree.addEventListener('click', function () {
|
||
processInvitationMessage(message, 'accept');
|
||
});
|
||
refuse.addEventListener('click', function () {
|
||
processInvitationMessage(message, 'reject');
|
||
});
|
||
user.appendChild(createMessageAvatar(message));
|
||
copy.appendChild(title);
|
||
copy.appendChild(meta);
|
||
user.appendChild(copy);
|
||
actions.appendChild(agree);
|
||
actions.appendChild(refuse);
|
||
card.appendChild(user);
|
||
card.appendChild(actions);
|
||
list.appendChild(card);
|
||
});
|
||
renderUnread();
|
||
}
|
||
|
||
function policyAmount(minor) {
|
||
return '$' + money(toNumber(minor) / 100);
|
||
}
|
||
|
||
function renderProfile() {
|
||
var image = $('profileAvatar');
|
||
var fallback = $('profileAvatarFallback');
|
||
var badge = $('profileBadge');
|
||
var profile = state.profile;
|
||
var agency = state.agency;
|
||
var loading = !!profile.loading;
|
||
|
||
setSkeleton($('profileName'), loading, profile.name || '');
|
||
setSkeleton(
|
||
$('profileUID'),
|
||
loading,
|
||
profile.uid ? 'UID: ' + profile.uid : ''
|
||
);
|
||
fallback.classList.toggle('avatar-skeleton', loading);
|
||
if (loading) {
|
||
image.hidden = true;
|
||
image.removeAttribute('src');
|
||
fallback.hidden = false;
|
||
fallback.textContent = '';
|
||
badge.hidden = true;
|
||
badge.replaceChildren();
|
||
badge.classList.remove('skeleton-line');
|
||
return;
|
||
}
|
||
|
||
fallback.textContent = initial(profile.name || profile.uid) || 'Y';
|
||
|
||
if (agency.loading) {
|
||
badge.hidden = false;
|
||
setSkeleton(badge, true, '');
|
||
} else if (agency.shortId) {
|
||
badge.hidden = false;
|
||
badge.classList.remove('skeleton-line');
|
||
renderAgencyBadge(badge, agency);
|
||
} else {
|
||
badge.hidden = true;
|
||
badge.classList.remove('skeleton-line');
|
||
badge.replaceChildren();
|
||
}
|
||
|
||
if (!profile.avatar) {
|
||
image.hidden = true;
|
||
image.removeAttribute('src');
|
||
fallback.hidden = false;
|
||
return;
|
||
}
|
||
image.onload = function () {
|
||
image.hidden = false;
|
||
fallback.hidden = true;
|
||
};
|
||
image.onerror = function () {
|
||
image.hidden = true;
|
||
fallback.hidden = false;
|
||
};
|
||
image.src = profile.avatar;
|
||
}
|
||
|
||
function renderAgencyBadge(container, agency) {
|
||
container.replaceChildren();
|
||
var avatar = document.createElement('span');
|
||
avatar.className = 'agency-badge-avatar';
|
||
var fallbackText =
|
||
initial(agency.name || agency.shortId) ||
|
||
t('host_center.agency', 'Agency').charAt(0);
|
||
if (agency.avatar) {
|
||
var img = document.createElement('img');
|
||
img.src = agency.avatar;
|
||
img.alt = '';
|
||
img.addEventListener('error', function () {
|
||
avatar.textContent = fallbackText;
|
||
});
|
||
avatar.appendChild(img);
|
||
} else {
|
||
avatar.textContent = fallbackText;
|
||
}
|
||
|
||
var text = document.createElement('span');
|
||
text.className = 'agency-badge-id';
|
||
text.textContent = t(
|
||
'host_center.agency_short_id',
|
||
'Agency ID: {id}'
|
||
).replace('{id}', agency.shortId);
|
||
container.appendChild(avatar);
|
||
container.appendChild(text);
|
||
}
|
||
|
||
function renderIncome() {
|
||
var salary = state.salary;
|
||
var loading = !!salary.loading;
|
||
setSkeleton($('availableSalary'), loading, money(salary.available));
|
||
setSkeleton(
|
||
$('incomeFoot'),
|
||
loading,
|
||
t(
|
||
'host_center.salary_month_note',
|
||
'{month} settlement · {currency}'
|
||
)
|
||
.replace('{month}', salaryMonthLabel(salary))
|
||
.replace('{currency}', salaryCurrencyLabel(salary))
|
||
);
|
||
var coinIcon = document.querySelector('.coin-icon');
|
||
if (coinIcon) coinIcon.classList.toggle('skeleton-coin', loading);
|
||
}
|
||
|
||
function renderLevel() {
|
||
var level = state.level;
|
||
var loading = !!level.loading;
|
||
if (loading) {
|
||
setSkeleton($('currentLevel'), true, '');
|
||
setSkeleton($('nextLevelNeed'), true, '');
|
||
setSkeleton($('levelStatus'), true, '');
|
||
$('levelProgress').style.width = '38%';
|
||
$('levelProgress').classList.add('skeleton-fill');
|
||
return;
|
||
}
|
||
var currentRequired = toNumber(level.currentRequired);
|
||
var currentValue = toNumber(level.currentValue);
|
||
var nextValue = toNumber(level.nextValue);
|
||
var need =
|
||
level.needed !== undefined && level.needed !== null
|
||
? toNumber(level.needed)
|
||
: Math.max(0, nextValue - currentValue);
|
||
var denominator = Math.max(1, nextValue - currentRequired);
|
||
var currentInLevel = Math.max(0, currentValue - currentRequired);
|
||
var percent =
|
||
nextValue > currentRequired
|
||
? Math.min(
|
||
100,
|
||
Math.round((currentInLevel / denominator) * 100)
|
||
)
|
||
: 100;
|
||
|
||
$('currentLevel').textContent = t(
|
||
'host_center.level_value',
|
||
'Lv. {level}'
|
||
).replace('{level}', String(level.current || 0));
|
||
$('nextLevelNeed').textContent = money(need);
|
||
$('currentLevel').classList.remove('skeleton-line');
|
||
$('nextLevelNeed').classList.remove('skeleton-line');
|
||
$('levelStatus').classList.remove('skeleton-line');
|
||
$('levelProgress').style.width = percent + '%';
|
||
$('levelProgress').classList.remove('skeleton-fill');
|
||
$('levelStatus').textContent = t(
|
||
'host_center.current_diamonds',
|
||
'Current diamonds: {value}'
|
||
).replace('{value}', integer(currentValue));
|
||
}
|
||
|
||
function appendPolicyCell(parent, label, value) {
|
||
var cell = document.createElement('div');
|
||
cell.className = 'policy-cell';
|
||
var labelNode = document.createElement('span');
|
||
labelNode.className = 'policy-cell-label';
|
||
labelNode.textContent = label;
|
||
var valueNode = document.createElement('strong');
|
||
valueNode.className = 'policy-cell-value';
|
||
valueNode.textContent = value;
|
||
cell.appendChild(labelNode);
|
||
cell.appendChild(valueNode);
|
||
parent.appendChild(cell);
|
||
}
|
||
|
||
function policyHighlightLevelNo() {
|
||
var current = toNumber(state.level && state.level.current);
|
||
return current > 0 ? current : 0;
|
||
}
|
||
|
||
function isPolicyOpen() {
|
||
var modal = $('policyModal');
|
||
return !!modal && !modal.hidden;
|
||
}
|
||
|
||
function scrollPolicyLevelIntoView(row) {
|
||
if (!policyAutoScrollPending || !row || !isPolicyOpen()) return;
|
||
policyAutoScrollPending = false;
|
||
var run = window.requestAnimationFrame || window.setTimeout;
|
||
run(function () {
|
||
try {
|
||
row.scrollIntoView({
|
||
behavior: 'smooth',
|
||
block: 'center',
|
||
inline: 'nearest',
|
||
});
|
||
} catch (_) {
|
||
var container = row.closest('.policy-sheet-body');
|
||
if (container) {
|
||
container.scrollTop =
|
||
row.offsetTop -
|
||
Math.max(
|
||
0,
|
||
(container.clientHeight - row.offsetHeight) / 2
|
||
);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
function renderPolicy() {
|
||
var summary = state.policy || {};
|
||
var policy = summary.policy || {};
|
||
var list = $('policyLevelList');
|
||
var empty = $('policyEmpty');
|
||
if (!list || !empty) return;
|
||
|
||
list.textContent = '';
|
||
if (summary.loading) {
|
||
setSkeleton($('policyName'), true, '');
|
||
setSkeleton($('policyMeta'), true, '');
|
||
empty.hidden = false;
|
||
empty.textContent = '';
|
||
empty.classList.add('policy-skeleton-block');
|
||
return;
|
||
}
|
||
$('policyName').classList.remove('skeleton-line');
|
||
$('policyMeta').classList.remove('skeleton-line');
|
||
empty.classList.remove('policy-skeleton-block');
|
||
if (!summary.found || !policy || !policy.name) {
|
||
$('policyName').textContent = 'Platform policy';
|
||
$('policyMeta').textContent = '';
|
||
empty.hidden = false;
|
||
empty.textContent = 'No platform policy configured.';
|
||
policyAutoScrollPending = false;
|
||
return;
|
||
}
|
||
|
||
var from = formatDate(policy.effective_from_ms);
|
||
var to = formatDate(policy.effective_to_ms);
|
||
var meta = [
|
||
'Mode: ' + formatMode(policy.settlement_mode),
|
||
'Trigger: ' + formatMode(policy.settlement_trigger_mode),
|
||
];
|
||
if (from || to) {
|
||
meta.push('Effective: ' + (from || '-') + ' - ' + (to || 'No end'));
|
||
}
|
||
$('policyName').textContent = policy.name || 'Current policy';
|
||
$('policyMeta').textContent = meta.join(' / ');
|
||
empty.hidden = true;
|
||
empty.textContent = '';
|
||
|
||
var levels = Array.isArray(policy.levels) ? policy.levels : [];
|
||
if (!levels.length) {
|
||
empty.hidden = false;
|
||
empty.textContent = 'No policy levels configured.';
|
||
policyAutoScrollPending = false;
|
||
return;
|
||
}
|
||
|
||
var highlightedRow = null;
|
||
var activeLevelNo = policyHighlightLevelNo();
|
||
levels.forEach(function (level) {
|
||
var row = document.createElement('div');
|
||
row.className = 'policy-level-row';
|
||
var levelNo = toNumber(level.level_no);
|
||
row.dataset.levelNo = String(levelNo || '');
|
||
if (levelNo > 0 && levelNo === activeLevelNo) {
|
||
row.className += ' is-current-level';
|
||
highlightedRow = row;
|
||
}
|
||
var head = document.createElement('div');
|
||
head.className = 'policy-row-head';
|
||
var title = document.createElement('strong');
|
||
title.className = 'policy-level';
|
||
title.textContent = 'Lv. ' + (levelNo || '-');
|
||
var required = document.createElement('span');
|
||
required.className = 'policy-effective';
|
||
required.textContent =
|
||
integer(level.required_diamonds) + ' diamonds';
|
||
head.appendChild(title);
|
||
head.appendChild(required);
|
||
|
||
var grid = document.createElement('div');
|
||
grid.className = 'policy-grid';
|
||
appendPolicyCell(
|
||
grid,
|
||
'Host salary',
|
||
policyAmount(level.host_salary_usd_minor)
|
||
);
|
||
var hostCoinReward = toNumber(level.host_coin_reward);
|
||
// Host 端只展示主播能直接获得的有效奖励;为 0 或缺失时隐藏,避免把无收益项误读成待完成权益。
|
||
if (hostCoinReward > 0) {
|
||
appendPolicyCell(
|
||
grid,
|
||
'Host coin reward',
|
||
integer(hostCoinReward)
|
||
);
|
||
}
|
||
row.appendChild(head);
|
||
row.appendChild(grid);
|
||
list.appendChild(row);
|
||
});
|
||
if (highlightedRow) {
|
||
scrollPolicyLevelIntoView(highlightedRow);
|
||
} else {
|
||
policyAutoScrollPending = false;
|
||
}
|
||
}
|
||
|
||
function render() {
|
||
renderProfile();
|
||
renderIncome();
|
||
renderLevel();
|
||
renderPolicy();
|
||
renderMessages();
|
||
}
|
||
|
||
function applyProfile(payload) {
|
||
var profile = unwrapData(payload);
|
||
if (!profile) return;
|
||
|
||
var name = firstValue(profile, [
|
||
'username',
|
||
'nickname',
|
||
'name',
|
||
'display_name',
|
||
]);
|
||
var uid = firstValue(profile, [
|
||
'display_user_id',
|
||
'default_display_user_id',
|
||
'uid',
|
||
'user_id',
|
||
]);
|
||
var avatar = firstValue(profile, ['avatar', 'avatar_url', 'avatarUrl']);
|
||
|
||
state.profile = {
|
||
loading: false,
|
||
name: name || state.profile.name,
|
||
uid: uid || state.profile.uid,
|
||
avatar:
|
||
avatar ||
|
||
avatarData(
|
||
initial(name || uid || state.profile.name),
|
||
'#dbc8ff',
|
||
'#7d57c7'
|
||
),
|
||
};
|
||
}
|
||
|
||
function applyAgency(payload) {
|
||
var data = unwrapData(payload);
|
||
var agency = data && data.agency ? data.agency : data;
|
||
if (!agency) {
|
||
return;
|
||
}
|
||
var shortId = firstValue(agency, [
|
||
'short_id',
|
||
'shortId',
|
||
'display_user_id',
|
||
'displayUserId',
|
||
'agency_id',
|
||
'agencyId',
|
||
]);
|
||
state.agency = {
|
||
loading: false,
|
||
shortId: shortId ? String(shortId) : '',
|
||
name: firstValue(agency, ['name', 'agency_name', 'agencyName']),
|
||
avatar: firstValue(agency, ['avatar', 'avatar_url', 'avatarUrl']),
|
||
};
|
||
}
|
||
|
||
function displayAmountForAsset(assetType, amount) {
|
||
if (assetType === HOST_SALARY_USD) return toNumber(amount) / 100;
|
||
return toNumber(amount);
|
||
}
|
||
|
||
function findBalance(payload, assetType) {
|
||
var data = unwrapData(payload);
|
||
var balances =
|
||
data && Array.isArray(data.balances) ? data.balances : [];
|
||
for (var i = 0; i < balances.length; i += 1) {
|
||
if (
|
||
balances[i].asset_type === assetType ||
|
||
balances[i].assetType === assetType
|
||
) {
|
||
return balances[i];
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function applyWallet(payload) {
|
||
var balance = findBalance(payload, HOST_SALARY_USD);
|
||
if (!balance) {
|
||
state.salary = Object.assign({}, state.salary, {
|
||
loading: false,
|
||
available: 0,
|
||
monthKey: '',
|
||
month: '',
|
||
currencyKey: 'host_center.currency_usd',
|
||
currency: 'USD',
|
||
useCurrentMonth: true,
|
||
});
|
||
return;
|
||
}
|
||
|
||
var amount = firstValue(balance, [
|
||
'available_amount',
|
||
'availableAmount',
|
||
]);
|
||
state.salary = Object.assign({}, state.salary, {
|
||
loading: false,
|
||
available: displayAmountForAsset(HOST_SALARY_USD, amount),
|
||
monthKey: '',
|
||
month: '',
|
||
currencyKey: 'host_center.currency_usd',
|
||
currency: 'USD',
|
||
useCurrentMonth: true,
|
||
});
|
||
}
|
||
|
||
function applyLevelProgress(progress) {
|
||
if (!progress) return;
|
||
state.level = {
|
||
loading: false,
|
||
current: toNumber(
|
||
firstValue(progress, ['current_level', 'currentLevel'])
|
||
),
|
||
nextLevelNo: toNumber(
|
||
firstValue(progress, ['next_level', 'nextLevel'])
|
||
),
|
||
currentRequired: toNumber(
|
||
firstValue(progress, [
|
||
'current_required_diamonds',
|
||
'currentRequiredDiamonds',
|
||
])
|
||
),
|
||
currentValue: toNumber(
|
||
firstValue(progress, ['current_value', 'currentValue'])
|
||
),
|
||
nextValue: toNumber(
|
||
firstValue(progress, [
|
||
'next_required_diamonds',
|
||
'nextRequiredDiamonds',
|
||
'current_value',
|
||
'currentValue',
|
||
])
|
||
),
|
||
needed: toNumber(
|
||
firstValue(progress, [
|
||
'needed_for_next_level',
|
||
'neededForNextLevel',
|
||
])
|
||
),
|
||
};
|
||
}
|
||
|
||
function fallbackLevelProgress() {
|
||
return {
|
||
loading: false,
|
||
current: 0,
|
||
nextLevelNo: 0,
|
||
currentRequired: 0,
|
||
currentValue: 0,
|
||
nextValue: 1,
|
||
needed: 0,
|
||
};
|
||
}
|
||
|
||
function levelProgressFromPolicy(policy, progress) {
|
||
var levels =
|
||
policy && Array.isArray(policy.levels) ? policy.levels : [];
|
||
var currentValue = toNumber(
|
||
firstValue(progress, [
|
||
'total_diamonds',
|
||
'totalDiamonds',
|
||
'gift_diamond_total',
|
||
'giftDiamondTotal',
|
||
])
|
||
);
|
||
if (!levels.length) {
|
||
return Object.assign(fallbackLevelProgress(), {
|
||
currentValue: currentValue,
|
||
});
|
||
}
|
||
|
||
var current = null;
|
||
var next = null;
|
||
levels.forEach(function (level) {
|
||
if (!level) return;
|
||
var required = toNumber(
|
||
firstValue(level, ['required_diamonds', 'requiredDiamonds'])
|
||
);
|
||
if (required <= currentValue) {
|
||
if (
|
||
!current ||
|
||
required >
|
||
toNumber(
|
||
firstValue(current, [
|
||
'required_diamonds',
|
||
'requiredDiamonds',
|
||
])
|
||
)
|
||
) {
|
||
current = level;
|
||
}
|
||
return;
|
||
}
|
||
if (
|
||
!next ||
|
||
required <
|
||
toNumber(
|
||
firstValue(next, [
|
||
'required_diamonds',
|
||
'requiredDiamonds',
|
||
])
|
||
)
|
||
) {
|
||
next = level;
|
||
}
|
||
});
|
||
|
||
var currentRequired = current
|
||
? toNumber(
|
||
firstValue(current, ['required_diamonds', 'requiredDiamonds'])
|
||
)
|
||
: 0;
|
||
var nextRequired = next
|
||
? toNumber(
|
||
firstValue(next, ['required_diamonds', 'requiredDiamonds'])
|
||
)
|
||
: currentRequired;
|
||
var currentLevel = current
|
||
? toNumber(firstValue(current, ['level_no', 'levelNo', 'level']))
|
||
: 0;
|
||
var nextLevel = next
|
||
? toNumber(firstValue(next, ['level_no', 'levelNo', 'level']))
|
||
: currentLevel;
|
||
|
||
return {
|
||
current_level: currentLevel,
|
||
current_required_diamonds: currentRequired,
|
||
current_value: currentValue,
|
||
next_level: nextLevel,
|
||
next_required_diamonds: nextRequired,
|
||
needed_for_next_level: next
|
||
? Math.max(0, nextRequired - currentValue)
|
||
: 0,
|
||
};
|
||
}
|
||
|
||
function applyPolicy(payload) {
|
||
var data = unwrapData(payload);
|
||
if (!data || !data.found || !data.policy) {
|
||
// 线上策略未配置或旧网关返回空数据时,等级区域不能停在骨架态;展示空进度比持续 loading 更能说明真实状态。
|
||
state.level = fallbackLevelProgress();
|
||
state.policy = {
|
||
loading: false,
|
||
loaded: true,
|
||
found: false,
|
||
policy: null,
|
||
};
|
||
return;
|
||
}
|
||
// 新网关直接返回 level_progress;旧网关只有 policy/progress 时,H5 按同一套档位和当前钻石累计计算目标进度。
|
||
applyLevelProgress(
|
||
firstValue(data, ['level_progress', 'levelProgress']) ||
|
||
levelProgressFromPolicy(
|
||
data.policy,
|
||
firstValue(data, [
|
||
'progress',
|
||
'salary_progress',
|
||
'salaryProgress',
|
||
])
|
||
)
|
||
);
|
||
state.policy = {
|
||
loading: false,
|
||
loaded: true,
|
||
found: true,
|
||
policy: data.policy,
|
||
};
|
||
}
|
||
|
||
function toast(message) {
|
||
if (!message) return;
|
||
if (window.HyAppToast && window.HyAppToast.show) {
|
||
window.HyAppToast.show(message);
|
||
}
|
||
}
|
||
|
||
function loadPolicy() {
|
||
if (!window.HyAppAPI || !window.HyAppAPI.hostCenter) {
|
||
renderPolicy();
|
||
return Promise.resolve(false);
|
||
}
|
||
if (!hasAccessToken()) {
|
||
renderPolicy();
|
||
return Promise.resolve(false);
|
||
}
|
||
state.policy = Object.assign({}, state.policy, { loading: true });
|
||
renderPolicy();
|
||
return safeRequest(function () {
|
||
return window.HyAppAPI.hostCenter.platformPolicy();
|
||
}).then(function (payload) {
|
||
if (payload) {
|
||
applyPolicy(payload);
|
||
} else {
|
||
// 失败分支同样要结束主卡片 loading,否则页面会像线上截图一样一直显示紫色骨架条。
|
||
state.level = fallbackLevelProgress();
|
||
state.policy = {
|
||
loading: false,
|
||
loaded: false,
|
||
found: false,
|
||
policy: null,
|
||
};
|
||
toast(
|
||
t(
|
||
'host_center.load_failed',
|
||
'Load failed. Try again later.'
|
||
)
|
||
);
|
||
}
|
||
renderPolicy();
|
||
return !!payload;
|
||
});
|
||
}
|
||
|
||
function loadRoleInvitations() {
|
||
if (
|
||
!window.HyAppAPI ||
|
||
!window.HyAppAPI.roleInvitations ||
|
||
!window.HyAppAPI.roleInvitations.list ||
|
||
!hasAccessToken()
|
||
) {
|
||
state.messages = [];
|
||
renderMessages();
|
||
return Promise.resolve(false);
|
||
}
|
||
return window.HyAppAPI.roleInvitations
|
||
.list('pending', 50)
|
||
.then(function (payload) {
|
||
var items =
|
||
(payload && (payload.items || payload.invitations)) || [];
|
||
state.messages = items
|
||
.map(normalizeInvitationMessage)
|
||
.filter(function (item) {
|
||
return !!item.id;
|
||
});
|
||
renderMessages();
|
||
return true;
|
||
})
|
||
.catch(function (error) {
|
||
if (window.console && window.console.warn) {
|
||
window.console.warn('[host-center] messages failed', error);
|
||
}
|
||
state.messages = [];
|
||
renderMessages();
|
||
return false;
|
||
});
|
||
}
|
||
|
||
function processInvitationMessage(message, action) {
|
||
if (
|
||
!message ||
|
||
!message.id ||
|
||
!window.HyAppAPI ||
|
||
!window.HyAppAPI.roleInvitations ||
|
||
!window.HyAppAPI.roleInvitations.process
|
||
) {
|
||
return;
|
||
}
|
||
state.processingMessages[message.id] = true;
|
||
renderMessages();
|
||
window.HyAppAPI.roleInvitations
|
||
.process(message.id, {
|
||
command_id: commandID('role-invite-' + action, message.id),
|
||
action: action,
|
||
reason: 'h5_message_center',
|
||
})
|
||
.then(function () {
|
||
state.messages = state.messages.filter(function (item) {
|
||
return item.id !== message.id;
|
||
});
|
||
toast(
|
||
t(
|
||
action === 'accept'
|
||
? 'host_center.invitation_accepted'
|
||
: 'host_center.invitation_rejected',
|
||
action === 'accept'
|
||
? 'Invitation accepted.'
|
||
: 'Invitation rejected.'
|
||
)
|
||
);
|
||
renderMessages();
|
||
return loadRealData();
|
||
})
|
||
.catch(function (error) {
|
||
toast(
|
||
(error && error.message) ||
|
||
t(
|
||
'host_center.process_failed',
|
||
'Process failed. Try again later.'
|
||
)
|
||
);
|
||
})
|
||
.finally(function () {
|
||
delete state.processingMessages[message.id];
|
||
renderMessages();
|
||
});
|
||
}
|
||
|
||
function openMessages() {
|
||
var modal = $('messageModal');
|
||
if (!modal) return;
|
||
renderMessages();
|
||
modal.hidden = false;
|
||
modal.setAttribute('aria-hidden', 'false');
|
||
document.body.classList.add('modal-open');
|
||
}
|
||
|
||
function closeMessages() {
|
||
var modal = $('messageModal');
|
||
if (!modal) return;
|
||
modal.hidden = true;
|
||
modal.setAttribute('aria-hidden', 'true');
|
||
document.body.classList.remove('modal-open');
|
||
}
|
||
|
||
function requestPolicy() {
|
||
if (!window.HyAppAPI || !window.HyAppAPI.hostCenter) {
|
||
return Promise.resolve(null);
|
||
}
|
||
return safeRequest(function () {
|
||
return window.HyAppAPI.hostCenter.platformPolicy();
|
||
});
|
||
}
|
||
|
||
function loadRealData() {
|
||
if (!window.HyAppAPI) return Promise.resolve(false);
|
||
if (window.HyAppParams && window.HyAppParams.parse) {
|
||
window.HyAppParams.parse();
|
||
}
|
||
if (!hasAccessToken()) return Promise.resolve(false);
|
||
|
||
return Promise.all([
|
||
safeRequest(function () {
|
||
return window.HyAppAPI.user.me();
|
||
}),
|
||
safeRequest(function () {
|
||
return window.HyAppAPI.hostCenter.agency();
|
||
}),
|
||
safeRequest(function () {
|
||
return window.HyAppAPI.wallet.balances([HOST_SALARY_USD]);
|
||
}),
|
||
requestPolicy(),
|
||
]).then(function (results) {
|
||
applyProfile(results[0]);
|
||
applyAgency(results[1]);
|
||
applyWallet(results[2]);
|
||
if (results[3]) {
|
||
applyPolicy(results[3]);
|
||
} else {
|
||
applyPolicy(null);
|
||
}
|
||
render();
|
||
|
||
var hasAnyData = results.some(function (item) {
|
||
return !!item;
|
||
});
|
||
if (!hasAnyData) {
|
||
toast(
|
||
t(
|
||
'host_center.load_failed',
|
||
'Load failed. Try again later.'
|
||
)
|
||
);
|
||
}
|
||
return hasAnyData;
|
||
});
|
||
}
|
||
|
||
function openWithdrawExchange() {
|
||
var params = new URLSearchParams(window.location.search);
|
||
params.set('identity', 'HOST');
|
||
params.set('salaryType', HOST_SALARY_USD);
|
||
params.set('asset_type', HOST_SALARY_USD);
|
||
if (!params.has('mode')) params.set('mode', 'withdraw');
|
||
window.location.href =
|
||
'../withdraw-exchange/index.html?' + params.toString();
|
||
}
|
||
|
||
function openPolicy() {
|
||
var modal = $('policyModal');
|
||
if (!modal) return;
|
||
policyAutoScrollPending = true;
|
||
modal.hidden = false;
|
||
modal.setAttribute('aria-hidden', 'false');
|
||
document.body.classList.add('modal-open');
|
||
renderPolicy();
|
||
policyAutoScrollPending = true;
|
||
loadPolicy();
|
||
}
|
||
|
||
function closePolicy() {
|
||
var modal = $('policyModal');
|
||
if (!modal) return;
|
||
modal.hidden = true;
|
||
modal.setAttribute('aria-hidden', 'true');
|
||
document.body.classList.remove('modal-open');
|
||
policyAutoScrollPending = false;
|
||
}
|
||
|
||
function bindEvents() {
|
||
$('backButton').addEventListener('click', function () {
|
||
if (window.HyAppBridge) window.HyAppBridge.back();
|
||
});
|
||
|
||
$('incomeWithdrawExchangeButton').addEventListener(
|
||
'click',
|
||
openWithdrawExchange
|
||
);
|
||
|
||
document
|
||
.querySelectorAll('[data-action="policy"]')
|
||
.forEach(function (button) {
|
||
button.addEventListener('click', openPolicy);
|
||
});
|
||
document
|
||
.querySelectorAll('[data-action="messages"]')
|
||
.forEach(function (button) {
|
||
button.addEventListener('click', openMessages);
|
||
});
|
||
document
|
||
.querySelectorAll('[data-close-policy]')
|
||
.forEach(function (button) {
|
||
button.addEventListener('click', closePolicy);
|
||
});
|
||
$('messageBackdrop').addEventListener('click', closeMessages);
|
||
$('messageCloseButton').addEventListener('click', closeMessages);
|
||
document.addEventListener('keydown', function (event) {
|
||
if (event.key === 'Escape') {
|
||
closePolicy();
|
||
closeMessages();
|
||
}
|
||
});
|
||
}
|
||
|
||
var initialized = false;
|
||
|
||
function init() {
|
||
if (initialized) return;
|
||
initialized = true;
|
||
render();
|
||
bindEvents();
|
||
loadRealData().then(function (loaded) {
|
||
loadRoleInvitations();
|
||
if (window.HyAppBridge) {
|
||
window.HyAppBridge.ready({
|
||
page: 'host-center',
|
||
mock: false,
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
} else {
|
||
init();
|
||
}
|
||
|
||
window.addEventListener('hyapp:i18n-ready', function () {
|
||
render();
|
||
});
|
||
|
||
window.addEventListener('hyapp:role-invitation-processed', function () {
|
||
loadRealData();
|
||
});
|
||
})();
|