tweb/src/index.ts

417 lines
13 KiB
TypeScript
Raw Normal View History

2021-04-08 17:13:05 +02:00
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import App from './config/app';
import blurActiveElement from './helpers/dom/blurActiveElement';
2022-04-15 14:09:21 +02:00
import cancelEvent from './helpers/dom/cancelEvent';
2022-08-04 08:49:54 +02:00
import {IS_STICKY_INPUT_BUGGED} from './helpers/dom/fixSafariStickyInputFocusing';
import loadFonts from './helpers/dom/loadFonts';
import IS_EMOJI_SUPPORTED from './environment/emojiSupport';
2022-08-04 08:49:54 +02:00
import {IS_ANDROID, IS_APPLE, IS_APPLE_MOBILE, IS_FIREFOX, IS_MOBILE, IS_MOBILE_SAFARI, IS_SAFARI} from './environment/userAgent';
2021-04-08 17:13:05 +02:00
import './materialize.scss';
import './scss/style.scss';
2022-04-15 14:09:21 +02:00
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 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 from './lib/mtproto/singleInstance';
2023-03-01 11:56:06 +01:00
import {parseUriParamsLine} from './helpers/string/parseUriParams';
2022-11-18 10:43:52 +01:00
import Modes from './config/modes';
import {AuthState} from './types';
2023-01-16 18:26:51 +01:00
import {IS_BETA} from './config/debug';
import IS_INSTALL_PROMPT_SUPPORTED from './environment/installPrompt';
import cacheInstallPrompt from './helpers/dom/installPrompt';
2022-11-18 10:43:52 +01:00
// import appNavigationController from './components/appNavigationController';
2022-04-15 14:09:21 +02:00
document.addEventListener('DOMContentLoaded', async() => {
toggleAttributePolyfill();
2022-11-01 18:39:23 +01:00
// polyfill for replaceChildren
if((Node as any).prototype.replaceChildren === undefined) {
(Node as any).prototype.replaceChildren = function(...nodes: any[]) {
this.textContent = '';
// while(this.lastChild) {
// this.removeChild(this.lastChild);
// }
if(nodes) {
this.append(...nodes);
}
}
}
rootScope.managers = getProxiedManagers();
2022-06-27 22:24:56 +02:00
const manifest = document.getElementById('manifest') as HTMLLinkElement;
manifest.href = `site${IS_APPLE && !IS_APPLE_MOBILE ? '_apple' : ''}.webmanifest?v=jw3mK7G9Aq`;
singleInstance.start();
2022-04-15 14:09:21 +02:00
// 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 = () => {
const vh = (setViewportVH && !overlayCounter.isOverlayActive ? (w as VisualViewport).height || (w as Window).innerHeight : window.innerHeight) * 0.01;
2022-04-15 14:09:21 +02:00
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)
2021-04-08 17:13:05 +02:00
}
2022-04-15 14:09:21 +02:00
lastVH = vh;
2021-04-15 10:44:27 +02:00
2022-08-04 08:49:54 +02:00
// const vh = document.documentElement.scrollHeight * 0.01;
2022-04-15 14:09:21 +02:00
document.documentElement.style.setProperty('--vh', `${vh}px`);
2021-04-08 17:13:05 +02:00
2022-08-04 08:49:54 +02:00
// console.log('setVH', vh, setViewportVH ? w : window);
2021-04-08 17:13:05 +02:00
2022-04-15 14:09:21 +02:00
/* 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);
}
} */
};
2022-05-04 15:58:26 +02:00
setWorkerProxy;
2022-04-15 14:09:21 +02:00
// const [_, touchSupport, userAgent, _rootScope, _appStateManager, _I18n, __/* , ___ */] = await Promise.all([
// import('./lib/polyfill'),
// import('./environment/touchSupport'),
// import('./environment/userAgent'),
// import('./lib/rootScope'),
// import('./lib/appManagers/appStateManager'),
// import('./lib/langPack'),
// import('./helpers/peerIdPolyfill'),
// // import('./helpers/cacheFunctionPolyfill')
// ]);
/* const {IS_TOUCH_SUPPORTED} = touchSupport;
const {IS_FIREFOX, IS_MOBILE, IS_APPLE, IS_SAFARI, IS_APPLE_MOBILE, IS_ANDROID} = userAgent;
const rootScope = _rootScope.default;
const appStateManager = _appStateManager.default;
const I18n = _I18n.default; */
window.addEventListener('resize', setVH);
setVH();
if(IS_STICKY_INPUT_BUGGED) {
const toggleResizeMode = () => {
setViewportVH = tabId === 1 && IS_STICKY_INPUT_BUGGED && !overlayCounter.isOverlayActive;
2022-04-15 14:09:21 +02:00
setVH();
if(w !== window) {
if(setViewportVH) {
window.removeEventListener('resize', setVH);
w.addEventListener('resize', setVH);
} else {
w.removeEventListener('resize', setVH);
window.addEventListener('resize', setVH);
2021-04-08 17:13:05 +02:00
}
2022-04-15 14:09:21 +02:00
}
2021-04-08 17:13:05 +02:00
};
2022-04-15 14:09:21 +02:00
let tabId: number;
(window as any).onImTabChange = (id: number) => {
2022-04-15 14:09:21 +02:00
const wasTabId = tabId !== undefined;
tabId = id;
2021-04-08 17:13:05 +02:00
2022-04-15 14:09:21 +02:00
if(wasTabId || tabId === 1) {
toggleResizeMode();
2021-04-08 17:13:05 +02:00
}
};
2022-08-04 08:49:54 +02:00
overlayCounter.addEventListener('change', () => {
2022-04-15 14:09:21 +02:00
toggleResizeMode();
});
}
2022-04-15 14:09:21 +02:00
if(IS_FIREFOX && !IS_EMOJI_SUPPORTED) {
2021-07-22 13:52:18 +02:00
document.addEventListener('dragstart', (e) => {
2022-04-15 14:09:21 +02:00
const target = e.target as HTMLElement;
if(target.tagName === 'IMG' && target.classList.contains('emoji')) {
cancelEvent(e);
2021-07-22 13:52:18 +02:00
return false;
}
});
2022-04-15 14:09:21 +02:00
}
2021-07-22 13:52:18 +02:00
2022-11-01 18:39:23 +01:00
if(IS_EMOJI_SUPPORTED) {
document.documentElement.classList.add('native-emoji');
}
2022-04-15 14:09:21 +02:00
// prevent firefox image dragging
document.addEventListener('dragstart', (e) => {
2022-08-04 08:49:54 +02:00
if((e.target as HTMLElement)?.tagName === 'IMG') {
2022-04-15 14:09:21 +02:00
e.preventDefault();
return false;
}
2022-04-15 14:09:21 +02:00
});
2022-06-08 14:06:04 +02:00
// restrict contextmenu on images (e.g. webp stickers)
document.addEventListener('contextmenu', (e) => {
if((e.target as HTMLElement).tagName === 'IMG' && !(window as any).appMediaViewer) {
cancelEvent(e);
}
});
2022-04-15 14:09:21 +02:00
if(IS_FIREFOX) {
2022-06-08 14:06:04 +02:00
document.documentElement.classList.add('is-firefox', 'no-backdrop');
2022-04-15 14:09:21 +02:00
}
2021-08-21 15:01:14 +02:00
2022-04-15 14:09:21 +02:00
if(IS_MOBILE) {
document.documentElement.classList.add('is-mobile');
}
2021-08-21 15:01:14 +02:00
2022-04-15 14:09:21 +02:00
if(IS_APPLE) {
if(IS_SAFARI) {
document.documentElement.classList.add('is-safari');
2021-04-08 17:13:05 +02:00
}
2022-08-04 08:49:54 +02:00
2022-04-15 14:09:21 +02:00
// document.documentElement.classList.add('emoji-supported');
2021-04-08 17:13:05 +02:00
2022-04-15 14:09:21 +02:00
if(IS_APPLE_MOBILE) {
document.documentElement.classList.add('is-ios');
2021-04-08 17:13:05 +02:00
} else {
2022-04-15 14:09:21 +02:00
document.documentElement.classList.add('is-mac');
2021-04-08 17:13:05 +02:00
}
2022-04-15 14:09:21 +02:00
} else if(IS_ANDROID) {
document.documentElement.classList.add('is-android');
2023-02-06 13:04:21 +01:00
// force losing focus on input blur
// focusin and focusout are not working on mobile
const onInResize = () => {
2022-04-15 14:09:21 +02:00
hasFocus = true;
2023-02-06 13:04:21 +01:00
window.addEventListener('resize', onOutResize, {once: true});
};
2022-04-15 14:09:21 +02:00
2023-02-06 13:04:21 +01:00
const onOutResize = () => {
2022-04-15 14:09:21 +02:00
hasFocus = false;
2023-02-06 13:04:21 +01:00
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});
}
});
2022-04-15 14:09:21 +02:00
}
if(!IS_TOUCH_SUPPORTED) {
document.documentElement.classList.add('no-touch');
} else {
document.documentElement.classList.add('is-touch');
/* document.addEventListener('touchmove', (event: any) => {
event = event.originalEvent || event;
if(event.scale && event.scale !== 1) {
event.preventDefault();
}
}, {capture: true, passive: false}); */
}
2021-04-08 17:13:05 +02:00
if(IS_INSTALL_PROMPT_SUPPORTED) {
cacheInstallPrompt();
}
2022-08-04 08:49:54 +02:00
const perf = performance.now();
2021-04-08 17:13:05 +02:00
// await pause(1000000);
2021-04-08 17:13:05 +02:00
const langPromise = I18n.getCacheLangPack();
2022-08-04 08:49:54 +02:00
const [stateResult, langPack] = await Promise.all([
// loadState(),
apiManagerProxy.sendState().then(([stateResult]) => stateResult),
2022-04-15 14:09:21 +02:00
langPromise
]);
I18n.setTimeFormat(stateResult.state.settings.timeFormat);
rootScope.managers.rootScope.getPremium().then((isPremium) => {
rootScope.premium = isPremium;
});
themeController.setThemeListener();
2021-04-08 17:13:05 +02:00
2022-04-15 14:09:21 +02:00
if(langPack.appVersion !== App.langPackVersion) {
I18n.getLangPack(langPack.lang_code);
}
2021-11-13 02:30:37 +01:00
2022-04-15 14:09:21 +02:00
/**
* won't fire if font is loaded too fast
*/
function fadeInWhenFontsReady(elem: HTMLElement, promise: Promise<any>) {
elem.style.opacity = '0';
2022-04-15 14:09:21 +02:00
promise.then(() => {
window.requestAnimationFrame(() => {
elem.style.opacity = '';
});
});
}
2021-04-08 17:13:05 +02:00
2022-04-15 14:09:21 +02:00
console.log('got state, time:', performance.now() - perf);
2023-01-16 18:26:51 +01:00
if(langPack.lang_code === 'ar' && IS_BETA && false) {
document.body.classList.add('is-rtl');
document.documentElement.dir = 'rtl';
document.documentElement.lang = langPack.lang_code;
I18n.setRTL(true);
} else {
document.documentElement.dir = 'ltr';
}
2022-11-18 10:43:52 +01:00
let authState = stateResult.state.authState;
const hash = location.hash;
const splitted = hash.split('?');
2023-03-01 14:07:26 +01:00
const params = parseUriParamsLine(splitted[1] ?? splitted[0].slice(1));
2022-11-18 10:43:52 +01:00
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));
}
2022-04-15 14:09:21 +02:00
if(authState._ !== 'authStateSignedIn'/* || 1 === 1 */) {
console.log('Will mount auth page:', authState._, Date.now() / 1000);
2022-04-15 14:09:21 +02:00
const el = document.getElementById('auth-pages');
let scrollable: HTMLElement;
if(el) {
scrollable = el.querySelector('.scrollable') as HTMLElement;
if((!IS_TOUCH_SUPPORTED || IS_MOBILE_SAFARI)) {
scrollable.classList.add('no-scrollbar');
}
2021-04-08 17:13:05 +02:00
2022-04-15 14:09:21 +02:00
// * don't remove this line
scrollable.style.opacity = '0';
2021-04-08 17:13:05 +02:00
2022-04-15 14:09:21 +02:00
const placeholder = document.createElement('div');
placeholder.classList.add('auth-placeholder');
2022-04-15 14:09:21 +02:00
scrollable.prepend(placeholder);
scrollable.append(placeholder.cloneNode());
}
2022-04-15 14:09:21 +02:00
try {
await Promise.all([
import('./lib/mtproto/telegramMeWebManager'),
import('./lib/mtproto/webPushApiManager')
]).then(([meModule, pushModule]) => {
meModule.default.setAuthorized(false);
pushModule.default.forceUnsubscribe();
});
} catch(err) {
2022-08-04 08:49:54 +02:00
2022-04-15 14:09:21 +02:00
}
2022-04-15 14:09:21 +02:00
let pagePromise: Promise<void>;
2022-08-04 08:49:54 +02:00
// 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;
2022-11-18 10:43:52 +01:00
case 'authStateSignImport':
pagePromise = (await import('./pages/pageSignImport')).default.mount(authState.data);
break;
2022-08-04 08:49:54 +02:00
}
// });
2022-04-15 14:09:21 +02:00
if(scrollable) {
// wait for text appear
if(pagePromise) {
await pagePromise;
2021-06-18 04:30:36 +02:00
}
2022-08-04 08:49:54 +02:00
const promise = 'fonts' in document ?
2022-04-15 14:09:21 +02:00
Promise.race([
2022-08-04 08:49:54 +02:00
pause(1000),
2022-04-15 14:09:21 +02:00
document.fonts.ready
2022-08-04 08:49:54 +02:00
]) :
2022-04-15 14:09:21 +02:00
Promise.resolve();
fadeInWhenFontsReady(scrollable, promise);
}
2021-05-01 16:33:08 +02:00
2022-04-15 14:09:21 +02:00
/* setTimeout(async() => {
(await import('./pages/pageAuthCode')).default.mount({
"_": "auth.sentCode",
"pFlags": {},
"flags": 6,
"type": {
"_": "auth.sentCodeTypeSms",
"length": 5
2021-04-08 17:13:05 +02:00
},
2022-08-04 08:49:54 +02:00
"phone_code_hash": "",
2022-04-15 14:09:21 +02:00
"next_type": {
"_": "auth.codeTypeCall"
},
"timeout": 120,
"phone_number": ""
});
2021-04-08 17:13:05 +02:00
2022-04-15 14:09:21 +02:00
(await import('./pages/pageSignQR')).default.mount();
2021-04-08 17:13:05 +02:00
2022-04-15 14:09:21 +02:00
(await import('./pages/pagePassword')).default.mount();
2021-04-08 17:13:05 +02:00
2022-04-15 14:09:21 +02:00
(await import('./pages/pageSignUp')).default.mount({
2022-08-04 08:49:54 +02:00
"phone_code_hash": "",
2022-04-15 14:09:21 +02:00
"phone_number": ""
});
}, 500); */
} else {
console.log('Will mount IM page:', Date.now() / 1000);
fadeInWhenFontsReady(document.getElementById('main-columns'), loadFonts());
(await import('./pages/pageIm')).default.mount();
}
});