MTProto schema converter
Types for methods
This commit is contained in:
parent
382fc9c4f3
commit
34546d49ee
@ -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",
|
||||
|
@ -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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
// если что - загрузит 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);
|
||||
|
@ -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[]) {
|
||||
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) {
|
||||
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) {
|
||||
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 {
|
||||
preloader = new ProgressivePreloader(null, true);
|
||||
}
|
||||
|
||||
download = appDocsManager.downloadDocNew(doc.id);
|
||||
download = appDocsManager.downloadDocNew(doc);
|
||||
preloader.attach(downloadDiv, true, download);
|
||||
|
||||
download.then(() => {
|
||||
|
@ -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 {
|
||||
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;
|
||||
|
@ -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";
|
||||
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 {
|
||||
}} = {};
|
||||
|
||||
private recentDiv: HTMLElement;
|
||||
private recentStickers: MTDocument[] = [];
|
||||
private recentStickers: MyDocument[] = [];
|
||||
|
||||
private scroll: Scrollable;
|
||||
|
||||
@ -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 {
|
||||
|
||||
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 {
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
//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 {
|
||||
});
|
||||
});
|
||||
}
|
||||
} 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 {
|
||||
}); */
|
||||
|
||||
$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 {
|
||||
});
|
||||
|
||||
$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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
this.init = null;
|
||||
}
|
||||
|
||||
pushRecentSticker(doc: MTDocument) {
|
||||
pushRecentSticker(doc: MyDocument) {
|
||||
if(!this.recentDiv.parentElement) {
|
||||
return;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -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 {
|
||||
});
|
||||
}
|
||||
|
||||
public open(postCanvas: HTMLCanvasElement, onCrop: (upload: () => Promise<any>) => void) {
|
||||
public open(postCanvas: HTMLCanvasElement, onCrop: PopupAvatar['onCrop']) {
|
||||
this.canvas = postCanvas;
|
||||
this.onCrop = onCrop;
|
||||
|
||||
|
@ -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";
|
||||
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 {
|
||||
|
||||
this.stickersDiv.innerHTML = '';
|
||||
for(let doc of set.documents) {
|
||||
if(doc._ == 'documentEmpty') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.classList.add('sticker-set-sticker');
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
|
||||
button.setAttribute('disabled', 'true');
|
||||
|
||||
appMessagesManager.filtersStorage.createDialogFilter(filter.filter).then(bool => {
|
||||
appMessagesManager.filtersStorage.createDialogFilter(filter.filter as any).then(bool => {
|
||||
if(bool) {
|
||||
div.remove();
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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 {
|
||||
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,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;
|
||||
|
@ -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 {
|
||||
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 {
|
||||
|
@ -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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
});
|
||||
}
|
||||
|
||||
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[];
|
||||
|
@ -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';
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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:
|
||||
}
|
||||
|
||||
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:
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
//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
|
||||
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
Normal file
10777
src/layer.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 {
|
||||
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 {
|
||||
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}));
|
||||
}
|
||||
|
@ -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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
$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 {
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
syncPending: false,
|
||||
syncLoading: false
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -343,6 +356,7 @@ export class ApiUpdatesManager {
|
||||
if(this.channelStates[channelID] === undefined) {
|
||||
this.addChannelState(channelID, pts);
|
||||
}
|
||||
|
||||
return this.channelStates[channelID];
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
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 {
|
||||
};
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
}).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);
|
||||
|
@ -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 {
|
||||
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);
|
||||
|
@ -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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
};
|
||||
}
|
||||
|
||||
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 {
|
||||
};
|
||||
}
|
||||
|
||||
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 {
|
||||
};
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
return download;
|
||||
}
|
||||
|
||||
public saveDocFile(doc: MTDocument) {
|
||||
public saveDocFile(doc: MyDocument) {
|
||||
const options = this.getFileDownloadOptions(doc);
|
||||
return appDownloadManager.downloadToDisc(options, doc.file_name);
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
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
|
||||
|
@ -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";
|
||||
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 {
|
||||
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 {
|
||||
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) {
|
||||
|
@ -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";
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
}
|
||||
});
|
||||
|
||||
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 {
|
||||
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
apiPromise = apiManager.invokeApi('messages.searchGlobal', {
|
||||
flags: 0,
|
||||
q: query,
|
||||
offset_rate: offsetRate,
|
||||
offset_peer: appPeersManager.getInputPeerByID(offsetPeerID),
|
||||
@ -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 {
|
||||
});
|
||||
}
|
||||
|
||||
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]);
|
||||
}));
|
||||
|
@ -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 = {
|
||||
}
|
||||
},
|
||||
|
||||
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 = {
|
||||
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 = {
|
||||
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;
|
||||
|
@ -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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -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 {
|
||||
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);
|
||||
|
@ -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 = {};
|
||||
|
@ -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 {
|
||||
};
|
||||
|
||||
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 {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
//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 {
|
||||
}, 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 {
|
||||
//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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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];
|
||||
|
||||
|
@ -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 = {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
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);
|
||||
}
|
||||
}
|
@ -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
|
||||
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) {
|
||||
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];
|
||||
|
@ -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 = {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
dcID,
|
||||
fileDownload: true/* ,
|
||||
singleInRequest: 'safari' in window */
|
||||
}) as Promise<UploadFile>;
|
||||
}) as Promise<MyUploadFile>;
|
||||
}, delta);
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
const resultInputFile: InputFile = {
|
||||
_: isBigFile ? 'inputFileBig' : 'inputFile',
|
||||
id: fileID,
|
||||
id: fileID as any,
|
||||
parts: totalParts,
|
||||
name: fileName,
|
||||
md5_checksum: ''
|
||||
|
@ -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 {
|
||||
|
||||
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 => {
|
||||
};
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import apiManager from './mtprotoworker';
|
||||
import { AccountPassword } from '../../types';
|
||||
import { AccountPassword } from '../../layer';
|
||||
//import { computeCheck } from "../crypto/srp";
|
||||
|
||||
export class PasswordManager {
|
||||
|
@ -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;
|
||||
|
@ -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() => {
|
||||
}, {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() => {
|
||||
}
|
||||
|
||||
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() => {
|
||||
|
||||
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
|
||||
});
|
||||
|
@ -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) {
|
||||
};
|
||||
});
|
||||
|
||||
require('fs').writeFileSync('./emoji.json', JSON.stringify(obj));
|
||||
require('fs').writeFileSync('./out/emoji.json', JSON.stringify(obj));
|
||||
}
|
||||
|
||||
{
|
||||
@ -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) {
|
||||
//console.log(item);
|
||||
});
|
||||
|
||||
require('fs').writeFileSync('./countries.json', JSON.stringify(arr));
|
||||
require('fs').writeFileSync('./out/countries.json', JSON.stringify(arr));
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
//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
Normal file
241
src/scripts/generate_mtproto_types.js
Normal file
@ -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);
|
44
src/scripts/in/schema_additional_params.json
Normal file
44
src/scripts/in/schema_additional_params.json
Normal file
@ -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"}
|
||||
]
|
||||
}]
|
@ -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() => {
|
||||
},
|
||||
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
141
src/types.d.ts
vendored
@ -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<{
|
||||
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;
|
||||
|
@ -63,6 +63,7 @@
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"./public/recorder.min.js",
|
||||
"public",
|
||||
"coverage",
|
||||
"./public/*.js",
|
||||
|
Loading…
x
Reference in New Issue
Block a user