Temp commit

This commit is contained in:
morethanwords 2021-03-31 17:02:23 +04:00
parent a720439b6f
commit 6475479390
6 changed files with 224 additions and 72 deletions

View File

@ -37,17 +37,11 @@
<use href="#logo" /> <use href="#logo" />
</svg> </svg>
</div> </div>
<h4>Sign in to Telegram</h4>
<p class="subtitle">Please confirm your country and<br> enter your phone number.</p>
</div> </div>
</div> </div>
<div class="page-signQR"> <div class="page-signQR">
<div class="container center-align"> <div class="container center-align">
<div class="auth-image"> <div class="auth-image"></div>
<canvas id="qr-code"></canvas>
</div>
<h4>Scan from mobile Telegram</h4>
<p class="qr-description">1. Open Telegram on your phone<br>2. Go to settings > Devices > Scan QR<br>3. Scan this image to Log in</p>
</div> </div>
</div> </div>
<div class="page-authCode"> <div class="page-authCode">

23
src/langSign.ts Normal file
View File

@ -0,0 +1,23 @@
const lang = {
"Login.Title": "Sign in to Telegram",
"Login.CountrySelectorLabel": "Country",
"Login.PhoneLabel": "Phone Number",
"Login.PhoneLabelInvalid": "Phone Number Invalid",
"Login.KeepSigned": "Keep me signed in",
"Login.StartText": "Please confirm your country and\nenter your phone number.",
// * android
// * macos
"Login.Next": "Next",
"Login.ContinueOnLanguage": "Continue in English",
"Login.QR.Title": "Log in to Telegram by QR Code",
"Login.QR.Help1": "Open Telegram on your phone",
"Login.QR.Help2": "Go to **Settings** > **Devices** > **Scan QR**",
"Login.QR.Help3": "Point your phone at this screen to confirm login",
"Login.QR.Cancel": "Log in by phone Number",
"Login.QR.Login": "Log in by QR Code",
};
export default lang;

View File

@ -2,6 +2,7 @@ import DEBUG, { MOUNT_CLASS_TO } from "../config/debug";
import { safeAssign } from "../helpers/object"; import { safeAssign } from "../helpers/object";
import { capitalizeFirstLetter } from "../helpers/string"; import { capitalizeFirstLetter } from "../helpers/string";
import type lang from "../lang"; import type lang from "../lang";
import type langSign from "../langSign";
import { LangPackDifference, LangPackString } from "../layer"; import { LangPackDifference, LangPackString } from "../layer";
import apiManager from "./mtproto/mtprotoworker"; import apiManager from "./mtproto/mtprotoworker";
import sessionStorage from "./sessionStorage"; import sessionStorage from "./sessionStorage";
@ -41,21 +42,23 @@ export const langPack: {[actionType: string]: LangPackKey} = {
"messageActionBotAllowed": "Chat.Service.BotPermissionAllowed" "messageActionBotAllowed": "Chat.Service.BotPermissionAllowed"
}; };
export type LangPackKey = string | keyof typeof lang; export type LangPackKey = string | keyof typeof lang | keyof typeof langSign;
namespace I18n { namespace I18n {
export const strings: Map<LangPackKey, LangPackString> = new Map(); export const strings: Map<LangPackKey, LangPackString> = new Map();
let pluralRules: Intl.PluralRules; let pluralRules: Intl.PluralRules;
let lastRequestedLangCode: string; let cacheLangPackPromise: Promise<LangPackDifference>;
export let lastRequestedLangCode: string;
export function getCacheLangPack(): Promise<LangPackDifference> { export function getCacheLangPack(): Promise<LangPackDifference> {
return Promise.all([ if(cacheLangPackPromise) return cacheLangPackPromise;
return cacheLangPackPromise = Promise.all([
sessionStorage.get('langPack'), sessionStorage.get('langPack'),
polyfillPromise polyfillPromise
]).then(([langPack]) => { ]).then(([langPack]) => {
if(!langPack/* || true */) { if(!langPack/* || true */) {
return getLangPack('en'); return loadLocalLangPack();
} else if(DEBUG) { } else if(DEBUG/* && false */) {
return getLangPack(langPack.lang_code); return getLangPack(langPack.lang_code);
} else if(langPack.appVersion !== App.langPackVersion) { } else if(langPack.appVersion !== App.langPackVersion) {
return getLangPack(langPack.lang_code); return getLangPack(langPack.lang_code);
@ -67,41 +70,87 @@ namespace I18n {
applyLangPack(langPack); applyLangPack(langPack);
return langPack; return langPack;
}).finally(() => {
cacheLangPackPromise = undefined;
}); });
} }
export function getLangPack(langCode: string) { export function loadLocalLangPack() {
lastRequestedLangCode = langCode; const defaultCode = 'en';
lastRequestedLangCode = defaultCode;
return Promise.all([ return Promise.all([
apiManager.invokeApi('langpack.getLangPack', { import('../lang'),
import('../langSign')
]).then(([lang, langSign]) => {
const strings: LangPackString[] = [];
formatLocalStrings(lang, strings);
formatLocalStrings(langSign, strings);
const langPack: LangPackDifference = {
_: 'langPackDifference',
from_version: 0,
lang_code: defaultCode,
strings,
version: 0
};
return saveLangPack(langPack);
});
}
export function loadLangPack(langCode: string) {
return Promise.all([
apiManager.invokeApiCacheable('langpack.getLangPack', {
lang_code: langCode, lang_code: langCode,
lang_pack: 'macos' lang_pack: App.langPack
}), }),
apiManager.invokeApi('langpack.getLangPack', { apiManager.invokeApiCacheable('langpack.getLangPack', {
lang_code: langCode, lang_code: langCode,
lang_pack: 'android' lang_pack: 'android'
}), }),
import('../lang'), import('../lang'),
import('../langSign'),
polyfillPromise polyfillPromise
]).then(([langPack, _langPack, __langPack, _]) => { ]);
let strings: LangPackString[] = []; }
for(const i in __langPack.default) {
// @ts-ignore export function getStrings(langCode: string, strings: string[]) {
const v = __langPack.default[i]; return apiManager.invokeApi('langpack.getStrings', {
if(typeof(v) === 'string') { lang_pack: App.langPack,
strings.push({ lang_code: langCode,
_: 'langPackString', keys: strings
key: i, });
value: v }
});
} else { export function formatLocalStrings(strings: any, pushTo: LangPackString[] = []) {
strings.push({ for(const i in strings) {
_: 'langPackStringPluralized', // @ts-ignore
key: i, const v = strings[i];
...v if(typeof(v) === 'string') {
}); pushTo.push({
} _: 'langPackString',
key: i,
value: v
});
} else {
pushTo.push({
_: 'langPackStringPluralized',
key: i,
...v
});
} }
}
return pushTo;
}
export function getLangPack(langCode: string) {
lastRequestedLangCode = langCode;
return loadLangPack(langCode).then(([langPack, _langPack, __langPack, ___langPack, _]) => {
let strings: LangPackString[] = [];
[__langPack, ___langPack].forEach(l => {
formatLocalStrings(l.default as any, strings);
});
strings = strings.concat(langPack.strings); strings = strings.concat(langPack.strings);
@ -110,13 +159,17 @@ namespace I18n {
} }
langPack.strings = strings; langPack.strings = strings;
// @ts-ignore return saveLangPack(langPack);
langPack.appVersion = App.langPackVersion; });
}
return sessionStorage.set({langPack}).then(() => { export function saveLangPack(langPack: LangPackDifference) {
applyLangPack(langPack); // @ts-ignore
return langPack; langPack.appVersion = App.langPackVersion;
});
return sessionStorage.set({langPack}).then(() => {
applyLangPack(langPack);
return langPack;
}); });
} }

View File

@ -4,7 +4,7 @@ import Countries, { Country as _Country } from "../countries";
import appStateManager from "../lib/appManagers/appStateManager"; import appStateManager from "../lib/appManagers/appStateManager";
import apiManager from "../lib/mtproto/mtprotoworker"; import apiManager from "../lib/mtproto/mtprotoworker";
import { RichTextProcessor } from '../lib/richtextprocessor'; import { RichTextProcessor } from '../lib/richtextprocessor';
import { findUpTag } from "../helpers/dom"; import { findUpTag, attachClickEvent, cancelEvent, replaceContent } from "../helpers/dom";
import Page from "./page"; import Page from "./page";
import pageAuthCode from "./pageAuthCode"; import pageAuthCode from "./pageAuthCode";
import InputField from "../components/inputField"; import InputField from "../components/inputField";
@ -15,6 +15,8 @@ import fastSmoothScroll from "../helpers/fastSmoothScroll";
import { isTouchSupported } from "../helpers/touchSupport"; import { isTouchSupported } from "../helpers/touchSupport";
import App from "../config/app"; import App from "../config/app";
import Modes from "../config/modes"; import Modes from "../config/modes";
import I18n, { _i18n, i18n } from "../lib/langPack";
import { LangPackString } from "../layer";
type Country = _Country & { type Country = _Country & {
li?: HTMLLIElement[] li?: HTMLLIElement[]
@ -45,7 +47,7 @@ let onFirstMount = () => {
inputWrapper.classList.add('input-wrapper'); inputWrapper.classList.add('input-wrapper');
const countryInputField = new InputField({ const countryInputField = new InputField({
label: 'Country', label: 'Login.CountrySelectorLabel',
name: 'countryCode', name: 'countryCode',
plainText: true plainText: true
}); });
@ -227,14 +229,13 @@ let onFirstMount = () => {
let lastValue = ''; let lastValue = '';
const telInputField = new InputField({ const telInputField = new InputField({
label: 'Phone Number', label: 'Login.PhoneLabel',
plainText: true, plainText: true,
name: 'phone' name: 'phone'
}); });
let telEl = telInputField.input as HTMLInputElement; let telEl = telInputField.input as HTMLInputElement;
telEl.type = 'tel'; telEl.type = 'tel';
telEl.autocomplete = 'rr55RandomRR55'; telEl.autocomplete = 'rr55RandomRR55';
const telLabel = telEl.nextElementSibling as HTMLLabelElement;
telEl.addEventListener('input', function(this: typeof telEl, e) { telEl.addEventListener('input', function(this: typeof telEl, e) {
//console.log('input', this.value); //console.log('input', this.value);
this.classList.remove('error'); this.classList.remove('error');
@ -247,7 +248,7 @@ let onFirstMount = () => {
pasted = false; pasted = false;
telLabel.innerText = 'Phone Number'; telInputField.setLabel();
let formatted: string, country: Country; let formatted: string, country: Country;
if(this.value.replace(/\++/, '+') === '+') { if(this.value.replace(/\++/, '+') === '+') {
@ -299,19 +300,19 @@ let onFirstMount = () => {
});*/ });*/
const signedCheckboxField = new CheckboxField({ const signedCheckboxField = new CheckboxField({
text: 'Keep me signed in', text: 'Login.KeepSigned',
name: 'keepSession', name: 'keepSession',
withRipple: true withRipple: true
}); });
signedCheckboxField.input.checked = true; signedCheckboxField.input.checked = true;
btnNext = Button('btn-primary btn-color-primary', {text: 'NEXT'}); btnNext = Button('btn-primary btn-color-primary', {text: 'Login.Next'});
btnNext.style.visibility = 'hidden'; btnNext.style.visibility = 'hidden';
btnNext.addEventListener('click', function(this: HTMLElement, e) { btnNext.addEventListener('click', function(this: HTMLElement, e) {
this.setAttribute('disabled', 'true'); this.setAttribute('disabled', 'true');
this.textContent = 'PLEASE WAIT...'; replaceContent(this, i18n('PleaseWait'));
putPreloader(this); putPreloader(this);
//this.innerHTML = 'PLEASE WAIT...'; //this.innerHTML = 'PLEASE WAIT...';
@ -333,11 +334,12 @@ let onFirstMount = () => {
}).catch(err => { }).catch(err => {
this.removeAttribute('disabled'); this.removeAttribute('disabled');
this.innerText = 'NEXT';
switch(err.type) { switch(err.type) {
case 'PHONE_NUMBER_INVALID': case 'PHONE_NUMBER_INVALID':
telLabel.innerText = 'Phone Number Invalid'; telInputField.setError();
replaceContent(telInputField.label, i18n('Login.PhoneLabelInvalid'));
telEl.classList.add('error'); telEl.classList.add('error');
replaceContent(this, i18n('Login.Next'));
break; break;
default: default:
console.error('auth.sendCode error:', err); console.error('auth.sendCode error:', err);
@ -347,7 +349,7 @@ let onFirstMount = () => {
}); });
}); });
const btnQr = Button('btn-primary btn-secondary btn-primary-transparent primary', {text: 'Quick log in using QR code'}); const btnQr = Button('btn-primary btn-secondary btn-primary-transparent primary', {text: 'Login.QR.Login'});
btnQr.addEventListener('click', () => { btnQr.addEventListener('click', () => {
import('./pageSignQR').then(module => { import('./pageSignQR').then(module => {
@ -357,7 +359,14 @@ let onFirstMount = () => {
inputWrapper.append(countryInputField.container, telInputField.container, signedCheckboxField.label, btnNext, btnQr); inputWrapper.append(countryInputField.container, telInputField.container, signedCheckboxField.label, btnNext, btnQr);
page.pageEl.querySelector('.container').append(inputWrapper); const h4 = document.createElement('h4');
_i18n(h4, 'Login.Title');
const subtitle = document.createElement('div');
subtitle.classList.add('subtitle');
_i18n(subtitle, 'Login.StartText');
page.pageEl.querySelector('.container').append(h4, subtitle, inputWrapper);
let tryAgain = () => { let tryAgain = () => {
apiManager.invokeApi('help.getNearestDc').then((nearestDcResult) => { apiManager.invokeApi('help.getNearestDc').then((nearestDcResult) => {
@ -405,12 +414,52 @@ let onFirstMount = () => {
}, 0); }, 0);
} }
apiManager.invokeApi('help.getConfig').then(config => {
if(config.suggested_lang_code !== I18n.lastRequestedLangCode) {
//I18n.loadLangPack(config.suggested_lang_code);
Promise.all([
I18n.getStrings(config.suggested_lang_code, ['Login.ContinueOnLanguage']),
I18n.getCacheLangPack()
]).then(res => {
const backup: LangPackString[] = [];
res[0].forEach(string => {
const backupString = I18n.strings.get(string.key);
if(!backupString) {
return;
}
backup.push(backupString);
I18n.strings.set(string.key, string);
});
const btnChangeLanguage = Button('btn-primary btn-secondary btn-primary-transparent primary', {text: 'Login.ContinueOnLanguage'});
inputWrapper.append(btnChangeLanguage);
backup.forEach(string => {
I18n.strings.set(string.key, string);
});
attachClickEvent(btnChangeLanguage, (e) => {
cancelEvent(e);
btnChangeLanguage.disabled = true;
putPreloader(btnChangeLanguage);
I18n.getLangPack(config.suggested_lang_code).then(() => {
btnChangeLanguage.remove();
});
});
});
}
});
tryAgain(); tryAgain();
}; };
const page = new Page('page-sign', true, onFirstMount, () => { const page = new Page('page-sign', true, onFirstMount, () => {
if(btnNext) { if(btnNext) {
btnNext.textContent = 'NEXT'; replaceContent(btnNext, i18n('Login.Next'));
btnNext.removeAttribute('disabled'); btnNext.removeAttribute('disabled');
} }

View File

@ -10,6 +10,7 @@ import { bytesCmp, bytesToBase64 } from '../helpers/bytes';
import { pause } from '../helpers/schedulers'; import { pause } from '../helpers/schedulers';
import App from '../config/app'; import App from '../config/app';
import Button from '../components/button'; import Button from '../components/button';
import { _i18n, i18n } from '../lib/langPack';
let onFirstMount = async() => { let onFirstMount = async() => {
const pageElement = page.pageEl; const pageElement = page.pageEl;
@ -18,9 +19,23 @@ let onFirstMount = async() => {
const inputWrapper = document.createElement('div'); const inputWrapper = document.createElement('div');
inputWrapper.classList.add('input-wrapper'); inputWrapper.classList.add('input-wrapper');
const btnBack = Button('btn-primary btn-secondary btn-primary-transparent primary', {text: 'Or log in using your phone number'}); const btnBack = Button('btn-primary btn-secondary btn-primary-transparent primary', {text: 'Login.QR.Cancel'});
inputWrapper.append(btnBack); inputWrapper.append(btnBack);
imageDiv.parentElement.append(inputWrapper);
const container = imageDiv.parentElement;
const h4 = document.createElement('h4');
_i18n(h4, 'Login.QR.Title');
const helpList = document.createElement('ol');
helpList.classList.add('qr-description');
['Login.QR.Help1', 'Login.QR.Help2', 'Login.QR.Help3'].forEach((key) => {
const li = document.createElement('li');
li.append(i18n(key));
helpList.append(li);
});
container.append(h4, helpList, inputWrapper);
btnBack.addEventListener('click', () => { btnBack.addEventListener('click', () => {
pageSignIn.mount(); pageSignIn.mount();
@ -86,8 +101,8 @@ let onFirstMount = async() => {
let url = "tg://login?token=" + encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=+$/, ""); let url = "tg://login?token=" + encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=+$/, "");
const qrCode = new QRCodeStyling({ const qrCode = new QRCodeStyling({
width: 166 * window.devicePixelRatio, width: 240 * window.devicePixelRatio,
height: 166 * window.devicePixelRatio, height: 240 * window.devicePixelRatio,
data: url, data: url,
image: "assets/img/logo_padded.svg", image: "assets/img/logo_padded.svg",
dotsOptions: { dotsOptions: {
@ -108,8 +123,22 @@ let onFirstMount = async() => {
qrCode.append(imageDiv); qrCode.append(imageDiv);
(imageDiv.lastChild as HTMLCanvasElement).classList.add('qr-canvas'); (imageDiv.lastChild as HTMLCanvasElement).classList.add('qr-canvas');
let promise: Promise<void>;
if(qrCode._drawingPromise) {
promise = qrCode._drawingPromise;
} else {
promise = Promise.race([
pause(1000),
new Promise<void>((resolve) => {
qrCode._canvas._image.addEventListener('load', () => {
window.requestAnimationFrame(() => resolve());
}, {once: true});
})
]);
}
// * это костыль, но библиотека не предоставляет никаких событий // * это костыль, но библиотека не предоставляет никаких событий
qrCode._drawingPromise.then(() => { promise.then(() => {
Array.from(imageDiv.children).slice(0, -1).forEach(el => { Array.from(imageDiv.children).slice(0, -1).forEach(el => {
el.remove(); el.remove();
}); });

View File

@ -3,6 +3,8 @@
overflow: hidden; overflow: hidden;
.btn-primary { .btn-primary {
text-transform: uppercase;
@include respond-to(handhelds) { @include respond-to(handhelds) {
height: 50px; height: 50px;
} }
@ -89,7 +91,7 @@
flex-direction: column; flex-direction: column;
@media screen and (max-height: 810px) { @media screen and (max-height: 810px) {
height: 700px; height: 760px;
} }
} }
} }
@ -156,22 +158,13 @@
.qr { .qr {
margin-top: 1.5rem; margin-top: 1.5rem;
} }
p.qr-description {
color: #707579;
line-height: 1.85;
text-align: left;
margin-left: auto;
margin-right: auto;
}
} }
.page-signQR { .page-signQR {
.auth-image { .auth-image {
@include respond-to(handhelds) { width: 240px !important;
width: 166px; height: 240px !important;
height: 166px; overflow: hidden;
}
.qr-canvas { .qr-canvas {
width: 100%; width: 100%;
@ -186,6 +179,17 @@
h4 { h4 {
flex: 0 0 auto; flex: 0 0 auto;
} }
.qr-description {
max-width: 480px;
margin: 1rem auto;
line-height: 1.3125;
text-align: left;
li {
margin-top: .5rem;
}
}
} }
/* .page-signQR { /* .page-signQR {