增加unhost h5

This commit is contained in:
zhx 2026-05-29 19:55:17 +08:00
parent 04f2260713
commit 691d5aba3d
24 changed files with 863 additions and 308 deletions

View File

@ -1,5 +1,5 @@
var test_api = 'https://api-test.global-interaction.com/';
var local_api = 'http://localhost:13000/';
var local_api = 'http://127.0.0.1:13000/';
var default_api = 'https://api.global-interaction.com/';
(function () {
@ -115,14 +115,34 @@ var default_api = 'https://api.global-interaction.com/';
if (shouldPersist()) window.localStorage.removeItem(APP_CODE_KEY);
}
function errorMessageForResponse(response, payload) {
if (response.status === 409) return 'Duplicate request.';
return payload.message || response.statusText || 'request_failed';
}
function notifyHTTPError(response, message) {
if (response.status !== 409 || !message) return;
if (window.HyAppToast && window.HyAppToast.show) {
window.HyAppToast.show(message);
return;
}
window.dispatchEvent(
new CustomEvent('hyapp:toast', {
detail: { message: message },
})
);
}
function parseResponse(response) {
return response.text().then(function (text) {
var payload = text ? JSON.parse(text) : {};
if (!response.ok) {
var httpError = new Error(
payload.message || response.statusText || 'request_failed'
);
var message = errorMessageForResponse(response, payload);
notifyHTTPError(response, message);
var httpError = new Error(message);
httpError.status = response.status;
httpError.code = payload.code;
httpError.request_id = payload.request_id;
httpError.payload = payload;
throw httpError;
}

View File

@ -2,6 +2,11 @@
var STORAGE_KEY = 'hyapp_h5_lang';
var DEFAULT_LANG = 'en';
var SUPPORTED = ['en', 'ar', 'tr', 'es'];
var scriptURL =
document.currentScript && document.currentScript.src
? document.currentScript.src
: '../common/i18n.js';
var localeBaseURL = new URL('locales/', scriptURL).toString();
var messages = {};
var currentLang = '';
@ -33,7 +38,7 @@
function load(lang) {
var normalized = normalizeLang(lang) || DEFAULT_LANG;
return window
.fetch('../common/locales/' + normalized + '.json', {
.fetch(localeBaseURL + normalized + '.json', {
cache: 'no-store',
})
.then(function (response) {

View File

@ -12,11 +12,19 @@
"host_center.search": "بحث",
"host_center.agency": "الوكالة",
"host_center.apply": "تقديم",
"host_center.agency_list": "قائمة الوكالات",
"host_center.search_required": "أدخل رقم الوكالة.",
"host_center.searching": "جار البحث...",
"host_center.no_agency": "لم يتم العثور على وكالة في منطقتك.",
"host_center.loading_agencies": "جار تحميل الوكالات...",
"host_center.no_agency": "لم يتم العثور على وكالة في بلدك.",
"host_center.search_failed": "فشل البحث. حاول لاحقا.",
"host_center.apply_failed": "فشل إرسال الطلب. حاول لاحقا.",
"host_center.apply_success": "تم إرسال الطلب.",
"host_center.applying": "جار الإرسال..."
"host_center.applying": "جار الإرسال...",
"host_center.host_count_one": "مضيف واحد",
"host_center.host_count_many": "{count} مضيفين",
"host_center.join": "انضم",
"host_center.cancel": "إلغاء",
"host_center.join_confirm_title": "انضم إلى وكالة",
"host_center.join_confirm_message": "هل أنت متأكد أنك تريد الانضمام إلى {agency}؟"
}

View File

@ -12,11 +12,19 @@
"host_center.search": "Search",
"host_center.agency": "Agency",
"host_center.apply": "Apply",
"host_center.agency_list": "Agency list",
"host_center.search_required": "Enter an agency ID.",
"host_center.searching": "Searching...",
"host_center.no_agency": "No agency found in your region.",
"host_center.loading_agencies": "Loading agencies...",
"host_center.no_agency": "No agency found in your country.",
"host_center.search_failed": "Search failed. Try again later.",
"host_center.apply_failed": "Application failed. Try again later.",
"host_center.apply_success": "Application submitted.",
"host_center.applying": "Submitting..."
"host_center.applying": "Submitting...",
"host_center.host_count_one": "1 host",
"host_center.host_count_many": "{count} hosts",
"host_center.join": "Join",
"host_center.cancel": "Cancel",
"host_center.join_confirm_title": "Join Agency",
"host_center.join_confirm_message": "Are you sure you want to join {agency}?"
}

View File

@ -12,11 +12,19 @@
"host_center.search": "Buscar",
"host_center.agency": "Agencia",
"host_center.apply": "Solicitar",
"host_center.agency_list": "Lista de agencias",
"host_center.search_required": "Ingresa un ID de agencia.",
"host_center.searching": "Buscando...",
"host_center.no_agency": "No se encontró ninguna agencia en tu región.",
"host_center.loading_agencies": "Cargando agencias...",
"host_center.no_agency": "No se encontró ninguna agencia en tu país.",
"host_center.search_failed": "La búsqueda falló. Inténtalo más tarde.",
"host_center.apply_failed": "La solicitud falló. Inténtalo más tarde.",
"host_center.apply_success": "Solicitud enviada.",
"host_center.applying": "Enviando..."
"host_center.applying": "Enviando...",
"host_center.host_count_one": "1 host",
"host_center.host_count_many": "{count} hosts",
"host_center.join": "Unirse",
"host_center.cancel": "Cancelar",
"host_center.join_confirm_title": "Unirse a una agencia",
"host_center.join_confirm_message": "¿Seguro que quieres unirte a {agency}?"
}

View File

@ -12,11 +12,19 @@
"host_center.search": "Cari",
"host_center.agency": "Agency",
"host_center.apply": "Ajukan",
"host_center.agency_list": "Daftar agency",
"host_center.search_required": "Masukkan ID agency.",
"host_center.searching": "Mencari...",
"host_center.no_agency": "Agency tidak ditemukan di wilayah Anda.",
"host_center.loading_agencies": "Memuat agency...",
"host_center.no_agency": "Agency tidak ditemukan di negara Anda.",
"host_center.search_failed": "Pencarian gagal. Coba lagi nanti.",
"host_center.apply_failed": "Pengajuan gagal. Coba lagi nanti.",
"host_center.apply_success": "Pengajuan terkirim.",
"host_center.applying": "Mengirim..."
"host_center.applying": "Mengirim...",
"host_center.host_count_one": "1 host",
"host_center.host_count_many": "{count} host",
"host_center.join": "Gabung",
"host_center.cancel": "Batal",
"host_center.join_confirm_title": "Gabung Agency",
"host_center.join_confirm_message": "Apakah Anda yakin ingin bergabung dengan {agency}?"
}

View File

@ -12,11 +12,19 @@
"host_center.search": "Ara",
"host_center.agency": "Ajans",
"host_center.apply": "Başvur",
"host_center.agency_list": "Ajans listesi",
"host_center.search_required": "Ajans ID girin.",
"host_center.searching": "Aranıyor...",
"host_center.no_agency": "Bölgenizde ajans bulunamadı.",
"host_center.loading_agencies": "Ajanslar yükleniyor...",
"host_center.no_agency": "Ülkenizde ajans bulunamadı.",
"host_center.search_failed": "Arama başarısız. Daha sonra deneyin.",
"host_center.apply_failed": "Başvuru başarısız. Daha sonra deneyin.",
"host_center.apply_success": "Başvuru gönderildi.",
"host_center.applying": "Gönderiliyor..."
"host_center.applying": "Gönderiliyor...",
"host_center.host_count_one": "1 host",
"host_center.host_count_many": "{count} host",
"host_center.join": "Katıl",
"host_center.cancel": "İptal",
"host_center.join_confirm_title": "Ajansa Katıl",
"host_center.join_confirm_message": "{agency} için katılmak istediğinizden emin misiniz?"
}

View File

@ -12,11 +12,19 @@
"host_center.search": "搜索",
"host_center.agency": "公会",
"host_center.apply": "申请",
"host_center.agency_list": "公会列表",
"host_center.search_required": "请输入公会 ID。",
"host_center.searching": "搜索中...",
"host_center.no_agency": "未找到同区域公会。",
"host_center.loading_agencies": "公会加载中...",
"host_center.no_agency": "未找到同国家公会。",
"host_center.search_failed": "搜索失败,请稍后重试。",
"host_center.apply_failed": "申请失败,请稍后重试。",
"host_center.apply_success": "申请已提交。",
"host_center.applying": "提交中..."
"host_center.applying": "提交中...",
"host_center.host_count_one": "1 位主播",
"host_center.host_count_many": "{count} 位主播",
"host_center.join": "加入",
"host_center.cancel": "取消",
"host_center.join_confirm_title": "加入公会",
"host_center.join_confirm_message": "确定要加入 {agency} 吗?"
}

View File

@ -25,9 +25,18 @@
return '';
}
function normalizeToken(value) {
var token = String(value || '').trim();
if (!token) return '';
var match = token.match(
/[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/
);
return match ? match[0] : token;
}
function parse() {
var params = query();
var token = first(params, TOKEN_KEYS);
var token = normalizeToken(first(params, TOKEN_KEYS));
var appCode =
first(params, APP_CODE_KEYS) ||
window.localStorage.getItem('hyapp_app_code') ||

View File

@ -13,8 +13,10 @@
--hy-theme-success: #31c6a8;
--hy-theme-danger: #d95d72;
--hy-theme-shadow: 0 10px 24px rgba(132, 90, 212, 0.075);
--hy-theme-button: #c5a6f6;
--hy-theme-button-shadow: 0 8px 18px rgba(132, 90, 212, 0.11);
--hy-theme-button: #7d57c7;
--hy-theme-button-disabled: #c5a6f6;
--hy-theme-button-shadow: 0 8px 18px rgba(125, 87, 199, 0.18);
--hy-theme-button-disabled-shadow: none;
--hy-theme-hero: linear-gradient(
151deg,
#f0e6ff 0%,

87
common/toast.js Normal file
View File

@ -0,0 +1,87 @@
(function () {
var STYLE_ID = 'hy-global-toast-style';
var TOAST_ID = 'hy-global-toast';
var timer = 0;
function ensureStyle() {
if (document.getElementById(STYLE_ID)) return;
var style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = [
'.hy-global-toast{',
'position:fixed;',
'left:50%;',
'top:max(84px,calc(env(safe-area-inset-top) + 24px));',
'z-index:10000;',
'max-width:min(320px,calc(100vw - 48px));',
'padding:10px 14px;',
'border-radius:8px;',
'background:rgba(0,0,0,.82);',
'color:#fff;',
'font:800 14px/1.35 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;',
'text-align:center;',
'box-shadow:0 10px 28px rgba(0,0,0,.22);',
'opacity:0;',
'pointer-events:none;',
'transform:translate(-50%,-8px);',
'transition:opacity .18s ease,transform .18s ease;',
'}',
'.hy-global-toast.is-visible{',
'opacity:1;',
'transform:translate(-50%,0);',
'}',
].join('');
document.head.appendChild(style);
}
function ensureToast() {
var node = document.getElementById(TOAST_ID);
if (node) return node;
ensureStyle();
node = document.createElement('div');
node.id = TOAST_ID;
node.className = 'hy-global-toast';
node.setAttribute('role', 'status');
node.setAttribute('aria-live', 'polite');
document.body.appendChild(node);
return node;
}
function hide() {
var node = document.getElementById(TOAST_ID);
if (node) node.classList.remove('is-visible');
}
function show(message, options) {
var text = String(message || '').trim();
if (!text) return;
if (!document.body) {
document.addEventListener(
'DOMContentLoaded',
function () {
show(text, options);
},
{ once: true }
);
return;
}
var node = ensureToast();
var duration = Number((options && options.duration) || 1800);
node.textContent = text;
window.requestAnimationFrame(function () {
node.classList.add('is-visible');
});
window.clearTimeout(timer);
timer = window.setTimeout(hide, duration);
}
window.HyAppToast = {
show: show,
hide: hide,
};
window.addEventListener('hyapp:toast', function (event) {
var detail = event.detail || {};
show(detail.message || detail.text, detail);
});
})();

View File

View File

View File

View File

@ -1,180 +0,0 @@
(function () {
var selectedAgency = null;
function $(id) {
return document.getElementById(id);
}
function t(key, fallback) {
return window.HyAppI18n && window.HyAppI18n.t
? window.HyAppI18n.t(key, fallback)
: fallback || key;
}
function setStatus(key, kind) {
var node = $('joinAgencyStatus');
if (!node) return;
if (!key) {
node.hidden = true;
node.textContent = '';
node.className = 'join-agency-status';
return;
}
node.hidden = false;
node.className = 'join-agency-status' + (kind ? ' is-' + kind : '');
node.textContent = t(key);
}
function setSearching(searching) {
var input = $('agencySearchInput');
var button = $('agencySearchButton');
if (input) input.disabled = searching;
if (button) {
button.disabled = searching;
button.textContent = searching
? t('host_center.searching', 'Searching...')
: t('host_center.search', 'Search');
}
}
function setApplying(applying) {
var button = $('joinAgencyApplyButton');
if (!button) return;
button.disabled = applying || !selectedAgency;
button.textContent = applying
? t('host_center.applying', 'Submitting...')
: t('host_center.apply', 'Apply');
}
function agencyID(agency) {
return String((agency && (agency.agency_id || agency.agencyId)) || '');
}
function agencyName(agency) {
return String((agency && agency.name) || '');
}
function renderAgency(agency) {
selectedAgency = agency || null;
var result = $('joinAgencyResult');
if (!result) return;
if (!selectedAgency) {
result.hidden = true;
setApplying(false);
return;
}
var name =
agencyName(selectedAgency) || t('host_center.agency', 'Agency');
result.hidden = false;
$('agencyName').textContent = name;
$('agencyAccount').textContent = 'ID: ' + agencyID(selectedAgency);
$('agencyInitial').textContent =
name.trim().charAt(0).toUpperCase() || 'A';
setApplying(false);
}
function searchAgency(event) {
if (event) event.preventDefault();
if (!window.HyAppAPI || !window.HyAppAPI.host) return;
var input = $('agencySearchInput');
var shortID = input ? input.value.trim() : '';
selectedAgency = null;
renderAgency(null);
if (!shortID) {
setStatus('host_center.search_required', 'error');
return;
}
setStatus('host_center.searching');
setSearching(true);
window.HyAppAPI.host
.searchAgencies(shortID, 20)
.then(function (data) {
var items = (data && (data.items || data.agencies)) || [];
if (!items.length) {
setStatus('host_center.no_agency', 'error');
return;
}
setStatus('');
renderAgency(items[0]);
})
.catch(function () {
setStatus('host_center.search_failed', 'error');
})
.finally(function () {
setSearching(false);
});
}
function applyAgency() {
if (!selectedAgency || !window.HyAppAPI || !window.HyAppAPI.host)
return;
setStatus('host_center.applying');
setApplying(true);
window.HyAppAPI.host
.applyToAgency({
agency_id: agencyID(selectedAgency),
command_id:
'host_apply_' +
Date.now() +
'_' +
Math.random().toString(16).slice(2),
})
.then(function () {
setStatus('host_center.apply_success', 'success');
})
.catch(function () {
setStatus('host_center.apply_failed', 'error');
})
.finally(function () {
setApplying(false);
});
}
function renderUser(current) {
var user = current && current.user;
var profile = user && (user.profile || user.user || user);
if (!profile) return;
var name = profile.username || profile.name || profile.nickname || '';
var uid =
profile.display_user_id ||
profile.displayUserId ||
profile.user_id ||
profile.userId ||
'';
if (name && $('profileName')) {
$('profileName').removeAttribute('data-i18n');
$('profileName').textContent = name;
}
if (uid && $('profileUID')) {
$('profileUID').removeAttribute('data-i18n');
$('profileUID').textContent = 'UID: ' + uid;
}
}
function init() {
if (window.HyAppParams) {
window.HyAppParams.parse();
window.HyAppParams.loadUser()
.then(renderUser)
.catch(function () {});
}
var form = $('joinAgencyForm');
if (form) form.addEventListener('submit', searchAgency);
var applyButton = $('joinAgencyApplyButton');
if (applyButton) applyButton.addEventListener('click', applyAgency);
var backButton = $('backButton');
if (backButton) {
backButton.addEventListener('click', function () {
if (window.HyAppBridge) window.HyAppBridge.back();
});
}
setApplying(false);
}
window.addEventListener('hyapp:i18n-ready', function () {
setSearching(false);
setApplying(false);
});
document.addEventListener('DOMContentLoaded', init);
})();

View File

View File

@ -7,7 +7,7 @@
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<title>Host Center</title>
<link rel="stylesheet" href="../common/theme.css" />
<link rel="stylesheet" href="../../common/theme.css" />
<link rel="stylesheet" href="./style.css" />
</head>
@ -78,30 +78,6 @@
</div>
</section>
<section class="card join-agency-card" id="joinAgencyCard">
<div class="join-agency-copy">
<div
class="join-agency-title"
data-i18n="host_center.join_title"
>
Join Agency
</div>
<div
class="join-agency-text"
data-i18n="host_center.join_text"
>
Search for an agency ID and submit your application.
</div>
</div>
<button
class="join-agency-action"
type="button"
data-i18n="host_center.join_now"
>
Join now
</button>
</section>
<section
class="join-agency-dialog-static"
aria-label="Join Agency"
@ -147,54 +123,79 @@
></div>
<section
class="join-agency-modal-card"
id="joinAgencyResult"
hidden
class="agency-list"
id="agencyList"
aria-label="Agency list"
data-i18n-aria="host_center.agency_list"
>
<div
class="join-agency-section-title"
data-i18n="host_center.agency"
class="agency-list-empty"
id="agencyListEmpty"
data-i18n="host_center.loading_agencies"
>
Agency
Loading agencies...
</div>
<div class="join-agency-profile-row">
<div class="join-agency-avatar">
<span id="agencyInitial">A</span>
</div>
<div class="join-agency-profile-copy">
<div
class="join-agency-profile-name"
id="agencyName"
>
Agency Name
</div>
<div
class="join-agency-profile-account"
id="agencyAccount"
>
ID: 87654321
</div>
</div>
</div>
<button
class="join-agency-apply-button"
id="joinAgencyApplyButton"
type="button"
data-i18n="host_center.apply"
>
Apply
</button>
</section>
</div>
</section>
</main>
<div class="home-indicator" aria-hidden="true"></div>
<div
class="join-agency-modal"
id="joinConfirmModal"
hidden
aria-hidden="true"
>
<button
class="join-agency-backdrop"
id="joinConfirmBackdrop"
type="button"
aria-label="Cancel"
data-i18n-aria="host_center.cancel"
></button>
<section
class="join-confirm-dialog"
role="dialog"
aria-modal="true"
aria-labelledby="joinConfirmTitle"
>
<h2
id="joinConfirmTitle"
data-i18n="host_center.join_confirm_title"
>
Join Agency
</h2>
<p id="joinConfirmMessage">
Are you sure you want to join this agency?
</p>
<div class="join-confirm-actions">
<button
class="join-confirm-cancel"
id="joinConfirmCancelButton"
type="button"
data-i18n="host_center.cancel"
>
Cancel
</button>
<button
class="join-confirm-submit"
id="joinConfirmSubmitButton"
type="button"
data-i18n="host_center.join"
>
Join
</button>
</div>
</section>
</div>
</div>
<script src="../common/api.js"></script>
<script src="../common/params.js"></script>
<script src="../common/jsbridge.js"></script>
<script src="../common/i18n.js"></script>
<script src="../../common/api.js"></script>
<script src="../../common/toast.js"></script>
<script src="../../common/params.js"></script>
<script src="../../common/jsbridge.js"></script>
<script src="../../common/i18n.js"></script>
<script src="./script.js"></script>
</body>
</html>

View File

@ -0,0 +1,337 @@
(function () {
var selectedAgency = null;
var agencies = [];
var isSearching = false;
var isApplying = false;
function $(id) {
return document.getElementById(id);
}
function t(key, fallback) {
return window.HyAppI18n && window.HyAppI18n.t
? window.HyAppI18n.t(key, fallback)
: fallback || key;
}
function setStatus(key, kind) {
var node = $('joinAgencyStatus');
if (!node) return;
if (!key) {
node.hidden = true;
node.textContent = '';
node.className = 'join-agency-status';
return;
}
node.hidden = false;
node.className = 'join-agency-status' + (kind ? ' is-' + kind : '');
node.textContent = t(key);
}
function setStatusMessage(message, kind) {
var node = $('joinAgencyStatus');
if (!node) return;
if (!message) {
setStatus('');
return;
}
node.hidden = false;
node.className = 'join-agency-status' + (kind ? ' is-' + kind : '');
node.textContent = message;
}
function setSearching(searching) {
isSearching = searching;
var input = $('agencySearchInput');
var button = $('agencySearchButton');
if (input) input.disabled = searching;
if (button) {
button.disabled = searching;
button.textContent = searching
? t('host_center.searching', 'Searching...')
: t('host_center.search', 'Search');
}
}
function setConfirmApplying(applying) {
isApplying = applying;
var submit = $('joinConfirmSubmitButton');
var cancel = $('joinConfirmCancelButton');
var backdrop = $('joinConfirmBackdrop');
if (submit) {
submit.disabled = applying || !selectedAgency;
submit.textContent = applying
? t('host_center.applying', 'Submitting...')
: t('host_center.join', 'Join');
}
if (cancel) cancel.disabled = applying;
if (backdrop) backdrop.disabled = applying;
document
.querySelectorAll('.agency-list-join')
.forEach(function (button) {
button.disabled = applying;
});
}
function agencyID(agency) {
return String((agency && (agency.agency_id || agency.agencyId)) || '');
}
function agencyName(agency) {
return String((agency && agency.name) || '');
}
function agencyAvatar(agency) {
return String(
(agency &&
(agency.avatar || agency.owner_avatar || agency.ownerAvatar)) ||
''
);
}
function agencyHostCount(agency) {
var value =
agency &&
(agency.host_count ||
agency.hostCount ||
agency.active_host_count ||
agency.activeHostCount);
var count = Number(value || 0);
return Number.isFinite(count) && count > 0 ? count : 0;
}
function hostCountText(count) {
if (count === 1) return t('host_center.host_count_one', '1 host');
return t('host_center.host_count_many', '{count} hosts').replace(
'{count}',
String(count)
);
}
function appendFallbackAvatar(avatar, name) {
var span = document.createElement('span');
span.textContent = name.trim().charAt(0).toUpperCase() || 'A';
avatar.appendChild(span);
}
function createAgencyAvatar(agency) {
var avatar = document.createElement('div');
var name = agencyName(agency) || t('host_center.agency', 'Agency');
avatar.className = 'agency-list-avatar';
if (!agencyAvatar(agency)) {
appendFallbackAvatar(avatar, name);
return avatar;
}
var img = document.createElement('img');
img.src = agencyAvatar(agency);
img.alt = '';
img.addEventListener('error', function () {
avatar.textContent = '';
appendFallbackAvatar(avatar, name);
});
avatar.appendChild(img);
return avatar;
}
function createAgencyRow(agency) {
var row = document.createElement('div');
var name = agencyName(agency) || t('host_center.agency', 'Agency');
row.className = 'agency-list-item';
var copy = document.createElement('div');
copy.className = 'agency-list-copy';
var title = document.createElement('div');
title.className = 'agency-list-name';
title.textContent = name;
var meta = document.createElement('div');
meta.className = 'agency-list-meta';
meta.textContent = hostCountText(agencyHostCount(agency));
var join = document.createElement('button');
join.className = 'agency-list-join';
join.type = 'button';
join.textContent = t('host_center.join', 'Join');
join.disabled = isApplying;
join.addEventListener('click', function () {
openJoinConfirm(agency);
});
copy.appendChild(title);
copy.appendChild(meta);
row.appendChild(createAgencyAvatar(agency));
row.appendChild(copy);
row.appendChild(join);
return row;
}
function renderAgencyList(items) {
var list = $('agencyList');
if (!list) return;
list.textContent = '';
agencies = items || [];
if (!agencies.length) {
var empty = document.createElement('div');
empty.className = 'agency-list-empty';
empty.id = 'agencyListEmpty';
empty.textContent = t(
'host_center.no_agency',
'No agency found in your country.'
);
list.appendChild(empty);
return;
}
agencies.forEach(function (agency) {
list.appendChild(createAgencyRow(agency));
});
}
function updateConfirmMessage() {
var message = $('joinConfirmMessage');
if (!message) return;
var name =
agencyName(selectedAgency) || t('host_center.agency', 'Agency');
message.textContent = t(
'host_center.join_confirm_message',
'Are you sure you want to join {agency}?'
).replace('{agency}', name);
}
function openJoinConfirm(agency) {
selectedAgency = agency || null;
var modal = $('joinConfirmModal');
if (!modal || !selectedAgency) return;
updateConfirmMessage();
modal.hidden = false;
modal.setAttribute('aria-hidden', 'false');
document.body.classList.add('daily-modal-open');
setConfirmApplying(false);
}
function closeJoinConfirm(force) {
if (isApplying && !force) return;
var modal = $('joinConfirmModal');
if (!modal) return;
modal.hidden = true;
modal.setAttribute('aria-hidden', 'true');
document.body.classList.remove('daily-modal-open');
selectedAgency = null;
setConfirmApplying(false);
}
function searchAgency(event) {
if (event) event.preventDefault();
if (!window.HyAppAPI || !window.HyAppAPI.host) return;
var input = $('agencySearchInput');
var shortID = input ? input.value.trim() : '';
setStatus('host_center.loading_agencies');
setSearching(true);
window.HyAppAPI.host
.searchAgencies(shortID, 100)
.then(function (data) {
var items = (data && (data.items || data.agencies)) || [];
setStatus('');
renderAgencyList(items);
})
.catch(function () {
renderAgencyList([]);
setStatus('host_center.search_failed', 'error');
})
.finally(function () {
setSearching(false);
});
}
function applyAgency() {
if (!selectedAgency || !window.HyAppAPI || !window.HyAppAPI.host)
return;
setStatus('host_center.applying');
setConfirmApplying(true);
window.HyAppAPI.host
.applyToAgency({
agency_id: agencyID(selectedAgency),
command_id:
'host_apply_' +
Date.now() +
'_' +
Math.random().toString(16).slice(2),
})
.then(function () {
closeJoinConfirm(true);
setStatus('host_center.apply_success', 'success');
})
.catch(function (error) {
if (error && error.status === 409) {
setStatus('');
return;
}
setStatusMessage(
(error && error.message) ||
t(
'host_center.apply_failed',
'Application failed. Try again later.'
),
'error'
);
})
.finally(function () {
setConfirmApplying(false);
});
}
function renderUser(current) {
var user = current && current.user;
var profile = user && (user.profile || user.user || user);
if (!profile) return;
var name = profile.username || profile.name || profile.nickname || '';
var uid =
profile.display_user_id ||
profile.displayUserId ||
profile.user_id ||
profile.userId ||
'';
if (name && $('profileName')) {
$('profileName').removeAttribute('data-i18n');
$('profileName').textContent = name;
}
if (uid && $('profileUID')) {
$('profileUID').removeAttribute('data-i18n');
$('profileUID').textContent = 'UID: ' + uid;
}
}
function init() {
if (window.HyAppParams) {
window.HyAppParams.parse();
window.HyAppParams.loadUser()
.then(renderUser)
.catch(function () {});
}
var form = $('joinAgencyForm');
if (form) form.addEventListener('submit', searchAgency);
var submit = $('joinConfirmSubmitButton');
if (submit) submit.addEventListener('click', applyAgency);
var cancel = $('joinConfirmCancelButton');
if (cancel) cancel.addEventListener('click', closeJoinConfirm);
var backdrop = $('joinConfirmBackdrop');
if (backdrop) backdrop.addEventListener('click', closeJoinConfirm);
var backButton = $('backButton');
if (backButton) {
backButton.addEventListener('click', function () {
if (window.HyAppBridge) window.HyAppBridge.back();
});
}
searchAgency();
}
window.addEventListener('hyapp:i18n-ready', function () {
var searching = isSearching;
setSearching(searching);
if (!searching) renderAgencyList(agencies);
if (selectedAgency) updateConfirmMessage();
setConfirmApplying(isApplying);
});
document.addEventListener('DOMContentLoaded', init);
})();

View File

@ -289,14 +289,14 @@ a {
}
.language-menu {
position: absolute;
z-index: 20;
top: calc(100% + 7px);
right: 0;
display: grid;
gap: 2px;
min-width: 72px;
padding: 5px;
position: absolute;
z-index: 20;
top: calc(100% + 7px);
right: 0;
display: grid;
gap: 2px;
min-width: 72px;
padding: 5px;
border: 1px solid rgba(35, 40, 46, 0.1);
border-radius: 8px;
background: rgba(255, 255, 255, 0.96);
@ -304,12 +304,12 @@ a {
}
.language-menu button {
min-height: 34px;
border-radius: 6px;
color: var(--hy-theme-text, #282333);
font-size: 12px;
font-weight: 900;
touch-action: manipulation;
min-height: 34px;
border-radius: 6px;
color: var(--hy-theme-text, #282333);
font-size: 12px;
font-weight: 900;
touch-action: manipulation;
}
.language-menu button.is-active {
@ -444,13 +444,13 @@ a {
flex: 0 0 auto;
padding: 0 15px;
border-radius: 8px;
background: var(--hy-theme-button, #c5a6f6);
background: var(--hy-theme-button, #7d57c7);
color: #ffffff;
font-size: 13px;
font-weight: 900;
box-shadow: var(
--hy-theme-button-shadow,
0 8px 18px rgba(132, 90, 212, 0.11)
0 8px 18px rgba(125, 87, 199, 0.18)
);
white-space: nowrap;
}
@ -1145,17 +1145,24 @@ a {
.join-agency-search-button {
min-width: 86px;
padding: 0 16px;
background: var(--hy-theme-button, #c5a6f6);
background: var(--hy-theme-button, #7d57c7);
color: #fff;
box-shadow: var(
--hy-theme-button-shadow,
0 8px 18px rgba(132, 90, 212, 0.11)
0 8px 18px rgba(125, 87, 199, 0.18)
);
}
.join-agency-search-button:disabled,
.join-agency-apply-button:disabled,
.join-agency-apply-button:disabled {
background: var(--hy-theme-button-disabled, #c5a6f6);
box-shadow: var(--hy-theme-button-disabled-shadow, none);
cursor: not-allowed;
opacity: 1;
}
.join-agency-cancel-button:disabled {
cursor: not-allowed;
opacity: 0.58;
}
@ -1178,6 +1185,103 @@ a {
color: var(--hy-theme-success, #31c6a8);
}
.agency-list {
display: grid;
gap: 0;
border-top: 1px solid var(--hy-theme-line, #eee7f8);
}
.agency-list-empty {
padding: 18px 0 4px;
color: var(--hy-theme-muted, #8d879a);
font-size: 13px;
font-weight: 800;
line-height: 1.35;
text-align: center;
}
.agency-list-item {
display: grid;
grid-template-columns: 52px minmax(0, 1fr) auto;
gap: 12px;
align-items: center;
min-height: 78px;
padding: 13px 0;
border-bottom: 1px solid var(--hy-theme-line, #eee7f8);
}
.agency-list-avatar {
display: flex;
align-items: center;
justify-content: center;
width: 52px;
height: 52px;
overflow: hidden;
border-radius: 50%;
background: var(--hy-theme-primary, #dbc8ff);
color: var(--hy-theme-primary-deep, #6d45bd);
font-size: 20px;
font-weight: 950;
}
.agency-list-avatar img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.agency-list-copy {
min-width: 0;
}
.agency-list-name {
overflow: hidden;
color: var(--hy-theme-text, #282333);
font-size: 16px;
font-weight: 900;
line-height: 1.2;
text-overflow: ellipsis;
white-space: nowrap;
}
.agency-list-meta {
margin-top: 5px;
overflow: hidden;
color: var(--hy-theme-muted, #8d879a);
font-size: 12px;
font-weight: 800;
line-height: 1.2;
text-overflow: ellipsis;
white-space: nowrap;
}
.agency-list-join {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 64px;
min-height: 34px;
padding: 0 14px;
border-radius: 8px;
background: var(--hy-theme-button, #7d57c7);
color: #fff;
font-size: 13px;
font-weight: 900;
box-shadow: var(
--hy-theme-button-shadow,
0 8px 18px rgba(125, 87, 199, 0.18)
);
white-space: nowrap;
}
.agency-list-join:disabled {
background: var(--hy-theme-button-disabled, #c5a6f6);
box-shadow: var(--hy-theme-button-disabled-shadow, none);
cursor: not-allowed;
opacity: 1;
}
.join-agency-modal-card {
display: grid;
gap: 14px;
@ -1244,11 +1348,11 @@ a {
.join-agency-apply-button {
width: 100%;
background: var(--hy-theme-button, #c5a6f6);
background: var(--hy-theme-button, #7d57c7);
color: #fff;
box-shadow: var(
--hy-theme-button-shadow,
0 8px 18px rgba(132, 90, 212, 0.11)
0 8px 18px rgba(125, 87, 199, 0.18)
);
}
@ -1281,6 +1385,80 @@ a {
line-height: 1.35;
}
.join-confirm-dialog {
position: relative;
display: grid;
gap: 14px;
width: min(342px, 100%);
padding: 20px 18px 18px;
border-radius: 14px;
background: #fff;
box-shadow: 0 18px 44px rgba(23, 31, 42, 0.2);
}
.join-confirm-dialog h2 {
margin: 0;
color: var(--hy-theme-text, #282333);
font-size: 18px;
font-weight: 950;
line-height: 1.2;
text-align: center;
}
.join-confirm-dialog p {
margin: 0;
color: var(--hy-theme-muted, #8d879a);
font-size: 14px;
font-weight: 800;
line-height: 1.35;
text-align: center;
}
.join-confirm-actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 4px;
}
.join-confirm-cancel,
.join-confirm-submit {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 42px;
border-radius: 8px;
font-size: 14px;
font-weight: 900;
}
.join-confirm-cancel {
border: 1px solid var(--hy-theme-line, #eee7f8);
background: #fff;
color: var(--hy-theme-text, #282333);
}
.join-confirm-submit {
background: var(--hy-theme-button, #7d57c7);
color: #fff;
box-shadow: var(
--hy-theme-button-shadow,
0 8px 18px rgba(125, 87, 199, 0.18)
);
}
.join-confirm-submit:disabled {
background: var(--hy-theme-button-disabled, #c5a6f6);
box-shadow: var(--hy-theme-button-disabled-shadow, none);
cursor: not-allowed;
opacity: 1;
}
.join-confirm-cancel:disabled {
cursor: not-allowed;
opacity: 0.58;
}
.daily-modal-open {
overflow: hidden;
}
@ -1347,6 +1525,7 @@ a {
}
[dir='rtl'] .join-agency-profile-copy,
[dir='rtl'] .agency-list-copy,
[dir='rtl'] .join-agency-field {
text-align: right;
}

View File

@ -155,6 +155,7 @@
<div id="toast" class="toast"></div>
<script src="../common/api.js"></script>
<script src="../common/toast.js"></script>
<script src="../common/params.js"></script>
<script src="../common/jsbridge.js"></script>
<script>

View File

@ -392,12 +392,14 @@
height: 14px;
}
.my-rank {
position: absolute;
left: 0;
position: fixed;
left: 50%;
bottom: 0;
z-index: 4;
width: 375px;
height: 80px;
z-index: 40;
width: min(375px, 100vw);
height: calc(80px + env(safe-area-inset-bottom));
padding-bottom: env(safe-area-inset-bottom);
transform: translateX(-50%);
border: 1px solid #fcd98f;
border-bottom: 0;
border-radius: 10px 10px 0 0;
@ -409,6 +411,9 @@
box-shadow: inset 0 0 4px #000;
overflow: hidden;
}
.my-rank[hidden] {
display: none;
}
.my-rank::before {
content: '';
position: absolute;
@ -447,6 +452,13 @@
border-radius: 100px;
background: #fff;
}
.rank-home-indicator {
position: fixed;
z-index: 41;
}
.rank-home-indicator[hidden] {
display: none;
}
.empty {
min-height: 180px;
opacity: 0;
@ -767,8 +779,12 @@
</section>
<section class="podium" id="podium"></section>
<section class="list" id="rankList"></section>
<section class="my-rank" id="myRank"></section>
<div class="home-indicator"></div>
<section class="my-rank" id="myRank" hidden></section>
<div
class="home-indicator rank-home-indicator"
id="rankHomeIndicator"
hidden
></div>
</main>
<main id="notePage" class="page note-page">
@ -834,6 +850,7 @@
</div>
<script src="../common/api.js"></script>
<script src="../common/toast.js"></script>
<script src="../common/params.js"></script>
<script src="../common/jsbridge.js"></script>
<script>
@ -1142,6 +1159,8 @@
}
function renderList() {
var items = visibleItems();
var myRank = byId('myRank');
var rankHomeIndicator = byId('rankHomeIndicator');
if (!items.length) {
byId('rankList').innerHTML =
'<div class="rank-state">' +
@ -1151,7 +1170,9 @@
? 'Loading'
: 'No ranking data') +
'</div>';
byId('myRank').innerHTML = '';
myRank.innerHTML = '';
myRank.hidden = true;
rankHomeIndicator.hidden = true;
return;
}
byId('rankList').innerHTML =
@ -1161,9 +1182,9 @@
return row(item);
})
.join('') + '<div class="empty"></div>';
byId('myRank').innerHTML = row(
state.myRank || items[3] || items[0]
);
myRank.innerHTML = row(state.myRank || items[3] || items[0]);
myRank.hidden = false;
rankHomeIndicator.hidden = false;
}
function applyTheme() {
var cfg = ASSETS[state.board];
@ -1243,6 +1264,19 @@
document.body.style.minHeight =
Math.ceil(height * scale) + 'px';
}
function mountFixedRankBar() {
var myRank = byId('myRank');
var rankHomeIndicator = byId('rankHomeIndicator');
if (myRank && myRank.parentNode !== document.body) {
document.body.appendChild(myRank);
}
if (
rankHomeIndicator &&
rankHomeIndicator.parentNode !== document.body
) {
document.body.appendChild(rankHomeIndicator);
}
}
function scheduleResize() {
window.requestAnimationFrame(resizeApp);
}
@ -1324,6 +1358,8 @@
function showNote() {
byId('rankPage').classList.add('hidden');
byId('notePage').classList.add('active');
byId('myRank').hidden = true;
byId('rankHomeIndicator').hidden = true;
byId('noteCopy').textContent = noteCopy[state.board];
renderRewards();
scheduleResize();
@ -1332,6 +1368,7 @@
function hideNote() {
byId('notePage').classList.remove('active');
byId('rankPage').classList.remove('hidden');
renderList();
scheduleResize();
}
function renderRewards() {
@ -1393,6 +1430,7 @@
});
byId('helpButton').addEventListener('click', showNote);
byId('noteBackButton').addEventListener('click', hideNote);
mountFixedRankBar();
window.addEventListener('scroll', function () {
if (
!state.total ||

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import argparse
import http.server
import json
import os
import socketserver
import urllib.error
@ -12,7 +13,7 @@ import urllib.request
PROXIES = {
"/__api_test__/": "https://api-test.global-interaction.com/",
"/__api_local__/": "http://localhost:13000/",
"/__api_local__/": "http://127.0.0.1:13000/",
}
HOP_BY_HOP_HEADERS = {
@ -20,6 +21,7 @@ HOP_BY_HOP_HEADERS = {
"content-length",
"host",
"keep-alive",
"proxy-connection",
"proxy-authenticate",
"proxy-authorization",
"te",
@ -118,24 +120,29 @@ class H5DevHandler(http.server.SimpleHTTPRequestHandler):
)
try:
with urllib.request.urlopen(request, timeout=30) as response:
self.send_response(response.status)
self.copy_response_headers(response.headers)
self.send_cors_headers()
self.end_headers()
self.write_body(response.read())
self.send_proxy_response(response.status, response.headers, response.read())
except urllib.error.HTTPError as error:
self.send_response(error.code)
self.copy_response_headers(error.headers)
self.send_cors_headers()
self.end_headers()
self.write_body(error.read())
self.send_proxy_response(error.code, error.headers, error.read())
except Exception as error:
body = json.dumps(
{"code": "PROXY_ERROR", "message": str(error)}
).encode("utf-8")
self.send_response(502)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.send_cors_headers()
self.end_headers()
message = str(error).replace('"', '\\"')
self.write_body((f'{{"code":"PROXY_ERROR","message":"{message}"}}').encode("utf-8"))
self.write_body(body)
def send_proxy_response(
self, status: int, headers: http.client.HTTPMessage, body: bytes
) -> None:
self.send_response(status)
self.copy_response_headers(headers)
self.send_header("Content-Length", str(len(body)))
self.send_cors_headers()
self.end_headers()
self.write_body(body)
def write_body(self, body: bytes) -> None:
if self.command == "HEAD":
@ -184,7 +191,7 @@ def main() -> None:
with ReusableTCPServer((args.host, args.port), handler) as server:
print(f"Serving H5 at http://{args.host}:{args.port}/")
print("API proxy: /__api_test__/ -> https://api-test.global-interaction.com/")
print("API proxy: /__api_local__/ -> http://localhost:13000/")
print("API proxy: /__api_local__/ -> http://127.0.0.1:13000/")
server.serve_forever()

View File

@ -753,6 +753,7 @@
<div id="toast" class="toast"></div>
<script src="../common/api.js"></script>
<script src="../common/toast.js"></script>
<script src="../common/params.js"></script>
<script src="../common/jsbridge.js"></script>
<script>