diff --git a/miniapp/index.html b/miniapp/index.html index 1a37f7ce..322f5f83 100644 --- a/miniapp/index.html +++ b/miniapp/index.html @@ -254,18 +254,6 @@ z-index: 1; } - .logo-icon.has-image { - font-size: 0; - line-height: 0; - } - - .logo-icon.has-image img { - width: 60px; - height: 60px; - object-fit: contain; - display: block; - } - .logo { font-size: 28px; font-weight: 800; @@ -3865,13 +3853,6 @@ margin-bottom: 4px; } - .app-meta { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 6px; - } - .featured-badge { display: inline-block; background: linear-gradient(135deg, var(--primary), rgba(var(--primary-rgb), 0.8)); @@ -3884,19 +3865,6 @@ letter-spacing: 0.5px; } - .app-platform { - display: inline-flex; - align-items: center; - padding: 4px 10px; - border-radius: 20px; - background: var(--bg-secondary); - color: var(--text-secondary); - font-size: 12px; - font-weight: 600; - text-transform: none; - letter-spacing: 0; - } - .app-steps { margin-top: 16px; } @@ -5526,15 +5494,7 @@ // All original constants and functions const LANG_STORAGE_KEY = 'remnawave-miniapp-language'; - const DEFAULT_SUPPORTED_LANGUAGES = ['en', 'ru']; - const BASE_LANGUAGE_ORDER = ['en', 'ru']; - const supportedLanguages = new Set(DEFAULT_SUPPORTED_LANGUAGES); - const LANGUAGE_LABELS = { - en: '🇬🇧 English', - ru: '🇷🇺 Русский', - zh: '🇨🇳 中文', - fa: '🇮🇷 فارسی', - }; + const SUPPORTED_LANGUAGES = ['en', 'ru']; const translations = { en: { @@ -5779,11 +5739,8 @@ 'apps.no_data': 'No installation guide available for this platform yet.', 'apps.featured': 'Recommended', 'apps.step.download': 'Download & install', - 'apps.step.before_add': 'Before adding subscription', 'apps.step.add': 'Add subscription', - 'apps.step.after_add': 'If the subscription is not added', 'apps.step.connect': 'Connect & use', - 'apps.button.open_link': 'Open link', 'faq.title': 'FAQ', 'faq.item_default_title': 'Question {index}', 'faq.item_empty': 'Answer will be added soon.', @@ -5905,11 +5862,6 @@ 'platform.android': 'Android', 'platform.pc': 'PC', 'platform.tv': 'TV', - 'platform.windows': 'Windows', - 'platform.macos': 'macOS', - 'platform.linux': 'Linux', - 'platform.android_tv': 'Android TV', - 'platform.apple_tv': 'Apple TV', 'units.gb': 'GB', 'values.unlimited': 'Unlimited', 'values.not_available': 'Not available', @@ -6182,11 +6134,8 @@ 'apps.no_data': 'Для этой платформы инструкция пока недоступна.', 'apps.featured': 'Рекомендуем', 'apps.step.download': 'Скачать и установить', - 'apps.step.before_add': 'Перед добавлением подписки', 'apps.step.add': 'Добавить подписку', - 'apps.step.after_add': 'Если подписка не добавилась', 'apps.step.connect': 'Подключиться и пользоваться', - 'apps.button.open_link': 'Открыть ссылку', 'faq.title': 'FAQ', 'faq.item_default_title': 'Вопрос {index}', 'faq.item_empty': 'Ответ будет добавлен позже.', @@ -6308,11 +6257,6 @@ 'platform.android': 'Android', 'platform.pc': 'ПК', 'platform.tv': 'ТВ', - 'platform.windows': 'Windows', - 'platform.macos': 'macOS', - 'platform.linux': 'Linux', - 'platform.android_tv': 'Android TV', - 'platform.apple_tv': 'Apple TV', 'units.gb': 'ГБ', 'values.unlimited': 'Безлимит', 'values.not_available': 'Недоступно', @@ -6364,103 +6308,6 @@ Object.assign(translations[lang], pcTranslations[lang]); }); - function getLanguageLabel(code) { - const normalized = String(code ?? '').toLowerCase(); - if (!normalized) { - return ''; - } - const base = normalized.split('-')[0]; - return LANGUAGE_LABELS[normalized] || LANGUAGE_LABELS[base] || normalized.toUpperCase(); - } - - function getSupportedLanguages() { - return Array.from(supportedLanguages); - } - - function addSupportedLanguages(locales = []) { - let updated = false; - locales.forEach(locale => { - if (!locale) { - return; - } - const normalized = String(locale).toLowerCase(); - if (!normalized) { - return; - } - if (!supportedLanguages.has(normalized)) { - supportedLanguages.add(normalized); - updated = true; - } - }); - return updated; - } - - function renderLanguageSelectOptions() { - const select = document.getElementById('languageSelect'); - if (!select) { - return; - } - - const normalizedPreferred = resolveLanguage(preferredLanguage) || 'en'; - if (!supportedLanguages.has(normalizedPreferred)) { - supportedLanguages.add(normalizedPreferred); - } - - const baseOrder = BASE_LANGUAGE_ORDER.map(lang => lang.toLowerCase()); - const extras = getSupportedLanguages() - .map(lang => lang.toLowerCase()) - .filter(lang => !baseOrder.includes(lang)); - extras.sort((a, b) => a.localeCompare(b)); - - const order = [...baseOrder]; - extras.forEach(lang => { - if (!order.includes(lang)) { - order.push(lang); - } - }); - if (!order.includes(normalizedPreferred)) { - order.push(normalizedPreferred); - } - - const seen = new Set(); - const optionsHtml = order.map(lang => { - const normalized = String(lang).toLowerCase(); - if (!normalized || seen.has(normalized)) { - return ''; - } - seen.add(normalized); - const value = escapeHtml(normalized); - const label = escapeHtml(getLanguageLabel(normalized)); - return ``; - }).filter(Boolean).join(''); - - select.innerHTML = optionsHtml; - select.value = normalizedPreferred; - - if (preferredLanguage !== normalizedPreferred) { - preferredLanguage = normalizedPreferred; - } - } - - const PLATFORM_GROUPS = { - ios: ['ios'], - android: ['android'], - pc: ['windows', 'macos', 'linux'], - tv: ['androidTV', 'appleTV'], - }; - - const HAPP_APP_IDS = new Set(['happ', 'happ-app']); - - const PLATFORM_LABEL_KEYS = { - ios: 'platform.ios', - android: 'platform.android', - windows: 'platform.windows', - macos: 'platform.macos', - linux: 'platform.linux', - androidTV: 'platform.android_tv', - appleTV: 'platform.apple_tv', - }; - const LEGAL_DOCUMENT_CONFIG = { public_offer: { icon: '📜', @@ -6485,44 +6332,26 @@ return; } + const { + service_name: rawServiceName = {}, + service_description: rawServiceDescription = {} + } = branding; + function normalizeMap(map) { const normalized = {}; - if (!map) { - return normalized; - } - - if (typeof map === 'string') { - const normalizedValue = normalizeLocalizedValue(map); - if (normalizedValue) { - normalized.default = normalizedValue; - } - return normalized; - } - Object.entries(map || {}).forEach(([lang, value]) => { - const normalizedValue = normalizeLocalizedValue(value); - if (!normalizedValue) { + if (typeof value !== 'string') { return; } - normalized[lang.toLowerCase()] = normalizedValue; + const trimmed = value.trim(); + if (!trimmed) { + return; + } + normalized[lang.toLowerCase()] = trimmed; }); - return normalized; } - function mergeLocaleMaps(...sources) { - const result = {}; - sources.forEach(source => { - const normalized = normalizeMap(source); - Object.entries(normalized).forEach(([lang, value]) => { - if (value) { - result[lang] = value; - } - }); - }); - return result; - } - function applyKey(key, map) { const normalized = normalizeMap(map); if (!Object.keys(normalized).length) { @@ -6559,50 +6388,14 @@ }); } - const serviceNameMap = mergeLocaleMaps( - branding.service_name, - branding.name, - branding.title - ); - const serviceDescriptionMap = mergeLocaleMaps( - branding.service_description, - branding.description, - branding.subtitle - ); - - applyKey('app.name', serviceNameMap); - applyKey('app.title', serviceNameMap); - applyKey('app.subtitle', serviceDescriptionMap); - - const logoUrl = normalizeUrl( - branding.logoUrl - || branding.logo_url - || branding.logo - || null - ); - - if (logoUrl) { - const logoIcon = document.querySelector('.logo-icon'); - if (logoIcon) { - logoIcon.classList.add('has-image'); - logoIcon.innerHTML = ''; - const img = document.createElement('img'); - img.src = logoUrl; - const altText = serviceNameMap.default - || serviceNameMap.en - || serviceNameMap.ru - || 'Logo'; - img.alt = altText; - logoIcon.appendChild(img); - } - } + applyKey('app.name', rawServiceName); + applyKey('app.title', rawServiceName); + applyKey('app.subtitle', rawServiceDescription); } let userData = null; let appsConfig = {}; let currentPlatform = 'android'; - let detectedDevicePlatform = 'android'; - let cachedHappCryptolinkRedirectTemplate; let configPurchaseUrl = null; let subscriptionPurchaseUrl = null; let preferredLanguage = 'en'; @@ -7226,11 +7019,11 @@ return null; } const normalized = String(lang).toLowerCase(); - if (supportedLanguages.has(normalized)) { + if (SUPPORTED_LANGUAGES.includes(normalized)) { return normalized; } const short = normalized.split('-')[0]; - if (supportedLanguages.has(short)) { + if (SUPPORTED_LANGUAGES.includes(short)) { return short; } return null; @@ -7445,7 +7238,6 @@ } } - renderLanguageSelectOptions(); applyTranslations(); updateConnectButtonLabel(); @@ -7692,55 +7484,10 @@ } userData = payload; - cachedHappCryptolinkRedirectTemplate = undefined; userData.subscriptionUrl = userData.subscription_url || null; userData.subscriptionCryptoLink = userData.subscription_crypto_link || null; userData.referral = userData.referral || null; - const happData = payload?.happ; - if (happData && typeof happData === 'object') { - userData.happ = { ...happData }; - - const happCryptoLinkCandidate = happData.cryptoLink - ?? happData.crypto_link - ?? null; - if (happCryptoLinkCandidate) { - if (!userData.happ_crypto_link) { - userData.happ_crypto_link = happCryptoLinkCandidate; - } - if (!userData.happCryptoLink) { - userData.happCryptoLink = happCryptoLinkCandidate; - } - } - - const happRedirectCandidate = happData.cryptolinkRedirectLink - ?? happData.cryptolink_redirect_link - ?? happData.redirectLink - ?? happData.redirect_link - ?? null; - if (happRedirectCandidate) { - if (!userData.happ_cryptolink_redirect_link) { - userData.happ_cryptolink_redirect_link = happRedirectCandidate; - } - if (!userData.happCryptolinkRedirectLink) { - userData.happCryptolinkRedirectLink = happRedirectCandidate; - } - } - - const happLinkCandidate = happData.link - ?? happData.appLink - ?? happData.url - ?? null; - if (happLinkCandidate) { - if (!userData.happ_link) { - userData.happ_link = happLinkCandidate; - } - if (!userData.happLink) { - userData.happLink = happLinkCandidate; - } - } - } - if (hasPaidSubscription()) { subscriptionAutopayState.loading = true; subscriptionAutopayState.saving = false; @@ -7885,13 +7632,9 @@ } const data = await response.json(); - appsConfig = sanitizeAppsConfig(data?.platforms || {}); + appsConfig = data?.platforms || {}; const configData = data?.config || {}; - if (configData.branding || data?.branding) { - applyBrandingOverrides(configData.branding || data.branding); - } - const configUrl = normalizeUrl( configData.subscriptionPurchaseUrl || configData.subscription_purchase_url @@ -7905,45 +7648,6 @@ if (configUrl) { configPurchaseUrl = configUrl; } - - const additionalLocalesRaw = configData.additionalLocales - || configData.additional_locales - || data?.additionalLocales - || data?.additional_locales - || []; - const additionalLocales = (Array.isArray(additionalLocalesRaw) - ? additionalLocalesRaw - : [additionalLocalesRaw]) - .map(locale => typeof locale === 'string' ? locale.trim() : '') - .filter(Boolean); - - const previousLanguage = preferredLanguage; - const languagesUpdated = addSupportedLanguages(additionalLocales); - - if (languagesUpdated && !languageLockedByUser) { - const storedLanguageRaw = safeGetStoredLanguage(); - const storedResolved = resolveLanguage(storedLanguageRaw); - if (storedResolved) { - preferredLanguage = storedResolved; - languageLockedByUser = true; - } else { - const telegramResolved = resolveLanguage(tg.initDataUnsafe?.user?.language_code); - if (telegramResolved) { - preferredLanguage = telegramResolved; - } - } - } - - const normalizedPreferred = resolveLanguage(preferredLanguage) || 'en'; - if (preferredLanguage !== normalizedPreferred) { - preferredLanguage = normalizedPreferred; - } - - renderLanguageSelectOptions(); - - if (languagesUpdated || preferredLanguage !== previousLanguage) { - refreshAfterLanguageChange(); - } } catch (error) { console.warn('Unable to load apps configuration:', error); appsConfig = {}; @@ -9060,14 +8764,12 @@ function detectPlatform() { const userAgent = navigator.userAgent.toLowerCase(); if (userAgent.includes('iphone') || userAgent.includes('ipad')) { - detectedDevicePlatform = 'ios'; + currentPlatform = 'ios'; } else if (userAgent.includes('android')) { - detectedDevicePlatform = 'android'; + currentPlatform = 'android'; } else { - detectedDevicePlatform = 'pc'; + currentPlatform = 'pc'; } - - currentPlatform = detectedDevicePlatform; } function setActivePlatformButton() { @@ -9076,25 +8778,20 @@ }); } - function getPlatformLabel(platformKey) { - const labelKey = PLATFORM_LABEL_KEYS[platformKey]; - if (!labelKey) { - return null; - } - const label = t(labelKey); - return label === labelKey ? platformKey : label; + function getPlatformKey(platform) { + const mapping = { + ios: 'ios', + android: 'android', + pc: 'windows', + tv: 'androidTV', + mac: 'macos' + }; + return mapping[platform] || platform; } function getAppsForCurrentPlatform() { - const platformKeys = PLATFORM_GROUPS[currentPlatform] || [currentPlatform]; - const aggregated = []; - platformKeys.forEach(key => { - const apps = Array.isArray(appsConfig?.[key]) ? appsConfig[key] : []; - apps.forEach(app => { - aggregated.push({ ...app, __platformKey: key }); - }); - }); - return aggregated; + const platformKey = getPlatformKey(currentPlatform); + return appsConfig?.[platformKey] || []; } function resolveConnectButtonLabel() { @@ -9106,10 +8803,7 @@ function renderConnectActionButton(appId = '') { const label = escapeHtml(resolveConnectButtonLabel()); - const normalizedId = typeof appId === 'string' ? appId.trim() : ''; - const appAttribute = normalizedId - ? ` data-app-id="${escapeHtml(normalizedId)}"` - : ''; + const appAttribute = appId ? ` data-app-id="${escapeHtml(appId)}"` : ''; return `