1372 lines
48 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function () {
const supportedLanguages = ["en", "ar", "tr", "id"];
const languageLabels = {
en: "EN",
ar: "AR",
tr: "TR",
id: "ID"
};
const API_BASE_URL = "https://jvapi.haiyihy.com";
const fallbackMessages = {
en: {
document_title: "Host Center",
page_label: "Host Center",
back: "Back",
title: "Host Center",
language_button_aria: "Change language",
uid: "UID: 1234567890",
agency: "Agency: Bilibili(101013)",
streaming_duration: "Work Time (min)",
target: "Target",
daily_details: "Daily data",
gift_points: "Gift points",
online_minutes: "Online minutes",
no_daily_data: "No daily data",
close: "Close",
open_daily_data: "Open daily data",
my_income: "Salary",
join_agency_title: "Join Agency",
join_agency_text: "Search for an agency ID and submit your application.",
join_agency_action: "Join now",
agency_id: "Agency ID",
enter_agent_id: "Enter agency ID",
search: "Search",
searching: "Searching...",
agency_label: "Agency",
apply: "Apply",
applying: "Applying...",
found: "Agency found",
account_not_found: "Account not found or not an agency",
application_submitted: "Application submitted",
application_failed: "Application failed",
application_records: "Application Records",
pending: "Pending",
join_pending_note: "Application is pending. Please wait for agency approval.",
cancel: "Cancel",
cancelling: "Cancelling...",
cancellation_failed: "Cancellation failed",
application_cancelled: "Application cancelled",
missing_token: "Missing token",
network_error: "Network error. Please try again.",
withdraw_exchange: "Withdraw/Exchange",
open_withdraw_exchange: "Open withdraw and exchange page",
open_anchor_policy: "Open host policy",
anchor_policy: "Host Policy",
policy_loading: "Loading policy...",
policy_empty: "No policy data",
policy_missing_token: "Missing token",
policy_load_failed: "Failed to load policy",
policy_level: "Level",
policy_online: "Online (Hour)",
policy_effective_day: "Valid days",
policy_target: "Target",
policy_host_salary: "Host salary",
policy_agent_salary: "Agent salary",
policy_total_salary: "Total salary"
},
ar: {
document_title: "مركز المضيف",
page_label: "مركز المضيف",
back: "رجوع",
title: "مركز المضيف",
language_button_aria: "تغيير اللغة",
uid: "معرّف المستخدم: 1234567890",
agency: "الوكالة: Bilibili(101013)",
streaming_duration: "وقت العمل (دقيقة)",
target: "الهدف",
daily_details: "البيانات اليومية",
gift_points: "نقاط الهدايا",
online_minutes: "دقائق الاتصال",
no_daily_data: "لا توجد بيانات يومية",
close: "إغلاق",
open_daily_data: "فتح البيانات اليومية",
my_income: "الراتب",
join_agency_title: "الانضمام إلى وكالة",
join_agency_text: "ابحث عن معرّف وكالة وأرسل طلبك.",
join_agency_action: "انضم الآن",
agency_id: "معرّف الوكالة",
enter_agent_id: "أدخل معرّف الوكالة",
search: "بحث",
searching: "جار البحث...",
agency_label: "الوكالة",
apply: "تقديم",
applying: "جار التقديم...",
found: "تم العثور على الوكالة",
account_not_found: "لم يتم العثور على الحساب أو ليس وكالة",
application_submitted: "تم إرسال الطلب",
application_failed: "فشل إرسال الطلب",
application_records: "سجلات الطلب",
pending: "قيد الانتظار",
join_pending_note: "الطلب قيد الانتظار. يرجى انتظار موافقة الوكالة.",
cancel: "إلغاء",
cancelling: "جار الإلغاء...",
cancellation_failed: "فشل الإلغاء",
application_cancelled: "تم إلغاء الطلب",
missing_token: "رمز الدخول مفقود",
network_error: "خطأ في الشبكة. حاول مرة أخرى.",
withdraw_exchange: "سحب/استبدال",
open_withdraw_exchange: "فتح صفحة السحب والاستبدال",
open_anchor_policy: "فتح سياسة المضيف",
anchor_policy: "سياسة المضيف",
policy_loading: "جار تحميل السياسة...",
policy_empty: "لا توجد بيانات سياسة",
policy_missing_token: "رمز الدخول مفقود",
policy_load_failed: "فشل تحميل السياسة",
policy_level: "المستوى",
policy_online: "الاتصال (ساعة)",
policy_effective_day: "الأيام الصالحة",
policy_target: "الهدف",
policy_host_salary: "راتب المضيف",
policy_agent_salary: "راتب الوكيل",
policy_total_salary: "إجمالي الراتب"
},
tr: {
document_title: "Host Merkezi",
page_label: "Host Merkezi",
back: "Geri",
title: "Host Merkezi",
language_button_aria: "Dili değiştir",
uid: "UID: 1234567890",
agency: "Ajans: Bilibili(101013)",
streaming_duration: "Çalışma Süresi (dk)",
target: "Hedef",
daily_details: "Günlük veri",
gift_points: "Hediye puanı",
online_minutes: "Online dakika",
no_daily_data: "Günlük veri yok",
close: "Kapat",
open_daily_data: "Günlük veriyi aç",
my_income: "Maaş",
join_agency_title: "Ajansa Katıl",
join_agency_text: "Ajans ID ile arama yapıp başvurunu gönder.",
join_agency_action: "Şimdi katıl",
agency_id: "Ajans ID",
enter_agent_id: "Ajans ID gir",
search: "Ara",
searching: "Aranıyor...",
agency_label: "Ajans",
apply: "Başvur",
applying: "Başvuruluyor...",
found: "Ajans bulundu",
account_not_found: "Hesap bulunamadı veya ajans değil",
application_submitted: "Başvuru gönderildi",
application_failed: "Başvuru başarısız",
application_records: "Başvuru Kayıtları",
pending: "Beklemede",
join_pending_note: "Başvuru beklemede. Lütfen ajans onayını bekleyin.",
cancel: "İptal",
cancelling: "İptal ediliyor...",
cancellation_failed: "İptal başarısız",
application_cancelled: "Başvuru iptal edildi",
missing_token: "Token eksik",
network_error: "Ağ hatası. Lütfen tekrar deneyin.",
withdraw_exchange: "Çek/Değiştir",
open_withdraw_exchange: "Çekme ve değişim sayfasını aç",
open_anchor_policy: "Host politikasını aç",
anchor_policy: "Host Politikası",
policy_loading: "Politika yükleniyor...",
policy_empty: "Politika verisi yok",
policy_missing_token: "Token eksik",
policy_load_failed: "Politika yüklenemedi",
policy_level: "Seviye",
policy_online: "Online (Saat)",
policy_effective_day: "Geçerli gün",
policy_target: "Hedef",
policy_host_salary: "Host maaşı",
policy_agent_salary: "Ajans maaşı",
policy_total_salary: "Toplam maaş"
},
id: {
document_title: "Pusat Host",
page_label: "Pusat Host",
back: "Kembali",
title: "Pusat Host",
language_button_aria: "Ubah bahasa",
uid: "UID: 1234567890",
agency: "Agensi: Bilibili(101013)",
streaming_duration: "Waktu Kerja (mnt)",
target: "Target",
daily_details: "Data harian",
gift_points: "Poin hadiah",
online_minutes: "Menit online",
no_daily_data: "Tidak ada data harian",
close: "Tutup",
open_daily_data: "Buka data harian",
my_income: "Gaji",
join_agency_title: "Gabung Agensi",
join_agency_text: "Cari ID agensi dan kirim pengajuan Anda.",
join_agency_action: "Gabung sekarang",
agency_id: "ID Agensi",
enter_agent_id: "Masukkan ID agensi",
search: "Cari",
searching: "Mencari...",
agency_label: "Agensi",
apply: "Ajukan",
applying: "Mengajukan...",
found: "Agensi ditemukan",
account_not_found: "Akun tidak ditemukan atau bukan agensi",
application_submitted: "Pengajuan terkirim",
application_failed: "Pengajuan gagal",
application_records: "Catatan Pengajuan",
pending: "Menunggu",
join_pending_note: "Pengajuan sedang menunggu. Harap tunggu persetujuan agensi.",
cancel: "Batal",
cancelling: "Membatalkan...",
cancellation_failed: "Gagal membatalkan",
application_cancelled: "Pengajuan dibatalkan",
missing_token: "Token tidak ditemukan",
network_error: "Kesalahan jaringan. Silakan coba lagi.",
withdraw_exchange: "Tarik/Tukar",
open_withdraw_exchange: "Buka halaman tarik dan tukar",
open_anchor_policy: "Buka kebijakan host",
anchor_policy: "Kebijakan Host",
policy_loading: "Memuat kebijakan...",
policy_empty: "Tidak ada data kebijakan",
policy_missing_token: "Token tidak ditemukan",
policy_load_failed: "Gagal memuat kebijakan",
policy_level: "Level",
policy_online: "Online (Jam)",
policy_effective_day: "Hari valid",
policy_target: "Target",
policy_host_salary: "Gaji host",
policy_agent_salary: "Gaji agensi",
policy_total_salary: "Total gaji"
}
};
const params = readURLParams();
let currentLang = normalizeLanguage(params.get("lang"));
let currentMessages = fallbackMessages[currentLang] || fallbackMessages.en;
let currentProfile = null;
let currentAgencyProfile = null;
let currentTeamId = "";
let currentIdentity = null;
let currentSalary = "0.00";
let currentStreamingDuration = "0";
let currentGiftValue = "0";
let currentTargetLevel = "-";
let currentDailyTargets = [];
let foundJoinAgency = null;
let pendingJoinRecord = null;
function readURLParams() {
const result = new URLSearchParams(window.location.search);
const hashQuery = window.location.hash.includes("?") ? window.location.hash.split("?")[1] : "";
const hashParams = new URLSearchParams(hashQuery);
hashParams.forEach((value, key) => {
if (!result.has(key)) result.set(key, value);
});
return result;
}
function readRawParam(name) {
const queries = [window.location.search.replace(/^\?/, "")];
if (window.location.hash.includes("?")) queries.push(window.location.hash.split("?")[1]);
for (const query of queries) {
for (const part of query.split("&")) {
if (!part) continue;
const equalIndex = part.indexOf("=");
const rawKey = equalIndex >= 0 ? part.slice(0, equalIndex) : part;
const rawValue = equalIndex >= 0 ? part.slice(equalIndex + 1) : "";
let decodedKey = "";
try {
decodedKey = decodeURIComponent(rawKey);
} catch (error) {
continue;
}
if (decodedKey !== name) continue;
try {
return decodeURIComponent(rawValue.replace(/\+/g, "%2B"));
} catch (error) {
return rawValue;
}
}
}
return "";
}
function normalizeLanguage(lang) {
const value = String(lang || "en").toLowerCase();
return supportedLanguages.includes(value) ? value : "en";
}
async function loadMessages(lang) {
const normalizedLang = normalizeLanguage(lang);
if (window.location.protocol === "file:") return fallbackMessages[normalizedLang];
try {
const response = await fetch(`./locales/${normalizedLang}.json?v=20260428-2070`, { cache: "no-store" });
if (response.ok) return await response.json();
} catch (error) {
return fallbackMessages[normalizedLang];
}
return fallbackMessages[normalizedLang];
}
function localizedValue(messages, key) {
return messages?.[key] || fallbackMessages[currentLang]?.[key] || fallbackMessages.en[key] || "";
}
function message(key, fallback = "") {
return localizedValue(currentMessages, key) || fallback || key;
}
function renderMessages(messages) {
document.querySelectorAll("[data-i18n]").forEach((node) => {
const value = localizedValue(messages, node.dataset.i18n);
if (value) node.textContent = value;
});
document.querySelectorAll("[data-i18n-html]").forEach((node) => {
const value = localizedValue(messages, node.dataset.i18nHtml);
if (value) node.innerHTML = value;
});
document.querySelectorAll("[data-i18n-aria]").forEach((node) => {
const value = localizedValue(messages, node.dataset.i18nAria);
if (value) node.setAttribute("aria-label", value);
});
document.querySelectorAll("[data-i18n-placeholder]").forEach((node) => {
const value = localizedValue(messages, node.dataset.i18nPlaceholder);
if (value) node.setAttribute("placeholder", value);
});
}
function apiBaseURL() {
const override = window.HYAPP_API_BASE_URL || params.get("apiBase");
if (override) return String(override).replace(/\/+$/, "");
return API_BASE_URL.replace(/\/+$/, "");
}
function normalizeAuthorization(token) {
const value = String(token || "").trim();
if (!value) return "";
return /^bearer\s+/i.test(value) ? value : `Bearer ${value}`;
}
function authHeadersFromToken(token) {
return {
Authorization: normalizeAuthorization(token),
"req-lang": currentLang,
"req-client": "H5",
"req-version": "V2",
"req-zone": Intl.DateTimeFormat().resolvedOptions().timeZone || "Asia/Shanghai",
"req-app-intel": "version=1.0.0;build=1;model=H5;sysVersion=Web;channel=Web",
"req-sys-origin": "origin=ATYOU;originChild=ATYOU"
};
}
async function requestJSON(path, options = {}) {
const token = readRawParam("token");
if (!token) throw new Error(message("missing_token", "Missing token"));
const headers = authHeadersFromToken(token);
const request = {
method: options.method || "GET",
cache: "no-store",
headers
};
if (options.body !== undefined) {
headers["Content-Type"] = "application/json";
request.body = JSON.stringify(options.body);
}
const response = await fetch(new URL(path, `${apiBaseURL()}/`).toString(), request);
const data = await response.json().catch(() => ({}));
if (!response.ok || data.status === false) {
const error = new Error(data.errorMsg || data.message || `Request failed: ${response.status}`);
error.response = data;
throw error;
}
return data.body ?? data.data ?? null;
}
function profileUidPrefix(messages) {
const template = messages.uid || fallbackMessages.en.uid;
const marker = "1234567890";
return template.includes(marker) ? template.replace(marker, "").trim() : "UID:";
}
function pickProfileName(profile) {
return profile?.userNickname || profile?.name || "Oscar";
}
function pickProfileAccount(profile) {
return profile?.ownSpecialId?.account || profile?.account || profile?.id || "1234567890";
}
function agencyPrefix(messages) {
const template = messages.agency || fallbackMessages.en.agency;
const marker = "Bilibili(101013)";
return template.includes(marker) ? template.replace(marker, "").trim() : "Agency:";
}
function pickAgencyName(profile) {
return profile?.userNickname || profile?.name || profile?.teamName || "-";
}
function pickAgencyAccount(profile) {
return profile?.ownSpecialId?.account || profile?.account || profile?.id || "";
}
function formatAgencyText(profile, messages = currentMessages) {
const prefix = agencyPrefix(messages);
if (!profile) return `${prefix} -`;
const name = pickAgencyName(profile);
const account = pickAgencyAccount(profile);
return account ? `${prefix} ${name}(${account})` : `${prefix} ${name}`;
}
function renderAgencyProfile(profile, messages = currentMessages) {
const agencyButton = document.querySelector(".agency");
const agencyTextNode = document.querySelector(".agency span:first-child");
if (!agencyButton || !agencyTextNode) return;
agencyTextNode.textContent = formatAgencyText(profile, messages);
agencyButton.dataset.teamId = String(currentTeamId || pickAgencyTeamId(profile) || "");
agencyButton.dataset.account = String(profile?.account || "");
agencyButton.dataset.specialAccount = String(profile?.ownSpecialId?.account || "");
syncPolicyLink();
}
function pickSpecialAccount(profile) {
return profile?.ownSpecialId?.account ||
profile?.specialId?.account ||
profile?.specialAccount ||
profile?.shortAccount ||
profile?.shortId ||
"";
}
function pickJoinAgencyProfile(body) {
return body?.ownUserProfile || body?.userProfile || body?.teamProfile || body || null;
}
function pickSearchAgency(body) {
const teamProfile = body?.teamProfile || null;
const userProfile = body?.ownUserProfile || body?.userProfile || teamProfile?.ownUserProfile || teamProfile?.userProfile || null;
if (!teamProfile?.id) return null;
const specialAccount = pickSpecialAccount(userProfile) || pickSpecialAccount(teamProfile);
return {
...(teamProfile || {}),
...(userProfile || {}),
id: teamProfile.id,
teamId: teamProfile.id,
teamName: teamProfile.teamName || userProfile?.teamName || "",
userNickname: userProfile?.userNickname || teamProfile.userNickname || teamProfile.name || teamProfile.teamName || "",
name: userProfile?.name || teamProfile.name || teamProfile.teamName || "",
userAvatar: userProfile?.userAvatar || teamProfile.userAvatar || userProfile?.avatar || teamProfile.avatar || "",
avatar: userProfile?.avatar || teamProfile.avatar || userProfile?.userAvatar || teamProfile.userAvatar || "",
displayAccount: specialAccount || userProfile?.account || teamProfile.account || "",
account: userProfile?.account || teamProfile.account || "",
ownSpecialId: userProfile?.ownSpecialId || teamProfile.ownSpecialId || (specialAccount ? { account: specialAccount } : null),
specialId: userProfile?.specialId || teamProfile.specialId || null,
specialAccount,
shortAccount: userProfile?.shortAccount || teamProfile.shortAccount || "",
shortId: userProfile?.shortId || teamProfile.shortId || ""
};
}
function pickJoinAgencyName(profile) {
return profile?.userNickname || profile?.name || profile?.teamName || "-";
}
function pickJoinAgencyAccount(profile) {
return pickSpecialAccount(profile) || profile?.displayAccount || profile?.account || profile?.id || "-";
}
function pickAgencyTeamId(profile) {
return profile?.teamId || profile?.teamProfile?.id || profile?.id || "";
}
function pickTeamEntryId(entry) {
return entry?.teamProfile?.id || entry?.teamId || entry?.id || "";
}
function removeAvatarFallback() {
document.querySelector("#profileAvatarFallback")?.remove();
}
function ensureAvatarFallback(displayName) {
const avatarShell = document.querySelector(".avatar-shell");
if (!avatarShell) return null;
let avatarFallback = document.querySelector("#profileAvatarFallback");
if (avatarFallback) {
avatarFallback.hidden = false;
avatarFallback.setAttribute("aria-label", `${displayName} avatar`);
return avatarFallback;
}
const template = document.createElement("template");
template.innerHTML = `
<svg class="avatar-fallback" id="profileAvatarFallback" viewBox="0 0 72 72" role="img">
<defs>
<linearGradient id="avatarBg" x1="12" y1="8" x2="60" y2="64">
<stop stop-color="#5AF6E4"></stop>
<stop offset="1" stop-color="#D9FFF5"></stop>
</linearGradient>
</defs>
<circle cx="36" cy="36" r="35" fill="url(#avatarBg)"></circle>
<path d="M22 30 20 18l10 7M50 30l2-12-10 7" fill="#101820"></path>
<path d="M20 31c3-9 28-9 32 0 4 10-3 21-16 21S16 41 20 31Z" fill="#F7FBF7"></path>
<circle cx="29" cy="36" r="4" fill="#101820"></circle>
<circle cx="46" cy="36" r="4" fill="#101820"></circle>
<circle cx="25" cy="41" r="5" fill="#F0B64A" opacity=".9"></circle>
<path d="M35 42h3l-1.5 2Z" fill="#101820"></path>
<path d="M36 45c-2 2-5 3-8 2M38 45c2 2 5 3 8 2" fill="none" stroke="#101820" stroke-width="2" stroke-linecap="round"></path>
</svg>
`;
avatarFallback = template.content.firstElementChild;
avatarFallback.setAttribute("aria-label", `${displayName} avatar`);
avatarShell.appendChild(avatarFallback);
return avatarFallback;
}
function renderProfile(profile, messages = currentMessages) {
if (!profile) return;
const profileCard = document.querySelector(".profile-card");
const nameNode = document.querySelector("#profileName");
const uidNode = document.querySelector("#profileUid");
const avatarImage = document.querySelector("#profileAvatar");
const displayName = pickProfileName(profile);
const displayAccount = pickProfileAccount(profile);
if (profileCard) {
profileCard.dataset.userId = String(profile.id || "");
profileCard.dataset.account = String(profile.account || "");
profileCard.dataset.specialAccount = String(profile?.ownSpecialId?.account || "");
}
if (nameNode) nameNode.textContent = displayName;
if (uidNode) uidNode.textContent = `${profileUidPrefix(messages)} ${displayAccount}`.trim();
if (!avatarImage) return;
const avatar = profile.userAvatar ? String(profile.userAvatar) : "";
avatarImage.alt = displayName;
if (!avatar) {
avatarImage.hidden = true;
avatarImage.onerror = null;
avatarImage.removeAttribute("src");
ensureAvatarFallback(displayName);
return;
}
avatarImage.hidden = false;
removeAvatarFallback();
avatarImage.onerror = () => {
avatarImage.hidden = true;
avatarImage.removeAttribute("src");
ensureAvatarFallback(displayName);
};
avatarImage.src = avatar;
}
function hasOwn(object, key) {
return Boolean(object && Object.prototype.hasOwnProperty.call(object, key));
}
function isTruthyIdentityValue(value) {
if (typeof value === "boolean") return value;
if (typeof value === "number") return value === 1;
if (typeof value === "string") return ["true", "1", "yes"].includes(value.toLowerCase());
return false;
}
function shouldOpenJoinAgency(identity) {
if (!hasOwn(identity, "anchor")) return false;
return !isTruthyIdentityValue(identity.anchor);
}
function setLoading(isLoading) {
const root = document.querySelector(".host-center");
const loadingMask = document.querySelector("#loadingMask");
if (root) root.dataset.loading = isLoading ? "true" : "false";
if (loadingMask) {
loadingMask.hidden = !isLoading;
loadingMask.setAttribute("aria-busy", String(isLoading));
}
}
function setIdentityView(identity) {
const root = document.querySelector(".host-center");
const showJoinAgency = shouldOpenJoinAgency(identity);
if (root) root.dataset.view = showJoinAgency ? "join-agency" : "host";
document.querySelectorAll(".host-only").forEach((node) => {
node.hidden = showJoinAgency;
});
const joinAgencyCard = document.querySelector("#joinAgencyCard");
if (joinAgencyCard) joinAgencyCard.hidden = !showJoinAgency;
const agencyButton = document.querySelector(".agency");
if (agencyButton) agencyButton.hidden = showJoinAgency;
}
function closeHostCenterPage() {
try {
if (window.app && typeof window.app.closePage === "function") {
window.app.closePage();
return;
}
if (window.FlutterPageControl && typeof window.FlutterPageControl.postMessage === "function") {
window.FlutterPageControl.postMessage("close_page");
return;
}
if (window.history.length > 1) window.history.back();
} catch (error) {
console.error("close host center page failed:", error);
if (window.history.length > 1) window.history.back();
}
}
function formatCurrentSalary(value) {
const numericValue = Number(value);
if (Number.isFinite(numericValue)) return numericValue.toFixed(2);
return "0.00";
}
function pickCurrentSalary(body) {
if (typeof body === "number") return body;
if (body && typeof body === "object") {
return body.currentSalary ?? body.availableBalance ?? body.balance ?? 0;
}
return 0;
}
function renderCurrentSalary(value) {
currentSalary = formatCurrentSalary(value);
const salaryNode = document.querySelector("#currentSalary");
if (salaryNode) salaryNode.textContent = currentSalary;
}
function formatStreamingDuration(value) {
const numericValue = Number(value);
if (!Number.isFinite(numericValue)) return "0";
const safeValue = Math.max(0, numericValue);
return formatCountValue(safeValue);
}
function pickStreamingDuration(body) {
const workTarget = pickFirstMemberWorkTarget(body);
return body?.onlineTime ?? body?.target?.onlineTime ?? workTarget?.target?.onlineTime ?? workTarget?.onlineTime ?? 0;
}
function renderStreamingDuration(value) {
currentStreamingDuration = formatStreamingDuration(value);
const durationNode = document.querySelector("#streamingDuration");
if (durationNode) durationNode.textContent = currentStreamingDuration;
}
function formatCountValue(value) {
if (value === null || value === undefined || value === "") return "-";
const number = Number(value);
if (Number.isFinite(number)) {
return new Intl.NumberFormat("en-US", {
maximumFractionDigits: 2
}).format(number);
}
return String(value);
}
function renderCurrentTargetLevel(value) {
currentTargetLevel = formatCountValue(value);
const levelNode = document.querySelector("#currentTargetLevel");
if (levelNode) levelNode.textContent = `Lv. ${currentTargetLevel}`;
}
function renderCurrentGiftValue(value) {
currentGiftValue = formatCountValue(value === null || value === undefined || value === "" ? 0 : value);
const giftNode = document.querySelector("#currentGiftValue");
const dailyGiftNode = document.querySelector("#dailyCurrentGift");
if (giftNode) giftNode.textContent = currentGiftValue;
if (dailyGiftNode) dailyGiftNode.textContent = currentGiftValue;
}
function pickFirstMemberWorkTarget(body) {
const targets = Array.isArray(body?.targets) ? body.targets : [];
return targets[0] || body?.target || null;
}
function pickGiftValue(workTarget) {
const target = workTarget?.target || workTarget || {};
return target.acceptGiftValue ?? target.giftValue ?? target.giftScore ?? target.currentGiftValue ?? null;
}
function pickTargetLevel(workTarget) {
const target = workTarget?.target || workTarget || {};
return target?.settlementResult?.level ?? target.level ?? "-";
}
function pickDailyTargets(workTarget) {
const target = workTarget?.target || workTarget || {};
return Array.isArray(target.dailyTargets) ? target.dailyTargets : [];
}
function formatDateNumber(value) {
const text = String(value || "");
if (/^\d{8}$/.test(text)) {
return `${text.slice(0, 4)}.${text.slice(4, 6)}.${text.slice(6, 8)}`;
}
return text || "-";
}
function createDailyRow(item) {
const row = document.createElement("div");
row.className = "daily-row";
const date = document.createElement("div");
date.className = "daily-date";
date.textContent = formatDateNumber(item?.dateNumber);
const values = document.createElement("div");
values.className = "daily-values";
const gift = document.createElement("div");
gift.className = "daily-cell";
gift.innerHTML = `<span>${currentMessages.gift_points || fallbackMessages.en.gift_points}</span><strong>${formatCountValue(item?.acceptGiftValue ?? 0)}</strong>`;
const online = document.createElement("div");
online.className = "daily-cell";
online.innerHTML = `<span>${currentMessages.online_minutes || fallbackMessages.en.online_minutes}</span><strong>${formatCountValue(item?.onlineTime ?? 0)}</strong>`;
values.appendChild(gift);
values.appendChild(online);
row.appendChild(date);
row.appendChild(values);
return row;
}
function replaceNodeChildren(node, children) {
while (node.firstChild) node.removeChild(node.firstChild);
children.forEach((child) => node.appendChild(child));
}
function renderDailyTargets(targets = currentDailyTargets) {
currentDailyTargets = Array.isArray(targets) ? targets : [];
const list = document.querySelector("#dailyList");
if (!list) return;
if (!currentDailyTargets.length) {
const empty = document.createElement("div");
empty.className = "daily-empty";
empty.textContent = currentMessages.no_daily_data || fallbackMessages.en.no_daily_data;
replaceNodeChildren(list, [empty]);
return;
}
replaceNodeChildren(list, currentDailyTargets.map(createDailyRow));
}
function updateModalBodyLock() {
const hasOpenModal = Array.from(document.querySelectorAll(".daily-modal, .join-agency-modal")).some((modal) => !modal.hidden);
document.body.classList.toggle("daily-modal-open", hasOpenModal);
}
function renderMemberWorkSummary(memberWork) {
const workTarget = pickFirstMemberWorkTarget(memberWork);
renderStreamingDuration(pickStreamingDuration(memberWork));
renderCurrentGiftValue(pickGiftValue(workTarget) ?? 0);
renderCurrentTargetLevel(pickTargetLevel(workTarget));
renderDailyTargets(pickDailyTargets(workTarget));
}
function setDailyModalOpen(open) {
const modal = document.querySelector("#dailyModal");
if (!modal) return;
modal.hidden = !open;
updateModalBodyLock();
if (open) renderDailyTargets();
}
function showJoinAgencyStatus(text, type = "") {
const node = document.querySelector("#joinAgencyStatus");
if (!node) return;
node.textContent = text || "";
node.hidden = !text;
node.classList.toggle("is-error", type === "error");
node.classList.toggle("is-success", type === "success");
}
function renderJoinAgencyAvatar(profile, imageSelector, fallbackSelector) {
const image = document.querySelector(imageSelector);
const fallback = document.querySelector(fallbackSelector);
if (!image || !fallback) return;
const name = pickJoinAgencyName(profile);
const avatar = profile?.userAvatar || profile?.avatar || "";
fallback.textContent = String(name || "A").slice(0, 1).toUpperCase() || "A";
image.alt = name;
if (!avatar) {
image.hidden = true;
image.removeAttribute("src");
fallback.hidden = false;
return;
}
fallback.hidden = true;
image.hidden = false;
image.onerror = () => {
image.hidden = true;
image.removeAttribute("src");
fallback.hidden = false;
};
image.src = avatar;
}
function syncJoinAgencyApplyAvailability() {
const button = document.querySelector("#joinAgencyApplyButton");
if (button) button.hidden = Boolean(pendingJoinRecord) || !foundJoinAgency?.id;
const cancelButton = document.querySelector("#joinAgencyCancelButton");
if (cancelButton) cancelButton.hidden = !pendingJoinRecord || !pickPendingJoinRecordId(pendingJoinRecord);
}
function pickPendingJoinRecordId(record) {
return record?.messageId || record?.id || "";
}
function renderFoundJoinAgency(profile = foundJoinAgency) {
const card = document.querySelector("#joinAgencyResult");
if (!card) return;
if (!profile) {
card.hidden = true;
syncJoinAgencyApplyAvailability();
return;
}
card.hidden = false;
const nameNode = document.querySelector("#joinAgencyName");
const accountNode = document.querySelector("#joinAgencyAccountText");
if (nameNode) nameNode.textContent = pickJoinAgencyName(profile);
if (accountNode) accountNode.textContent = `${profileUidPrefix(currentMessages)} ${pickJoinAgencyAccount(profile)}`.trim();
renderJoinAgencyAvatar(profile, "#joinAgencyAvatar", "#joinAgencyAvatarFallback");
syncJoinAgencyApplyAvailability();
}
function renderPendingJoinRecord(record = pendingJoinRecord) {
const card = document.querySelector("#joinAgencyPending");
if (!card) return;
if (!record) {
card.hidden = true;
syncJoinAgencyApplyAvailability();
return;
}
const profile = pickJoinAgencyProfile(record);
card.hidden = false;
const nameNode = document.querySelector("#joinAgencyPendingName");
const accountNode = document.querySelector("#joinAgencyPendingAccount");
if (nameNode) nameNode.textContent = pickJoinAgencyName(profile);
if (accountNode) accountNode.textContent = `${profileUidPrefix(currentMessages)} ${pickJoinAgencyAccount(profile)}`.trim();
renderJoinAgencyAvatar(profile, "#joinAgencyPendingAvatar", "#joinAgencyPendingAvatarFallback");
syncJoinAgencyApplyAvailability();
}
function joinAgencyErrorMessage(error, fallbackKey) {
return error?.response?.errorMsg || error?.response?.message || error?.message || message(fallbackKey);
}
async function fetchPendingJoinRecord() {
try {
pendingJoinRecord = await requestJSON("/team/wait-apply/profile");
renderPendingJoinRecord(pendingJoinRecord);
return pendingJoinRecord;
} catch (error) {
pendingJoinRecord = null;
renderPendingJoinRecord(null);
if (error.message === message("missing_token")) showJoinAgencyStatus(error.message, "error");
return null;
}
}
async function searchJoinAgency(event) {
event?.preventDefault();
const input = document.querySelector("#joinAgencyAccount");
const button = document.querySelector("#joinAgencySearchButton");
const account = String(input?.value || "").trim();
if (!account) {
showJoinAgencyStatus(message("enter_agent_id"), "error");
return;
}
foundJoinAgency = null;
renderFoundJoinAgency(null);
if (button) {
button.disabled = true;
button.textContent = message("searching");
}
showJoinAgencyStatus("");
try {
const body = await requestJSON(`/team/search-account?account=${encodeURIComponent(account)}`);
const profile = pickSearchAgency(body);
if (!profile?.id) {
showJoinAgencyStatus(message("account_not_found"), "error");
return;
}
foundJoinAgency = profile;
renderFoundJoinAgency(foundJoinAgency);
showJoinAgencyStatus(message("found"), "success");
} catch (error) {
foundJoinAgency = null;
renderFoundJoinAgency(null);
showJoinAgencyStatus(joinAgencyErrorMessage(error, "network_error"), "error");
} finally {
if (button) {
button.disabled = false;
button.textContent = message("search");
}
}
}
async function applyToJoinAgency() {
if (pendingJoinRecord || !foundJoinAgency?.id) return;
const button = document.querySelector("#joinAgencyApplyButton");
if (button) {
button.disabled = true;
button.textContent = message("applying");
}
showJoinAgencyStatus("");
try {
const appliedAgency = foundJoinAgency;
await requestJSON("/team/user/apply", {
method: "POST",
body: {
teamId: appliedAgency.id,
reason: "JOIN"
}
});
foundJoinAgency = null;
renderFoundJoinAgency(null);
showJoinAgencyStatus(message("application_submitted"), "success");
const pending = await fetchPendingJoinRecord();
if (!pending) {
pendingJoinRecord = { ownUserProfile: appliedAgency };
renderPendingJoinRecord(pendingJoinRecord);
}
} catch (error) {
showJoinAgencyStatus(joinAgencyErrorMessage(error, "application_failed"), "error");
} finally {
if (button) {
button.disabled = false;
button.textContent = message("apply");
}
syncJoinAgencyApplyAvailability();
}
}
async function cancelJoinApplication() {
const id = pickPendingJoinRecordId(pendingJoinRecord);
if (!id) return;
const button = document.querySelector("#joinAgencyCancelButton");
if (button) {
button.disabled = true;
button.textContent = message("cancelling");
}
showJoinAgencyStatus("");
try {
await requestJSON("/team/user/join-apply-cancel", {
method: "POST",
body: { id }
});
pendingJoinRecord = null;
renderPendingJoinRecord(null);
showJoinAgencyStatus(message("application_cancelled"), "success");
} catch (error) {
showJoinAgencyStatus(joinAgencyErrorMessage(error, "cancellation_failed"), "error");
} finally {
if (button) {
button.disabled = false;
button.textContent = message("cancel");
}
syncJoinAgencyApplyAvailability();
}
}
function setJoinAgencyModalOpen(open) {
const modal = document.querySelector("#joinAgencyModal");
if (!modal) return;
modal.hidden = !open;
updateModalBodyLock();
if (!open) return;
showJoinAgencyStatus("");
fetchPendingJoinRecord();
window.setTimeout(() => {
document.querySelector("#joinAgencyAccount")?.focus();
}, 0);
}
async function fetchUserProfile() {
const token = readRawParam("token");
if (!token) {
console.warn("Missing token for /team/member/profile");
return null;
}
try {
const url = new URL("/team/member/profile", `${apiBaseURL()}/`);
const response = await fetch(url.toString(), {
cache: "no-store",
headers: authHeadersFromToken(token)
});
const data = await response.json().catch(() => ({}));
if (!response.ok || data.status === false) {
throw new Error(data.errorMsg || data.message || `Request failed: ${response.status}`);
}
currentProfile = data.body || data.data || {};
renderProfile(currentProfile);
return currentProfile;
} catch (error) {
console.error("Failed to load user profile:", error);
return null;
}
}
async function fetchIdentity() {
const token = readRawParam("token");
if (!token) {
console.warn("Missing token for /app/h5/identity");
return null;
}
try {
const url = new URL("/app/h5/identity", `${apiBaseURL()}/`);
const response = await fetch(url.toString(), {
cache: "no-store",
headers: authHeadersFromToken(token)
});
const data = await response.json().catch(() => ({}));
if (!response.ok || data.status === false) {
throw new Error(data.errorMsg || data.message || `Request failed: ${response.status}`);
}
currentIdentity = data.body || data.data || {};
return currentIdentity;
} catch (error) {
console.error("Failed to load H5 identity:", error);
return null;
}
}
async function fetchAgencyProfile() {
const token = readRawParam("token");
if (!token) {
console.warn("Missing token for /team/team-own/profile");
renderAgencyProfile(null);
return null;
}
try {
const url = new URL("/team/team-own/profile", `${apiBaseURL()}/`);
const response = await fetch(url.toString(), {
cache: "no-store",
headers: authHeadersFromToken(token)
});
const data = await response.json().catch(() => ({}));
if (!response.ok || data.status === false) {
throw new Error(data.errorMsg || data.message || `Request failed: ${response.status}`);
}
currentAgencyProfile = data.body || data.data || null;
renderAgencyProfile(currentAgencyProfile);
return currentAgencyProfile;
} catch (error) {
console.error("Failed to load agency profile:", error);
renderAgencyProfile(null);
return null;
}
}
async function fetchTeamEntry() {
const token = readRawParam("token");
if (!token) {
console.warn("Missing token for /team/entry");
return "";
}
try {
const url = new URL("/team/entry", `${apiBaseURL()}/`);
const response = await fetch(url.toString(), {
cache: "no-store",
headers: authHeadersFromToken(token)
});
const data = await response.json().catch(() => ({}));
if (!response.ok || data.status === false) {
throw new Error(data.errorMsg || data.message || `Request failed: ${response.status}`);
}
currentTeamId = pickTeamEntryId(data.body || data.data || {});
renderAgencyProfile(currentAgencyProfile);
syncPolicyLink();
return currentTeamId;
} catch (error) {
console.error("Failed to load team entry:", error);
return "";
}
}
async function fetchBankBalance() {
const token = readRawParam("token");
if (!token) {
console.warn("Missing token for /wallet/bank/balance");
return;
}
try {
const url = new URL("/wallet/bank/balance", `${apiBaseURL()}/`);
const response = await fetch(url.toString(), {
cache: "no-store",
headers: authHeadersFromToken(token)
});
const data = await response.json().catch(() => ({}));
if (!response.ok || data.status === false) {
throw new Error(data.errorMsg || data.message || `Request failed: ${response.status}`);
}
renderCurrentSalary(pickCurrentSalary(data.body ?? data.data));
} catch (error) {
console.error("Failed to load bank balance:", error);
renderCurrentSalary(0);
}
}
async function fetchStreamingDuration(memberId) {
const memberWork = await fetchMemberWork(memberId);
renderMemberWorkSummary(memberWork);
return memberWork;
}
async function fetchMemberWork(memberId) {
if (!memberId) {
console.warn("Missing memberId for /team/member-work");
return null;
}
try {
const url = new URL("/team/member-work", `${apiBaseURL()}/`);
url.searchParams.set("userId", String(memberId));
const token = readRawParam("token");
if (!token) {
console.warn("Missing token for /team/member-work");
return null;
}
const response = await fetch(url.toString(), {
cache: "no-store",
headers: authHeadersFromToken(token)
});
const data = await response.json().catch(() => ({}));
if (!response.ok || data.status === false) {
throw new Error(data.errorMsg || data.message || `Request failed: ${response.status}`);
}
return data.body || data.data || null;
} catch (error) {
console.error("Failed to load member work:", error);
return null;
}
}
function syncLocalizedLinks() {
const nextParams = new URLSearchParams(params);
const token = readRawParam("token");
if (token) nextParams.set("token", token);
if (currentLang) nextParams.set("lang", currentLang);
const query = nextParams.toString();
document.querySelectorAll('a[href^="../withdraw-exchange/index.html"]').forEach((link) => {
link.setAttribute("href", `../withdraw-exchange/index.html${query ? `?${query}` : ""}`);
});
}
function syncPolicyLink() {
const link = document.querySelector("#anchorPolicyLink");
if (!link) return;
const nextParams = new URLSearchParams(params);
const token = readRawParam("token");
const teamId = currentTeamId;
if (token) nextParams.set("token", token);
if (currentLang) nextParams.set("lang", currentLang);
if (teamId) nextParams.set("teamId", teamId);
const query = nextParams.toString();
link.setAttribute("href", `./policy.html${query ? `?${query}` : ""}`);
}
function setLanguageMenuOpen(open) {
const languageButton = document.querySelector(".language-button");
const languageMenu = document.querySelector(".language-menu");
if (!languageButton || !languageMenu) return;
languageMenu.hidden = !open;
languageButton.setAttribute("aria-expanded", String(open));
}
function renderLanguageControls(messages) {
const languageButton = document.querySelector(".language-button");
if (languageButton) {
languageButton.textContent = languageLabels[currentLang];
languageButton.setAttribute("aria-label", messages.language_button_aria || "Change language");
}
document.querySelectorAll(".language-menu [data-lang]").forEach((button) => {
const isActive = button.dataset.lang === currentLang;
button.classList.toggle("is-active", isActive);
button.setAttribute("aria-pressed", String(isActive));
});
}
async function applyLanguage(lang) {
currentLang = normalizeLanguage(lang);
const messages = await loadMessages(currentLang);
currentMessages = messages;
document.documentElement.lang = currentLang;
document.documentElement.dir = currentLang === "ar" ? "rtl" : "ltr";
document.title = messages.document_title || messages.title || "Host Center";
renderMessages(messages);
renderLanguageControls(messages);
syncLocalizedLinks();
syncPolicyLink();
renderProfile(currentProfile, messages);
renderAgencyProfile(currentAgencyProfile, messages);
renderDailyTargets();
renderFoundJoinAgency(foundJoinAgency);
renderPendingJoinRecord(pendingJoinRecord);
}
document.querySelector(".language-button")?.addEventListener("click", (event) => {
event.stopPropagation();
const languageMenu = document.querySelector(".language-menu");
setLanguageMenuOpen(Boolean(languageMenu?.hidden));
});
document.querySelectorAll(".language-menu [data-lang]").forEach((button) => {
button.addEventListener("click", (event) => {
event.stopPropagation();
setLanguageMenuOpen(false);
applyLanguage(button.dataset.lang);
});
});
document.addEventListener("click", () => {
setLanguageMenuOpen(false);
});
document.querySelector(".back-button")?.addEventListener("click", () => {
closeHostCenterPage();
});
document.querySelector(".agency")?.addEventListener("click", () => {
if (currentIdentity && shouldOpenJoinAgency(currentIdentity)) setJoinAgencyModalOpen(true);
});
document.querySelector("#joinAgencyButton")?.addEventListener("click", () => {
setJoinAgencyModalOpen(true);
});
document.querySelector("#joinAgencySearchForm")?.addEventListener("submit", searchJoinAgency);
document.querySelector("#joinAgencyApplyButton")?.addEventListener("click", () => {
applyToJoinAgency();
});
document.querySelector("#joinAgencyCancelButton")?.addEventListener("click", () => {
cancelJoinApplication();
});
document.querySelectorAll("[data-close-join-agency]").forEach((node) => {
node.addEventListener("click", () => {
setJoinAgencyModalOpen(false);
});
});
document.querySelector(".gift-value")?.addEventListener("click", () => {
setDailyModalOpen(true);
});
document.querySelectorAll("[data-close-daily]").forEach((node) => {
node.addEventListener("click", () => {
setDailyModalOpen(false);
});
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
setDailyModalOpen(false);
setJoinAgencyModalOpen(false);
}
});
ensureAvatarFallback("Oscar");
async function initializePage() {
setLoading(true);
try {
await applyLanguage(currentLang);
const identity = await fetchIdentity();
setIdentityView(identity);
const profile = await fetchUserProfile();
const pendingRequests = [];
if (!shouldOpenJoinAgency(identity)) {
pendingRequests.push(fetchAgencyProfile());
pendingRequests.push(fetchTeamEntry());
pendingRequests.push(fetchBankBalance());
const memberId = profile?.id || profile?.account;
if (memberId) pendingRequests.push(fetchStreamingDuration(memberId));
}
await Promise.allSettled(pendingRequests);
} catch (error) {
console.error("Failed to initialize host center:", error);
setIdentityView(currentIdentity);
} finally {
setLoading(false);
}
}
initializePage();
})();