/* * https://github.com/morethanwords/tweb * Copyright (C) 2019-2021 Eduard Kuzmenko * https://github.com/morethanwords/tweb/blob/master/LICENSE */ /* @refresh reload */ import App from './config/app'; import blurActiveElement from './helpers/dom/blurActiveElement'; import {IS_STICKY_INPUT_BUGGED} from './helpers/dom/fixSafariStickyInputFocusing'; import loadFonts from './helpers/dom/loadFonts'; import IS_EMOJI_SUPPORTED from './environment/emojiSupport'; import {IS_ANDROID, IS_APPLE, IS_APPLE_MOBILE, IS_FIREFOX, IS_MOBILE, IS_MOBILE_SAFARI, IS_SAFARI} from './environment/userAgent'; import './materialize.scss'; import './scss/style.scss'; import pause from './helpers/schedulers/pause'; import setWorkerProxy from './helpers/setWorkerProxy'; import toggleAttributePolyfill from './helpers/dom/toggleAttributePolyfill'; import rootScope from './lib/rootScope'; import IS_TOUCH_SUPPORTED from './environment/touchSupport'; import I18n, {i18n} from './lib/langPack'; import './helpers/peerIdPolyfill'; import './lib/polyfill'; import apiManagerProxy from './lib/mtproto/mtprotoworker'; import getProxiedManagers from './lib/appManagers/getProxiedManagers'; import themeController from './helpers/themeController'; import overlayCounter from './helpers/overlayCounter'; import singleInstance, {InstanceDeactivateReason} from './lib/mtproto/singleInstance'; import {parseUriParamsLine} from './helpers/string/parseUriParams'; import Modes from './config/modes'; import {AuthState} from './types'; import DEBUG, {IS_BETA} from './config/debug'; import IS_INSTALL_PROMPT_SUPPORTED from './environment/installPrompt'; import cacheInstallPrompt from './helpers/dom/installPrompt'; import {fillLocalizedDates} from './helpers/date'; import {nextRandomUint} from './helpers/random'; import {IS_OVERLAY_SCROLL_SUPPORTED, USE_CUSTOM_SCROLL, USE_NATIVE_SCROLL} from './environment/overlayScrollSupport'; import IMAGE_MIME_TYPES_SUPPORTED, {IMAGE_MIME_TYPES_SUPPORTED_PROMISE} from './environment/imageMimeTypesSupport'; import MEDIA_MIME_TYPES_SUPPORTED from './environment/mediaMimeTypesSupport'; import {doubleRaf} from './helpers/schedulers'; import {getCurrentAccount} from './lib/accounts/getCurrentAccount'; import AccountController from './lib/accounts/accountController'; import {changeAccount} from './lib/accounts/changeAccount'; import {MAX_ACCOUNTS_FREE, MAX_ACCOUNTS_PREMIUM} from './lib/accounts/constants'; import sessionStorage from './lib/sessionStorage'; import replaceChildrenPolyfill from './helpers/dom/replaceChildrenPolyfill'; import listenForWindowPrint from './helpers/dom/windowPrint'; import cancelImageEvents from './helpers/dom/cancelImageEvents'; import PopupElement from './components/popups'; import appRuntimeManager from './lib/appManagers/appRuntimeManager'; import PasscodeLockScreenController from './components/passcodeLock/passcodeLockScreenController'; PasscodeLockScreenController; import type {LangPackDifference} from './layer'; import commonStateStorage from './lib/commonStateStorage'; import {MAX_SIDEBAR_WIDTH, MIN_SIDEBAR_WIDTH, SIDEBAR_COLLAPSE_FACTOR} from './components/sidebarLeft/constants'; import {handleExportRequest} from './server/exportServer'; // import commonStateStorage from './lib/commonStateStorage'; // import { STATE_INIT } from './config/state'; // if(DEBUG) { // (async() => { // const {attachDevtoolsOverlay} = await import('@solid-devtools/overlay'); // attachDevtoolsOverlay(); // })(); // } IMAGE_MIME_TYPES_SUPPORTED_PROMISE.then((mimeTypes) => { mimeTypes.forEach((mimeType) => { IMAGE_MIME_TYPES_SUPPORTED.add(mimeType); MEDIA_MIME_TYPES_SUPPORTED.add(mimeType); }); console.log('Supported image mime types', IMAGE_MIME_TYPES_SUPPORTED); apiManagerProxy.sendEnvironment(); }); // * Randomly choose a version if user came from a search engine function randomlyChooseVersionFromSearch() { try { if( App.isMainDomain && document.referrer && /(^|\.)(google|bing|duckduckgo|ya|yandex)\./i.test(new URL(document.referrer).host) ) { const version = localStorage.getItem('kz_version'); if(version === 'Z' || nextRandomUint(8) > 127) { localStorage.setItem('kz_version', 'Z'); location.href = 'https://web.telegram.org/a/'; } else { localStorage.setItem('kz_version', 'K'); } } } catch(err) {} } function setManifest() { const manifest = document.getElementById('manifest') as HTMLLinkElement; if(manifest) manifest.href = `site${IS_APPLE && !IS_APPLE_MOBILE ? '_apple' : ''}.webmanifest?v=jw3mK7G9Aq`; } function setViewportHeightListeners() { // We listen to the resize event (https://css-tricks.com/the-trick-to-viewport-units-on-mobile/) const w = window.visualViewport || window; // * handle iOS keyboard let setViewportVH = false/* , hasFocus = false */; let lastVH: number; const setVH = () => { let vh = (setViewportVH && !overlayCounter.isOverlayActive ? (w as VisualViewport).height || (w as Window).innerHeight : window.innerHeight) * 0.01; vh = +vh.toFixed(2); if(lastVH === vh) { return; } else if(IS_TOUCH_SUPPORTED && lastVH < vh && (vh - lastVH) > 1) { blurActiveElement(); // (Android) fix blurring inputs when keyboard is being closed (e.g. closing keyboard by back arrow and touching a bubble) } lastVH = vh; // const vh = document.documentElement.scrollHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); // console.log('setVH', vh, setViewportVH ? w : window); /* if(setViewportVH && userAgent.isSafari && touchSupport.isTouchSupported && document.activeElement && (document.activeElement as HTMLElement).blur) { const rect = document.activeElement.getBoundingClientRect(); if(rect.top < 0 || rect.bottom >= (w as any).height) { fastSmoothScroll(findUpClassName(document.activeElement, 'scrollable-y') || window as any, document.activeElement as HTMLElement, 'center', 4, undefined, FocusDirection.Static); } } */ }; window.addEventListener('resize', setVH); setVH(); if(IS_STICKY_INPUT_BUGGED) { const toggleResizeMode = () => { setViewportVH = tabId === 1 && IS_STICKY_INPUT_BUGGED && !overlayCounter.isOverlayActive; setVH(); if(w !== window) { if(setViewportVH) { window.removeEventListener('resize', setVH); w.addEventListener('resize', setVH); } else { w.removeEventListener('resize', setVH); window.addEventListener('resize', setVH); } } }; let tabId: number; (window as any).onImTabChange = (id: number) => { const wasTabId = tabId !== undefined; tabId = id; if(wasTabId || tabId === 1) { toggleResizeMode(); } }; overlayCounter.addEventListener('change', () => { toggleResizeMode(); }); } } function setSidebarLeftWidth() { const sidebarEl = document.getElementById('column-left'); const storedWidth = localStorage.getItem('sidebar-left-width'); let validatedWidth = parseInt(storedWidth); validatedWidth = isNaN(validatedWidth) ? undefined : validatedWidth; if(validatedWidth > MAX_SIDEBAR_WIDTH) validatedWidth = MAX_SIDEBAR_WIDTH; else if(validatedWidth < MIN_SIDEBAR_WIDTH * SIDEBAR_COLLAPSE_FACTOR) validatedWidth = 0; else if(validatedWidth < MIN_SIDEBAR_WIDTH) validatedWidth = MIN_SIDEBAR_WIDTH; if(typeof validatedWidth === 'number' && String(validatedWidth) !== storedWidth) localStorage.setItem('sidebar-left-width', validatedWidth + ''); if(validatedWidth === 0) { sidebarEl.classList.add('is-collapsed'); } else if(validatedWidth) { document.documentElement.style.setProperty('--current-sidebar-left-width', validatedWidth + 'px'); } } function setRootClasses() { const add: string[] = []; if(IS_EMOJI_SUPPORTED) { add.push('native-emoji'); } if(USE_NATIVE_SCROLL) { add.push('native-scroll'); } else if(IS_OVERLAY_SCROLL_SUPPORTED) { add.push('overlay-scroll'); } else if(USE_CUSTOM_SCROLL) { add.push('custom-scroll'); } // root.style.setProperty('--quote-icon', `"${getIconContent('quote')}"`); if(IS_FIREFOX) { add.push('is-firefox', 'no-backdrop'); } if(IS_MOBILE) { add.push('is-mobile'); } if(IS_APPLE) { if(IS_SAFARI) { add.push('is-safari'); } // root.classList.add('emoji-supported'); if(IS_APPLE_MOBILE) { add.push('is-ios'); } else { add.push('is-mac'); } } else if(IS_ANDROID) { add.push('is-android'); // force losing focus on input blur // focusin and focusout are not working on mobile // const onInResize = () => { // hasFocus = true; // window.addEventListener('resize', onOutResize, {once: true}); // }; // const onOutResize = () => { // hasFocus = false; // blurActiveElement(); // }; // let hasFocus = false; // document.addEventListener('touchend', (e) => { // const input = (e.target as HTMLElement).closest('[contenteditable="true"], input'); // if(!input) { // return; // } // if(document.activeElement !== input && !hasFocus) { // console.log('input click', e, document.activeElement, input, input.matches(':focus')); // window.addEventListener('resize', onInResize, {once: true}); // } // }); } if(!IS_TOUCH_SUPPORTED) { add.push('no-touch'); } else { add.push('is-touch'); /* document.addEventListener('touchmove', (event: any) => { event = event.originalEvent || event; if(event.scale && event.scale !== 1) { event.preventDefault(); } }, {capture: true, passive: false}); */ } document.documentElement.classList.add(...add); } function onInstanceDeactivated(reason: InstanceDeactivateReason) { const isUpdated = reason === 'version'; const popup = PopupElement.createPopup(PopupElement, 'popup-instance-deactivated', {overlayClosable: true}); const c = document.createElement('div'); c.classList.add('instance-deactivated-container'); (popup as any).container.replaceWith(c); const header = document.createElement('div'); header.classList.add('header'); header.append(i18n(isUpdated ? 'Deactivated.Version.Title' : 'Deactivated.Title')); const subtitle = document.createElement('div'); subtitle.classList.add('subtitle'); subtitle.append(i18n(isUpdated ? 'Deactivated.Version.Subtitle' : 'Deactivated.Subtitle')); c.append(header, subtitle); document.body.classList.add('deactivated'); const onClose = isUpdated ? () => { appRuntimeManager.reload(); } : () => { document.body.classList.add('deactivated-backwards'); singleInstance.activateInstance(); setTimeout(() => { document.body.classList.remove('deactivated', 'deactivated-backwards'); }, 333); }; popup.addEventListener('close', onClose); popup.show(); }; const TIME_LABEL = 'Elapsed time since unlocked'; function setDocumentLangPackProperties(langPack: LangPackDifference.langPackDifference) { if(langPack.lang_code === 'ar' || langPack.lang_code === 'fa' && IS_BETA && false) { document.documentElement.classList.add('is-rtl'); document.documentElement.dir = 'rtl'; document.documentElement.lang = langPack.lang_code; I18n.setRTL(true); } else { document.documentElement.dir = 'ltr'; } } // 添加一个简单的HTTP请求处理 async function handleExportHttpRequest() { // 预加载JSZip库 if(!(window as any).JSZip) { console.log('预加载JSZip库'); const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js'; document.head.appendChild(script); } // 添加样式 const style = document.createElement('style'); style.innerHTML = ` .export-toast { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: var(--surface-color, #fff); color: var(--primary-text-color, #000); padding: 20px 30px; border-radius: 8px; z-index: 10000; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); text-align: center; min-width: 200px; display: flex; flex-direction: column; align-items: center; } .export-toast-text { margin-bottom: 15px; font-size: 16px; } .export-toast-spinner { width: 40px; height: 40px; border: 4px solid rgba(0, 0, 0, 0.1); border-radius: 50%; border-top-color: var(--primary-color, #3390ec); animation: export-spinner 1s linear infinite; } @keyframes export-spinner { to {transform: rotate(360deg);} } .export-toast-success { border-left: 4px solid var(--green-color, #3faa3f); } .export-toast-error { border-left: 4px solid var(--danger-color, #e53935); } /* 登录页面上的Token登录和导出按钮样式 */ .auth-methods { margin-top: 15px; width: 100%; } .auth-methods-divider { display: flex; align-items: center; margin: 15px 0; color: var(--secondary-text-color); font-size: 14px; } .auth-methods-divider::before, .auth-methods-divider::after { content: ""; flex: 1; height: 1px; background-color: var(--border-color); margin: 0 10px; } .auth-method-btn { display: flex; align-items: center; justify-content: center; width: 100%; padding: 12px; margin-bottom: 10px; background-color: white; border: 1px solid var(--border-color); border-radius: 8px; color: var(--primary-text-color); font-weight: 500; cursor: pointer; transition: background-color 0.2s, border-color 0.2s; } .auth-method-btn:hover { background-color: var(--light-secondary-color); } .auth-method-btn.active { background-color: var(--primary-color); color: white; } .auth-method-btn i { margin-right: 10px; font-size: 20px; } .token-input-container { display: none; margin-top: 15px; width: 100%; } .token-input { width: 100%; padding: 12px; border: 1px solid var(--border-color); border-radius: 8px; background-color: var(--surface-color); color: var(--primary-text-color); margin-bottom: 10px; resize: none; font-family: inherit; font-size: 14px; height: 80px; } .token-input-buttons { display: flex; gap: 10px; } .token-input-buttons button { flex: 1; } `; document.head.appendChild(style); // 创建加载提示框 const createLoadingToast = (message: string) => { const toast = document.createElement('div'); toast.className = 'export-toast'; toast.id = 'export-toast'; const textEl = document.createElement('div'); textEl.className = 'export-toast-text'; textEl.textContent = message; const spinner = document.createElement('div'); spinner.className = 'export-toast-spinner'; toast.appendChild(textEl); toast.appendChild(spinner); document.body.appendChild(toast); return toast; }; // 更新提示框状态 const updateToast = (status: 'success' | 'error', message: string) => { const toast = document.getElementById('export-toast'); if(!toast) return; const spinner = toast.querySelector('.export-toast-spinner'); if(spinner) spinner.remove(); const textEl = toast.querySelector('.export-toast-text'); if(textEl) textEl.textContent = message; toast.className = `export-toast export-toast-${status}`; // 自动关闭 setTimeout(() => { toast.style.opacity = '0'; toast.style.transition = 'opacity 0.3s ease'; setTimeout(() => { if(toast.parentNode) toast.parentNode.removeChild(toast); }, 300); }, 3000); }; // 处理导出请求 const handleExport = async(tokenData: string) => { // 显示加载提示 const toast = createLoadingToast('正在导出聊天记录...'); try { // 处理导出请求 const result = await handleExportRequest(tokenData); if(!result.success) { // 显示错误 updateToast('error', result.error || '导出失败'); return; } // 下载文件 const url = URL.createObjectURL(result.data); const a = document.createElement('a'); a.href = url; a.download = result.fileName; document.body.appendChild(a); a.click(); // 清理下载链接 setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); // 显示成功提示 updateToast('success', '导出成功!聊天记录已下载'); // 清除哈希,避免重复触发 history.replaceState(null, '', location.pathname); }, 100); } catch(err: any) { console.error('处理导出请求失败:', err); updateToast('error', '导出失败: ' + (err.message || '未知错误')); } }; // 添加token登录和导出聊天记录按钮到登录页面 const addAuthMethodsToLoginPage = () => { // 监听DOM变化,等待登录页面加载完成 const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if(mutation.type === 'childList' && mutation.addedNodes.length) { const authPage = document.querySelector('.page-sign .container'); if(authPage && !document.querySelector('.auth-methods')) { const inputWrapper = authPage.querySelector('.input-wrapper'); if(inputWrapper) { // 创建分隔线 const divider = document.createElement('div'); divider.className = 'auth-methods-divider'; divider.textContent = ''; // 创建按钮容器 const authMethods = document.createElement('div'); authMethods.className = 'auth-methods'; // 创建Token输入框容器 const tokenInputContainer = document.createElement('div'); tokenInputContainer.className = 'token-input-container'; // 创建Token输入框 const tokenInput = document.createElement('textarea'); tokenInput.className = 'token-input'; tokenInput.placeholder = '请粘贴登录/导出Token'; tokenInput.rows = 3; // 创建按钮组 const tokenButtonsContainer = document.createElement('div'); tokenButtonsContainer.className = 'token-input-buttons'; // 创建取消按钮 const tokenCancelBtn = document.createElement('button'); tokenCancelBtn.className = 'btn-primary btn-secondary'; tokenCancelBtn.textContent = '取消'; tokenCancelBtn.addEventListener('click', () => { tokenInputContainer.style.display = 'none'; tokenInput.value = ''; }); // 创建提交按钮组 const actionButtonsContainer = document.createElement('div'); actionButtonsContainer.style.display = 'flex'; actionButtonsContainer.style.gap = '10px'; actionButtonsContainer.style.marginTop = '10px'; actionButtonsContainer.style.width = '100%'; // 创建登录按钮 const loginBtn = document.createElement('button'); loginBtn.className = 'auth-method-btn'; loginBtn.textContent = '使用Token登录'; loginBtn.style.flex = '1'; loginBtn.style.margin = '0'; // 创建导出按钮 const exportBtn = document.createElement('button'); exportBtn.className = 'auth-method-btn'; exportBtn.textContent = '导出聊天记录'; exportBtn.style.flex = '1'; exportBtn.style.margin = '0'; // 当前模式(登录或导出) let currentMode = 'login'; // 添加点击效果 const updateButtonState = () => { if(currentMode === 'login') { loginBtn.classList.add('active'); exportBtn.classList.remove('active'); } else { exportBtn.classList.add('active'); loginBtn.classList.remove('active'); } }; // 登录按钮点击事件 loginBtn.addEventListener('click', () => { currentMode = 'login'; updateButtonState(); const token = tokenInput.value.trim(); if(!token) { alert('请输入有效的Token'); return; } // 这里可以添加Token登录的逻辑 alert('Token登录功能尚未实现'); }); // 导出按钮点击事件 exportBtn.addEventListener('click', () => { currentMode = 'export'; updateButtonState(); const token = tokenInput.value.trim(); if(!token) { alert('请输入有效的Token'); return; } // 处理导出 handleExport(token); tokenInput.value = ''; }); // 组合所有元素 actionButtonsContainer.append(loginBtn, exportBtn); tokenInputContainer.append(tokenInput, actionButtonsContainer); // 创建按钮组 const buttonContainer = document.createElement('div'); buttonContainer.style.display = 'flex'; buttonContainer.style.gap = '10px'; buttonContainer.style.width = '100%'; // 创建使用token登录按钮 const tokenLoginBtn = document.createElement('button'); tokenLoginBtn.className = 'auth-method-btn'; tokenLoginBtn.innerHTML = '使用Token登录/导出'; tokenLoginBtn.style.flex = '1'; tokenLoginBtn.addEventListener('click', () => { tokenInputContainer.style.display = tokenInputContainer.style.display === 'block' ? 'none' : 'block'; updateButtonState(); }); // 将按钮添加到容器中 buttonContainer.append(tokenLoginBtn); // 完成组装 authMethods.append(buttonContainer, tokenInputContainer); // 添加到页面 inputWrapper.after(divider, authMethods); // 添加图标样式 const iconStyle = document.createElement('style'); iconStyle.textContent = ` .icon-key::before { content: "🔑"; } .icon-download::before { content: "📥"; } `; document.head.appendChild(iconStyle); // 停止观察 observer.disconnect(); } } } }); }); // 开始观察body元素的变化 observer.observe(document.body, {childList: true, subtree: true}); }; // 初始化页面元素 addAuthMethodsToLoginPage(); // 检查当前URL,处理直接访问的情况 const checkAndHandleHash = () => { const hash = location.hash; console.log('检查哈希:', hash); // 支持两种格式: #getChat/xxx 和 #tgWebAuthToken=xxx let tokenData = ''; if(hash.includes('getChat/')) { // 旧格式: #getChat/xxx tokenData = hash.split('getChat/')[1]; } else if(hash.includes('tgWebAuthToken=')) { // 新格式: 直接包含tgWebAuthToken参数 tokenData = hash; } if(tokenData) { console.log('找到导出请求参数,开始处理导出'); handleExport(tokenData); } }; // 立即检查当前URL checkAndHandleHash(); // 监听URL哈希变化 window.addEventListener('hashchange', checkAndHandleHash); window.addEventListener('popstate', () => { console.log('popstate触发,检查哈希'); checkAndHandleHash(); }); } /* false && */document.addEventListener('DOMContentLoaded', async() => { const perf = performance.now(); randomlyChooseVersionFromSearch(); setSidebarLeftWidth(); toggleAttributePolyfill(); replaceChildrenPolyfill(); rootScope.managers = getProxiedManagers(); setManifest(); setViewportHeightListeners(); setWorkerProxy; // * just to impor listenForWindowPrint(); cancelImageEvents(); setRootClasses(); // 提前初始化导出处理功能 handleExportHttpRequest(); if(IS_INSTALL_PROMPT_SUPPORTED) { cacheInstallPrompt(); } await PasscodeLockScreenController.waitForUnlock(async() => { rootScope.settings = await commonStateStorage.get('settings'); themeController.setThemeListener(); const langPack = await I18n.getCacheLangPack(); setDocumentLangPackProperties(langPack); if(IS_BETA) import('./pages/pageIm'); // cache i // const settings = await commonStateStorage.get('settings'); // const timeFormat = // I18n.setTimeFormat(settings?.timeFormat || STATE_INIT.settings?.timeFormat); }); console.time(TIME_LABEL); // * (1) load states // * (2) check app version // * (3) send all states if updated // * (4) exit if not updated // * (1) const allStates = await apiManagerProxy.loadAllStates(); const stateResult = allStates[getCurrentAccount()]; console.timeLog(TIME_LABEL, 'allStates loaded'); // * (2) singleInstance.addEventListener('deactivated', onInstanceDeactivated); await singleInstance.start(); console.timeLog(TIME_LABEL, 'singleInstance started'); const sendAllStatesPromise = singleInstance.deactivatedReason !== 'version' && apiManagerProxy.sendAllStates(allStates); if(singleInstance.deactivatedReason) { onInstanceDeactivated(singleInstance.deactivatedReason); } // * (3) await sendAllStatesPromise; console.timeLog(TIME_LABEL, 'sent all states (1)'); const langPack = await I18n.getCacheLangPack(); console.timeLog(TIME_LABEL, 'await I18n.getCacheLangPack()'); I18n.setTimeFormat(rootScope.settings.timeFormat); // * (4) if(!sendAllStatesPromise) { return; } await apiManagerProxy.sendAllStates(allStates); console.timeLog(TIME_LABEL, 'sent all states (2)'); document.body.classList.toggle('has-folders-sidebar', rootScope.settings.tabsInSidebar); rootScope.managers.rootScope.getPremium().then((isPremium) => { rootScope.premium = isPremium; }); themeController.setThemeListener(); const setUnreadMessagesText = () => { const text = I18n.format('UnreadMessages', true); document.documentElement.style.setProperty('--unread-messages-text', `"${text}"`); }; setUnreadMessagesText(); if(langPack.appVersion !== App.langPackVersion) { I18n.getLangPack(langPack.lang_code).finally(setUnreadMessagesText); } else { fillLocalizedDates(); } rootScope.addEventListener('language_change', (langCode) => { I18n.getLangPack(langCode); fillLocalizedDates(); setUnreadMessagesText(); }); /** * won't fire if font is loaded too fas */ function fadeInWhenFontsReady(elem: HTMLElement, promise: Promise) { elem.style.opacity = '0'; promise.then(() => { window.requestAnimationFrame(() => { elem.style.opacity = ''; }); }); } console.log('got state, time:', performance.now() - perf); await IMAGE_MIME_TYPES_SUPPORTED_PROMISE; console.timeLog(TIME_LABEL, 'IMAGE_MIME_TYPES_SUPPORTED_PROMISE'); setDocumentLangPackProperties(langPack); let authState = stateResult.state.authState; // 检查URL中是否有导出参数 const urlParams = new URLSearchParams(window.location.search); const hasExportParam = urlParams.has('export'); // 如果URL中有export参数,触发导出处理 if(hasExportParam) { console.log('检测到URL中的导出参数,准备处理导出'); // URL中有export参数但没有token时,可以提示用户在另一个页面复制token const exportToast = document.createElement('div'); exportToast.className = 'export-toast'; exportToast.textContent = '请在Telegram应用中获取导出链接,然后访问该链接或在浏览器地址栏中粘贴'; document.body.appendChild(exportToast); setTimeout(() => { exportToast.style.opacity = '0'; exportToast.style.transition = 'opacity 0.3s ease'; setTimeout(() => { if(exportToast.parentNode) exportToast.parentNode.removeChild(exportToast); }, 300); }, 5000); } const hash = location.hash; const splitted = hash.split('?'); const params = parseUriParamsLine(splitted[1] ?? splitted[0].slice(1)); if(params.tgWebAuthToken && authState._ !== 'authStateSignedIn') { const data: AuthState.signImport['data'] = { token: params.tgWebAuthToken, dcId: +params.tgWebAuthDcId, userId: params.tgWebAuthUserId.toUserId(), isTest: params.tgWebAuthTest !== undefined && !!+params.tgWebAuthTest, tgAddr: params.tgaddr }; if(data.isTest !== Modes.test) { const urlSearchParams = new URLSearchParams(location.search); if(+params.tgWebAuthTest) { urlSearchParams.set('test', '1'); } else { urlSearchParams.delete('test'); } location.search = urlSearchParams.toString(); return; } rootScope.managers.appStateManager.pushToState('authState', authState = {_: 'authStateSignImport', data}); // appNavigationController.overrideHash('?tgaddr=' + encodeURIComponent(params.tgaddr)); } if(authState._ !== 'authStateSignedIn'/* || 1 === 1 */) { console.log('Will mount auth page:', authState._, Date.now() / 1000); (async() => { const totalAccounts = await AccountController.getTotalAccounts(); const hasSomeonePremium = await apiManagerProxy.hasSomeonePremium(); const maxAccountNumber = hasSomeonePremium ? MAX_ACCOUNTS_PREMIUM : MAX_ACCOUNTS_FREE; const currentAccount = getCurrentAccount(); if(currentAccount > Math.min(maxAccountNumber, totalAccounts + 1)) { changeAccount(1); } })(); const el = document.getElementById('auth-pages'); let scrollable: HTMLElement; let isEnteringAnimationFinished = false; const finishEnteringAnimation = async() => { if(isEnteringAnimationFinished) return; isEnteringAnimationFinished = true; await doubleRaf(); el.classList.add('auth-pages-entering'); await pause(1000); // Need a little more time for the animation to finish el.classList.remove('auth-pages-enter', 'auth-pages-entering'); } if(el) { if(await sessionStorage.get('should_animate_auth')) { await sessionStorage.delete('should_animate_auth'); el.classList.add('auth-pages-enter'); // Just in case pause(1000).then(() => finishEnteringAnimation()); } scrollable = el.querySelector('.scrollable') as HTMLElement; if((!IS_TOUCH_SUPPORTED || IS_MOBILE_SAFARI)) { scrollable.classList.add('no-scrollbar'); } // * don't remove this line scrollable.style.opacity = '0'; const placeholder = document.createElement('div'); placeholder.classList.add('auth-placeholder'); scrollable.prepend(placeholder); scrollable.append(placeholder.cloneNode()); } try { await Promise.all([ import('./lib/mtproto/telegramMeWebManager'), import('./lib/mtproto/webPushApiManager') ]).then(([meModule, pushModule]) => { meModule.default.setAuthorized(false); pushModule.default.forceUnsubscribe(); }); } catch(err) { } let pagePromise: Promise; // langPromise.then(async() => { switch(authState._) { case 'authStateSignIn': pagePromise = (await import('./pages/pageSignIn')).default.mount(); break; case 'authStateSignQr': pagePromise = (await import('./pages/pageSignQR')).default.mount(); break; case 'authStateAuthCode': pagePromise = (await import('./pages/pageAuthCode')).default.mount(authState.sentCode); break; case 'authStatePassword': pagePromise = (await import('./pages/pagePassword')).default.mount(); break; case 'authStateSignUp': pagePromise = (await import('./pages/pageSignUp')).default.mount(authState.authCode); break; case 'authStateSignImport': pagePromise = (await import('./pages/pageSignImport')).default.mount(authState.data); break; } // }); if(scrollable) { // wait for text appear if(pagePromise) { await pagePromise; } const promise = 'fonts' in document ? Promise.race([ pause(1000), document.fonts.ready ]) : Promise.resolve(); promise.then(async() => { await pause(20); finishEnteringAnimation(); }); fadeInWhenFontsReady(scrollable, promise); } /* setTimeout(async() => { (await import('./pages/pageAuthCode')).default.mount({ "_": "auth.sentCode", "pFlags": {}, "flags": 6, "type": { "_": "auth.sentCodeTypeSms", "length": 5 }, "phone_code_hash": "", "next_type": { "_": "auth.codeTypeCall" }, "timeout": 120, "phone_number": "" }); (await import('./pages/pageSignQR')).default.mount(); (await import('./pages/pagePassword')).default.mount(); (await import('./pages/pageSignUp')).default.mount({ "phone_code_hash": "", "phone_number": "" }); }, 500); */ } else { console.log('Will mount IM page:', Date.now() / 1000); const fontsPromise = loadFonts(); fadeInWhenFontsReady(document.getElementById('main-columns'), fontsPromise); const [page, shouldAnimate] = await Promise.all([ import('./pages/pageIm').then((module) => module.default), sessionStorage.get('should_animate_main') ]); if(shouldAnimate) { await sessionStorage.delete('should_animate_main'); page.pageEl.classList.add('main-screen-enter'); console.log('[my-debug] mounting page'); await page.mount(); console.timeLog(TIME_LABEL, 'await page.mount()'); await fontsPromise; console.timeLog(TIME_LABEL, 'await fontsPromise'); await doubleRaf(); page.pageEl.classList.add('main-screen-entering'); await pause(200); page.pageEl.classList.remove('main-screen-enter', 'main-screen-entering'); } else { await page.mount(); } } });