Language change button for QR page

Refactor apiManagerProxy
This commit is contained in:
morethanwords 2021-05-01 14:25:03 +04:00
parent 3a9023a50b
commit 29c6273715
8 changed files with 253 additions and 177 deletions

View File

@ -0,0 +1,76 @@
import { attachClickEvent, cancelEvent } from "../helpers/dom";
import { Config, LangPackDifference, LangPackString } from "../layer";
import I18n, { LangPackKey } from "../lib/langPack";
import apiManager from "../lib/mtproto/mtprotoworker";
import rootScope from "../lib/rootScope";
import Button from "./button";
import { putPreloader } from "./misc";
let set = false, times = 0;
rootScope.addEventListener('language_change', () => {
if(++times < 2) {
return;
}
console.log('language_change');
set = true;
});
function getLang(): Promise<[Config.config, LangPackString[], LangPackDifference.langPackDifference]> {
if(cachedPromise) return cachedPromise;
return cachedPromise = apiManager.invokeApiCacheable('help.getConfig').then(config => {
if(config.suggested_lang_code !== I18n.lastRequestedLangCode) {
//I18n.loadLangPack(config.suggested_lang_code);
return Promise.all([
config,
I18n.getStrings(config.suggested_lang_code, ['Login.ContinueOnLanguage']),
I18n.getCacheLangPack()
]);
} else {
return [] as any;
}
});
}
let cachedPromise: ReturnType<typeof getLang>;
export default function getLanguageChangeButton(appendTo: HTMLElement) {
if(set) return;
getLang().then(([config, strings]) => {
if(!config) {
return;
}
const backup: LangPackString[] = [];
strings.forEach(string => {
const backupString = I18n.strings.get(string.key as LangPackKey);
if(!backupString) {
return;
}
backup.push(backupString);
I18n.strings.set(string.key as LangPackKey, string);
});
const btnChangeLanguage = Button('btn-primary btn-secondary btn-primary-transparent primary', {text: 'Login.ContinueOnLanguage'});
appendTo.append(btnChangeLanguage);
rootScope.addEventListener('language_change', () => {
btnChangeLanguage.remove();
}, true);
backup.forEach(string => {
I18n.strings.set(string.key as LangPackKey, string);
});
attachClickEvent(btnChangeLanguage, (e) => {
cancelEvent(e);
btnChangeLanguage.disabled = true;
putPreloader(btnChangeLanguage);
I18n.getLangPack(config.suggested_lang_code);
});
});
}

View File

@ -17,16 +17,16 @@ import { logger, LogTypes } from "../logger";
import { RichTextProcessor } from "../richtextprocessor";
import rootScope from "../rootScope";
import { positionElementByIndex, replaceContent } from "../../helpers/dom";
import apiUpdatesManager from "./apiUpdatesManager";
import appPeersManager from './appPeersManager';
import appImManager from "./appImManager";
import appMessagesManager, { Dialog } from "./appMessagesManager";
import {MyDialogFilter as DialogFilter} from "../storages/filters";
import appPeersManager from './appPeersManager';
import appStateManager from "./appStateManager";
import appUsersManager from "./appUsersManager";
import Button from "../../components/button";
import SetTransition from "../../components/singleTransition";
import sessionStorage from '../sessionStorage';
import apiUpdatesManager from "./apiUpdatesManager";
import appDraftsManager, { MyDraftMessage } from "./appDraftsManager";
import ProgressivePreloader from "../../components/preloader";
import App from "../../config/app";
@ -490,6 +490,7 @@ export class AppDialogsManager {
//selectTab(0);
(this.folders.menu.firstElementChild as HTMLElement).click();
appMessagesManager.construct();
appStateManager.getState().then((state) => {
appNotificationsManager.getNotifyPeerTypeSettings();

View File

@ -30,13 +30,17 @@ export class AppDocsManager {
private docs: {[docId: string]: MyDocument} = {};
private savingLottiePreview: {[docId: string]: true} = {};
public onServiceWorkerFail() {
constructor() {
apiManager.onServiceWorkerFail = this.onServiceWorkerFail;
}
public onServiceWorkerFail = () => {
for(const id in this.docs) {
const doc = this.docs[id];
delete doc.supportsStreaming;
delete doc.url;
}
}
};
public saveDoc(doc: Document, context?: ReferenceContext): MyDocument {
if(doc._ === 'documentEmpty') {

View File

@ -17,7 +17,7 @@ import { createPosterForVideo } from "../../helpers/files";
import { copy, defineNotNumerableProperties, getObjectKeysAndSort } from "../../helpers/object";
import { randomLong } from "../../helpers/random";
import { splitStringByLength, limitSymbols, escapeRegExp } from "../../helpers/string";
import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageMedia, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update, Photo } from "../../layer";
import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageMedia, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update, Photo } from "../../layer";
import { InvokeApiOptions } from "../../types";
import I18n, { i18n, join, langPack, LangPackKey, _i18n } from "../langPack";
import { logger, LogTypes } from "../logger";
@ -42,7 +42,6 @@ import appStateManager from "./appStateManager";
import appUsersManager from "./appUsersManager";
import appWebPagesManager from "./appWebPagesManager";
import appDraftsManager from "./appDraftsManager";
import pushHeavyTask from "../../helpers/heavyQueue";
import { getFileNameByLocation } from "../../helpers/fileName";
import appProfileManager from "./appProfileManager";
import DEBUG, { MOUNT_CLASS_TO } from "../../config/debug";
@ -193,9 +192,6 @@ export class AppMessagesManager {
private groupedTempId = 0;
constructor() {
this.dialogsStorage = new DialogsStorage(this, appChatsManager, appPeersManager, appUsersManager, appDraftsManager, appNotificationsManager, appStateManager, apiUpdatesManager, serverTimeManager);
this.filtersStorage = new FiltersStorage(this, appPeersManager, appUsersManager, appNotificationsManager, appStateManager, apiUpdatesManager, /* apiManager, */ rootScope);
rootScope.addMultipleEventsListeners({
updateMessageID: this.onUpdateMessageId,
@ -298,7 +294,7 @@ export class AppMessagesManager {
this.reloadConversation(peerId);
}
});
appStateManager.getState().then(state => {
if(state.maxSeenMsgId) {
this.maxSeenId = state.maxSeenMsgId;
@ -308,6 +304,11 @@ export class AppMessagesManager {
appNotificationsManager.start();
}
public construct() {
this.dialogsStorage = new DialogsStorage(this, appChatsManager, appPeersManager, appUsersManager, appDraftsManager, appNotificationsManager, appStateManager, apiUpdatesManager, serverTimeManager);
this.filtersStorage = new FiltersStorage(this, appPeersManager, appUsersManager, appNotificationsManager, appStateManager, apiUpdatesManager, /* apiManager, */ rootScope);
}
public getInputEntities(entities: MessageEntity[]) {
var sendEntites = copy(entities);
sendEntites.forEach((entity: any) => {

View File

@ -14,12 +14,9 @@ import { logger } from '../logger';
import rootScope from '../rootScope';
import webpWorkerController from '../webp/webpWorkerController';
import type { DownloadOptions } from './apiFileManager';
import { ApiError } from './apiManager';
import type { ServiceWorkerTask, ServiceWorkerTaskResponse } from './mtproto.service';
import type { ServiceWorkerTask } from './mtproto.service';
import { UserAuth } from './mtproto_config';
import type { MTMessage } from './networker';
import referenceDatabase from './referenceDatabase';
import appDocsManager from '../appManagers/appDocsManager';
import DEBUG, { MOUNT_CLASS_TO } from '../../config/debug';
import Socket from './transports/websocket';
@ -80,136 +77,29 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
private sockets: Map<number, Socket> = new Map();
private taskListeners: {[taskType: string]: (task: any) => void} = {};
public onServiceWorkerFail: () => void;
constructor() {
super();
this.log('constructor');
this.registerServiceWorker();
/// #if !MTPROTO_SW
this.registerWorker();
/// #endif
}
public isServiceWorkerOnline() {
return this.isSWRegistered;
}
private registerServiceWorker() {
if(!('serviceWorker' in navigator)) return;
const worker = navigator.serviceWorker;
worker.register('./sw.js', {scope: './'}).then(registration => {
this.log('SW registered', registration);
this.isSWRegistered = true;
const sw = registration.installing || registration.waiting || registration.active;
sw.addEventListener('statechange', (e) => {
this.log('SW statechange', e);
});
/// #if MTPROTO_SW
const controller = worker.controller || registration.installing || registration.waiting || registration.active;
this.onWorkerFirstMessage(controller);
/// #endif
}, (err) => {
this.isSWRegistered = false;
this.log.error('SW registration failed!', err);
appDocsManager.onServiceWorkerFail();
});
worker.addEventListener('controllerchange', () => {
this.log.warn('controllerchange');
this.releasePending();
worker.controller.addEventListener('error', (e) => {
this.log.error('controller error:', e);
});
});
/// #if MTPROTO_SW
worker.addEventListener('message', this.onWorkerMessage);
/// #else
worker.addEventListener('message', (e) => {
const task: ServiceWorkerTask = e.data;
if(!isObject(task)) {
return;
}
this.postMessage(task);
});
/// #endif
worker.addEventListener('messageerror', (e) => {
this.log.error('SW messageerror:', e);
});
}
private onWorkerFirstMessage(worker: any) {
if(!this.worker) {
this.worker = worker;
this.log('set webWorker');
this.postMessage = this.worker.postMessage.bind(this.worker);
const isWebpSupported = webpWorkerController.isWebpSupported();
this.log('WebP supported:', isWebpSupported);
this.postMessage({type: 'webpSupport', payload: isWebpSupported});
this.releasePending();
}
}
private onWorkerMessage = (e: MessageEvent) => {
//this.log('got message from worker:', e.data);
const task = e.data;
if(!isObject(task)) {
return;
}
if(task.update) {
if(this.updatesProcessor) {
this.updatesProcessor(task.update);
}
} else if(task.progress) {
rootScope.broadcast('download_progress', task.progress);
} else if(task.type === 'reload') {
this.addTaskListener('reload', () => {
location.reload();
} else if(task.type === 'connectionStatusChange') {
});
this.addTaskListener('connectionStatusChange', (task: any) => {
rootScope.broadcast('connection_status_change', task.payload);
} else if(task.type === 'convertWebp') {
});
this.addTaskListener('convertWebp', (task) => {
webpWorkerController.postMessage(task);
} else if((task as ServiceWorkerTaskResponse).type === 'requestFilePart') {
const _task = task as ServiceWorkerTaskResponse;
if(_task.error) {
const onError = (error: ApiError) => {
if(error?.type === 'FILE_REFERENCE_EXPIRED') {
// @ts-ignore
const bytes = _task.originalPayload[1].file_reference;
referenceDatabase.refreshReference(bytes).then(() => {
// @ts-ignore
_task.originalPayload[1].file_reference = referenceDatabase.getReferenceByLink(bytes);
const newTask: ServiceWorkerTask = {
type: _task.type,
id: _task.id,
payload: _task.originalPayload
};
});
this.postMessage(newTask);
}).catch(onError);
} else {
navigator.serviceWorker.controller.postMessage(task);
}
};
onError(_task.error);
} else {
navigator.serviceWorker.controller.postMessage(task);
}
} else if(task.type === 'socketProxy') {
this.addTaskListener('socketProxy', (task) => {
const socketTask = task.payload;
const id = socketTask.id;
//console.log('socketProxy', socketTask, id);
@ -263,6 +153,110 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
socket.addEventListener('message', onMessage);
this.sockets.set(id, socket);
}
});
/// #if !MTPROTO_SW
this.registerWorker();
/// #endif
}
public isServiceWorkerOnline() {
return this.isSWRegistered;
}
private registerServiceWorker() {
if(!('serviceWorker' in navigator)) return;
const worker = navigator.serviceWorker;
worker.register('./sw.js', {scope: './'}).then(registration => {
this.log('SW registered', registration);
this.isSWRegistered = true;
const sw = registration.installing || registration.waiting || registration.active;
sw.addEventListener('statechange', (e) => {
this.log('SW statechange', e);
});
/// #if MTPROTO_SW
const controller = worker.controller || registration.installing || registration.waiting || registration.active;
this.onWorkerFirstMessage(controller);
/// #endif
}, (err) => {
this.isSWRegistered = false;
this.log.error('SW registration failed!', err);
if(this.onServiceWorkerFail) {
this.onServiceWorkerFail();
}
});
worker.addEventListener('controllerchange', () => {
this.log.warn('controllerchange');
this.releasePending();
worker.controller.addEventListener('error', (e) => {
this.log.error('controller error:', e);
});
});
/// #if MTPROTO_SW
worker.addEventListener('message', this.onWorkerMessage);
/// #else
worker.addEventListener('message', (e) => {
const task: ServiceWorkerTask = e.data;
if(!isObject(task)) {
return;
}
this.postMessage(task);
});
/// #endif
worker.addEventListener('messageerror', (e) => {
this.log.error('SW messageerror:', e);
});
}
private onWorkerFirstMessage(worker: any) {
if(!this.worker) {
this.worker = worker;
this.log('set webWorker');
this.postMessage = this.worker.postMessage.bind(this.worker);
const isWebpSupported = webpWorkerController.isWebpSupported();
this.log('WebP supported:', isWebpSupported);
this.postMessage({type: 'webpSupport', payload: isWebpSupported});
this.releasePending();
}
}
public addTaskListener(name: keyof ApiManagerProxy['taskListeners'], callback: ApiManagerProxy['taskListeners'][typeof name]) {
this.taskListeners[name] = callback;
}
private onWorkerMessage = (e: MessageEvent) => {
//this.log('got message from worker:', e.data);
const task = e.data;
if(!isObject(task)) {
return;
}
const callback = this.taskListeners[task.type];
if(callback) {
callback(task);
return;
}
if(task.update) {
if(this.updatesProcessor) {
this.updatesProcessor(task.update);
}
} else if(task.progress) {
rootScope.broadcast('download_progress', task.progress);
} else if(task.hasOwnProperty('result') || task.hasOwnProperty('error')) {
this.finalizeTask(task.taskId, task.result, task.error);
}

View File

@ -4,11 +4,14 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import type { ServiceWorkerTask, ServiceWorkerTaskResponse } from "./mtproto.service";
import type { ApiError } from "./apiManager";
import appMessagesManager from "../appManagers/appMessagesManager";
import { Photo } from "../../layer";
import { bytesToHex } from "../../helpers/bytes";
import { deepEqual } from "../../helpers/object";
import { MOUNT_CLASS_TO } from "../../config/debug";
import apiManager from "./mtprotoworker";
export type ReferenceContext = ReferenceContext.referenceContextProfilePhoto | ReferenceContext.referenceContextMessage;
export namespace ReferenceContext {
@ -34,6 +37,36 @@ class ReferenceDatabase {
//private references: Map<ReferenceBytes, number[]> = new Map();
private links: {[hex: string]: ReferenceBytes} = {};
constructor() {
apiManager.addTaskListener('requestFilePart', (task: ServiceWorkerTaskResponse) => {
if(task.error) {
const onError = (error: ApiError) => {
if(error?.type === 'FILE_REFERENCE_EXPIRED') {
// @ts-ignore
const bytes = task.originalPayload[1].file_reference;
referenceDatabase.refreshReference(bytes).then(() => {
// @ts-ignore
task.originalPayload[1].file_reference = referenceDatabase.getReferenceByLink(bytes);
const newTask: ServiceWorkerTask = {
type: task.type,
id: task.id,
payload: task.originalPayload
};
apiManager.postMessage(newTask);
}).catch(onError);
} else {
navigator.serviceWorker.controller.postMessage(task);
}
};
onError(task.error);
} else {
navigator.serviceWorker.controller.postMessage(task);
}
});
}
public saveContext(reference: ReferenceBytes, context: ReferenceContext, contexts?: ReferenceContexts) {
[contexts, reference] = this.getContexts(reference);
if(!contexts) {

View File

@ -20,8 +20,7 @@ import fastSmoothScroll from "../helpers/fastSmoothScroll";
import { isTouchSupported } from "../helpers/touchSupport";
import App from "../config/app";
import Modes from "../config/modes";
import I18n, { _i18n, i18n, LangPackKey } from "../lib/langPack";
import { LangPackString } from "../layer";
import { _i18n, i18n } from "../lib/langPack";
import lottieLoader from "../lib/lottieLoader";
import { ripple } from "../components/ripple";
import findUpTag from "../helpers/dom/findUpTag";
@ -29,6 +28,8 @@ import findUpClassName from "../helpers/dom/findUpClassName";
import { randomLong } from "../helpers/random";
import AppStorage from "../lib/storage";
import CacheStorageController from "../lib/cacheStorage";
import pageSignQR from "./pageSignQR";
import getLanguageChangeButton from "../components/languageChangeButton";
type Country = _Country & {
li?: HTMLLIElement[]
@ -383,7 +384,8 @@ let onFirstMount = () => {
let qrMounted = false;
btnQr.addEventListener('click', () => {
const promise = import('./pageSignQR');
pageSignQR.mount();
/* const promise = import('./pageSignQR');
btnQr.disabled = true;
let preloaderDiv: HTMLElement;
@ -401,7 +403,7 @@ let onFirstMount = () => {
preloaderDiv.remove();
}
}, 200);
});
}); */
});
inputWrapper.append(countryInputField.container, telInputField.container, signedCheckboxField.label, btnNext, btnQr);
@ -461,45 +463,7 @@ let onFirstMount = () => {
}, 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 as LangPackKey);
if(!backupString) {
return;
}
backup.push(backupString);
I18n.strings.set(string.key as LangPackKey, 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 as LangPackKey, string);
});
attachClickEvent(btnChangeLanguage, (e) => {
cancelEvent(e);
btnChangeLanguage.disabled = true;
putPreloader(btnChangeLanguage);
I18n.getLangPack(config.suggested_lang_code).then(() => {
btnChangeLanguage.remove();
});
});
});
}
});
getLanguageChangeButton(inputWrapper);
tryAgain();
};

View File

@ -16,6 +16,7 @@ import { _i18n, i18n, LangPackKey } from '../lib/langPack';
import appStateManager from '../lib/appManagers/appStateManager';
import rootScope from '../lib/rootScope';
import { putPreloader } from '../components/misc';
import getLanguageChangeButton from '../components/languageChangeButton';
let onFirstMount = async() => {
const pageElement = page.pageEl;
@ -29,6 +30,8 @@ let onFirstMount = async() => {
const btnBack = Button('btn-primary btn-secondary btn-primary-transparent primary', {text: 'Login.QR.Cancel'});
inputWrapper.append(btnBack);
getLanguageChangeButton(inputWrapper);
const container = imageDiv.parentElement;
const h4 = document.createElement('h4');