Browse Source

MTProto schema converter

Types for methods
master
morethanwords 4 years ago
parent
commit
34546d49ee
  1. 2
      package.json
  2. 7
      src/components/appMediaPlaybackController.ts
  3. 13
      src/components/audio.ts
  4. 22
      src/components/emoticonsDropdown/tabs/gifs.ts
  5. 29
      src/components/emoticonsDropdown/tabs/stickers.ts
  6. 5
      src/components/gifsMasonry.ts
  7. 4
      src/components/popupAvatar.ts
  8. 9
      src/components/popupStickers.ts
  9. 17
      src/components/sidebarLeft/chatFolders.ts
  10. 2
      src/components/sidebarLeft/editFolder.ts
  11. 3
      src/components/sidebarLeft/editProfile.ts
  12. 4
      src/components/sidebarLeft/includedChats.ts
  13. 3
      src/components/sidebarRight/gifs.ts
  14. 14
      src/components/sidebarRight/stickers.ts
  15. 43
      src/components/wrappers.ts
  16. 10777
      src/layer.d.ts
  17. 62
      src/lib/appManagers/AppInlineBotsManager.ts
  18. 90
      src/lib/appManagers/apiUpdatesManager.ts
  19. 25
      src/lib/appManagers/appChatsManager.ts
  20. 9
      src/lib/appManagers/appDialogsManager.ts
  21. 76
      src/lib/appManagers/appDocsManager.ts
  22. 2
      src/lib/appManagers/appDownloadManager.ts
  23. 19
      src/lib/appManagers/appImManager.ts
  24. 7
      src/lib/appManagers/appMediaViewer.ts
  25. 206
      src/lib/appManagers/appMessagesManager.ts
  26. 20
      src/lib/appManagers/appPeersManager.ts
  27. 98
      src/lib/appManagers/appPhotosManager.ts
  28. 16
      src/lib/appManagers/appPollsManager.ts
  29. 2
      src/lib/appManagers/appStateManager.ts
  30. 174
      src/lib/appManagers/appStickersManager.ts
  31. 87
      src/lib/appManagers/appUsersManager.ts
  32. 2
      src/lib/bin_utils.ts
  33. 3
      src/lib/crypto/crypto_methods.ts
  34. 6
      src/lib/crypto/srp.ts
  35. 26
      src/lib/mtproto/apiFileManager.ts
  36. 9
      src/lib/mtproto/mtproto.service.ts
  37. 1
      src/lib/mtproto/mtproto.worker.ts
  38. 5
      src/lib/mtproto/mtprotoworker.ts
  39. 2
      src/lib/mtproto/passwordManager.ts
  40. 2
      src/pages/pagePassword.ts
  41. 51
      src/pages/pageSignQR.ts
  42. 10
      src/scripts/format_jsons.js
  43. 4
      src/scripts/format_schema.js
  44. 241
      src/scripts/generate_mtproto_types.js
  45. 0
      src/scripts/in/countries.dat
  46. 0
      src/scripts/in/countries_pretty.json
  47. 0
      src/scripts/in/emoji_pretty.json
  48. 0
      src/scripts/in/schema.json
  49. 44
      src/scripts/in/schema_additional_params.json
  50. 0
      src/scripts/out/countries.json
  51. 0
      src/scripts/out/emoji.json
  52. 0
      src/scripts/out/schema.json
  53. 7
      src/tests/srp.test.ts
  54. 141
      src/types.d.ts
  55. 1
      tsconfig.json

2
package.json

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --config webpack.dev.js",
"start": "node --max-old-space-size=8192 node_modules/webpack-dev-server/bin/webpack-dev-server.js --config webpack.dev.js",
"start:production": "webpack-dev-server --config webpack.prod.js",
"serve": "npm run build; node server.js",
"build": "webpack --config webpack.prod.js",

7
src/components/appMediaPlaybackController.ts

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
import { MTDocument } from "../types";
import { $rootScope } from "../lib/utils";
import appMessagesManager from "../lib/appManagers/appMessagesManager";
import appDocsManager from "../lib/appManagers/appDocsManager";
import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
import { isSafari } from "../lib/config";
import { CancellablePromise, deferredPromise } from "../lib/polyfill";
@ -34,7 +33,7 @@ class AppMediaPlaybackController { @@ -34,7 +33,7 @@ class AppMediaPlaybackController {
document.body.append(this.container);
}
public addMedia(doc: MTDocument, mid: number, autoload = true): HTMLMediaElement {
public addMedia(doc: MyDocument, mid: number, autoload = true): HTMLMediaElement {
if(this.media[mid]) return this.media[mid];
const media = document.createElement(doc.type == 'round' ? 'video' : 'audio');
@ -93,7 +92,7 @@ class AppMediaPlaybackController { @@ -93,7 +92,7 @@ class AppMediaPlaybackController {
}
// если что - загрузит voice или round заранее, так правильнее
const downloadPromise: Promise<any> = !doc.supportsStreaming ? appDocsManager.downloadDocNew(doc.id) : Promise.resolve();
const downloadPromise: Promise<any> = !doc.supportsStreaming ? appDocsManager.downloadDocNew(doc) : Promise.resolve();
Promise.all([deferred, downloadPromise]).then(() => {
//media.autoplay = true;
//console.log('will set media url:', media, doc, doc.type, doc.url);

13
src/components/audio.ts

@ -1,13 +1,12 @@ @@ -1,13 +1,12 @@
import appDocsManager from "../lib/appManagers/appDocsManager";
import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
import { RichTextProcessor } from "../lib/richtextprocessor";
import { formatDate } from "./wrappers";
import ProgressivePreloader from "./preloader";
import { MediaProgressLine } from "../lib/mediaPlayer";
import appMediaPlaybackController from "./appMediaPlaybackController";
import { MTDocument } from "../types";
import { DocumentAttribute } from "../layer";
import { mediaSizes, isSafari } from "../lib/config";
import { Download } from "../lib/appManagers/appDownloadManager";
import { deferredPromise, CancellablePromise } from "../lib/polyfill";
// https://github.com/LonamiWebs/Telethon/blob/4393ec0b83d511b6a20d8a20334138730f084375/telethon/utils.py#L1285
export function decodeWaveform(waveform: Uint8Array | number[]) {
@ -43,7 +42,7 @@ export function decodeWaveform(waveform: Uint8Array | number[]) { @@ -43,7 +42,7 @@ export function decodeWaveform(waveform: Uint8Array | number[]) {
return result;
}
function wrapVoiceMessage(doc: MTDocument, audioEl: AudioElement) {
function wrapVoiceMessage(doc: MyDocument, audioEl: AudioElement) {
audioEl.classList.add('is-voice');
const barWidth = 2;
@ -62,7 +61,7 @@ function wrapVoiceMessage(doc: MTDocument, audioEl: AudioElement) { @@ -62,7 +61,7 @@ function wrapVoiceMessage(doc: MTDocument, audioEl: AudioElement) {
timeDiv.classList.add('audio-time');
audioEl.append(svg, timeDiv);
let waveform = doc.attributes[0].waveform || [];
let waveform = (doc.attributes.find(attribute => attribute._ == 'documentAttributeAudio') as DocumentAttribute.documentAttributeAudio).waveform || [];
waveform = decodeWaveform(waveform.slice());
//console.log('decoded waveform:', waveform);
@ -209,7 +208,7 @@ function wrapVoiceMessage(doc: MTDocument, audioEl: AudioElement) { @@ -209,7 +208,7 @@ function wrapVoiceMessage(doc: MTDocument, audioEl: AudioElement) {
return onLoad;
}
function wrapAudio(doc: MTDocument, audioEl: AudioElement) {
function wrapAudio(doc: MyDocument, audioEl: AudioElement) {
const withTime = !!+audioEl.getAttribute('with-time');
const title = doc.audioTitle || doc.file_name;
@ -368,7 +367,7 @@ export default class AudioElement extends HTMLElement { @@ -368,7 +367,7 @@ export default class AudioElement extends HTMLElement {
preloader = new ProgressivePreloader(null, true);
}
download = appDocsManager.downloadDocNew(doc.id);
download = appDocsManager.downloadDocNew(doc);
preloader.attach(downloadDiv, true, download);
download.then(() => {

22
src/components/emoticonsDropdown/tabs/gifs.ts

@ -3,8 +3,7 @@ import GifsMasonry from "../../gifsMasonry"; @@ -3,8 +3,7 @@ import GifsMasonry from "../../gifsMasonry";
import Scrollable from "../../scrollable_new";
import { putPreloader } from "../../misc";
import apiManager from "../../../lib/mtproto/mtprotoworker";
import { MTDocument } from "../../../types";
import appDocsManager from "../../../lib/appManagers/appDocsManager";
import appDocsManager, {MyDocument} from "../../../lib/appManagers/appDocsManager";
export default class GifsTab implements EmoticonsTab {
public content: HTMLElement;
@ -18,21 +17,20 @@ export default class GifsTab implements EmoticonsTab { @@ -18,21 +17,20 @@ export default class GifsTab implements EmoticonsTab {
const scroll = new Scrollable(this.content, 'y', 'GIFS', null);
const preloader = putPreloader(this.content, true);
apiManager.invokeApi('messages.getSavedGifs', {hash: 0}).then((_res) => {
let res = _res as {
_: 'messages.savedGifs',
gifs: MTDocument[],
hash: number
};
apiManager.invokeApi('messages.getSavedGifs', {hash: 0}).then((res) => {
//console.log('getSavedGifs res:', res);
if(res._ == 'messages.savedGifs') {
res.gifs.forEach((doc, idx) => {
res.gifs[idx] = doc = appDocsManager.saveDoc(doc);
//if(doc._ == 'documentEmpty') return;
masonry.add(doc as MyDocument, EMOTICONSSTICKERGROUP, EmoticonsDropdown.lazyLoadQueue);
});
}
//let line: MTDocument[] = [];
preloader.remove();
res.gifs.forEach((doc, idx) => {
res.gifs[idx] = appDocsManager.saveDoc(doc);
masonry.add(res.gifs[idx], EMOTICONSSTICKERGROUP, EmoticonsDropdown.lazyLoadQueue);
});
});
this.init = null;

29
src/components/emoticonsDropdown/tabs/stickers.ts

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
import emoticonsDropdown, { EmoticonsTab, EMOTICONSSTICKERGROUP, EmoticonsDropdown } from "..";
import { MTDocument } from "../../../types";
import { StickerSet } from "../../../layer";
import Scrollable from "../../scrollable_new";
import { wrapSticker } from "../../wrappers";
import appStickersManager, { MTStickerSet, MTStickerSetFull } from "../../../lib/appManagers/appStickersManager";
import appStickersManager from "../../../lib/appManagers/appStickersManager";
import appDownloadManager from "../../../lib/appManagers/appDownloadManager";
import { readBlobAsText } from "../../../helpers/blob";
import lottieLoader from "../../../lib/lottieLoader";
@ -11,7 +11,7 @@ import { RichTextProcessor } from "../../../lib/richtextprocessor"; @@ -11,7 +11,7 @@ import { RichTextProcessor } from "../../../lib/richtextprocessor";
import { $rootScope } from "../../../lib/utils";
import apiManager from "../../../lib/mtproto/mtprotoworker";
import StickyIntersector from "../../stickyIntersector";
import appDocsManager from "../../../lib/appManagers/appDocsManager";
import appDocsManager, {MyDocument} from "../../../lib/appManagers/appDocsManager";
import animationIntersector from "../../animationIntersector";
export default class StickersTab implements EmoticonsTab {
@ -23,7 +23,7 @@ export default class StickersTab implements EmoticonsTab { @@ -23,7 +23,7 @@ export default class StickersTab implements EmoticonsTab {
}} = {};
private recentDiv: HTMLElement;
private recentStickers: MTDocument[] = [];
private recentStickers: MyDocument[] = [];
private scroll: Scrollable;
@ -38,7 +38,7 @@ export default class StickersTab implements EmoticonsTab { @@ -38,7 +38,7 @@ export default class StickersTab implements EmoticonsTab {
private animatedDivs: Set<HTMLDivElement> = new Set();
private animatedIntersector: IntersectionObserver;
categoryPush(categoryDiv: HTMLElement, categoryTitle: string, promise: Promise<MTDocument[]>, prepend?: boolean) {
categoryPush(categoryDiv: HTMLElement, categoryTitle: string, promise: Promise<MyDocument[]>, prepend?: boolean) {
//if((docs.length % 5) != 0) categoryDiv.classList.add('not-full');
const itemsDiv = document.createElement('div');
@ -56,6 +56,7 @@ export default class StickersTab implements EmoticonsTab { @@ -56,6 +56,7 @@ export default class StickersTab implements EmoticonsTab {
promise.then(documents => {
documents.forEach(doc => {
//if(doc._ == 'documentEmpty') return;
itemsDiv.append(this.renderSticker(doc));
});
@ -76,7 +77,7 @@ export default class StickersTab implements EmoticonsTab { @@ -76,7 +77,7 @@ export default class StickersTab implements EmoticonsTab {
});
}
renderSticker(doc: MTDocument, div?: HTMLDivElement) {
renderSticker(doc: MyDocument, div?: HTMLDivElement) {
if(!div) {
div = document.createElement('div');
@ -101,7 +102,7 @@ export default class StickersTab implements EmoticonsTab { @@ -101,7 +102,7 @@ export default class StickersTab implements EmoticonsTab {
return div;
}
async renderStickerSet(set: MTStickerSet, prepend = false) {
async renderStickerSet(set: StickerSet.stickerSet, prepend = false) {
const categoryDiv = document.createElement('div');
categoryDiv.classList.add('sticker-category');
@ -122,7 +123,7 @@ export default class StickersTab implements EmoticonsTab { @@ -122,7 +123,7 @@ export default class StickersTab implements EmoticonsTab {
//stickersScroll.append(categoryDiv);
const promise = appStickersManager.getStickerSet(set);
this.categoryPush(categoryDiv, RichTextProcessor.wrapEmojiText(set.title), promise.then(stickerSet => stickerSet.documents), prepend);
this.categoryPush(categoryDiv, RichTextProcessor.wrapEmojiText(set.title), promise.then(stickerSet => stickerSet.documents as MyDocument[]), prepend);
const stickerSet = await promise;
//console.log('got stickerSet', stickerSet, li);
@ -153,7 +154,7 @@ export default class StickersTab implements EmoticonsTab { @@ -153,7 +154,7 @@ export default class StickersTab implements EmoticonsTab {
});
});
}
} else { // as thumb will be used first sticker
} else if(stickerSet.documents[0]._ != 'documentEmpty') { // as thumb will be used first sticker
wrapSticker({
doc: stickerSet.documents[0],
div: li as any,
@ -196,7 +197,7 @@ export default class StickersTab implements EmoticonsTab { @@ -196,7 +197,7 @@ export default class StickersTab implements EmoticonsTab {
}); */
$rootScope.$on('stickers_installed', (e: CustomEvent) => {
const set: MTStickerSet = e.detail;
const set: StickerSet.stickerSet = e.detail;
if(!this.stickerSets[set.id] && this.mounted) {
this.renderStickerSet(set, true);
@ -204,7 +205,7 @@ export default class StickersTab implements EmoticonsTab { @@ -204,7 +205,7 @@ export default class StickersTab implements EmoticonsTab {
});
$rootScope.$on('stickers_deleted', (e: CustomEvent) => {
const set: MTStickerSet = e.detail;
const set: StickerSet.stickerSet = e.detail;
if(this.stickerSets[set.id] && this.mounted) {
const elements = this.stickerSets[set.id];
@ -225,7 +226,7 @@ export default class StickersTab implements EmoticonsTab { @@ -225,7 +226,7 @@ export default class StickersTab implements EmoticonsTab {
Promise.all([
appStickersManager.getRecentStickers().then(stickers => {
this.recentStickers = stickers.stickers.slice(0, 20);
this.recentStickers = stickers.stickers.slice(0, 20) as MyDocument[];
//stickersScroll.prepend(categoryDiv);
@ -242,7 +243,7 @@ export default class StickersTab implements EmoticonsTab { @@ -242,7 +243,7 @@ export default class StickersTab implements EmoticonsTab {
let stickers: {
_: 'messages.allStickers',
hash: number,
sets: Array<MTStickerSet>
sets: Array<StickerSet.stickerSet>
} = res as any;
preloader.remove();
@ -353,7 +354,7 @@ export default class StickersTab implements EmoticonsTab { @@ -353,7 +354,7 @@ export default class StickersTab implements EmoticonsTab {
this.init = null;
}
pushRecentSticker(doc: MTDocument) {
pushRecentSticker(doc: MyDocument) {
if(!this.recentDiv.parentElement) {
return;
}

5
src/components/gifsMasonry.ts

@ -1,6 +1,5 @@ @@ -1,6 +1,5 @@
import { MTDocument } from "../types";
import { calcImageInBox, findUpClassName } from "../lib/utils";
import appDocsManager from "../lib/appManagers/appDocsManager";
import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
import { wrapVideo } from "./wrappers";
import { renderImageFromUrl } from "./misc";
import LazyLoadQueue from "./lazyLoadQueue";
@ -14,7 +13,7 @@ export default class GifsMasonry { @@ -14,7 +13,7 @@ export default class GifsMasonry {
}
public add(doc: MTDocument, group: string, lazyLoadQueue?: LazyLoadQueue) {
public add(doc: MyDocument, group: string, lazyLoadQueue?: LazyLoadQueue) {
let gifWidth = doc.w;
let gifHeight = doc.h;
if(gifHeight < height) {

4
src/components/popupAvatar.ts

@ -15,7 +15,7 @@ export class PopupAvatar { @@ -15,7 +15,7 @@ export class PopupAvatar {
removeHandlers: () => {}
};
private onCrop: (upload: () => Promise<any>) => void;
private onCrop: (upload: () => ReturnType<typeof appDownloadManager.upload>) => void;
constructor() {
this.container.style.display = ''; // need for no blink
@ -81,7 +81,7 @@ export class PopupAvatar { @@ -81,7 +81,7 @@ export class PopupAvatar {
});
}
public open(postCanvas: HTMLCanvasElement, onCrop: (upload: () => Promise<any>) => void) {
public open(postCanvas: HTMLCanvasElement, onCrop: PopupAvatar['onCrop']) {
this.canvas = postCanvas;
this.onCrop = onCrop;

9
src/components/popupStickers.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import { PopupElement } from "./popup";
import appStickersManager, { MTStickerSet } from "../lib/appManagers/appStickersManager";
import appStickersManager from "../lib/appManagers/appStickersManager";
import { RichTextProcessor } from "../lib/richtextprocessor";
import Scrollable from "./scrollable_new";
import { wrapSticker } from "./wrappers";
@ -8,13 +8,14 @@ import { putPreloader } from "./misc"; @@ -8,13 +8,14 @@ import { putPreloader } from "./misc";
import animationIntersector from "./animationIntersector";
import { findUpClassName } from "../lib/utils";
import appImManager from "../lib/appManagers/appImManager";
import { StickerSet } from "../layer";
export default class PopupStickers extends PopupElement {
private stickersFooter: HTMLElement;
private stickersDiv: HTMLElement;
private h6: HTMLElement;
private set: MTStickerSet;
private set: StickerSet.stickerSet;
constructor(private stickerSetInput: {
//_: 'inputStickerSetID',
@ -118,6 +119,10 @@ export default class PopupStickers extends PopupElement { @@ -118,6 +119,10 @@ export default class PopupStickers extends PopupElement {
this.stickersDiv.innerHTML = '';
for(let doc of set.documents) {
if(doc._ == 'documentEmpty') {
continue;
}
const div = document.createElement('div');
div.classList.add('sticker-set-sticker');

17
src/components/sidebarLeft/chatFolders.ts

@ -1,19 +1,14 @@ @@ -1,19 +1,14 @@
import { SliderTab } from "../slider";
import lottieLoader, { RLottiePlayer } from "../../lib/lottieLoader";
import apiManager from "../../lib/mtproto/mtprotoworker";
import appMessagesManager, { DialogFilter } from "../../lib/appManagers/appMessagesManager";
import appMessagesManager, { MyDialogFilter } from "../../lib/appManagers/appMessagesManager";
import { RichTextProcessor } from "../../lib/richtextprocessor";
import appPeersManager from "../../lib/appManagers/appPeersManager";
import { $rootScope, cancelEvent } from "../../lib/utils";
import appSidebarLeft from "../../lib/appManagers/appSidebarLeft";
import { ripple } from "../ripple";
import { toast } from "../toast";
type DialogFilterSuggested = {
_: 'dialogFilterSuggested',
filter: DialogFilter,
description: string
};
import { DialogFilterSuggested, DialogFilter } from "../../layer";
export default class AppChatFoldersTab implements SliderTab {
public container: HTMLElement;
@ -25,8 +20,8 @@ export default class AppChatFoldersTab implements SliderTab { @@ -25,8 +20,8 @@ export default class AppChatFoldersTab implements SliderTab {
private filtersRendered: {[filterID: number]: HTMLElement} = {};
private renderFolder(dialogFilter: DialogFilterSuggested | DialogFilter, container?: HTMLElement, div: HTMLElement = document.createElement('div')) {
let filter: DialogFilter;
private renderFolder(dialogFilter: DialogFilterSuggested | DialogFilter | MyDialogFilter, container?: HTMLElement, div: HTMLElement = document.createElement('div')) {
let filter: DialogFilter | MyDialogFilter;
let description = '';
let d: string[] = [];
if(dialogFilter._ == 'dialogFilterSuggested') {
@ -157,7 +152,7 @@ export default class AppChatFoldersTab implements SliderTab { @@ -157,7 +152,7 @@ export default class AppChatFoldersTab implements SliderTab {
this.suggestedContainer.style.display = suggestedFilters.length ? '' : 'none';
Array.from(this.suggestedContainer.children).slice(1).forEach(el => el.remove());
(suggestedFilters as DialogFilterSuggested[]).forEach(filter => {
suggestedFilters.forEach(filter => {
const div = this.renderFolder(filter);
const button = document.createElement('button');
button.classList.add('btn-primary');
@ -175,7 +170,7 @@ export default class AppChatFoldersTab implements SliderTab { @@ -175,7 +170,7 @@ export default class AppChatFoldersTab implements SliderTab {
button.setAttribute('disabled', 'true');
appMessagesManager.filtersStorage.createDialogFilter(filter.filter).then(bool => {
appMessagesManager.filtersStorage.createDialogFilter(filter.filter as any).then(bool => {
if(bool) {
div.remove();
}

2
src/components/sidebarLeft/editFolder.ts

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import { SliderTab } from "../slider";
import appSidebarLeft, { AppSidebarLeft } from "../../lib/appManagers/appSidebarLeft";
import lottieLoader, { RLottiePlayer } from "../../lib/lottieLoader";
import appMessagesManager, { DialogFilter } from "../../lib/appManagers/appMessagesManager";
import appMessagesManager, { MyDialogFilter as DialogFilter } from "../../lib/appManagers/appMessagesManager";
import { parseMenuButtonsTo } from "../misc";
import appDialogsManager from "../../lib/appManagers/appDialogsManager";
import { copy, deepEqual } from "../../lib/utils";

3
src/components/sidebarLeft/editProfile.ts

@ -6,6 +6,7 @@ import appSidebarLeft from "../../lib/appManagers/appSidebarLeft"; @@ -6,6 +6,7 @@ import appSidebarLeft from "../../lib/appManagers/appSidebarLeft";
import Scrollable from "../scrollable_new";
import appUsersManager from "../../lib/appManagers/appUsersManager";
import { $rootScope } from "../../lib/utils";
import { InputFile } from "../../layer";
// TODO: аватарка не поменяется в этой вкладке после изменения почему-то (если поставить в другом клиенте, и потом тут проверить, для этого ещё вышел в чатлист)
@ -14,7 +15,7 @@ export default class AppEditProfileTab implements SliderTab { @@ -14,7 +15,7 @@ export default class AppEditProfileTab implements SliderTab {
private scrollWrapper = this.container.querySelector('.scroll-wrapper') as HTMLDivElement;
private nextBtn = this.container.querySelector('.btn-corner') as HTMLButtonElement;
private canvas = this.container.querySelector('.avatar-edit-canvas') as HTMLCanvasElement;
private uploadAvatar: () => Promise<any> = null;
private uploadAvatar: () => Promise<InputFile> = null;
private firstNameInput = this.container.querySelector('.firstname') as HTMLInputElement;
private lastNameInput = this.container.querySelector('.lastname') as HTMLInputElement;

4
src/components/sidebarLeft/includedChats.ts

@ -4,8 +4,8 @@ import appSidebarLeft, { AppSidebarLeft } from "../../lib/appManagers/appSidebar @@ -4,8 +4,8 @@ import appSidebarLeft, { AppSidebarLeft } from "../../lib/appManagers/appSidebar
import appDialogsManager from "../../lib/appManagers/appDialogsManager";
import appPeersManager from "../../lib/appManagers/appPeersManager";
import appUsersManager from "../../lib/appManagers/appUsersManager";
import { $rootScope, copy, deepEqual } from "../../lib/utils";
import { DialogFilter } from "../../lib/appManagers/appMessagesManager";
import { $rootScope, copy } from "../../lib/utils";
import { MyDialogFilter as DialogFilter } from "../../lib/appManagers/appMessagesManager";
export default class AppIncludedChatsTab implements SliderTab {
public container: HTMLElement;

3
src/components/sidebarRight/gifs.ts

@ -9,6 +9,7 @@ import appInlineBotsManager, { AppInlineBotsManager } from "../../lib/appManager @@ -9,6 +9,7 @@ import appInlineBotsManager, { AppInlineBotsManager } from "../../lib/appManager
import GifsMasonry from "../gifsMasonry";
import { findUpClassName } from "../../lib/utils";
import appImManager from "../../lib/appManagers/appImManager";
import type { MyDocument } from "../../lib/appManagers/appDocsManager";
const ANIMATIONGROUP = 'GIFS-SEARCH';
@ -116,7 +117,7 @@ export default class AppGifsTab implements SliderTab { @@ -116,7 +117,7 @@ export default class AppGifsTab implements SliderTab {
if(results.length) {
results.forEach((result) => {
if(result._ === 'botInlineMediaResult' && result.document) {
this.masonry.add(result.document, ANIMATIONGROUP, this.lazyLoadQueue);
this.masonry.add(result.document as MyDocument, ANIMATIONGROUP, this.lazyLoadQueue);
}
});
} else {

14
src/components/sidebarRight/stickers.ts

@ -4,12 +4,13 @@ import Scrollable from "../scrollable_new"; @@ -4,12 +4,13 @@ import Scrollable from "../scrollable_new";
import LazyLoadQueue from "../lazyLoadQueue";
import { findUpClassName } from "../../lib/utils";
import appImManager from "../../lib/appManagers/appImManager";
import appStickersManager, { MTStickerSet, MTStickerSetCovered, MTStickerSetMultiCovered } from "../../lib/appManagers/appStickersManager";
import appStickersManager from "../../lib/appManagers/appStickersManager";
import PopupStickers from "../popupStickers";
import animationIntersector from "../animationIntersector";
import { RichTextProcessor } from "../../lib/richtextprocessor";
import { wrapSticker } from "../wrappers";
import appSidebarRight, { AppSidebarRight } from "../../lib/appManagers/appSidebarRight";
import { StickerSet, StickerSetCovered, Document } from "../../layer";
export default class AppStickersTab implements SliderTab {
private container = document.getElementById('stickers-container') as HTMLDivElement;
@ -80,7 +81,7 @@ export default class AppStickersTab implements SliderTab { @@ -80,7 +81,7 @@ export default class AppStickersTab implements SliderTab {
animationIntersector.checkAnimations(undefined, 'STICKERS-SEARCH');
}
public renderSet(set: MTStickerSet) {
public renderSet(set: StickerSet.stickerSet) {
//console.log('renderSet:', set);
const div = document.createElement('div');
div.classList.add('sticker-set');
@ -124,8 +125,13 @@ export default class AppStickersTab implements SliderTab { @@ -124,8 +125,13 @@ export default class AppStickersTab implements SliderTab {
for(let i = 0; i < count; ++i) {
const div = stickersDiv.children[i] as HTMLDivElement;
const doc = set.documents[i];
if(doc._ == 'documentEmpty') {
continue;
}
wrapSticker({
doc: set.documents[i],
doc,
div,
lazyLoadQueue: this.lazyLoadQueue,
group: 'STICKERS-SEARCH',
@ -194,7 +200,7 @@ export default class AppStickersTab implements SliderTab { @@ -194,7 +200,7 @@ export default class AppStickersTab implements SliderTab {
});
}
private filterRendered(query: string, coveredSets: (MTStickerSetCovered | MTStickerSetMultiCovered)[]) {
private filterRendered(query: string, coveredSets: StickerSetCovered[]) {
coveredSets = coveredSets.slice();
const children = Array.from(this.setsDiv.children) as HTMLElement[];

43
src/components/wrappers.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import appPhotosManager, { MTPhoto } from '../lib/appManagers/appPhotosManager';
import appPhotosManager, {MyPhoto} from '../lib/appManagers/appPhotosManager';
import LottieLoader from '../lib/lottieLoader';
import appDocsManager from "../lib/appManagers/appDocsManager";
import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager";
import { formatBytes, getEmojiToneIndex, isInDOM } from "../lib/utils";
import ProgressivePreloader from './preloader';
import LazyLoadQueue from './lazyLoadQueue';
@ -11,16 +11,16 @@ import appMessagesManager from '../lib/appManagers/appMessagesManager'; @@ -11,16 +11,16 @@ import appMessagesManager from '../lib/appManagers/appMessagesManager';
import { Layouter, RectPart } from './groupedLayout';
import PollElement from './poll';
import { mediaSizes, isSafari } from '../lib/config';
import { MTDocument, MTPhotoSize } from '../types';
import animationIntersector from './animationIntersector';
import AudioElement from './audio';
import { DownloadBlob } from '../lib/appManagers/appDownloadManager';
import webpWorkerController from '../lib/webp/webpWorkerController';
import { readBlobAsText } from '../helpers/blob';
import appMediaPlaybackController from './appMediaPlaybackController';
import { PhotoSize } from '../layer';
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: {
doc: MTDocument,
doc: MyDocument,
container?: HTMLDivElement,
message?: any,
boxWidth?: number,
@ -140,7 +140,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -140,7 +140,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
appPhotosManager.setAttachmentSize(doc, container, boxWidth, boxHeight, false, true);
}
if(doc.thumbs && doc.thumbs[0]?.bytes) {
if(doc.thumbs?.length && 'bytes' in doc.thumbs[0]) {
appPhotosManager.setAttachmentPreview(doc.thumbs[0].bytes, container, false);
}
@ -281,7 +281,7 @@ export const formatDate = (timestamp: number, monthShort = false, withYear = tru @@ -281,7 +281,7 @@ export const formatDate = (timestamp: number, monthShort = false, withYear = tru
return str + ' at ' + date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2);
};
export function wrapDocument(doc: MTDocument, withTime = false, uploading = false, mid?: number): HTMLElement {
export function wrapDocument(doc: MyDocument, withTime = false, uploading = false, mid?: number): HTMLElement {
if(doc.type == 'audio' || doc.type == 'voice') {
return wrapAudio(doc, withTime, mid);
}
@ -351,7 +351,7 @@ export function wrapDocument(doc: MTDocument, withTime = false, uploading = fals @@ -351,7 +351,7 @@ export function wrapDocument(doc: MTDocument, withTime = false, uploading = fals
return docDiv;
}
export function wrapAudio(doc: MTDocument, withTime = false, mid?: number): HTMLElement {
export function wrapAudio(doc: MyDocument, withTime = false, mid?: number): HTMLElement {
let elem = new AudioElement();
elem.setAttribute('doc-id', doc.id);
elem.setAttribute('with-time', '' + +withTime);
@ -359,7 +359,7 @@ export function wrapAudio(doc: MTDocument, withTime = false, mid?: number): HTML @@ -359,7 +359,7 @@ export function wrapAudio(doc: MTDocument, withTime = false, mid?: number): HTML
return elem;
}
function wrapMediaWithTail(photo: MTPhoto | MTDocument, message: {mid: number, message: string}, container: HTMLDivElement, boxWidth: number, boxHeight: number, isOut: boolean) {
function wrapMediaWithTail(photo: MyPhoto | MyDocument, message: {mid: number, message: string}, container: HTMLDivElement, boxWidth: number, boxHeight: number, isOut: boolean) {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.classList.add('bubble__media-container', isOut ? 'is-out' : 'is-in');
@ -414,7 +414,7 @@ function wrapMediaWithTail(photo: MTPhoto | MTDocument, message: {mid: number, m @@ -414,7 +414,7 @@ function wrapMediaWithTail(photo: MTPhoto | MTDocument, message: {mid: number, m
return img;
}
export function wrapPhoto(photo: MTPhoto | MTDocument, message: any, container: HTMLDivElement, boxWidth = mediaSizes.active.regular.width, boxHeight = mediaSizes.active.regular.height, withTail: boolean, isOut: boolean, lazyLoadQueue: LazyLoadQueue, middleware: () => boolean, size: MTPhotoSize = null) {
export function wrapPhoto(photo: MyPhoto | MyDocument, message: any, container: HTMLDivElement, boxWidth = mediaSizes.active.regular.width, boxHeight = mediaSizes.active.regular.height, withTail: boolean, isOut: boolean, lazyLoadQueue: LazyLoadQueue, middleware: () => boolean, size: PhotoSize = null) {
let image: HTMLImageElement;
if(withTail) {
image = wrapMediaWithTail(photo, message, container, boxWidth, boxHeight, isOut);
@ -424,8 +424,8 @@ export function wrapPhoto(photo: MTPhoto | MTDocument, message: any, container: @@ -424,8 +424,8 @@ export function wrapPhoto(photo: MTPhoto | MTDocument, message: any, container:
}
if(photo._ == 'document' || !photo.downloaded) {
const thumbs = (photo as MTPhoto).sizes || (photo as MTDocument).thumbs;
if(thumbs && thumbs[0]?.bytes) {
const thumbs = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs;
if(thumbs?.length && 'bytes' in thumbs[0]) {
appPhotosManager.setAttachmentPreview(thumbs[0].bytes, container, false);
}
}
@ -465,7 +465,7 @@ export function wrapPhoto(photo: MTPhoto | MTDocument, message: any, container: @@ -465,7 +465,7 @@ export function wrapPhoto(photo: MTPhoto | MTDocument, message: any, container:
}
export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop}: {
doc: MTDocument,
doc: MyDocument,
div: HTMLDivElement,
middleware?: () => boolean,
lazyLoadQueue?: LazyLoadQueue,
@ -504,7 +504,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o @@ -504,7 +504,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
const toneIndex = emoji ? getEmojiToneIndex(emoji) : -1;
if(doc.thumbs?.length && !div.firstElementChild && (!doc.downloaded || stickerType == 2 || onlyThumb) && toneIndex <= 0) {
if(doc.thumbs?.length && !div.firstElementChild && (!doc.downloaded || stickerType == 2 || onlyThumb) && toneIndex <= 0/* && doc.thumbs[0]._ != 'photoSizeEmpty' */) {
const thumb = doc.thumbs[0];
//console.log('wrap sticker', thumb, div);
@ -515,16 +515,19 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o @@ -515,16 +515,19 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
div.append(img);
}
};
if(thumb.bytes || thumb.url) {
if('url' in thumb) {
img = new Image();
renderImageFromUrl(img, thumb.url, afterRender);
} else if('bytes' in thumb) {
img = new Image();
if((!isSafari || doc.stickerThumbConverted || thumb.url)/* && false */) {
if((!isSafari || doc.pFlags.stickerThumbConverted || thumb.url)/* && false */) {
renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb, true), afterRender);
} else {
webpWorkerController.convert(doc.id, thumb.bytes).then(bytes => {
webpWorkerController.convert(doc.id, thumb.bytes as Uint8Array).then(bytes => {
thumb.bytes = bytes;
doc.stickerThumbConverted = true;
doc.pFlags.stickerThumbConverted = true;
if(middleware && !middleware()) return;
@ -578,7 +581,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o @@ -578,7 +581,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
//appDocsManager.downloadDocNew(doc.id).promise.then(res => res.json()).then(async(json) => {
//fetch(doc.url).then(res => res.json()).then(async(json) => {
await appDocsManager.downloadDocNew(doc.id)
await appDocsManager.downloadDocNew(doc)
.then(readBlobAsText)
.then(JSON.parse)
.then(async(json) => {
@ -718,7 +721,7 @@ export function wrapAlbum({groupID, attachmentDiv, middleware, uploading, lazyLo @@ -718,7 +721,7 @@ export function wrapAlbum({groupID, attachmentDiv, middleware, uploading, lazyLo
uploading?: boolean,
isOut: boolean
}) {
const items: {size: MTPhotoSize, media: any, message: any}[] = [];
const items: {size: PhotoSize.photoSize, media: any, message: any}[] = [];
// !higher msgID will be the FIRST in album
const storage = Object.keys(appMessagesManager.groupedMessagesStorage[groupID]).map(id => +id).sort((a, b) => a - b);

10777
src/layer.d.ts vendored

File diff suppressed because it is too large Load Diff

62
src/lib/appManagers/AppInlineBotsManager.ts

@ -4,41 +4,10 @@ import appPeersManager from "../appManagers/appPeersManager"; @@ -4,41 +4,10 @@ import appPeersManager from "../appManagers/appPeersManager";
import appMessagesIDsManager from "./appMessagesIDsManager";
import { RichTextProcessor } from "../richtextprocessor";
import { toast } from "../../components/toast";
import appUsersManager, { User } from "./appUsersManager";
import appPhotosManager, { MTPhoto } from "./appPhotosManager";
import { MTDocument } from "../../types";
import appUsersManager from "./appUsersManager";
import appPhotosManager from "./appPhotosManager";
import appDocsManager from "./appDocsManager";
type botInlineResult = {
_: 'botInlineResult',
flags: number,
id: string,
type: string,
title?: string,
description?: string,
url?: string,
thumb: any,
content: any,
send_message: any
};
type botInlineMediaResult = {
_: 'botInlineMediaResult',
flags: number,
id: string,
type: string,
photo?: MTPhoto,
document?: MTDocument,
title?: string,
description?: string,
send_message: any
};
type BotInlineResult = (botInlineResult | botInlineMediaResult) & Partial<{
qID: string,
botID: number,
rTitle: string,
rDescription: string,
initials: string
}>;
import { BotInlineResult } from "../../layer";
export class AppInlineBotsManager {
private inlineResults: {[qID: string]: BotInlineResult} = {};
@ -51,39 +20,30 @@ export class AppInlineBotsManager { @@ -51,39 +20,30 @@ export class AppInlineBotsManager {
query: query,
geo_point: geo && {_: 'inputGeoPoint', lat: geo['lat'], long: geo['long']},
offset
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then((botResults: {
_: 'messages.botResults',
flags: number,
pFlags: Partial<{gallery: true}>,
query_id: string,
next_offset?: string,
switch_pm?: any,
results: BotInlineResult[],
cache_time: number,
users: User[]
}) => {
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then(botResults => {
const queryID = botResults.query_id;
/* delete botResults._;
delete botResults.flags;
delete botResults.query_id; */
if(botResults.switch_pm) {
/* if(botResults.switch_pm) {
botResults.switch_pm.rText = RichTextProcessor.wrapRichText(botResults.switch_pm.text, {noLinebreaks: true, noLinks: true});
}
} */
botResults.results.forEach((result: BotInlineResult) => {
botResults.results.forEach(result => {
const qID = queryID + '_' + result.id;
result.qID = qID;
/* result.qID = qID;
result.botID = botID;
result.rTitle = RichTextProcessor.wrapRichText(result.title, {noLinebreaks: true, noLinks: true});
result.rDescription = RichTextProcessor.wrapRichText(result.description, {noLinebreaks: true, noLinks: true});
result.initials = ((result as botInlineResult).url || result.title || result.type || '').substr(0, 1);
result.initials = ((result as botInlineResult).url || result.title || result.type || '').substr(0, 1); */
if(result._ == 'botInlineMediaResult') {
if(result.document) {
result.document = appDocsManager.saveDoc(result.document);
}
if(result.photo) {
result.photo = appPhotosManager.savePhoto(result.photo);
}
@ -321,7 +281,7 @@ export class AppInlineBotsManager { @@ -321,7 +281,7 @@ export class AppInlineBotsManager {
peer: appPeersManager.getInputPeerByID(peerID),
msg_id: appMessagesIDsManager.getMessageLocalID(mid),
data: button.data
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then((callbackAnswer: any) => {
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then((callbackAnswer) => {
if(typeof callbackAnswer.message === 'string' && callbackAnswer.message.length) {
toast(RichTextProcessor.wrapRichText(callbackAnswer.message, {noLinks: true, noLinebreaks: true}));
}

90
src/lib/appManagers/apiUpdatesManager.ts

@ -6,6 +6,7 @@ import appPeersManager from "./appPeersManager"; @@ -6,6 +6,7 @@ import appPeersManager from "./appPeersManager";
import appUsersManager from "./appUsersManager";
import appChatsManager from "./appChatsManager";
import { logger, LogLevels } from '../logger';
import { Updates, UpdatesState } from '../../layer';
export class ApiUpdatesManager {
public updatesState: {
@ -192,12 +193,13 @@ export class ApiUpdatesManager { @@ -192,12 +193,13 @@ export class ApiUpdatesManager {
}
return apiManager.invokeApi('updates.getDifference', {
flags: 0,
pts: updatesState.pts,
date: updatesState.date,
qts: -1
}, {
timeout: 0x7fffffff
}).then((differenceResult: any) => {
}).then((differenceResult) => {
if(differenceResult._ == 'updates.differenceEmpty') {
this.log('apply empty diff', differenceResult.seq);
updatesState.date = differenceResult.date;
@ -206,39 +208,45 @@ export class ApiUpdatesManager { @@ -206,39 +208,45 @@ export class ApiUpdatesManager {
$rootScope.$broadcast('stateSynchronized');
return false;
}
appUsersManager.saveApiUsers(differenceResult.users);
appChatsManager.saveApiChats(differenceResult.chats);
// Should be first because of updateMessageID
// this.log('applying', differenceResult.other_updates.length, 'other updates')
differenceResult.other_updates.forEach((update: any) => {
switch(update._) {
case 'updateChannelTooLong':
case 'updateNewChannelMessage':
case 'updateEditChannelMessage':
this.processUpdate(update);
return;
}
this.saveUpdate(update);
});
// this.log('applying', differenceResult.new_messages.length, 'new messages')
differenceResult.new_messages.forEach((apiMessage: any) => {
this.saveUpdate({
_: 'updateNewMessage',
message: apiMessage,
pts: updatesState.pts,
pts_count: 0
if(differenceResult._ != 'updates.differenceTooLong') {
appUsersManager.saveApiUsers(differenceResult.users);
appChatsManager.saveApiChats(differenceResult.chats);
// Should be first because of updateMessageID
// this.log('applying', differenceResult.other_updates.length, 'other updates')
differenceResult.other_updates.forEach((update: any) => {
switch(update._) {
case 'updateChannelTooLong':
case 'updateNewChannelMessage':
case 'updateEditChannelMessage':
this.processUpdate(update);
return;
}
this.saveUpdate(update);
});
});
const nextState = differenceResult.intermediate_state || differenceResult.state;
updatesState.seq = nextState.seq;
updatesState.pts = nextState.pts;
updatesState.date = nextState.date;
// this.log('applying', differenceResult.new_messages.length, 'new messages')
differenceResult.new_messages.forEach((apiMessage: any) => {
this.saveUpdate({
_: 'updateNewMessage',
message: apiMessage,
pts: updatesState.pts,
pts_count: 0
});
});
const nextState = differenceResult._ == 'updates.difference' ? differenceResult.state : differenceResult.intermediate_state;
updatesState.seq = nextState.seq;
updatesState.pts = nextState.pts;
updatesState.date = nextState.date;
} else {
updatesState.pts = differenceResult.pts;
delete updatesState.seq;
delete updatesState.date;
}
// this.log('apply diff', updatesState.seq, updatesState.pts)
@ -254,27 +262,29 @@ export class ApiUpdatesManager { @@ -254,27 +262,29 @@ export class ApiUpdatesManager {
});
}
public getChannelDifference(channelID: any) {
var channelState = this.getChannelState(channelID);
public getChannelDifference(channelID: number) {
const channelState = this.getChannelState(channelID);
if(!channelState.syncLoading) {
channelState.syncLoading = true;
channelState.pendingPtsUpdates = [];
}
if(channelState.syncPending) {
clearTimeout(channelState.syncPending.timeout);
channelState.syncPending = false;
}
// this.log('Get channel diff', appChatsManager.getChat(channelID), channelState.pts)
apiManager.invokeApi('updates.getChannelDifference', {
channel: appChatsManager.getChannelInput(channelID),
filter: {_: 'channelMessagesFilterEmpty'},
pts: channelState.pts,
limit: 30
}, {timeout: 0x7fffffff}).then((differenceResult: any) => {
}, {timeout: 0x7fffffff}).then((differenceResult) => {
// this.log('channel diff result', differenceResult)
channelState.pts = differenceResult.pts;
channelState.pts = 'pts' in differenceResult ? differenceResult.pts : undefined;
if (differenceResult._ == 'updates.channelDifferenceEmpty') {
if(differenceResult._ == 'updates.channelDifferenceEmpty') {
this.log('apply channel empty diff', differenceResult);
channelState.syncLoading = false;
$rootScope.$broadcast('stateSynchronized');
@ -327,6 +337,7 @@ export class ApiUpdatesManager { @@ -327,6 +337,7 @@ export class ApiUpdatesManager {
if(!pts) {
throw new Error('Add channel state without pts ' + channelID);
}
if(!(channelID in this.channelStates)) {
this.channelStates[channelID] = {
pts: pts,
@ -334,8 +345,10 @@ export class ApiUpdatesManager { @@ -334,8 +345,10 @@ export class ApiUpdatesManager {
syncPending: false,
syncLoading: false
};
return true;
}
return false;
}
@ -343,6 +356,7 @@ export class ApiUpdatesManager { @@ -343,6 +356,7 @@ export class ApiUpdatesManager {
if(this.channelStates[channelID] === undefined) {
this.addChannelState(channelID, pts);
}
return this.channelStates[channelID];
}

25
src/lib/appManagers/appChatsManager.ts

@ -5,6 +5,7 @@ import apiManager from '../mtproto/mtprotoworker'; @@ -5,6 +5,7 @@ import apiManager from '../mtproto/mtprotoworker';
import apiUpdatesManager from "./apiUpdatesManager";
import appProfileManager from "./appProfileManager";
import searchIndexManager from "../searchIndexManager";
import { InputPeer, InputChannel, Updates, InputChatPhoto } from "../../layer";
export type Channel = {
_: 'channel',
@ -265,11 +266,7 @@ export class AppChatsManager { @@ -265,11 +266,7 @@ export class AppChatsManager {
return this.isChannel(id) && !this.isMegagroup(id);
}
public getChannelInput(id: number) {
if(!id) {
return {_: 'inputChannelEmpty'};
}
public getChannelInput(id: number): InputChannel {
if(id < 0) id = -id;
return {
_: 'inputChannel',
@ -278,18 +275,14 @@ export class AppChatsManager { @@ -278,18 +275,14 @@ export class AppChatsManager {
};
}
public getChatInputPeer(id: number) {
public getChatInputPeer(id: number): InputPeer.inputPeerChat {
return {
_: 'inputPeerChat',
chat_id: id
};
}
public getChannelInputPeer(id: number) {
if(!id) {
return {_: 'inputPeerEmpty'};
}
public getChannelInputPeer(id: number): InputPeer.inputPeerChannel {
return {
_: 'inputPeerChannel',
channel_id: id,
@ -380,7 +373,7 @@ export class AppChatsManager { @@ -380,7 +373,7 @@ export class AppChatsManager {
return participants;
}
public createChannel(title: string, about: String): Promise<number> {
public createChannel(title: string, about: string): Promise<number> {
return apiManager.invokeApi('channels.createChannel', {
flags: 1,
broadcast: true,
@ -412,21 +405,21 @@ export class AppChatsManager { @@ -412,21 +405,21 @@ export class AppChatsManager {
}).then(updates => {
apiUpdatesManager.processUpdateMessage(updates);
return updates.chats[0].id;
return (updates as any as Updates.updates).chats[0].id;
});
}
public editPhoto(id: number, inputFile: any) {
let isChannel = this.isChannel(id);
const isChannel = this.isChannel(id);
let inputChatPhoto = {
const inputChatPhoto: InputChatPhoto.inputChatUploadedPhoto = {
_: 'inputChatUploadedPhoto',
file: inputFile
};
if(isChannel) {
return apiManager.invokeApi('channels.editPhoto', {
channel: this.getChannelInputPeer(id),
channel: this.getChannelInput(id),
photo: inputChatPhoto
}).then(updates => {
apiUpdatesManager.processUpdateMessage(updates);

9
src/lib/appManagers/appDialogsManager.ts

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import { findUpClassName, $rootScope, escapeRegExp, whichChild, findUpTag, cancelEvent, positionElementByIndex } from "../utils";
import { findUpClassName, $rootScope, escapeRegExp, findUpTag, cancelEvent, positionElementByIndex } from "../utils";
import appImManager, { AppImManager } from "./appImManager";
import appPeersManager from './appPeersManager';
import appMessagesManager, { AppMessagesManager, Dialog, DialogFilter } from "./appMessagesManager";
import appMessagesManager, { Dialog, MyDialogFilter as DialogFilter } from "./appMessagesManager";
import appUsersManager, { User } from "./appUsersManager";
import { RichTextProcessor } from "../richtextprocessor";
import { putPreloader, positionMenu, openBtnMenu, parseMenuButtonsTo, attachContextMenuListener } from "../../components/misc";
@ -1168,9 +1168,10 @@ export class AppDialogsManager { @@ -1168,9 +1168,10 @@ export class AppDialogsManager {
if(!container) {
let peer: any;
// for muted icon
titleSpan.classList.add('tgico');
if(peerID < 0) {
titleSpan.classList.add('tgico');
peer = appChatsManager.getChat(-peerID);
} else {
peer = appUsersManager.getUser(peerID);

76
src/lib/appManagers/appDocsManager.ts

@ -1,30 +1,40 @@ @@ -1,30 +1,40 @@
import {RichTextProcessor} from '../richtextprocessor';
import { isObject, getFileURL, FileURLType } from '../utils';
import opusDecodeController from '../opusDecodeController';
import { MTDocument, inputDocumentFileLocation, MTPhotoSize } from '../../types';
import { getFileNameByLocation } from '../bin_utils';
import appDownloadManager, { DownloadBlob } from './appDownloadManager';
import appPhotosManager from './appPhotosManager';
import { isServiceWorkerSupported } from '../config';
import { InputFileLocation, Document, PhotoSize } from '../../layer';
export type MyDocument = Document.document;
class AppDocsManager {
private docs: {[docID: string]: MTDocument} = {};
private docs: {[docID: string]: MyDocument} = {};
public saveDoc(doc: MTDocument, context?: any) {
public saveDoc(doc: Document, context?: any): MyDocument {
if(doc._ == 'documentEmpty') {
return undefined;
}
//console.log('saveDoc', apiDoc, this.docs[apiDoc.id]);
if(this.docs[doc.id]) {
const d = this.docs[doc.id];
if(doc.thumbs) {
if(!d.thumbs) d.thumbs = doc.thumbs;
/* else if(apiDoc.thumbs[0].bytes && !d.thumbs[0].bytes) {
d.thumbs.unshift(apiDoc.thumbs[0]);
} else if(d.thumbs[0].url) { // fix for converted thumb in safari
apiDoc.thumbs[0] = d.thumbs[0];
} */
}
//if(doc._ != 'documentEmpty' && doc._ == d._) {
if(doc.thumbs) {
if(!d.thumbs) d.thumbs = doc.thumbs;
/* else if(apiDoc.thumbs[0].bytes && !d.thumbs[0].bytes) {
d.thumbs.unshift(apiDoc.thumbs[0]);
} else if(d.thumbs[0].url) { // fix for converted thumb in safari
apiDoc.thumbs[0] = d.thumbs[0];
} */
}
d.file_reference = doc.file_reference;
//}
d.file_reference = doc.file_reference;
return d;
//return Object.assign(d, apiDoc, context);
@ -37,7 +47,7 @@ class AppDocsManager { @@ -37,7 +47,7 @@ class AppDocsManager {
this.docs[doc.id] = doc;
doc.attributes.forEach((attribute: any) => {
doc.attributes.forEach(attribute => {
switch(attribute._) {
case 'documentAttributeFilename':
doc.file_name = RichTextProcessor.wrapPlainText(attribute.file_name);
@ -145,10 +155,6 @@ class AppDocsManager { @@ -145,10 +155,6 @@ class AppDocsManager {
doc.animated = true;
doc.sticker = 2;
}
if(doc._ == 'documentEmpty') {
doc.size = 0;
}
/* if(!doc.url) {
doc.url = this.getFileURL(doc);
@ -157,11 +163,11 @@ class AppDocsManager { @@ -157,11 +163,11 @@ class AppDocsManager {
return doc;
}
public getDoc(docID: string | MTDocument): MTDocument {
return isObject(docID) && typeof(docID) !== 'string' ? docID : this.docs[docID as string];
public getDoc(docID: string | MyDocument): MyDocument {
return isObject(docID) && typeof(docID) !== 'string' ? docID as any : this.docs[docID as string] as any;
}
public getMediaInput(doc: MTDocument) {
public getMediaInput(doc: MyDocument) {
return {
_: 'inputMediaDocument',
flags: 0,
@ -175,7 +181,7 @@ class AppDocsManager { @@ -175,7 +181,7 @@ class AppDocsManager {
};
}
public getInput(doc: MTDocument, thumbSize?: string): inputDocumentFileLocation {
public getInput(doc: MyDocument, thumbSize?: string): InputFileLocation.inputDocumentFileLocation {
return {
_: 'inputDocumentFileLocation',
id: doc.id,
@ -185,7 +191,7 @@ class AppDocsManager { @@ -185,7 +191,7 @@ class AppDocsManager {
};
}
public getFileDownloadOptions(doc: MTDocument, thumb?: MTPhotoSize) {
public getFileDownloadOptions(doc: MyDocument, thumb?: PhotoSize.photoSize) {
const inputFileLocation = this.getInput(doc, thumb?.type);
let mimeType: string;
@ -204,7 +210,7 @@ class AppDocsManager { @@ -204,7 +210,7 @@ class AppDocsManager {
};
}
public getFileURL(doc: MTDocument, download = false, thumb?: MTPhotoSize) {
public getFileURL(doc: MyDocument, download = false, thumb?: PhotoSize.photoSize) {
let type: FileURLType;
if(download) {
type = 'download';
@ -219,11 +225,11 @@ class AppDocsManager { @@ -219,11 +225,11 @@ class AppDocsManager {
return getFileURL(type, this.getFileDownloadOptions(doc, thumb));
}
public getThumbURL(doc: MTDocument, thumb: MTPhotoSize) {
public getThumbURL(doc: MyDocument, thumb: PhotoSize.photoSize | PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize) {
let promise: Promise<any> = Promise.resolve();
if(!thumb.url) {
if(thumb.bytes) {
if('bytes' in thumb) {
thumb.url = appPhotosManager.getPreviewURLFromBytes(thumb.bytes, !!doc.sticker);
} else {
//return this.getFileURL(doc, false, thumb);
@ -234,34 +240,28 @@ class AppDocsManager { @@ -234,34 +240,28 @@ class AppDocsManager {
return {thumb, promise};
}
public getThumb(doc: MTDocument, useBytes = true) {
public getThumb(doc: MyDocument, useBytes = true) {
if(doc.thumbs?.length) {
let thumb: MTPhotoSize;
let thumb: PhotoSize;
if(!useBytes) {
thumb = doc.thumbs.find(t => !t.bytes);
thumb = doc.thumbs.find(t => !('bytes' in t));
}
if(!thumb) {
thumb = doc.thumbs[0];
}
return this.getThumbURL(doc, thumb);
return this.getThumbURL(doc, thumb as any);
}
return null;
}
public getInputFileName(doc: MTDocument, thumbSize?: string) {
public getInputFileName(doc: MyDocument, thumbSize?: string) {
return getFileNameByLocation(this.getInput(doc, thumbSize), {fileName: doc.file_name});
}
public downloadDocNew(docID: string | MTDocument, thumb?: MTPhotoSize): DownloadBlob {
const doc = this.getDoc(docID);
if(doc._ == 'documentEmpty') {
throw new Error('Document empty!');
}
public downloadDocNew(doc: MyDocument, thumb?: PhotoSize.photoSize): DownloadBlob {
const fileName = this.getInputFileName(doc, thumb?.type);
let download: DownloadBlob = appDownloadManager.getDownload(fileName);
@ -311,7 +311,7 @@ class AppDocsManager { @@ -311,7 +311,7 @@ class AppDocsManager {
return download;
}
public saveDocFile(doc: MTDocument) {
public saveDocFile(doc: MyDocument) {
const options = this.getFileDownloadOptions(doc);
return appDownloadManager.downloadToDisc(options, doc.file_name);
}

2
src/lib/appManagers/appDownloadManager.ts

@ -3,7 +3,7 @@ import apiManager from "../mtproto/mtprotoworker"; @@ -3,7 +3,7 @@ import apiManager from "../mtproto/mtprotoworker";
import { deferredPromise, CancellablePromise } from "../polyfill";
import type { DownloadOptions } from "../mtproto/apiFileManager";
import { getFileNameByLocation } from "../bin_utils";
import { InputFile } from "../../types";
import { InputFile } from "../../layer";
export type ResponseMethodBlob = 'blob';
export type ResponseMethodJson = 'json';

19
src/lib/appManagers/appImManager.ts

@ -40,6 +40,7 @@ import appPollsManager from './appPollsManager'; @@ -40,6 +40,7 @@ import appPollsManager from './appPollsManager';
import { ripple } from '../../components/ripple';
import { horizontalMenu } from '../../components/horizontalMenu';
import AudioElement from '../../components/audio';
import { InputNotifyPeer, InputPeerNotifySettings } from '../../layer';
//console.log('appImManager included33!');
@ -650,14 +651,14 @@ export class AppImManager { @@ -650,14 +651,14 @@ export class AppImManager {
if(message.media) {
if(message.media.photo) {
const photo = appPhotosManager.getPhoto(tempID);
if(photo) {
//if(photo._ != 'photoEmpty') {
const newPhoto = message.media.photo;
newPhoto.downloaded = photo.downloaded;
newPhoto.url = photo.url;
}
//}
} else if(message.media.document) {
const doc = appDocsManager.getDoc(tempID);
if(doc && doc.type && doc.type != 'sticker') {
if(/* doc._ != 'documentEmpty' && */doc.type && doc.type != 'sticker') {
const newDoc = message.media.document;
newDoc.downloaded = doc.downloaded;
newDoc.url = doc.url;
@ -2216,10 +2217,12 @@ export class AppImManager { @@ -2216,10 +2217,12 @@ export class AppImManager {
case 'photo': {
//if(pending.size < 5e6) {
this.log('will wrap pending photo:', pending, message, appPhotosManager.getPhoto(message.id));
const photo = appPhotosManager.getPhoto(message.id);
//if(photo._ == 'photoEmpty') break;
this.log('will wrap pending photo:', pending, message, photo);
const tailSupported = !isAndroid;
if(tailSupported) bubble.classList.add('with-media-tail');
wrapPhoto(appPhotosManager.getPhoto(message.id), message, attachmentDiv, undefined, undefined, tailSupported, true, this.lazyLoadQueue, null);
wrapPhoto(photo, message, attachmentDiv, undefined, undefined, tailSupported, true, this.lazyLoadQueue, null);
bubble.classList.add('hide-name', 'photo');
//}
@ -2230,6 +2233,7 @@ export class AppImManager { @@ -2230,6 +2233,7 @@ export class AppImManager {
case 'video': {
//if(pending.size < 5e6) {
let doc = appDocsManager.getDoc(message.id);
//if(doc._ == 'documentEmpty') break;
this.log('will wrap pending video:', pending, message, doc);
const tailSupported = !isAndroid && !isApple && doc.type != 'round';
if(tailSupported) bubble.classList.add('with-media-tail');
@ -2256,6 +2260,7 @@ export class AppImManager { @@ -2256,6 +2260,7 @@ export class AppImManager {
case 'voice':
case 'document': {
const doc = appDocsManager.getDoc(message.id);
//if(doc._ == 'documentEmpty') break;
this.log('will wrap pending doc:', doc);
const docDiv = wrapDocument(doc, false, true, message.id);
@ -2948,12 +2953,12 @@ export class AppImManager { @@ -2948,12 +2953,12 @@ export class AppImManager {
public mutePeer(peerID: number) {
let inputPeer = appPeersManager.getInputPeerByID(peerID);
let inputNotifyPeer = {
let inputNotifyPeer: InputNotifyPeer.inputNotifyPeer = {
_: 'inputNotifyPeer',
peer: inputPeer
};
let settings = {
let settings: InputPeerNotifySettings = {
_: 'inputPeerNotifySettings',
flags: 0,
mute_until: 0

7
src/lib/appManagers/appMediaViewer.ts

@ -5,7 +5,7 @@ import { RichTextProcessor } from "../richtextprocessor"; @@ -5,7 +5,7 @@ import { RichTextProcessor } from "../richtextprocessor";
import { logger } from "../logger";
import ProgressivePreloader from "../../components/preloader";
import { findUpClassName, $rootScope, generatePathData, fillPropertyValue, cancelEvent } from "../utils";
import appDocsManager from "./appDocsManager";
import appDocsManager, {MyDocument} from "./appDocsManager";
import VideoPlayer from "../mediaPlayer";
import { renderImageFromUrl, parseMenuButtonsTo } from "../../components/misc";
import AvatarElement from "../../components/avatar";
@ -13,7 +13,6 @@ import LazyLoadQueue from "../../components/lazyLoadQueue"; @@ -13,7 +13,6 @@ import LazyLoadQueue from "../../components/lazyLoadQueue";
import appForward from "../../components/appForward";
import { isSafari, mediaSizes, touchSupport } from "../config";
import { deferredPromise } from "../polyfill";
import { MTDocument } from "../../types";
import appMediaPlaybackController from "../../components/appMediaPlaybackController";
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
@ -246,7 +245,7 @@ export class AppMediaViewer { @@ -246,7 +245,7 @@ export class AppMediaViewer {
if(message.media.photo) {
appPhotosManager.savePhotoFile(message.media.photo);
} else {
let document: MTDocument = null;
let document: MyDocument = null;
if(message.media.webpage) document = message.media.webpage.document;
else document = message.media.document;
@ -840,7 +839,7 @@ export class AppMediaViewer { @@ -840,7 +839,7 @@ export class AppMediaViewer {
this.log('openMedia doc:', message);
const media = message.media.photo || message.media.document || message.media.webpage.document || message.media.webpage.photo;
const isVideo = (media as MTDocument).type == 'video' || (media as MTDocument).type == 'gif';
const isVideo = (media as MyDocument).type == 'video' || (media as MyDocument).type == 'gif';
const isFirstOpen = !this.peerID;
if(isFirstOpen) {

206
src/lib/appManagers/appMessagesManager.ts

@ -11,7 +11,7 @@ import appPhotosManager from "./appPhotosManager"; @@ -11,7 +11,7 @@ import appPhotosManager from "./appPhotosManager";
import AppStorage from '../storage';
import appPeersManager from "./appPeersManager";
import ServerTimeManager from "../mtproto/serverTimeManager";
import appDocsManager from "./appDocsManager";
import appDocsManager, {MyDocument} from "./appDocsManager";
import ProgressivePreloader from "../../components/preloader";
import serverTimeManager from "../mtproto/serverTimeManager";
//import apiManager from '../mtproto/apiManager';
@ -20,10 +20,11 @@ import appWebPagesManager from "./appWebPagesManager"; @@ -20,10 +20,11 @@ import appWebPagesManager from "./appWebPagesManager";
import { CancellablePromise, deferredPromise } from "../polyfill";
import appPollsManager from "./appPollsManager";
import searchIndexManager from '../searchIndexManager';
import { MTDocument, MTPhotoSize } from "../../types";
import { Modify } from "../../types";
import { logger, LogLevels } from "../logger";
import type {ApiFileManager} from '../mtproto/apiFileManager';
import appDownloadManager from "./appDownloadManager";
import { DialogFilter, InputDialogPeer, InputMessage, MethodDeclMap, MessagesFilter, PhotoSize } from "../../layer";
//console.trace('include');
@ -237,30 +238,15 @@ export class DialogsStorage { @@ -237,30 +238,15 @@ export class DialogsStorage {
}
}
export type DialogFilter = {
_: 'dialogFilter',
flags: number,
pFlags: Partial<{
contacts: true,
non_contacts: true,
groups: true,
broadcasts: true,
bots: true,
exclude_muted: true,
exclude_read: true,
exclude_archived: true
}>,
id: number,
title: string,
emoticon?: string,
export type MyDialogFilter = Modify<DialogFilter, {
pinned_peers: number[],
include_peers: number[],
exclude_peers: number[],
orderIndex?: number
};
}>;
export class FiltersStorage {
public filters: {[filterID: string]: DialogFilter} = {};
public filters: {[filterID: string]: MyDialogFilter} = {};
public orderIndex = 0;
constructor() {
@ -286,7 +272,7 @@ export class FiltersStorage { @@ -286,7 +272,7 @@ export class FiltersStorage {
}
}
public testDialogForFilter(dialog: Dialog, filter: DialogFilter) {
public testDialogForFilter(dialog: Dialog, filter: MyDialogFilter) {
// exclude_peers
for(const peerID of filter.exclude_peers) {
if(peerID == dialog.peerID) {
@ -352,39 +338,6 @@ export class FiltersStorage { @@ -352,39 +338,6 @@ export class FiltersStorage {
return false;
}
/* public processDialog(dialog: Dialog) {
for(const filterID in this.filters) {
const filter = this.filters[filterID];
const good = this.testDialogForFilter(dialog, filter);
const folder = appMessagesManager.dialogsStorage.getFolder(+filterID);
if(good) {
folder.push(dialog);
}
console.log('processDialog:', dialog, filter, good);
}
} */
/* public initFilters() {
return Promise.all([
appUsersManager.getContacts(),
this.getDialogFilters(),
appMessagesManager.getConversations('', 0, 20, 0),
appMessagesManager.getConversations('', 0, 20, 1)
]).then(() => {
const dialogs = appMessagesManager.dialogsStorage.dialogs;
for(const peerID in dialogs) {
const dialog = dialogs[peerID];
this.processDialog(dialog);
}
this.inited = true;
});
} */
public toggleDialogPin(peerID: number, filterID: number) {
const filter = this.filters[filterID];
@ -396,14 +349,14 @@ export class FiltersStorage { @@ -396,14 +349,14 @@ export class FiltersStorage {
return this.updateDialogFilter(filter);
}
public createDialogFilter(filter: DialogFilter) {
public createDialogFilter(filter: MyDialogFilter) {
let maxID = Math.max(1, ...Object.keys(this.filters).map(i => +i));
filter = copy(filter);
filter.id = maxID + 1;
return this.updateDialogFilter(filter);
}
public updateDialogFilter(filter: DialogFilter, remove = false) {
public updateDialogFilter(filter: MyDialogFilter, remove = false) {
const flags = remove ? 0 : 1;
if(!remove) {
@ -456,8 +409,8 @@ export class FiltersStorage { @@ -456,8 +409,8 @@ export class FiltersStorage {
});
}
public getOutputDialogFilter(filter: DialogFilter) {
const c: DialogFilter = copy(filter);
public getOutputDialogFilter(filter: MyDialogFilter) {
const c: MyDialogFilter = copy(filter);
['pinned_peers', 'exclude_peers', 'include_peers'].forEach(key => {
// @ts-ignore
c[key] = c[key].map((peerID: number) => appPeersManager.getInputPeerByID(peerID));
@ -469,31 +422,24 @@ export class FiltersStorage { @@ -469,31 +422,24 @@ export class FiltersStorage {
}
});
return c;
return c as any as DialogFilter;
}
public async getDialogFilters(overwrite = false) {
if(Object.keys(this.filters).length && !overwrite) {
/* // УБРАТЬ НА ПРОДЕ!!!!!!!!!!!!
for(const folderID in this.filters) {
const filter = this.filters[folderID];
if(typeof(filter.pinned_peers[0]) !== 'number') filter.pinned_peers = filter.pinned_peers.map(peer => appPeersManager.getPeerID(peer));
if(typeof(filter.exclude_peers[0]) !== 'number') filter.exclude_peers = filter.exclude_peers.map(peer => appPeersManager.getPeerID(peer));
if(typeof(filter.include_peers[0]) !== 'number') filter.include_peers = filter.include_peers.map(peer => appPeersManager.getPeerID(peer));
} */
return this.filters;
}
const filters = await (apiManager.invokeApi('messages.getDialogFilters') as Promise<DialogFilter[]>);
const filters = await apiManager.invokeApi('messages.getDialogFilters');
for(const filter of filters) {
this.saveDialogFilter(filter, false);
this.saveDialogFilter(filter as any as MyDialogFilter, false);
}
//console.log(this.filters);
return this.filters;
}
public saveDialogFilter(filter: DialogFilter, update = true) {
public saveDialogFilter(filter: MyDialogFilter, update = true) {
['pinned_peers', 'exclude_peers', 'include_peers'].forEach(key => {
// @ts-ignore
filter[key] = filter[key].map((peer: any) => appPeersManager.getPeerID(peer));
@ -507,40 +453,7 @@ export class FiltersStorage { @@ -507,40 +453,7 @@ export class FiltersStorage {
filter.include_peers = filter.pinned_peers.concat(filter.include_peers);
/* if(this.filters[filter.id]) {
// ну давай же найдём различия теперь, раз они сами не хотят приходить
const oldFilter = this.filters[filter.id];
const updateDialogs: {[peerID: number]: Dialog} = {};
const pinnedChanged = !deepEqual(oldFilter.pinned_peers, filter.pinned_peers);
if(pinnedChanged) {
for(const peerID of oldFilter.pinned_peers) {
updateDialogs[peerID] = appMessagesManager.getDialogByPeerID(peerID)[0];
}
for(const peerID of filter.pinned_peers) {
updateDialogs[peerID] = appMessagesManager.getDialogByPeerID(peerID)[0];
}
}
// это и так отфильтрует
//const excludeChanged = !deepEqual(oldFilter.exclude_peers, filter.exclude_peers);
const includeChanged = !deepEqual(oldFilter.include_peers, filter.include_peers);
if(includeChanged) {
for(const peerID of filter.include_peers) {
updateDialogs[peerID] = appMessagesManager.getDialogByPeerID(peerID)[0];
}
}
Object.assign(this.filters[filter.id], filter);
if(pinnedChanged) {
$rootScope.$broadcast('filter_pinned_order', {id: filter.id, order: filter.pinned_peers});
}
$rootScope.$broadcast('dialogs_multiupdate', updateDialogs);
} */if(this.filters[filter.id]) {
if(this.filters[filter.id]) {
Object.assign(this.filters[filter.id], filter);
} else {
this.filters[filter.id] = filter;
@ -553,7 +466,7 @@ export class FiltersStorage { @@ -553,7 +466,7 @@ export class FiltersStorage {
}
}
public setOrderIndex(filter: DialogFilter) {
public setOrderIndex(filter: MyDialogFilter) {
if(filter.hasOwnProperty('orderIndex')) {
if(filter.orderIndex > this.orderIndex) {
this.orderIndex = filter.orderIndex;
@ -734,7 +647,7 @@ export class AppMessagesManager { @@ -734,7 +647,7 @@ export class AppMessagesManager {
message: text,
media: message.media,
entities: this.getInputEntities(entities),
no_webpage: noWebPage,
no_webpage: noWebPage || undefined,
}).then((updates) => {
apiUpdatesManager.processUpdateMessage(updates);
}, (error) => {
@ -753,8 +666,8 @@ export class AppMessagesManager { @@ -753,8 +666,8 @@ export class AppMessagesManager {
entities: any[],
replyToMsgID: number,
viaBotID: number,
queryID: number,
resultID: number,
queryID: string,
resultID: string,
noWebPage: boolean,
reply_markup: any,
clearDraft: boolean,
@ -877,7 +790,7 @@ export class AppMessagesManager { @@ -877,7 +790,7 @@ export class AppMessagesManager {
apiPromise = apiManager.invokeApi('messages.sendInlineBotResult', {
flags: flags,
peer: appPeersManager.getInputPeerByID(peerID),
random_id: randomID,
random_id: randomID as any,
reply_to_msg_id: appMessagesIDsManager.getMessageLocalID(replyToMsgID),
query_id: options.queryID,
id: options.resultID
@ -889,10 +802,10 @@ export class AppMessagesManager { @@ -889,10 +802,10 @@ export class AppMessagesManager {
apiPromise = apiManager.invokeApi('messages.sendMessage', {
flags: flags,
no_webpage: noWebPage,
no_webpage: noWebPage || undefined,
peer: appPeersManager.getInputPeerByID(peerID),
message: text,
random_id: randomID,
random_id: randomID as any,
reply_to_msg_id: appMessagesIDsManager.getMessageLocalID(replyToMsgID),
entities: sendEntites
}, sentRequestOptions);
@ -967,7 +880,7 @@ export class AppMessagesManager { @@ -967,7 +880,7 @@ export class AppMessagesManager {
this.pendingByRandomID[randomIDS] = [peerID, messageID];
}
public sendFile(peerID: number, file: File | Blob | MTDocument, options: Partial<{
public sendFile(peerID: number, file: File | Blob | MyDocument, options: Partial<{
isMedia: boolean,
replyToMsgID: number,
caption: string,
@ -1033,7 +946,7 @@ export class AppMessagesManager { @@ -1033,7 +946,7 @@ export class AppMessagesManager {
h: options.height,
type: 'm',
size: file.size
} as MTPhotoSize],
} as PhotoSize],
w: options.width,
h: options.height,
downloaded: file.size,
@ -1190,12 +1103,12 @@ export class AppMessagesManager { @@ -1190,12 +1103,12 @@ export class AppMessagesManager {
return apiManager.invokeApi('messages.sendMedia', {
flags: flags,
background: options.background,
background: options.background || undefined,
clear_draft: true,
peer: appPeersManager.getInputPeerByID(peerID),
media: inputMedia,
message: caption,
random_id: randomID,
random_id: randomID as any,
reply_to_msg_id: appMessagesIDsManager.getMessageLocalID(replyToMsgID)
}).then((updates) => {
apiUpdatesManager.processUpdateMessage(updates);
@ -1225,7 +1138,7 @@ export class AppMessagesManager { @@ -1225,7 +1138,7 @@ export class AppMessagesManager {
flags |= 128; // clear_draft
if(isDocument) {
const {id, access_hash, file_reference} = file as MTDocument;
const {id, access_hash, file_reference} = file as MyDocument;
const inputMedia = {
_: 'inputMediaDocument',
@ -1421,7 +1334,7 @@ export class AppMessagesManager { @@ -1421,7 +1334,7 @@ export class AppMessagesManager {
h: details.height,
type: 'm',
size: file.size
} as MTPhotoSize],
} as PhotoSize],
w: details.width,
h: details.height,
downloaded: file.size,
@ -1573,13 +1486,11 @@ export class AppMessagesManager { @@ -1573,13 +1486,11 @@ export class AppMessagesManager {
}
let inputMedia: any;
if(messageMedia.photo) {
let photo = messageMedia.photo;
appPhotosManager.savePhoto(photo);
if(messageMedia._ == 'messageMediaPhoto') {
const photo = appPhotosManager.savePhoto(messageMedia.photo);
inputMedia = appPhotosManager.getInput(photo);
} else {
let doc = messageMedia.document;
appDocsManager.saveDoc(doc);
} else if(messageMedia._ == 'messageMediaDocument') {
const doc = appDocsManager.saveDoc(messageMedia.document);
inputMedia = appDocsManager.getMediaInput(doc);
}
@ -1613,8 +1524,8 @@ export class AppMessagesManager { @@ -1613,8 +1524,8 @@ export class AppMessagesManager {
viaBotID: number,
reply_markup: any,
clearDraft: boolean,
queryID: number
resultID: number
queryID: string
resultID: string
}> = {}) {
peerID = appPeersManager.getPeerMigratedTo(peerID) || peerID;
@ -1785,7 +1696,7 @@ export class AppMessagesManager { @@ -1785,7 +1696,7 @@ export class AppMessagesManager {
apiPromise = apiManager.invokeApi('messages.sendInlineBotResult', {
flags: flags,
peer: appPeersManager.getInputPeerByID(peerID),
random_id: randomID,
random_id: randomID as any,
reply_to_msg_id: appMessagesIDsManager.getMessageLocalID(replyToMsgID),
query_id: options.queryID,
id: options.resultID
@ -1795,8 +1706,9 @@ export class AppMessagesManager { @@ -1795,8 +1706,9 @@ export class AppMessagesManager {
flags: flags,
peer: appPeersManager.getInputPeerByID(peerID),
media: inputMedia,
random_id: randomID,
reply_to_msg_id: appMessagesIDsManager.getMessageLocalID(replyToMsgID)
random_id: randomID as any,
reply_to_msg_id: appMessagesIDsManager.getMessageLocalID(replyToMsgID),
message: ''
}, sentRequestOptions);
}
apiPromise.then((updates) => {
@ -2076,7 +1988,7 @@ export class AppMessagesManager { @@ -2076,7 +1988,7 @@ export class AppMessagesManager {
flags: flags,
from_peer: appPeersManager.getInputPeerByID(-channelID),
id: msgIDs,
random_id: randomIDs,
random_id: randomIDs as any,
to_peer: appPeersManager.getInputPeerByID(peerID)
}, sentRequestOptions).then((updates) => {
apiUpdatesManager.processUpdateMessage(updates);
@ -2127,7 +2039,7 @@ export class AppMessagesManager { @@ -2127,7 +2039,7 @@ export class AppMessagesManager {
if(this.reloadConversationsPromise) return this.reloadConversationsPromise;
return this.reloadConversationsPromise = new Promise((resolve, reject) => {
setTimeout(() => {
let peers = this.reloadConversationsPeers.map(peerID => appPeersManager.getInputPeerByID(peerID));
const peers = this.reloadConversationsPeers.map(peerID => appPeersManager.getInputDialogPeerByID(peerID));
this.reloadConversationsPeers.length = 0;
apiManager.invokeApi('messages.getPeerDialogs', {peers}).then((result) => {
@ -2576,10 +2488,7 @@ export class AppMessagesManager { @@ -2576,10 +2488,7 @@ export class AppMessagesManager {
const dialog = this.getDialogByPeerID(peerID)[0];
if(!dialog) return Promise.reject();
const peer = {
_: 'inputDialogPeer',
peer: appPeersManager.getInputPeerByID(peerID)
};
const peer = appPeersManager.getInputDialogPeerByID(peerID);
const flags = dialog.pFlags?.pinned ? 0 : 1;
return apiManager.invokeApi('messages.toggleDialogPin', {
@ -2599,15 +2508,12 @@ export class AppMessagesManager { @@ -2599,15 +2508,12 @@ export class AppMessagesManager {
}
public markDialogUnread(peerID: number, read?: boolean) {
let dialog = this.getDialogByPeerID(peerID)[0];
const dialog = this.getDialogByPeerID(peerID)[0];
if(!dialog) return Promise.reject();
let peer = {
_: 'inputDialogPeer',
peer: appPeersManager.getInputPeerByID(peerID)
};
const peer = appPeersManager.getInputDialogPeerByID(peerID);
let flags = read || dialog.pFlags?.unread_mark ? 0 : 1;
const flags = read || dialog.pFlags?.unread_mark ? 0 : 1;
return apiManager.invokeApi('messages.markDialogUnread', {
flags,
peer
@ -3040,14 +2946,15 @@ export class AppMessagesManager { @@ -3040,14 +2946,15 @@ export class AppMessagesManager {
flags: 0,
peer: appPeersManager.getInputPeerByID(peerID),
q: query || '',
filter: inputFilter || {_: 'inputMessagesFilterEmpty'},
filter: (inputFilter || {_: 'inputMessagesFilterEmpty'}) as any as MessagesFilter,
min_date: 0,
max_date: 0,
limit: limit,
offset_id: appMessagesIDsManager.getMessageLocalID(maxID) || 0,
add_offset: backLimit ? -backLimit : 0,
max_id: 0,
min_id: 0
min_id: 0,
hash: 0
}, {
timeout: APITIMEOUT,
noErrorBox: true
@ -3065,6 +2972,7 @@ export class AppMessagesManager { @@ -3065,6 +2972,7 @@ export class AppMessagesManager {
}
apiPromise = apiManager.invokeApi('messages.searchGlobal', {
flags: 0,
q: query,
offset_rate: offsetRate,
offset_peer: appPeersManager.getInputPeerByID(offsetPeerID),
@ -4370,14 +4278,14 @@ export class AppMessagesManager { @@ -4370,14 +4278,14 @@ export class AppMessagesManager {
Object.keys(splitted.msgIDs).forEach((channelID: number | string) => {
channelID = +channelID;
let msgIDs = splitted.msgIDs[channelID].map((msgID: number) => {
const msgIDs: InputMessage[] = splitted.msgIDs[channelID].map((msgID: number) => {
return {
_: 'inputMessageID',
id: msgID
};
});
var promise;
let promise: Promise<MethodDeclMap['channels.getMessages']['res'] | MethodDeclMap['messages.getMessages']['res']>;
if(channelID > 0) {
promise = apiManager.invokeApi('channels.getMessages', {
channel: appChatsManager.getChannelInput(channelID),
@ -4389,10 +4297,12 @@ export class AppMessagesManager { @@ -4389,10 +4297,12 @@ export class AppMessagesManager {
});
}
promises.push(promise.then((getMessagesResult: any) => {
appUsersManager.saveApiUsers(getMessagesResult.users);
appChatsManager.saveApiChats(getMessagesResult.chats);
this.saveMessages(getMessagesResult.messages);
promises.push(promise.then(getMessagesResult => {
if(getMessagesResult._ != 'messages.messagesNotModified') {
appUsersManager.saveApiUsers(getMessagesResult.users);
appChatsManager.saveApiChats(getMessagesResult.chats);
this.saveMessages(getMessagesResult.messages);
}
$rootScope.$broadcast('messages_downloaded', splitted.mids[+channelID]);
}));

20
src/lib/appManagers/appPeersManager.ts

@ -2,6 +2,7 @@ import appUsersManager from "./appUsersManager"; @@ -2,6 +2,7 @@ import appUsersManager from "./appUsersManager";
import appChatsManager from "./appChatsManager";
import { isObject } from "../utils";
import { RichTextProcessor } from "../richtextprocessor";
import { InputPeer, InputDialogPeer } from "../../layer";
// https://github.com/eelcohn/Telegram-API/wiki/Calculating-color-for-a-Telegram-user-on-IRC
/*
@ -161,13 +162,13 @@ const AppPeersManager = { @@ -161,13 +162,13 @@ const AppPeersManager = {
}
},
getInputPeerByID: (peerID: number) => {
getInputPeerByID: (peerID: number): InputPeer => {
if(!peerID) {
return {_: 'inputPeerEmpty'};
}
if(peerID < 0) {
let chatID = -peerID;
const chatID = -peerID;
if(!appChatsManager.isChannel(chatID)) {
return appChatsManager.getChatInputPeer(chatID);
} else {
@ -178,13 +179,20 @@ const AppPeersManager = { @@ -178,13 +179,20 @@ const AppPeersManager = {
return {
_: 'inputPeerUser',
user_id: peerID,
access_hash: appUsersManager.getUser(peerID).access_hash || 0
access_hash: appUsersManager.getUser(peerID).access_hash
};
},
getInputDialogPeerByID: (peerID: number): InputDialogPeer => {
return {
_: 'inputDialogPeer',
peer: AppPeersManager.getInputPeerByID(peerID)
}
},
getPeerColorByID: (peerID: number, pic = true) => {
let idx = DialogColorsMap[(peerID < 0 ? -peerID : peerID) % 7];
let color = (pic ? DialogColors : DialogColorsFg)[idx];
const idx = DialogColorsMap[(peerID < 0 ? -peerID : peerID) % 7];
const color = (pic ? DialogColors : DialogColorsFg)[idx];
return color;
},
@ -193,7 +201,7 @@ const AppPeersManager = { @@ -193,7 +201,7 @@ const AppPeersManager = {
if(peerID > 0) {
text = '%pu ' + appUsersManager.getUserSearchText(peerID);
} else if(peerID < 0) {
let chat = appChatsManager.getChat(-peerID);
const chat = appChatsManager.getChat(-peerID);
text = '%pg ' + (chat.title || '');
}
return text;

98
src/lib/appManagers/appPhotosManager.ts

@ -1,29 +1,16 @@ @@ -1,29 +1,16 @@
import { calcImageInBox, isObject } from "../utils";
import { bytesFromHex, getFileNameByLocation } from "../bin_utils";
import { MTPhotoSize, inputPhotoFileLocation, inputDocumentFileLocation, FileLocation, MTDocument } from "../../types";
import appDownloadManager from "./appDownloadManager";
import { CancellablePromise } from "../polyfill";
import { isSafari } from "../../helpers/userAgent";
import { FileLocation, InputFileLocation, Photo, PhotoSize } from "../../layer";
import { MyDocument } from "./appDocsManager";
export type MTPhoto = {
_: 'photo' | 'photoEmpty',
pFlags: any,
flags: number,
id: string,
access_hash: string,
file_reference: Uint8Array,
date: number,
sizes: Array<MTPhotoSize>,
dc_id: number,
user_id: number,
downloaded?: boolean | number,
url?: string
};
export type MyPhoto = Photo.photo;
export class AppPhotosManager {
private photos: {
[id: string]: MTPhoto
[id: string]: MyPhoto
} = {};
private documentThumbsCache: {
[docID: string]: {
@ -47,7 +34,9 @@ export class AppPhotosManager { @@ -47,7 +34,9 @@ export class AppPhotosManager {
this.windowH = document.body.scrollHeight;
}
public savePhoto(photo: MTPhoto, context?: any) {
public savePhoto(photo: Photo, context?: any) {
if(photo._ == 'photoEmpty') return undefined;
if(this.photos[photo.id]) return Object.assign(this.photos[photo.id], photo);
/* if(context) {
@ -56,12 +45,12 @@ export class AppPhotosManager { @@ -56,12 +45,12 @@ export class AppPhotosManager {
if(!photo.id) {
console.warn('no apiPhoto.id', photo);
} else this.photos[photo.id] = photo as any;
} else this.photos[photo.id] = photo;
return photo;
}
public choosePhotoSize(photo: MTPhoto | MTDocument, width = 0, height = 0) {
public choosePhotoSize(photo: MyPhoto | MyDocument, width = 0, height = 0) {
//if(Config.Navigator.retina) {
if(window.devicePixelRatio > 1) {
width *= 2;
@ -79,11 +68,11 @@ export class AppPhotosManager { @@ -79,11 +68,11 @@ export class AppPhotosManager {
c crop 640x640
d crop 1280x1280 */
let bestPhotoSize: MTPhotoSize = {_: 'photoSizeEmpty'};
const sizes = ((photo as MTPhoto).sizes || (photo as MTDocument).thumbs) as typeof bestPhotoSize[];
let bestPhotoSize: PhotoSize = {_: 'photoSizeEmpty', type: ''};
const sizes = ((photo as MyPhoto).sizes || (photo as MyDocument).thumbs) as PhotoSize[];
if(sizes) {
for(const photoSize of sizes) {
if(!photoSize.w || !photoSize.h) continue;
if(!('w' in photoSize) && !('h' in photoSize)) continue;
bestPhotoSize = photoSize;
@ -142,7 +131,7 @@ export class AppPhotosManager { @@ -142,7 +131,7 @@ export class AppPhotosManager {
return URL.createObjectURL(blob);
}
public getPreviewURLFromThumb(thumb: MTPhotoSize, isSticker = false) {
public getPreviewURLFromThumb(thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize, isSticker = false) {
return thumb.url ?? (thumb.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker));
}
@ -171,26 +160,30 @@ export class AppPhotosManager { @@ -171,26 +160,30 @@ export class AppPhotosManager {
}
}
public setAttachmentSize(photo: MTPhoto | MTDocument, element: HTMLElement | SVGForeignObjectElement, boxWidth: number, boxHeight: number, isSticker = false, dontRenderPreview = false) {
let photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight);
public setAttachmentSize(photo: MyPhoto | MyDocument, element: HTMLElement | SVGForeignObjectElement, boxWidth: number, boxHeight: number, isSticker = false, dontRenderPreview = false) {
const photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight);
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
let sizes = (photo as MTPhoto).sizes || (photo as MTDocument).thumbs;
if((!photo.downloaded || (photo as MTDocument).type == 'video' || (photo as MTDocument).type == 'gif') && !isSticker && sizes?.length && sizes[0].bytes && !dontRenderPreview) {
this.setAttachmentPreview(sizes[0].bytes, element, isSticker);
const sizes = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs;
const thumb = sizes?.length ? sizes[0] : null;
if(thumb && ('bytes' in thumb)) {
if((!photo.downloaded || (photo as MyDocument).type == 'video' || (photo as MyDocument).type == 'gif') && !isSticker && !dontRenderPreview) {
this.setAttachmentPreview(thumb.bytes, element, isSticker);
}
}
let width: number;
let height: number;
if(photo._ == 'document') {
width = photo.w || 512;
height = photo.h || 512;
} else {
width = photoSize.w || 100;
height = photoSize.h || 100;
width = 'w' in photoSize ? photoSize.w : 100;
height = 'h' in photoSize ? photoSize.h : 100;
}
let {w, h} = calcImageInBox(width, height, boxWidth, boxHeight);
const {w, h} = calcImageInBox(width, height, boxWidth, boxHeight);
if(element instanceof SVGForeignObjectElement) {
element.setAttributeNS(null, 'width', '' + w);
element.setAttributeNS(null, 'height', '' + h);
@ -204,8 +197,8 @@ export class AppPhotosManager { @@ -204,8 +197,8 @@ export class AppPhotosManager {
return photoSize;
}
public getPhotoDownloadOptions(photo: MTPhoto | MTDocument, photoSize: MTPhotoSize) {
const isDocument = photo._ == 'document';
public getPhotoDownloadOptions(photo: MyPhoto | MyDocument, photoSize: PhotoSize) {
const isMyDocument = photo._ == 'document';
if(!photoSize || photoSize._ == 'photoSizeEmpty') {
//console.error('no photoSize by photo:', photo);
@ -213,27 +206,32 @@ export class AppPhotosManager { @@ -213,27 +206,32 @@ export class AppPhotosManager {
}
// maybe it's a thumb
const isPhoto = photoSize.size && photo.access_hash && photo.file_reference;
const location: inputPhotoFileLocation | inputDocumentFileLocation | FileLocation = isPhoto ? {
_: isDocument ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
const isPhoto = photoSize._ == 'photoSize' && photo.access_hash && photo.file_reference;
const location: InputFileLocation.inputPhotoFileLocation | InputFileLocation.inputDocumentFileLocation | FileLocation = isPhoto ? {
_: isMyDocument ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
id: photo.id,
access_hash: photo.access_hash,
file_reference: photo.file_reference,
thumb_size: photoSize.type
} : photoSize.location;
} : (photoSize as PhotoSize.photoSize).location;
return {dcID: photo.dc_id, location, size: isPhoto ? photoSize.size : undefined};
return {dcID: photo.dc_id, location, size: isPhoto ? (photoSize as PhotoSize.photoSize).size : undefined};
}
/* public getPhotoURL(photo: MTPhoto | MTDocument, photoSize: MTPhotoSize) {
/* public getPhotoURL(photo: MTPhoto | MTMyDocument, photoSize: MTPhotoSize) {
const downloadOptions = this.getPhotoDownloadOptions(photo, photoSize);
return {url: getFileURL('photo', downloadOptions), location: downloadOptions.location};
} */
public preloadPhoto(photoID: any, photoSize?: MTPhotoSize): CancellablePromise<Blob> {
public preloadPhoto(photoID: any, photoSize?: PhotoSize): CancellablePromise<Blob> {
const photo = this.getPhoto(photoID);
// @ts-ignore
if(photo._ == 'photoEmpty') {
throw new Error('preloadPhoto photoEmpty!');
}
if(!photoSize) {
const fullWidth = this.windowW;
const fullHeight = this.windowH;
@ -242,7 +240,7 @@ export class AppPhotosManager { @@ -242,7 +240,7 @@ export class AppPhotosManager {
}
const cacheContext = this.getCacheContext(photo);
if(cacheContext.downloaded >= photoSize.size && cacheContext.url) {
if(cacheContext.downloaded >= ('size' in photoSize ? photoSize.size : 0) && cacheContext.url) {
return Promise.resolve() as any;
}
@ -277,11 +275,11 @@ export class AppPhotosManager { @@ -277,11 +275,11 @@ export class AppPhotosManager {
return this.documentThumbsCache[docID] ?? (this.documentThumbsCache[docID] = {downloaded: 0, url: ''});
}
public getPhoto(photoID: any): MTPhoto {
public getPhoto(photoID: any): MyPhoto {
return isObject(photoID) ? photoID : this.photos[photoID];
}
public getInput(photo: MTPhoto) {
public getInput(photo: MyPhoto) {
return {
_: 'inputMediaPhoto',
flags: 0,
@ -295,11 +293,13 @@ export class AppPhotosManager { @@ -295,11 +293,13 @@ export class AppPhotosManager {
};
}
public savePhotoFile(photo: MTPhoto | MTDocument) {
const fullWidth = this.windowW;
const fullHeight = this.windowH;
const fullPhotoSize = this.choosePhotoSize(photo, fullWidth, fullHeight);
const location: inputDocumentFileLocation | inputPhotoFileLocation = {
public savePhotoFile(photo: MyPhoto | MyDocument) {
const fullPhotoSize = this.choosePhotoSize(photo, 0xFFFF, 0xFFFF);
if(fullPhotoSize._ != 'photoSize') {
return;
}
const location: InputFileLocation.inputDocumentFileLocation | InputFileLocation.inputPhotoFileLocation = {
_: photo._ == 'document' ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
id: photo.id,
access_hash: photo.access_hash,

16
src/lib/appManagers/appPollsManager.ts

@ -5,7 +5,7 @@ import apiManager from "../mtproto/mtprotoworker"; @@ -5,7 +5,7 @@ import apiManager from "../mtproto/mtprotoworker";
import apiUpdatesManager from "./apiUpdatesManager";
import { $rootScope } from "../utils";
import { logger, LogLevels } from "../logger";
import appUsersManager, { User } from "./appUsersManager";
import appUsersManager from "./appUsersManager";
export type PollAnswer = {
_: 'pollAnswer',
@ -195,19 +195,7 @@ class AppPollsManager { @@ -195,19 +195,7 @@ class AppPollsManager {
option,
offset,
limit
}).then((votesList: {
_: 'messages.votesList',
flags: number,
count: number,
next_offset: string,
pFlags: {},
users: User[],
votes: {
_: 'messageUserVoteInputOption',
date: number,
user_id: number
}[]
}) => {
}).then((votesList) => {
this.log('getPollVotes messages:', votesList);
appUsersManager.saveApiUsers(votesList.users);

2
src/lib/appManagers/appStateManager.ts

@ -26,7 +26,7 @@ type State = Partial<{ @@ -26,7 +26,7 @@ type State = Partial<{
}>;
export class AppStateManager {
public loaded: Promise<any>;
public loaded: Promise<State>;
private log = logger('STATE'/* , LogLevels.error */);
private state: State = {};

174
src/lib/appManagers/appStickersManager.ts

@ -2,76 +2,23 @@ import AppStorage from '../storage'; @@ -2,76 +2,23 @@ import AppStorage from '../storage';
//import apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker';
import appDocsManager from './appDocsManager';
import { MTDocument, inputStickerSetThumb } from '../../types';
import { $rootScope } from '../utils';
export type MTStickerSet = {
_: 'stickerSet',
flags: number,
archived?: true,
official?: true,
masks?: true,
animated?: true,
installed_date?: number,
id: string, // long
access_hash: string, // long,
title: string,
short_name: string, // Short name of stickerset to use in tg://addstickers?set=short_name
thumb?: {
_: 'photoSize',
type: 'm',
location: {
_: string,
volume_id: string,
local_id: number
},
w: number,
h: number,
size: number
},
pFlags: {
animated?: boolean
}
thumb_dc_id?: number,
count: number,
hash: number
};
export type MTStickerSetFull = {
set: MTStickerSet,
packs: any[],
documents: MTDocument[]
};
export type MTStickerSetCovered = {
_: 'stickerSetCovered',
set: MTStickerSet,
cover: MTDocument
};
export type MTStickerSetMultiCovered = {
_: 'stickerSetMultiCovered',
set: MTStickerSet,
covers: MTDocument[]
};
import { StickerSet, InputStickerSet, StickerSetCovered, MessagesRecentStickers, Document, InputFileLocation, MessagesStickerSet, PhotoSize } from '../../layer';
import { Modify } from '../../types';
class AppStickersManager {
private documents: {
[fileID: string]: MTDocument
} = {};
private stickerSets: {
[stickerSetID: string]: MTStickerSetFull
[stickerSetID: string]: MessagesStickerSet
} = {};
private saveSetsTimeout: number;
private hashes: Partial<{
featured: Partial<{hash: number, result: (MTStickerSetCovered | MTStickerSetMultiCovered)[]}>,
featured: Partial<{hash: number, result: StickerSetCovered[]}>,
search: {
[query: string]: Partial<{
hash: number,
result: (MTStickerSetCovered | MTStickerSetMultiCovered)[]
result: StickerSetCovered[]
}>
}
}> = {
@ -80,9 +27,7 @@ class AppStickersManager { @@ -80,9 +27,7 @@ class AppStickersManager {
};
constructor() {
AppStorage.get<{
[stickerSetID: string]: MTStickerSetFull
}>('stickerSets').then((sets) => {
AppStorage.get<AppStickersManager['stickerSets']>('stickerSets').then((sets) => {
if(sets) {
for(let id in sets) {
let set = sets[id];
@ -109,63 +54,43 @@ class AppStickersManager { @@ -109,63 +54,43 @@ class AppStickersManager {
}
});
}
public saveSticker(doc: MTDocument) {
if(this.documents[doc.id]) return this.documents[doc.id];
doc = appDocsManager.saveDoc(doc);
this.documents[doc.id] = doc;
public saveStickers(docs: Document[]) {
docs.forEachReverse((doc, idx) => {
doc = appDocsManager.saveDoc(doc);
return doc;
}
public saveStickers(docs: MTDocument[]) {
docs.forEach((doc, idx) => {
docs[idx] = this.saveSticker(doc);
if(!doc) docs.splice(idx, 1);
else docs[idx] = doc;
});
}
public getSticker(fileID: string) {
return this.documents[fileID];
}
public async getStickerSet(set: {
id: string,
access_hash: string
}, params: Partial<{
overwrite: boolean
}> = {}) {
}> = {}): Promise<MessagesStickerSet> {
if(this.stickerSets[set.id] && !params.overwrite && this.stickerSets[set.id].documents?.length) return this.stickerSets[set.id];
let promise = apiManager.invokeApi('messages.getStickerSet', {
const stickerSet = await apiManager.invokeApi('messages.getStickerSet', {
stickerset: this.getStickerSetInput(set)
});
let res = await promise;
let stickerSet: {
_: "messages.stickerSet",
set: MTStickerSet,
packs: any[],
documents: MTDocument[]
} = res as any;
this.saveStickerSet(stickerSet, set.id);
return stickerSet;
return stickerSet as any;
}
public async getRecentStickers() {
let res: {
_: string,
hash: number,
packs: any[],
stickers: MTDocument[],
dates: number[]
} = await apiManager.invokeApi('messages.getRecentStickers', {flags: 0, hash: 0});
public async getRecentStickers(): Promise<Modify<MessagesRecentStickers.messagesRecentStickers, {
stickers: Document[]
}>> {
const res = await apiManager.invokeApi('messages.getRecentStickers', {flags: 0, hash: 0}) as MessagesRecentStickers.messagesRecentStickers;
this.saveStickers(res.stickers);
if(res._ == 'messages.recentStickers') {
this.saveStickers(res.stickers);
}
return res;
return res as any;
}
public getAnimatedEmojiSticker(emoji: string) {
@ -173,21 +98,17 @@ class AppStickersManager { @@ -173,21 +98,17 @@ class AppStickersManager {
if(!stickerSet || !stickerSet.documents) return undefined;
emoji = emoji.replace(/\ufe0f/g, '').replace(/🏻|🏼|🏽|🏾|🏿/g, '');
return stickerSet.documents.find(doc => doc.stickerEmojiRaw == emoji);
return stickerSet.documents.find(doc => (doc as Document.document).stickerEmojiRaw == emoji);
}
public saveStickerSet(res: {
//_: "messages.stickerSet",
set: MTStickerSet,
packs: any[],
documents: MTDocument[]
}, id: string) {
public saveStickerSet(res: Omit<MessagesStickerSet.messagesStickerSet, '_'>, id: string) {
//console.log('stickers save set', res);w
const newSet = {
const newSet: MessagesStickerSet = {
_: 'messages.stickerSet',
set: res.set,
packs: res.packs,
documents: res.documents
documents: res.documents as Document[]
};
if(this.stickerSets[id]) {
@ -201,7 +122,7 @@ class AppStickersManager { @@ -201,7 +122,7 @@ class AppStickersManager {
//console.log('stickers wrote', this.stickerSets);
if(this.saveSetsTimeout) return;
this.saveSetsTimeout = setTimeout(() => {
const savedSets: {[id: string]: MTStickerSetFull} = {};
const savedSets: {[id: string]: MessagesStickerSet} = {};
for(const id in this.stickerSets) {
const set = this.stickerSets[id];
if(set.set.installed_date || id == 'emoji') {
@ -217,13 +138,13 @@ class AppStickersManager { @@ -217,13 +138,13 @@ class AppStickersManager {
}, 100);
}
public getStickerSetThumbDownloadOptions(stickerSet: MTStickerSet) {
const thumb = stickerSet.thumb;
public getStickerSetThumbDownloadOptions(stickerSet: StickerSet.stickerSet) {
const thumb = stickerSet.thumb as PhotoSize.photoSize;
const dcID = stickerSet.thumb_dc_id;
const isAnimated = stickerSet.pFlags?.animated;
const input: inputStickerSetThumb = {
const input: InputFileLocation.inputStickerSetThumb = {
_: 'inputStickerSetThumb',
stickerset: this.getStickerSetInput(stickerSet),
volume_id: thumb.location.volume_id,
@ -252,7 +173,7 @@ class AppStickersManager { @@ -252,7 +173,7 @@ class AppStickersManager {
//return promise;
} */
public getStickerSetInput(set: {id: string, access_hash: string}) {
public getStickerSetInput(set: {id: string, access_hash: string}): InputStickerSet {
return set.id == 'emoji' ? {
_: 'inputStickerSetAnimatedEmoji'
} : {
@ -263,18 +184,9 @@ class AppStickersManager { @@ -263,18 +184,9 @@ class AppStickersManager {
}
public async getFeaturedStickers() {
const res = (await apiManager.invokeApi('messages.getFeaturedStickers', {
const res = await apiManager.invokeApi('messages.getFeaturedStickers', {
hash: this.hashes.featured?.hash || 0
})) as {
_: 'messages.featuredStickers',
unread: string[],
count: number,
hash: number,
sets: (MTStickerSetMultiCovered | MTStickerSetCovered)[]
} | {
_: 'messages.featuredStickersNotModified',
count: number
};
});
const hashed = this.hashes.featured ?? (this.hashes.featured = {});
if(res._ != 'messages.featuredStickersNotModified') {
@ -289,7 +201,7 @@ class AppStickersManager { @@ -289,7 +201,7 @@ class AppStickersManager {
return hashed.result;
}
public async toggleStickerSet(set: MTStickerSet) {
public async toggleStickerSet(set: StickerSet.stickerSet) {
if(set.installed_date) {
const res = await apiManager.invokeApi('messages.uninstallStickerSet', {
stickerset: this.getStickerSetInput(set)
@ -320,16 +232,10 @@ class AppStickersManager { @@ -320,16 +232,10 @@ class AppStickersManager {
const flags = excludeFeatured ? 1 : 0;
const res = await apiManager.invokeApi('messages.searchStickerSets', {
flags,
exclude_featured: excludeFeatured,
exclude_featured: excludeFeatured || undefined,
q: query,
hash: this.hashes.search[query]?.hash || 0
}) as {
_: 'messages.foundStickerSets',
hash: number,
sets: Array<MTStickerSetCovered | MTStickerSetMultiCovered>
} | {
_: 'messages.foundStickerSetsNotModified'
};
});
const hashed = this.hashes.search[query] ?? (this.hashes.search[query] = {});
if(res._ != 'messages.foundStickerSetsNotModified') {
@ -341,7 +247,7 @@ class AppStickersManager { @@ -341,7 +247,7 @@ class AppStickersManager {
this.saveStickerSet({set: covered.set, documents: [], packs: []}, covered.set.id);
});
const foundSaved: MTStickerSetCovered[] = [];
const foundSaved: StickerSetCovered[] = [];
for(let id in this.stickerSets) {
const {set} = this.stickerSets[id];

87
src/lib/appManagers/appUsersManager.ts

@ -8,8 +8,9 @@ import { formatPhoneNumber } from "../../components/misc"; @@ -8,8 +8,9 @@ import { formatPhoneNumber } from "../../components/misc";
import searchIndexManager from "../searchIndexManager";
import appPeersManager from "./appPeersManager";
import appStateManager from "./appStateManager";
import { InputUser, User as MTUser } from "../../layer";
export type User = {
/* export type User = {
_: 'user',
access_hash: string,
first_name: string,
@ -35,7 +36,16 @@ export type User = { @@ -35,7 +36,16 @@ export type User = {
rPhone?: string,
sortName?: string,
sortStatus?: number,
};
}; */
export interface User extends MTUser.user {
initials?: string,
num?: number,
rFirstName?: string,
rFullName?: string,
rPhone?: string,
sortName?: string,
sortStatus?: number,
}
export class AppUsersManager {
public users: {[userID: number]: User} = {};
@ -73,11 +83,11 @@ export class AppUsersManager { @@ -73,11 +83,11 @@ export class AppUsersManager {
if(user) {
user.status = update.status;
if(user.status) {
if(user.status.expires) {
if('expires' in user.status) {
user.status.expires -= serverTimeManager.serverTimeOffset;
}
if(user.status.was_online) {
if('was_online' in user.status) {
user.status.was_online -= serverTimeManager.serverTimeOffset;
}
}
@ -284,12 +294,13 @@ export class AppUsersManager { @@ -284,12 +294,13 @@ export class AppUsersManager {
public getUserStatusForSort(status: User['status']) {
if(status) {
var expires = status.expires || status.was_online;
const expires = status._ == 'userStatusOnline' ? status.expires : (status._ == 'userStatusOffline' ? status.was_online : 0);
if(expires) {
return expires;
}
var timeNow = tsNow(true);
switch (status._) {
const timeNow = tsNow(true);
switch(status._) {
case 'userStatusRecently':
return timeNow - 86400 * 3;
case 'userStatusLastWeek':
@ -411,12 +422,12 @@ export class AppUsersManager { @@ -411,12 +422,12 @@ export class AppUsersManager {
}
public getUserString(id: number) {
var user = this.getUser(id);
const user = this.getUser(id);
return 'u' + id + (user.access_hash ? '_' + user.access_hash : '');
}
public getUserInput(id: number) {
var user = this.getUser(id);
public getUserInput(id: number): InputUser {
const user = this.getUser(id);
if(user.pFlags && user.pFlags.self) {
return {_: 'inputUserSelf'};
}
@ -424,21 +435,20 @@ export class AppUsersManager { @@ -424,21 +435,20 @@ export class AppUsersManager {
return {
_: 'inputUser',
user_id: id,
access_hash: user.access_hash || 0
access_hash: user.access_hash
};
}
public updateUsersStatuses() {
var timestampNow = tsNow(true);
for(let i in this.users) {
let user = this.users[i];
const timestampNow = tsNow(true);
for(const i in this.users) {
const user = this.users[i];
if(user.status &&
user.status._ == 'userStatusOnline' &&
user.status.expires < timestampNow) {
user.status = user.status.wasStatus || {_: 'userStatusOffline', was_online: user.status.expires};
delete user.status.wasStatus;
user.status = {_: 'userStatusOffline', was_online: user.status.expires};
$rootScope.$broadcast('user_update', user.id);
}
}
@ -449,22 +459,18 @@ export class AppUsersManager { @@ -449,22 +459,18 @@ export class AppUsersManager {
return;
}
var user = this.getUser(id);
const user = this.getUser(id);
if(user &&
user.status &&
user.status._ != 'userStatusOnline' &&
user.status._ != 'userStatusEmpty') {
var wasStatus;
if(user.status._ != 'userStatusOffline') {
delete user.status.wasStatus
wasStatus = copy(user.status);
}
user.status._ != 'userStatusEmpty' &&
!user.pFlags.support) {
user.status = {
_: 'userStatusOnline',
expires: tsNow(true) + 60,
wasStatus: wasStatus
expires: tsNow(true) + 60
};
user.sortStatus = this.getUserStatusForSort(user.status);
$rootScope.$broadcast('user_update', id);
}
@ -554,20 +560,23 @@ export class AppUsersManager { @@ -554,20 +560,23 @@ export class AppUsersManager {
offset: 0,
limit: 30,
hash: 0,
}).then((result: any) => {
//console.log(result);
this.saveApiUsers(result.users);
appChatsManager.saveApiChats(result.chats);
const peerIDs = result.categories[0].peers.map((topPeer: {
_: 'topPeer',
peer: any,
rating: number
}) => {
const peerID = appPeersManager.getPeerID(topPeer.peer);
appStateManager.pushPeer(peerID);
return peerID;
});
}).then((result) => {
let peerIDs: number[];
if(result._ == 'contacts.topPeers') {
//console.log(result);
this.saveApiUsers(result.users);
appChatsManager.saveApiChats(result.chats);
peerIDs = result.categories[0].peers.map((topPeer: {
_: 'topPeer',
peer: any,
rating: number
}) => {
const peerID = appPeersManager.getPeerID(topPeer.peer);
appStateManager.pushPeer(peerID);
return peerID;
});
}
appStateManager.pushToState('topPeers', peerIDs);

2
src/lib/bin_utils.ts

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
// @ts-ignore
import {BigInteger, SecureRandom} from 'jsbn';
import { InputFileLocation, FileLocation } from '../types';
import { InputFileLocation, FileLocation } from '../layer';
/// #if !MTPROTO_WORKER
// @ts-ignore

3
src/lib/crypto/crypto_methods.ts

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
import { convertToArrayBuffer, convertToByteArray } from "../bin_utils";
import { InputCheckPasswordSRP } from "../../layer";
export default abstract class CryptoWorkerMethods {
abstract performTaskWorker<T>(task: string, ...args: any[]): Promise<T>;
@ -44,7 +45,7 @@ export default abstract class CryptoWorkerMethods { @@ -44,7 +45,7 @@ export default abstract class CryptoWorkerMethods {
return this.performTaskWorker<T>('gzipUncompress', bytes, toString);
}
public computeSRP(password: string, state: any) {
public computeSRP(password: string, state: any): Promise<InputCheckPasswordSRP> {
return this.performTaskWorker('computeSRP', password, state);
}
}

6
src/lib/crypto/srp.ts

@ -5,7 +5,7 @@ import {str2bigInt, isZero, @@ -5,7 +5,7 @@ import {str2bigInt, isZero,
bigInt2str, powMod, int2bigInt, mult, mod, sub, bitSize, negative, add, greater} from 'leemon';
import {logger, LogLevels} from '../logger';
import { AccountPassword } from "../../types";
import { AccountPassword, PasswordKdfAlgo } from "../../layer";
const log = logger('SRP', LogLevels.error);
@ -35,7 +35,7 @@ export async function makePasswordHash(password: string, client_salt: Uint8Array @@ -35,7 +35,7 @@ export async function makePasswordHash(password: string, client_salt: Uint8Array
export async function computeSRP(password: string, state: AccountPassword) {
//console.log('computeCheck:', password, state);
let algo = state.current_algo;
let algo = state.current_algo as PasswordKdfAlgo.passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow;
let p = str2bigInt(bytesToHex(algo.p), 16);
let B = str2bigInt(bytesToHex(state.srp_B), 16);
@ -109,7 +109,7 @@ export async function computeSRP(password: string, state: AccountPassword) { @@ -109,7 +109,7 @@ export async function computeSRP(password: string, state: AccountPassword) {
return true;
};
var flipper = (arr: Uint8Array) => {
var flipper = (arr: Uint8Array | number[]) => {
let out = new Uint8Array(arr.length);
for(let i = 0; i < arr.length; i += 4) {
out[i] = arr[i + 3];

26
src/lib/mtproto/apiFileManager.ts

@ -5,10 +5,10 @@ import FileManager from "../filemanager"; @@ -5,10 +5,10 @@ import FileManager from "../filemanager";
import apiManager from "./apiManager";
import { deferredPromise, CancellablePromise } from "../polyfill";
import { logger, LogLevels } from "../logger";
import { InputFileLocation, FileLocation, UploadFile, InputFile } from "../../types";
import { isSafari } from "../../helpers/userAgent";
import cryptoWorker from "../crypto/cryptoworker";
import { notifySomeone, notifyAll } from "../../helpers/context";
import { InputFileLocation, FileLocation, InputFile, UploadFile } from "../../layer";
type Delayed = {
offset: number,
@ -26,6 +26,8 @@ export type DownloadOptions = { @@ -26,6 +26,8 @@ export type DownloadOptions = {
processPart?: (bytes: Uint8Array, offset?: number, queue?: Delayed[]) => Promise<any>
};
type MyUploadFile = UploadFile.uploadFile;
const MAX_FILE_SAVE_SIZE = 20e6;
export class ApiFileManager {
@ -39,7 +41,7 @@ export class ApiFileManager { @@ -39,7 +41,7 @@ export class ApiFileManager {
public downloadPulls: {
[x: string]: Array<{
cb: () => Promise<UploadFile | void>,
cb: () => Promise<MyUploadFile | void>,
deferred: {
resolve: (...args: any[]) => void,
reject: (...args: any[]) => void
@ -54,8 +56,8 @@ export class ApiFileManager { @@ -54,8 +56,8 @@ export class ApiFileManager {
private log: ReturnType<typeof logger> = logger('AFM', LogLevels.error);
public downloadRequest(dcID: 'upload', cb: () => Promise<void>, activeDelta: number): Promise<void>;
public downloadRequest(dcID: number, cb: () => Promise<UploadFile>, activeDelta: number): Promise<UploadFile>;
public downloadRequest(dcID: number | string, cb: () => Promise<UploadFile | void>, activeDelta: number) {
public downloadRequest(dcID: number, cb: () => Promise<MyUploadFile>, activeDelta: number): Promise<MyUploadFile>;
public downloadRequest(dcID: number | string, cb: () => Promise<MyUploadFile | void>, activeDelta: number) {
if(this.downloadPulls[dcID] === undefined) {
this.downloadPulls[dcID] = [];
this.downloadActives[dcID] = 0;
@ -63,7 +65,7 @@ export class ApiFileManager { @@ -63,7 +65,7 @@ export class ApiFileManager {
const downloadPull = this.downloadPulls[dcID];
const promise = new Promise<UploadFile | void>((resolve, reject) => {
const promise = new Promise<MyUploadFile | void>((resolve, reject) => {
downloadPull.push({cb, deferred: {resolve, reject}, activeDelta});
});
@ -135,7 +137,7 @@ export class ApiFileManager { @@ -135,7 +137,7 @@ export class ApiFileManager {
dcID,
fileDownload: true/* ,
singleInRequest: 'safari' in window */
}) as Promise<UploadFile>;
}) as Promise<MyUploadFile>;
}, delta);
}
@ -330,22 +332,24 @@ export class ApiFileManager { @@ -330,22 +332,24 @@ export class ApiFileManager {
try {
const result = await this.requestFilePart(dcID, location, offset, limit, checkCancel);
const bytes: Uint8Array = result.bytes as any;
if(delayed.length) {
superpuper();
}
this.log('downloadFile requestFilePart result:', fileName, result);
const isFinal = offset + limit >= size || !result.bytes.byteLength;
if(result.bytes.byteLength) {
const isFinal = offset + limit >= size || !bytes.byteLength;
if(bytes.byteLength) {
//done += limit;
done += result.bytes.byteLength;
done += bytes.byteLength;
//if(!isFinal) {
////this.log('deferred notify 2:', {done: offset + limit, total: size}, deferred);
deferred.notify({done, offset, total: size});
//}
const processedResult = await processDownloaded(result.bytes, offset);
const processedResult = await processDownloaded(bytes, offset);
checkCancel();
await writeFilePromise;
@ -430,7 +434,7 @@ export class ApiFileManager { @@ -430,7 +434,7 @@ export class ApiFileManager {
const resultInputFile: InputFile = {
_: isBigFile ? 'inputFileBig' : 'inputFile',
id: fileID,
id: fileID as any,
parts: totalParts,
name: fileName,
md5_checksum: ''

9
src/lib/mtproto/mtproto.service.ts

@ -1,9 +1,10 @@ @@ -1,9 +1,10 @@
import { isSafari } from '../../helpers/userAgent';
import { logger, LogLevels } from '../logger';
import type { DownloadOptions } from './apiFileManager';
import type { InputFileLocation, FileLocation, UploadFile, WorkerTaskTemplate } from '../../types';
import type { WorkerTaskTemplate } from '../../types';
import { deferredPromise, CancellablePromise } from '../polyfill';
import { notifySomeone } from '../../helpers/context';
import { InputFileLocation, FileLocation, UploadFile } from '../../layer';
const log = logger('SW', LogLevels.error/* | LogLevels.debug | LogLevels.log */);
const ctx = self as any as ServiceWorkerGlobalScope;
@ -32,7 +33,7 @@ export interface ServiceWorkerTask extends WorkerTaskTemplate { @@ -32,7 +33,7 @@ export interface ServiceWorkerTask extends WorkerTaskTemplate {
export interface ServiceWorkerTaskResponse extends WorkerTaskTemplate {
type: 'requestFilePart',
payload: UploadFile
payload: UploadFile.uploadFile
};
const onFetch = (event: FetchEvent): void => {
@ -81,9 +82,9 @@ const onFetch = (event: FetchEvent): void => { @@ -81,9 +82,9 @@ const onFetch = (event: FetchEvent): void => {
};
const deferred = deferredPromises[task.id] = deferredPromise<UploadFile>();
const deferred = deferredPromises[task.id] = deferredPromise<UploadFile.uploadFile>();
deferred.then(result => {
let ab = result.bytes;
let ab = result.bytes as Uint8Array;
log.debug('[stream] requestFilePart result:', result);

1
src/lib/mtproto/mtproto.worker.ts

@ -9,6 +9,7 @@ import networkerFactory from "./networkerFactory"; @@ -9,6 +9,7 @@ import networkerFactory from "./networkerFactory";
import apiFileManager, { ApiFileManager } from './apiFileManager';
import { logger, LogLevels } from '../logger';
import type { ServiceWorkerTask, ServiceWorkerTaskResponse } from './mtproto.service';
import { UploadFile } from '../../layer';
const log = logger('DW', LogLevels.error);

5
src/lib/mtproto/mtprotoworker.ts

@ -7,6 +7,7 @@ import MTProtoWorker from 'worker-loader!./mtproto.worker'; @@ -7,6 +7,7 @@ import MTProtoWorker from 'worker-loader!./mtproto.worker';
import type { DownloadOptions } from './apiFileManager';
import type { ServiceWorkerTask, ServiceWorkerTaskResponse } from './mtproto.service';
import { isServiceWorkerSupported } from '../config';
import { MethodDeclMap } from '../../layer';
type Task = {
taskID: number,
@ -175,7 +176,7 @@ class ApiManagerProxy extends CryptoWorkerMethods { @@ -175,7 +176,7 @@ class ApiManagerProxy extends CryptoWorkerMethods {
this.updatesProcessor = callback;
}
public invokeApi(method: string, params: any = {}, options: {
public invokeApi<T extends keyof MethodDeclMap>(method: T, params: MethodDeclMap[T]['req'] = {}, options: {
dcID?: number,
timeout?: number,
noErrorBox?: boolean,
@ -189,7 +190,7 @@ class ApiManagerProxy extends CryptoWorkerMethods { @@ -189,7 +190,7 @@ class ApiManagerProxy extends CryptoWorkerMethods {
waitTime?: number,
stopTime?: number,
rawError?: any
} = {}): Promise<any> {
} = {}): Promise<MethodDeclMap[T]['res']> {
//console.log('will invokeApi:', method, params, options);
return this.performTaskWorker('invokeApi', method, params, options);
}

2
src/lib/mtproto/passwordManager.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import apiManager from './mtprotoworker';
import { AccountPassword } from '../../types';
import { AccountPassword } from '../../layer';
//import { computeCheck } from "../crypto/srp";
export class PasswordManager {

2
src/pages/pagePassword.ts

@ -9,8 +9,8 @@ import apiManager from '../lib/mtproto/mtprotoworker'; @@ -9,8 +9,8 @@ import apiManager from '../lib/mtproto/mtprotoworker';
import Page from './page';
import { mediaSizes } from '../lib/config';
import passwordManager from '../lib/mtproto/passwordManager';
import { AccountPassword } from '../types';
import { cancelEvent } from '../lib/utils';
import { AccountPassword } from '../layer';
let onFirstMount = (): Promise<any> => {
let needFrame = 0;

51
src/pages/pageSignQR.ts

@ -7,48 +7,7 @@ import pageSignIn from './pageSignIn'; @@ -7,48 +7,7 @@ import pageSignIn from './pageSignIn';
import { App } from '../lib/mtproto/mtproto_config';
import { bytesToBase64, bytesCmp } from '../lib/bin_utils';
import serverTimeManager from '../lib/mtproto/serverTimeManager';
import { User } from '../lib/appManagers/appUsersManager';
/* interface Authorization {
_: 'authorization',
flags: number,
pFlags: Partial<{current: true, official_app: true, password_pending: true}>,
hash: number[],
device_model: string,
platform: string,
system_version: string,
api_id: number,
app_name: string,
app_version: string,
date_created: number,
date_active: number,
ip: string,
country: string,
region: string
}; */
interface AuthAuthorization {
flags: number,
pFlags: Partial<{tmp_sessions: number}>,
user: User
}
interface LoginToken {
_: 'auth.loginToken',
expires: number,
token: Uint8Array
};
interface LoginTokenMigrateTo {
_: 'auth.loginTokenMigrateTo',
dc_id: number,
token: Uint8Array
};
interface LoginTokenSuccess {
_: 'auth.loginTokenSuccess',
authorization: AuthAuthorization
};
import { AuthAuthorization, AuthLoginToken } from '../layer';
let onFirstMount = async() => {
const pageElement = page.pageEl;
@ -71,7 +30,7 @@ let onFirstMount = async() => { @@ -71,7 +30,7 @@ let onFirstMount = async() => {
}, {once: true});
let options: {dcID?: number, ignoreErrors: true} = {ignoreErrors: true};
let prevToken: Uint8Array;
let prevToken: Uint8Array | number[];
return async() => {
stop = false;
@ -82,7 +41,7 @@ let onFirstMount = async() => { @@ -82,7 +41,7 @@ let onFirstMount = async() => {
}
try {
let loginToken: LoginToken | LoginTokenMigrateTo | LoginTokenSuccess = await apiManager.invokeApi('auth.exportLoginToken', {
let loginToken = await apiManager.invokeApi('auth.exportLoginToken', {
api_id: App.id,
api_hash: App.hash,
except_ids: []
@ -97,11 +56,11 @@ let onFirstMount = async() => { @@ -97,11 +56,11 @@ let onFirstMount = async() => {
loginToken = await apiManager.invokeApi('auth.importLoginToken', {
token: loginToken.token
}, options) as LoginToken;
}, options) as AuthLoginToken.authLoginToken;
}
if(loginToken._ == 'auth.loginTokenSuccess') {
let authorization = loginToken.authorization;
const authorization = loginToken.authorization as any as AuthAuthorization.authAuthorization;
apiManager.setUserAuth({
id: authorization.user.id
});

10
src/scripts/format_jsons.js

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
let emoji = require('./emoji_pretty.json');
let emoji = require('./in/emoji_pretty.json');
//let countries = require('./countries_pretty.json');
let countries = require('fs').readFileSync('./countries.dat').toString();
let countries = require('fs').readFileSync('./in/countries.dat').toString();
//console.log(countries);
//console.log(emoji, countries);
@ -77,7 +77,7 @@ if(false) { @@ -77,7 +77,7 @@ if(false) {
};
});
require('fs').writeFileSync('./emoji.json', JSON.stringify(obj));
require('fs').writeFileSync('./out/emoji.json', JSON.stringify(obj));
}
{
@ -153,7 +153,7 @@ if(false) { @@ -153,7 +153,7 @@ if(false) {
console.log(obj);
require('fs').writeFileSync('./emoji.json', JSON.stringify(obj));
require('fs').writeFileSync('./out/emoji.json', JSON.stringify(obj));
}
/* {
@ -203,5 +203,5 @@ if(false) { @@ -203,5 +203,5 @@ if(false) {
//console.log(item);
});
require('fs').writeFileSync('./countries.json', JSON.stringify(arr));
require('fs').writeFileSync('./out/countries.json', JSON.stringify(arr));
}

4
src/scripts/format_schema.js

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
let json = require('./schema');
let json = require('./in/schema');
let top = {};
/* ['MTProto', 'API'].forEach(key => {
@ -65,4 +65,4 @@ top = json; @@ -65,4 +65,4 @@ top = json;
//console.log(out);
require('fs').writeFileSync('./schema_pretty.json', JSON.stringify(top/* , null, '\t' */));
require('fs').writeFileSync('./out/schema.json', JSON.stringify(top/* , null, '\t' */));

241
src/scripts/generate_mtproto_types.js

@ -0,0 +1,241 @@ @@ -0,0 +1,241 @@
// @ts-check
const schema = require('./in/schema.json');
const additional = require('./in/schema_additional_params.json');
const mtproto = schema.API;
for(const constructor of additional) {
constructor.params.forEach(param => {
param.type = 'flags.-1?' + param.type;
});
const realConstructor = mtproto.constructors.find(c => c.predicate == constructor.predicate);
realConstructor.params.splice(realConstructor.params.length, 0, ...constructor.params);
}
['Vector t', 'Bool', 'True', 'Null'].forEach(key => {
let idx = -1;
do {
idx = mtproto.constructors.findIndex(c => c.type == key);
if(idx !== -1) {
mtproto.constructors.splice(idx, 1);
} else {
break;
}
} while(true);
//delete types[key];
});
/** @type {(string: string) => string} */
function capitalizeFirstLetter(string) {
return string[0].toUpperCase() + string.slice(1);
}
/** @type {(string: string, camelizeFirstLetterIfFound: boolean, camelizeFirstLetterIfNotFound: boolean) => string} */
function camelizeName(string, camelizeFirstLetterIfFound, camelizeFirstLetterIfNotFound = false) {
if(!string.includes('.')) {
if(camelizeFirstLetterIfNotFound) {
string = capitalizeFirstLetter(string);
}
return string;
}
if(camelizeFirstLetterIfFound) {
string = capitalizeFirstLetter(string);
}
return string.replace(/\../g, (match, index) => {
return match[1].toUpperCase();
});
}
/** @type {(type: string) => any} */
const processParamType = (type) => {
const isAdditional = type.indexOf('flags.-1?') === 0;
if(type.includes('?')) {
type = type.split('?')[1];
}
if(type.includes('Vector')) {
return `Array<${processParamType(type.slice(7, -1))}>`;
}
switch(type) {
case '#':
case 'int':
return 'number';
case 'true':
return 'true';
case 'Bool':
return 'boolean';
case 'double':
return 'number';
case 'long':
return 'string';
case 'bytes':
return 'Uint8Array | number[]';
case 'string':
return 'string';
case 'X':
case '!X':
return 'any';
default:
//console.log('no such type', type);
//throw new Error('no such type: ' + type);
return isAdditional ? type : camelizeName(type, true);
}
};
/** @type {(params: {name: string, type: string}[], object: any, parseBooleanFlags: boolean) => any} */
const processParams = (params, object = {}, parseBooleanFlags = true) => {
for(const param of params) {
let {name, type} = param;
if(type.includes('?') || name == 'flags') {
name += '?';
}
const processed = processParamType(type);
if(type.includes('?true') && parseBooleanFlags) {
if(!object.pFlags) object.pFlags = {};
object.pFlags[name] = processed;
} else {
object[name] = processed;
}
}
return object;
};
/** @type {(object: any) => boolean} */
function isObject(object) {
return typeof(object) === 'object' && object !== null;
}
/** @type {(object: any, outArray: string[], space: string) => string[]} */
function serializeObject(object, outArray, space) {
for(const key in object) {
const value = object[key];
if(isObject(value)) { // only pFlags
outArray.push(`${space}${key}?: Partial<{`);
serializeObject(value, outArray, space + '\t');
outArray.push(`${space}}>`);
} else {
outArray.push(`${space}${key}: ${value}`);
}
}
return outArray;
}
let out = '';
/** @type {Array<{key: 'predicate' | 'method', instanceKey: 'constructors' | 'methods', name: string}>} */
/* const lol = [{key: 'predicate', instanceKey: 'constructors', name: 'Constructor'}, {key: 'method', instanceKey: 'methods', name: 'Method'}];
lol.forEach(info => {
const {key: keyName, instanceKey, name: mapName} = info; */
/** @type {{[type: string]: string[]}} */
const types = {};
/** @type {{[predicate: string]: any}} */
const constructors = {};
/** @type {{[predicate: string]: string}} */
const constructorsTypes = {};
mtproto.constructors.forEach((constructor) => {
const {type, predicate, params} = constructor;
if(!types.hasOwnProperty(type)) {
types[type] = [];
}
types[type].push(predicate);
constructorsTypes[predicate] = camelizeName(type, true) + '.' + camelizeName(predicate, false);
// type end
/** @type {any} */
const c = {
_: `'${predicate}'`
};
constructors[predicate] = c;
processParams(params, c, true);
/* if(predicate == 'inputFileLocation') {
console.log(c);
} */
});
for(const type in types) {
const cs = types[type];
const camelizedType = camelizeName(type, true);
const csTypes = cs.map(name => {
const str = `export type ${camelizeName(name, false)} = {\n`;
const params = serializeObject(constructors[name], [], '\t\t');
return str + params.join(',\n').replace(/\{,/g, '{') + '\n\t};';
});
out += `/**
* @link https://core.telegram.org/type/${type}
*/
export type ${camelizedType} = ${cs.map(name => camelizedType + '.' + camelizeName(name, false)).join(' | ')};
export namespace ${camelizedType} {
${csTypes.join('\n\n\t')}
}
`;
}
//console.log(types['InputUser']);
out += `export interface ConstructorDeclMap {\n`;
for(const predicate in constructorsTypes) {
out += `\t'${predicate}': ${constructorsTypes[predicate]},\n`;
}
out += `}\n\n`;
/** @type {{[method: string]: {req: string, res: string}}} */
const methodsMap = {};
mtproto.methods.forEach((_method) => {
const {method, type, params} = _method;
const camelizedMethod = camelizeName(method, true, true);
methodsMap[method] = {req: camelizedMethod, res: processParamType(type)};
let str = `export type ${camelizedMethod} = {\n`;
const object = processParams(params, {}, false);
const serialized = serializeObject(object, [], '\t');
str += serialized.join(',\n').replace(/\{,/g, '{') + '\n};\n\n';
out += str;
});
out += `export interface MethodDeclMap {\n`;
for(const method in methodsMap) {
out += `\t'${method}': {req: ${methodsMap[method].req}, res: ${methodsMap[method].res}},\n`;
}
out += `}\n\n`;
require('fs').writeFileSync('./out/layer.d.ts', out);

0
src/scripts/countries.dat → src/scripts/in/countries.dat

0
src/scripts/countries_pretty.json → src/scripts/in/countries_pretty.json

0
src/scripts/emoji_pretty.json → src/scripts/in/emoji_pretty.json

0
src/scripts/schema.json → src/scripts/in/schema.json

44
src/scripts/in/schema_additional_params.json

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
[{
"predicate": "document",
"params": [
{"name": "thumbs", "type": "Array<PhotoSize.photoSize | PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize>"},
{"name": "type", "type": "'gif' | 'sticker' | 'audio' | 'voice' | 'video' | 'round' | 'photo'"},
{"name": "h", "type": "number"},
{"name": "w", "type": "number"},
{"name": "file_name", "type": "string"},
{"name": "file", "type": "File"},
{"name": "duration", "type": "number"},
{"name": "downloaded", "type": "boolean"},
{"name": "url", "type": "string"},
{"name": "audioTitle", "type": "string"},
{"name": "audioPerformer", "type": "string"},
{"name": "sticker", "type": "number"},
{"name": "stickerEmoji", "type": "string"},
{"name": "stickerEmojiRaw", "type": "string"},
{"name": "stickerSetInput", "type": "InputStickerSet.inputStickerSetID"},
{"name": "stickerThumbConverted", "type": "true"},
{"name": "animated", "type": "boolean"},
{"name": "supportsStreaming", "type": "boolean"}
]
}, {
"predicate": "photo",
"params": [
{"name": "downloaded", "type": "boolean | number"},
{"name": "url", "type": "string"}
]
}, {
"predicate": "photoSize",
"params": [
{"name": "url", "type": "string"}
]
}, {
"predicate": "photoCachedSize",
"params": [
{"name": "url", "type": "string"}
]
}, {
"predicate": "photoStrippedSize",
"params": [
{"name": "url", "type": "string"}
]
}]

0
src/scripts/countries.json → src/scripts/out/countries.json

0
src/scripts/emoji.json → src/scripts/out/emoji.json

0
src/scripts/schema_pretty.json → src/scripts/out/schema.json

7
src/tests/srp.test.ts

@ -8,7 +8,9 @@ test('2FA hash', async() => { @@ -8,7 +8,9 @@ test('2FA hash', async() => {
test('2FA whole (with negative)', async() => {
return await computeSRP(password, {
_: 'account.password',
current_algo: {
_: 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
salt1,
salt2,
p,
@ -16,7 +18,10 @@ test('2FA whole (with negative)', async() => { @@ -16,7 +18,10 @@ test('2FA whole (with negative)', async() => {
},
srp_id,
srp_B,
secure_random
secure_random,
new_algo: null,
new_secure_algo: null
}).then(res => {
expect(res.srp_id).toEqual(srp_id);
expect(res.A).toEqual(A);

141
src/types.d.ts vendored

@ -1,54 +1,3 @@ @@ -1,54 +1,3 @@
export type MTDocument = {
_: 'document' | 'documentEmpty',
pFlags: any,
flags: number,
id: string,
access_hash: string,
file_reference: Uint8Array | number[],
date: number,
mime_type: string,
size: number,
thumbs: MTPhotoSize[],
video_thumbs?: MTVideoSize[],
dc_id: number,
attributes: any[],
type?: 'gif' | 'sticker' | 'audio' | 'voice' | 'video' | 'round' | 'photo',
h?: number,
w?: number,
file_name?: string,
file?: File,
duration?: number,
downloaded?: boolean,
url?: string,
audioTitle?: string,
audioPerformer?: string,
sticker?: number,
stickerEmoji?: string,
stickerEmojiRaw?: string,
stickerSetInput?: any,
stickerThumbConverted?: true,
animated?: boolean,
supportsStreaming?: boolean
};
export type MTPhotoSize = {
_: string,
w?: number,
h?: number,
size?: number,
type?: string, // i, m, x, y, w by asc
location?: FileLocation,
bytes?: Uint8Array, // if type == 'i',
url?: string
};
export type MTVideoSize = Omit<MTPhotoSize, '_'> & {_: 'videoSize'};
export type InvokeApiOptions = Partial<{
dcID: number,
timeout: number,
@ -68,98 +17,10 @@ export type InvokeApiOptions = Partial<{ @@ -68,98 +17,10 @@ export type InvokeApiOptions = Partial<{
rawError: any
}>;
type Algo = {
salt1: Uint8Array,
salt2: Uint8Array,
p: Uint8Array,
g: number
};
export type AccountPassword = {
_?: 'accont.password',
flags?: number,
pFlags?: Partial<{
has_recovery: true,
has_secure_values: true,
has_password: true
}>,
current_algo: Algo,
new_algo?: Algo,
new_secure_algo?: Algo,
hint?: string,
email_unconfirmed_pattern?: string,
srp_B?: Uint8Array,
srp_id?: string,
secure_random: Uint8Array,
};
export type storageFileType = 'storage.fileUnknown' | 'storage.filePartial' | 'storage.fileJpeg' |
'storage.fileGif' | 'storage.filePng' | 'storage.filePdf' | 'storage.fileMp3' | 'storage.fileMov' |
'storage.fileMp4' | 'storage.fileWebp';
export type UploadFile = {
_: 'upload.file',
type: storageFileType,
mtime: number,
bytes: Uint8Array
};
export type FileLocation = {
_: 'fileLocationToBeDeprecated',
volume_id: string,
local_id: number
};
export type inputFileLocation = {
_: 'inputFileLocation',
volume_id: string,
local_id: number,
secret: string,
file_reference: Uint8Array | number[]
};
export type inputDocumentFileLocation = {
_: 'inputDocumentFileLocation',
id: string,
access_hash: string,
file_reference: Uint8Array | number[],
thumb_size: string
};
export type inputPhotoFileLocation = Omit<inputDocumentFileLocation, '_'> & {_: 'inputPhotoFileLocation'};
export type inputPeerPhotoFileLocation = {
_: 'inputPeerPhotoFileLocation',
flags: number,
big?: true,
peer: any,
volume_id: string,
local_id: number
};
export type inputStickerSetThumb = {
_: 'inputStickerSetThumb',
stickerset: any,
volume_id: string,
local_id: number
};
export type InputFileLocation = inputFileLocation | inputDocumentFileLocation | inputPhotoFileLocation | inputPeerPhotoFileLocation | inputStickerSetThumb;
export type WorkerTaskTemplate = {
type: string,
id: number,
payload: any
};
export type inputFile = {
_: 'inputFile',
parts: number,
id: [number, number],
name: string,
md5_checksum: string
};
export type inputFileBig = Omit<inputFile, 'md5_checksum' | '_'> & {_: 'inputFileBig'};
export type InputFile = inputFile | inputFileBig;
export type Modify<T, R> = Omit<T, keyof R> & R;

1
tsconfig.json

@ -63,6 +63,7 @@ @@ -63,6 +63,7 @@
},
"exclude": [
"node_modules",
"./public/recorder.min.js",
"public",
"coverage",
"./public/*.js",

Loading…
Cancel
Save