Browse Source

Stickers optimizations

File reference database
Misc
master
morethanwords 4 years ago
parent
commit
f2b5363bb8
  1. 70
      package-lock.json
  2. 12
      package.json
  3. 2
      src/components/audio.ts
  4. 3
      src/components/emoticonsDropdown/tabs/stickers.ts
  5. 2
      src/components/sidebarRight/stickers.ts
  6. 31
      src/helpers/json.ts
  7. 211
      src/layer.d.ts
  8. 17
      src/lib/appManagers/apiUpdatesManager.ts
  9. 26
      src/lib/appManagers/appDocsManager.ts
  10. 41
      src/lib/appManagers/appDownloadManager.ts
  11. 7
      src/lib/appManagers/appImManager.ts
  12. 150
      src/lib/appManagers/appMessagesManager.ts
  13. 34
      src/lib/appManagers/appPhotosManager.ts
  14. 33
      src/lib/appManagers/appStateManager.ts
  15. 3
      src/lib/appManagers/appWebPagesManager.ts
  16. 29
      src/lib/bin_utils.ts
  17. 1
      src/lib/config.ts
  18. 29
      src/lib/crypto/crypto_utils.ts
  19. 37
      src/lib/crypto/srp.ts
  20. 53
      src/lib/lottieLoader.ts
  21. 8
      src/lib/mediaPlayer.ts
  22. 1
      src/lib/mtproto/apiFileManager.ts
  23. 25
      src/lib/mtproto/authorizer.ts
  24. 5
      src/lib/mtproto/networker.ts
  25. 64
      src/lib/mtproto/referenceDatabase.ts
  26. 12
      src/lib/polyfill.ts
  27. 22
      src/lib/rlottie/rlottie.worker.ts
  28. 24
      src/lib/storage.ts
  29. 22
      src/lib/utils.ts
  30. 20
      src/scripts/generate_mtproto_types.js
  31. 26
      src/scripts/in/schema_additional_params.json
  32. 3
      src/scripts/in/schema_replace_types.json
  33. 29
      src/scss/partials/_chat.scss
  34. 4
      src/scss/partials/_chatBubble.scss
  35. 16
      src/scss/partials/_chatlist.scss
  36. 7
      src/scss/partials/_emojiDropdown.scss
  37. 2
      src/scss/partials/_leftSidebar.scss
  38. 2
      src/scss/partials/_rightSidebar.scss
  39. 2
      src/scss/partials/_selector.scss
  40. 2
      src/scss/partials/_slider.scss
  41. 2
      src/scss/partials/popups/_popup.scss
  42. 2
      src/scss/partials/popups/_stickers.scss
  43. 11
      src/scss/style.scss
  44. 2136
      src/vendor/leemon.ts

70
package-lock.json generated

@ -3177,15 +3177,6 @@ @@ -3177,15 +3177,6 @@
"integrity": "sha512-GdZbRSJ3Cv5fiwT6I0SQ3ckeN2PWNqxd26W9Z2fCK1tGrrasGy4puvNFtnddqH9UJFMQYXxEuuB7B8UK+LLwSg==",
"dev": true
},
"@types/puppeteer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-3.0.1.tgz",
"integrity": "sha512-t03eNKCvWJXhQ8wkc5C6GYuSqMEdKLOX0GLMGtks25YZr38wKZlKTwGM/BoAPVtdysX7Bb9tdwrDS1+NrW3RRA==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/pvutils": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/@types/pvutils/-/pvutils-0.0.2.tgz",
@ -6461,15 +6452,6 @@ @@ -6461,15 +6452,6 @@
"pako": "^1.0.10"
}
},
"fastdom": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/fastdom/-/fastdom-1.0.9.tgz",
"integrity": "sha512-SSp4fbVzu8JkkG01NUX+0iOwe9M5PN3MGIQ84txLf4TkkJG4q30khkzumKgi4hUqO1+jX6wLHfnCPoZ6eSZ6Tg==",
"dev": true,
"requires": {
"strictdom": "^1.0.1"
}
},
"fastparse": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
@ -6499,28 +6481,6 @@ @@ -6499,28 +6481,6 @@
"integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==",
"dev": true
},
"file-loader": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.3.0.tgz",
"integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==",
"dev": true,
"requires": {
"loader-utils": "^1.2.3",
"schema-utils": "^2.5.0"
},
"dependencies": {
"schema-utils": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.2.tgz",
"integrity": "sha512-sazKNMBX/jwrXRkOI7N6dtiTVYqzSckzol8SGuHt0lE/v3xSW6cUkOqzu6Bq2tW+dlUzq3CWIqHU3ZKauliqdg==",
"dev": true,
"requires": {
"ajv": "^6.10.2",
"ajv-keywords": "^3.4.1"
}
}
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@ -9288,12 +9248,6 @@ @@ -9288,12 +9248,6 @@
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"lottie-web": {
"version": "5.6.10",
"resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.6.10.tgz",
"integrity": "sha512-ucTzaiBJOMm56/K7Wqjajyh7qXlonHKB3+b5fHvhSiz+jOrXt6QDNKAinI3qdw/zvvYHKzXvFMy5SgOXbOJ5ng==",
"dev": true
},
"loud-rejection": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
@ -16721,24 +16675,12 @@ @@ -16721,24 +16675,12 @@
"integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
"dev": true
},
"streamsaver": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/streamsaver/-/streamsaver-2.0.4.tgz",
"integrity": "sha512-rGES0zmHIPxinV32H2Dz55qgOE5dXui8yfu+JEjPAI294cDmwX4Oe7tCZLrnhjc/1z7hyIv9H6TMH2kqN2Tdqw==",
"dev": true
},
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
"dev": true
},
"strictdom": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/strictdom/-/strictdom-1.0.1.tgz",
"integrity": "sha1-GJ3pFkn3PUTVm4Qy76aO+dJllGA=",
"dev": true
},
"string-length": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz",
@ -17974,12 +17916,6 @@ @@ -17974,12 +17916,6 @@
"minimalistic-assert": "^1.0.0"
}
},
"web-streams-polyfill": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-2.1.1.tgz",
"integrity": "sha512-dlNpL2aab3g8CKfGz6rl8FNmGaRWLLn2g/DtSc9IjB30mEdE6XxzPfPSig5BwGSzI+oLxHyETrQGKjrVVhbLCg==",
"dev": true
},
"webcrypto-core": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.1.6.tgz",
@ -18007,12 +17943,6 @@ @@ -18007,12 +17943,6 @@
"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
"dev": true
},
"webp-hero": {
"version": "0.0.0-dev.24",
"resolved": "https://registry.npmjs.org/webp-hero/-/webp-hero-0.0.0-dev.24.tgz",
"integrity": "sha512-3XG47dRMV36RFwfYLUynOiKDXuqXfQ3dz7yvqGH5VplldV0NvngQzF0qBsZ3vRQwrigekRLeX/NdNwsaGfSnPQ==",
"dev": true
},
"webpack": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.43.0.tgz",

12
package.json

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"start": "node --max-old-space-size=8192 node_modules/webpack-dev-server/bin/webpack-dev-server.js --config webpack.dev.js",
"start": "node --max-old-space-size=12048 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",
@ -12,7 +12,8 @@ @@ -12,7 +12,8 @@
"test": "jest --config=jest.config.js",
"profile": "webpack --profile --json > stats.json --config webpack.prod.js",
"profile:dev": "webpack --profile --json > stats.json --config webpack.dev.js",
"whybundled": "npm run profile; whybundled stats.json"
"whybundled": "npm run profile && whybundled stats.json",
"generate-mtproto-types": "node ./src/scripts/generate_mtproto_types.js src/"
},
"author": "",
"license": "ISC",
@ -31,7 +32,6 @@ @@ -31,7 +32,6 @@
"@types/aes-js": "^3.1.1",
"@types/chrome": "0.0.91",
"@types/jest": "^24.9.1",
"@types/puppeteer": "^3.0.1",
"@types/serviceworker-webpack-plugin": "^1.0.1",
"aes-js": "^3.1.2",
"autoprefixer": "^9.8.0",
@ -41,8 +41,6 @@ @@ -41,8 +41,6 @@
"css-loader": "^3.5.3",
"express": "^4.17.1",
"fast-png": "^5.0.2",
"fastdom": "^1.0.9",
"file-loader": "^4.3.0",
"handlebars": "^4.7.6",
"handlebars-loader": "^1.7.1",
"html-webpack-plugin": "^3.2.0",
@ -50,7 +48,6 @@ @@ -50,7 +48,6 @@
"install": "^0.13.0",
"jest": "^24.9.0",
"leemon": "^6.2.0",
"lottie-web": "^5.6.10",
"media-query-plugin": "^1.3.1",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.14.1",
@ -65,15 +62,12 @@ @@ -65,15 +62,12 @@
"resolve-url-loader": "^3.1.1",
"sass-loader": "^8.0.2",
"serviceworker-webpack-plugin": "^1.0.1",
"streamsaver": "^2.0.4",
"style-loader": "^1.2.1",
"terser-webpack-plugin": "^3.0.2",
"ts-jest": "^24.3.0",
"ts-loader": "^6.2.2",
"typescript": "^3.9.3",
"url-loader": "^2.3.0",
"web-streams-polyfill": "^2.1.1",
"webp-hero": "0.0.0-dev.24",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-merge": "^4.2.2",

2
src/components/audio.ts

@ -62,7 +62,7 @@ function wrapVoiceMessage(doc: MyDocument, audioEl: AudioElement) { @@ -62,7 +62,7 @@ function wrapVoiceMessage(doc: MyDocument, audioEl: AudioElement) {
timeDiv.classList.add('audio-time');
audioEl.append(svg, timeDiv);
let waveform = (doc.attributes.find(attribute => attribute._ == 'documentAttributeAudio') as DocumentAttribute.documentAttributeAudio).waveform || [];
let waveform = (doc.attributes.find(attribute => attribute._ == 'documentAttributeAudio') as DocumentAttribute.documentAttributeAudio).waveform || new Uint8Array([]);
waveform = decodeWaveform(waveform.slice());
//console.log('decoded waveform:', waveform);

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

@ -147,7 +147,8 @@ export default class StickersTab implements EmoticonsTab { @@ -147,7 +147,8 @@ export default class StickersTab implements EmoticonsTab {
autoplay: false,
animationData: json,
width: 32,
height: 32
height: 32,
needUpscale: true
}, EMOTICONSSTICKERGROUP);
});
} else {

2
src/components/sidebarRight/stickers.ts

@ -10,7 +10,7 @@ import animationIntersector from "../animationIntersector"; @@ -10,7 +10,7 @@ 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";
import { StickerSet, StickerSetCovered } from "../../layer";
export default class AppStickersTab implements SliderTab {
private container = document.getElementById('stickers-container') as HTMLDivElement;

31
src/helpers/json.ts

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
export function parse(text: string) {
let arr: number[] = [], performedValue: any = null;
return JSON.parse(text, (key, value) => {
//console.log(key, value);
if(key == 'type' && value == 'bytes') {
arr = [];
return undefined;
} else if(arr) {
if(key == 'value') {
performedValue = new Uint8Array(arr);
arr = null;
return undefined;
} else arr[+key] = value;
} else if(performedValue) {
const v = performedValue;
performedValue = null;
return v;
}
return value;
});
}
// parse('{"file_reference": {"type": "bytes", "value": [1,2,3]}, "file_reference2": {"type": "bytes", "value": [3,2,1]}}');
// -> {file_reference: Uint8Array}
export function stringify(value: any) {
return JSON.stringify(value, (key, value) => {
if(key == 'downloaded' || (key == 'url' && value.indexOf('blob:') === 0)) return undefined;
return value;
});
}

211
src/layer.d.ts vendored

@ -223,7 +223,7 @@ export namespace InputMedia { @@ -223,7 +223,7 @@ export namespace InputMedia {
description: string,
photo?: InputWebDocument,
invoice: Invoice,
payload: Uint8Array | number[],
payload: Uint8Array,
provider: string,
provider_data: DataJSON,
start_param: string
@ -243,7 +243,7 @@ export namespace InputMedia { @@ -243,7 +243,7 @@ export namespace InputMedia {
_: 'inputMediaPoll',
flags?: number,
poll: Poll,
correct_answers?: Array<Uint8Array | number[]>,
correct_answers?: Array<Uint8Array>,
solution?: string,
solution_entities?: Array<MessageEntity>
};
@ -770,7 +770,8 @@ export type Message = Message.messageEmpty | Message.message | Message.messageSe @@ -770,7 +770,8 @@ export type Message = Message.messageEmpty | Message.message | Message.messageSe
export namespace Message {
export type messageEmpty = {
_: 'messageEmpty',
id: number
id: number,
deleted?: boolean
};
export type message = {
@ -785,6 +786,7 @@ export namespace Message { @@ -785,6 +786,7 @@ export namespace Message {
from_scheduled?: true,
legacy?: true,
edit_hide?: true,
unread?: true,
}>,
id: number,
from_id?: number,
@ -801,7 +803,12 @@ export namespace Message { @@ -801,7 +803,12 @@ export namespace Message {
edit_date?: number,
post_author?: string,
grouped_id?: string,
restriction_reason?: Array<RestrictionReason>
restriction_reason?: Array<RestrictionReason>,
mid?: number,
deleted?: boolean,
peerID?: number,
fromID?: number,
canBeEdited?: boolean
};
export type messageService = {
@ -814,13 +821,19 @@ export namespace Message { @@ -814,13 +821,19 @@ export namespace Message {
silent?: true,
post?: true,
legacy?: true,
unread?: true,
}>,
id: number,
from_id?: number,
to_id: Peer,
reply_to_msg_id?: number,
date: number,
action: MessageAction
action: MessageAction,
mid?: number,
deleted?: boolean,
peerID?: number,
fromID?: number,
canBeEdited?: boolean
};
}
@ -1001,7 +1014,7 @@ export namespace MessageAction { @@ -1001,7 +1014,7 @@ export namespace MessageAction {
flags?: number,
currency: string,
total_amount: string,
payload: Uint8Array | number[],
payload: Uint8Array,
info?: PaymentRequestedInfo,
shipping_option_id?: string,
charge: PaymentCharge
@ -1155,14 +1168,14 @@ export namespace PhotoSize { @@ -1155,14 +1168,14 @@ export namespace PhotoSize {
location: FileLocation,
w: number,
h: number,
bytes: Uint8Array | number[],
bytes: Uint8Array,
url?: string
};
export type photoStrippedSize = {
_: 'photoStrippedSize',
type: string,
bytes: Uint8Array | number[],
bytes: Uint8Array,
url?: string
};
}
@ -1230,7 +1243,7 @@ export namespace AuthExportedAuthorization { @@ -1230,7 +1243,7 @@ export namespace AuthExportedAuthorization {
export type authExportedAuthorization = {
_: 'auth.exportedAuthorization',
id: number,
bytes: Uint8Array | number[]
bytes: Uint8Array
};
}
@ -2003,7 +2016,7 @@ export namespace Update { @@ -2003,7 +2016,7 @@ export namespace Update {
peer: Peer,
msg_id: number,
chat_instance: string,
data?: Uint8Array | number[],
data?: Uint8Array,
game_short_name?: string
};
@ -2021,7 +2034,7 @@ export namespace Update { @@ -2021,7 +2034,7 @@ export namespace Update {
user_id: number,
msg_id: InputBotInlineMessageID,
chat_instance: string,
data?: Uint8Array | number[],
data?: Uint8Array,
game_short_name?: string
};
@ -2094,7 +2107,7 @@ export namespace Update { @@ -2094,7 +2107,7 @@ export namespace Update {
_: 'updateBotShippingQuery',
query_id: string,
user_id: number,
payload: Uint8Array | number[],
payload: Uint8Array,
shipping_address: PostAddress
};
@ -2103,7 +2116,7 @@ export namespace Update { @@ -2103,7 +2116,7 @@ export namespace Update {
flags?: number,
query_id: string,
user_id: number,
payload: Uint8Array | number[],
payload: Uint8Array,
info?: PaymentRequestedInfo,
shipping_option_id?: string,
currency: string,
@ -2230,7 +2243,7 @@ export namespace Update { @@ -2230,7 +2243,7 @@ export namespace Update {
_: 'updateMessagePollVote',
poll_id: string,
user_id: number,
options: Array<Uint8Array | number[]>
options: Array<Uint8Array>
};
export type updateDialogFilter = {
@ -2440,15 +2453,15 @@ export namespace UploadFile { @@ -2440,15 +2453,15 @@ export namespace UploadFile {
_: 'upload.file',
type: StorageFileType,
mtime: number,
bytes: Uint8Array | number[]
bytes: Uint8Array
};
export type uploadFileCdnRedirect = {
_: 'upload.fileCdnRedirect',
dc_id: number,
file_token: Uint8Array | number[],
encryption_key: Uint8Array | number[],
encryption_iv: Uint8Array | number[],
file_token: Uint8Array,
encryption_key: Uint8Array,
encryption_iv: Uint8Array,
file_hashes: Array<FileHash>
};
}
@ -2472,7 +2485,7 @@ export namespace DcOption { @@ -2472,7 +2485,7 @@ export namespace DcOption {
id: number,
ip_address: string,
port: number,
secret?: Uint8Array | number[]
secret?: Uint8Array
};
}
@ -2619,7 +2632,7 @@ export namespace EncryptedChat { @@ -2619,7 +2632,7 @@ export namespace EncryptedChat {
date: number,
admin_id: number,
participant_id: number,
g_a: Uint8Array | number[]
g_a: Uint8Array
};
export type encryptedChat = {
@ -2629,7 +2642,7 @@ export namespace EncryptedChat { @@ -2629,7 +2642,7 @@ export namespace EncryptedChat {
date: number,
admin_id: number,
participant_id: number,
g_a_or_b: Uint8Array | number[],
g_a_or_b: Uint8Array,
key_fingerprint: string
};
@ -2715,7 +2728,7 @@ export namespace EncryptedMessage { @@ -2715,7 +2728,7 @@ export namespace EncryptedMessage {
random_id: string,
chat_id: number,
date: number,
bytes: Uint8Array | number[],
bytes: Uint8Array,
file: EncryptedFile
};
@ -2724,7 +2737,7 @@ export namespace EncryptedMessage { @@ -2724,7 +2737,7 @@ export namespace EncryptedMessage {
random_id: string,
chat_id: number,
date: number,
bytes: Uint8Array | number[]
bytes: Uint8Array
};
}
@ -2736,15 +2749,15 @@ export type MessagesDhConfig = MessagesDhConfig.messagesDhConfigNotModified | Me @@ -2736,15 +2749,15 @@ export type MessagesDhConfig = MessagesDhConfig.messagesDhConfigNotModified | Me
export namespace MessagesDhConfig {
export type messagesDhConfigNotModified = {
_: 'messages.dhConfigNotModified',
random: Uint8Array | number[]
random: Uint8Array
};
export type messagesDhConfig = {
_: 'messages.dhConfig',
g: number,
p: Uint8Array | number[],
p: Uint8Array,
version: number,
random: Uint8Array | number[]
random: Uint8Array
};
}
@ -3183,7 +3196,7 @@ export namespace DocumentAttribute { @@ -3183,7 +3196,7 @@ export namespace DocumentAttribute {
duration: number,
title?: string,
performer?: string,
waveform?: Uint8Array | number[]
waveform?: Uint8Array
};
export type documentAttributeFilename = {
@ -3359,13 +3372,13 @@ export namespace AccountPassword { @@ -3359,13 +3372,13 @@ export namespace AccountPassword {
has_password?: true,
}>,
current_algo?: PasswordKdfAlgo,
srp_B?: Uint8Array | number[],
srp_B?: Uint8Array,
srp_id?: string,
hint?: string,
email_unconfirmed_pattern?: string,
new_algo: PasswordKdfAlgo,
new_secure_algo: SecurePasswordKdfAlgo,
secure_random: Uint8Array | number[]
secure_random: Uint8Array
};
}
@ -3393,7 +3406,7 @@ export namespace AccountPasswordInputSettings { @@ -3393,7 +3406,7 @@ export namespace AccountPasswordInputSettings {
_: 'account.passwordInputSettings',
flags?: number,
new_algo?: PasswordKdfAlgo,
new_password_hash?: Uint8Array | number[],
new_password_hash?: Uint8Array,
hint?: string,
email?: string,
new_secure_settings?: SecureSecretSettings
@ -3587,7 +3600,7 @@ export namespace KeyboardButton { @@ -3587,7 +3600,7 @@ export namespace KeyboardButton {
export type keyboardButtonCallback = {
_: 'keyboardButtonCallback',
text: string,
data: Uint8Array | number[]
data: Uint8Array
};
export type keyboardButtonRequestPhone = {
@ -5307,7 +5320,7 @@ export namespace UploadWebFile { @@ -5307,7 +5320,7 @@ export namespace UploadWebFile {
mime_type: string,
file_type: StorageFileType,
mtime: number,
bytes: Uint8Array | number[]
bytes: Uint8Array
};
}
@ -5414,7 +5427,7 @@ export namespace InputPaymentCredentials { @@ -5414,7 +5427,7 @@ export namespace InputPaymentCredentials {
export type inputPaymentCredentialsSaved = {
_: 'inputPaymentCredentialsSaved',
id: string,
tmp_password: Uint8Array | number[]
tmp_password: Uint8Array
};
export type inputPaymentCredentials = {
@ -5446,7 +5459,7 @@ export type AccountTmpPassword = AccountTmpPassword.accountTmpPassword; @@ -5446,7 +5459,7 @@ export type AccountTmpPassword = AccountTmpPassword.accountTmpPassword;
export namespace AccountTmpPassword {
export type accountTmpPassword = {
_: 'account.tmpPassword',
tmp_password: Uint8Array | number[],
tmp_password: Uint8Array,
valid_until: number
};
}
@ -5530,7 +5543,7 @@ export namespace PhoneCall { @@ -5530,7 +5543,7 @@ export namespace PhoneCall {
date: number,
admin_id: number,
participant_id: number,
g_a_hash: Uint8Array | number[],
g_a_hash: Uint8Array,
protocol: PhoneCallProtocol
};
@ -5545,7 +5558,7 @@ export namespace PhoneCall { @@ -5545,7 +5558,7 @@ export namespace PhoneCall {
date: number,
admin_id: number,
participant_id: number,
g_b: Uint8Array | number[],
g_b: Uint8Array,
protocol: PhoneCallProtocol
};
@ -5560,7 +5573,7 @@ export namespace PhoneCall { @@ -5560,7 +5573,7 @@ export namespace PhoneCall {
date: number,
admin_id: number,
participant_id: number,
g_a_or_b: Uint8Array | number[],
g_a_or_b: Uint8Array,
key_fingerprint: string,
protocol: PhoneCallProtocol,
connections: Array<PhoneConnection>,
@ -5593,7 +5606,7 @@ export namespace PhoneConnection { @@ -5593,7 +5606,7 @@ export namespace PhoneConnection {
ip: string,
ipv6: string,
port: number,
peer_tag: Uint8Array | number[]
peer_tag: Uint8Array
};
}
@ -5637,12 +5650,12 @@ export type UploadCdnFile = UploadCdnFile.uploadCdnFileReuploadNeeded | UploadCd @@ -5637,12 +5650,12 @@ export type UploadCdnFile = UploadCdnFile.uploadCdnFileReuploadNeeded | UploadCd
export namespace UploadCdnFile {
export type uploadCdnFileReuploadNeeded = {
_: 'upload.cdnFileReuploadNeeded',
request_token: Uint8Array | number[]
request_token: Uint8Array
};
export type uploadCdnFile = {
_: 'upload.cdnFile',
bytes: Uint8Array | number[]
bytes: Uint8Array
};
}
@ -6132,7 +6145,7 @@ export namespace FileHash { @@ -6132,7 +6145,7 @@ export namespace FileHash {
_: 'fileHash',
offset: number,
limit: number,
hash: Uint8Array | number[]
hash: Uint8Array
};
}
@ -6178,8 +6191,8 @@ export namespace InputSecureFile { @@ -6178,8 +6191,8 @@ export namespace InputSecureFile {
id: string,
parts: number,
md5_checksum: string,
file_hash: Uint8Array | number[],
secret: Uint8Array | number[]
file_hash: Uint8Array,
secret: Uint8Array
};
export type inputSecureFile = {
@ -6206,8 +6219,8 @@ export namespace SecureFile { @@ -6206,8 +6219,8 @@ export namespace SecureFile {
size: number,
dc_id: number,
date: number,
file_hash: Uint8Array | number[],
secret: Uint8Array | number[]
file_hash: Uint8Array,
secret: Uint8Array
};
}
@ -6219,9 +6232,9 @@ export type SecureData = SecureData.secureData; @@ -6219,9 +6232,9 @@ export type SecureData = SecureData.secureData;
export namespace SecureData {
export type secureData = {
_: 'secureData',
data: Uint8Array | number[],
data_hash: Uint8Array | number[],
secret: Uint8Array | number[]
data: Uint8Array,
data_hash: Uint8Array,
secret: Uint8Array
};
}
@ -6318,7 +6331,7 @@ export namespace SecureValue { @@ -6318,7 +6331,7 @@ export namespace SecureValue {
translation?: Array<SecureFile>,
files?: Array<SecureFile>,
plain_data?: SecurePlainData,
hash: Uint8Array | number[]
hash: Uint8Array
};
}
@ -6351,7 +6364,7 @@ export namespace SecureValueHash { @@ -6351,7 +6364,7 @@ export namespace SecureValueHash {
export type secureValueHash = {
_: 'secureValueHash',
type: SecureValueType,
hash: Uint8Array | number[]
hash: Uint8Array
};
}
@ -6364,7 +6377,7 @@ export namespace SecureValueError { @@ -6364,7 +6377,7 @@ export namespace SecureValueError {
export type secureValueErrorData = {
_: 'secureValueErrorData',
type: SecureValueType,
data_hash: Uint8Array | number[],
data_hash: Uint8Array,
field: string,
text: string
};
@ -6372,56 +6385,56 @@ export namespace SecureValueError { @@ -6372,56 +6385,56 @@ export namespace SecureValueError {
export type secureValueErrorFrontSide = {
_: 'secureValueErrorFrontSide',
type: SecureValueType,
file_hash: Uint8Array | number[],
file_hash: Uint8Array,
text: string
};
export type secureValueErrorReverseSide = {
_: 'secureValueErrorReverseSide',
type: SecureValueType,
file_hash: Uint8Array | number[],
file_hash: Uint8Array,
text: string
};
export type secureValueErrorSelfie = {
_: 'secureValueErrorSelfie',
type: SecureValueType,
file_hash: Uint8Array | number[],
file_hash: Uint8Array,
text: string
};
export type secureValueErrorFile = {
_: 'secureValueErrorFile',
type: SecureValueType,
file_hash: Uint8Array | number[],
file_hash: Uint8Array,
text: string
};
export type secureValueErrorFiles = {
_: 'secureValueErrorFiles',
type: SecureValueType,
file_hash: Array<Uint8Array | number[]>,
file_hash: Array<Uint8Array>,
text: string
};
export type secureValueError = {
_: 'secureValueError',
type: SecureValueType,
hash: Uint8Array | number[],
hash: Uint8Array,
text: string
};
export type secureValueErrorTranslationFile = {
_: 'secureValueErrorTranslationFile',
type: SecureValueType,
file_hash: Uint8Array | number[],
file_hash: Uint8Array,
text: string
};
export type secureValueErrorTranslationFiles = {
_: 'secureValueErrorTranslationFiles',
type: SecureValueType,
file_hash: Array<Uint8Array | number[]>,
file_hash: Array<Uint8Array>,
text: string
};
}
@ -6434,9 +6447,9 @@ export type SecureCredentialsEncrypted = SecureCredentialsEncrypted.secureCreden @@ -6434,9 +6447,9 @@ export type SecureCredentialsEncrypted = SecureCredentialsEncrypted.secureCreden
export namespace SecureCredentialsEncrypted {
export type secureCredentialsEncrypted = {
_: 'secureCredentialsEncrypted',
data: Uint8Array | number[],
hash: Uint8Array | number[],
secret: Uint8Array | number[]
data: Uint8Array,
hash: Uint8Array,
secret: Uint8Array
};
}
@ -6530,10 +6543,10 @@ export namespace PasswordKdfAlgo { @@ -6530,10 +6543,10 @@ export namespace PasswordKdfAlgo {
export type passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow = {
_: 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
salt1: Uint8Array | number[],
salt2: Uint8Array | number[],
salt1: Uint8Array,
salt2: Uint8Array,
g: number,
p: Uint8Array | number[]
p: Uint8Array
};
}
@ -6549,12 +6562,12 @@ export namespace SecurePasswordKdfAlgo { @@ -6549,12 +6562,12 @@ export namespace SecurePasswordKdfAlgo {
export type securePasswordKdfAlgoPBKDF2HMACSHA512iter100000 = {
_: 'securePasswordKdfAlgoPBKDF2HMACSHA512iter100000',
salt: Uint8Array | number[]
salt: Uint8Array
};
export type securePasswordKdfAlgoSHA512 = {
_: 'securePasswordKdfAlgoSHA512',
salt: Uint8Array | number[]
salt: Uint8Array
};
}
@ -6567,7 +6580,7 @@ export namespace SecureSecretSettings { @@ -6567,7 +6580,7 @@ export namespace SecureSecretSettings {
export type secureSecretSettings = {
_: 'secureSecretSettings',
secure_algo: SecurePasswordKdfAlgo,
secure_secret: Uint8Array | number[],
secure_secret: Uint8Array,
secure_secret_id: string
};
}
@ -6585,8 +6598,8 @@ export namespace InputCheckPasswordSRP { @@ -6585,8 +6598,8 @@ export namespace InputCheckPasswordSRP {
export type inputCheckPasswordSRP = {
_: 'inputCheckPasswordSRP',
srp_id: string,
A: Uint8Array | number[],
M1: Uint8Array | number[]
A: Uint8Array,
M1: Uint8Array
};
}
@ -6858,7 +6871,7 @@ export namespace PollAnswer { @@ -6858,7 +6871,7 @@ export namespace PollAnswer {
export type pollAnswer = {
_: 'pollAnswer',
text: string,
option: Uint8Array | number[]
option: Uint8Array
};
}
@ -6898,7 +6911,7 @@ export namespace PollAnswerVoters { @@ -6898,7 +6911,7 @@ export namespace PollAnswerVoters {
chosen?: true,
correct?: true,
}>,
option: Uint8Array | number[],
option: Uint8Array,
voters: number
};
}
@ -7385,13 +7398,13 @@ export namespace AuthLoginToken { @@ -7385,13 +7398,13 @@ export namespace AuthLoginToken {
export type authLoginToken = {
_: 'auth.loginToken',
expires: number,
token: Uint8Array | number[]
token: Uint8Array
};
export type authLoginTokenMigrateTo = {
_: 'auth.loginTokenMigrateTo',
dc_id: number,
token: Uint8Array | number[]
token: Uint8Array
};
export type authLoginTokenSuccess = {
@ -7515,7 +7528,7 @@ export namespace MessageUserVote { @@ -7515,7 +7528,7 @@ export namespace MessageUserVote {
export type messageUserVote = {
_: 'messageUserVote',
user_id: number,
option: Uint8Array | number[],
option: Uint8Array,
date: number
};
@ -7528,7 +7541,7 @@ export namespace MessageUserVote { @@ -7528,7 +7541,7 @@ export namespace MessageUserVote {
export type messageUserVoteMultiple = {
_: 'messageUserVoteMultiple',
user_id: number,
options: Array<Uint8Array | number[]>,
options: Array<Uint8Array>,
date: number
};
}
@ -8645,14 +8658,14 @@ export type AuthExportAuthorization = { @@ -8645,14 +8658,14 @@ export type AuthExportAuthorization = {
export type AuthImportAuthorization = {
id: number,
bytes: Uint8Array | number[]
bytes: Uint8Array
};
export type AuthBindTempAuthKey = {
perm_auth_key_id: string,
nonce: string,
expires_at: number,
encrypted_message: Uint8Array | number[]
encrypted_message: Uint8Array
};
export type AuthImportBotAuthorization = {
@ -8695,11 +8708,11 @@ export type AuthExportLoginToken = { @@ -8695,11 +8708,11 @@ export type AuthExportLoginToken = {
};
export type AuthImportLoginToken = {
token: Uint8Array | number[]
token: Uint8Array
};
export type AuthAcceptLoginToken = {
token: Uint8Array | number[]
token: Uint8Array
};
export type AccountRegisterDevice = {
@ -8708,7 +8721,7 @@ export type AccountRegisterDevice = { @@ -8708,7 +8721,7 @@ export type AccountRegisterDevice = {
token_type: number,
token: string,
app_sandbox: boolean,
secret: Uint8Array | number[],
secret: Uint8Array,
other_uids: Array<number>
};
@ -9314,12 +9327,12 @@ export type MessagesGetDhConfig = { @@ -9314,12 +9327,12 @@ export type MessagesGetDhConfig = {
export type MessagesRequestEncryption = {
user_id: InputUser,
random_id: number,
g_a: Uint8Array | number[]
g_a: Uint8Array
};
export type MessagesAcceptEncryption = {
peer: InputEncryptedChat,
g_b: Uint8Array | number[],
g_b: Uint8Array,
key_fingerprint: string
};
@ -9340,20 +9353,20 @@ export type MessagesReadEncryptedHistory = { @@ -9340,20 +9353,20 @@ export type MessagesReadEncryptedHistory = {
export type MessagesSendEncrypted = {
peer: InputEncryptedChat,
random_id: string,
data: Uint8Array | number[]
data: Uint8Array
};
export type MessagesSendEncryptedFile = {
peer: InputEncryptedChat,
random_id: string,
data: Uint8Array | number[],
data: Uint8Array,
file: InputEncryptedFile
};
export type MessagesSendEncryptedService = {
peer: InputEncryptedChat,
random_id: string,
data: Uint8Array | number[]
data: Uint8Array
};
export type MessagesReceivedQueue = {
@ -9448,7 +9461,7 @@ export type MessagesReorderStickerSets = { @@ -9448,7 +9461,7 @@ export type MessagesReorderStickerSets = {
};
export type MessagesGetDocumentByHash = {
sha256: Uint8Array | number[],
sha256: Uint8Array,
size: number,
mime_type: string
};
@ -9533,7 +9546,7 @@ export type MessagesGetBotCallbackAnswer = { @@ -9533,7 +9546,7 @@ export type MessagesGetBotCallbackAnswer = {
game?: true,
peer: InputPeer,
msg_id: number,
data?: Uint8Array | number[]
data?: Uint8Array
};
export type MessagesSetBotCallbackAnswer = {
@ -9769,7 +9782,7 @@ export type MessagesUpdatePinnedMessage = { @@ -9769,7 +9782,7 @@ export type MessagesUpdatePinnedMessage = {
export type MessagesSendVote = {
peer: InputPeer,
msg_id: number,
options: Array<Uint8Array | number[]>
options: Array<Uint8Array>
};
export type MessagesGetPollResults = {
@ -9862,7 +9875,7 @@ export type MessagesGetPollVotes = { @@ -9862,7 +9875,7 @@ export type MessagesGetPollVotes = {
flags?: number,
peer: InputPeer,
id: number,
option?: Uint8Array | number[],
option?: Uint8Array,
offset?: string,
limit: number
};
@ -9942,7 +9955,7 @@ export type PhotosGetUserPhotos = { @@ -9942,7 +9955,7 @@ export type PhotosGetUserPhotos = {
export type UploadSaveFilePart = {
file_id: string,
file_part: number,
bytes: Uint8Array | number[]
bytes: Uint8Array
};
export type UploadGetFile = {
@ -9958,7 +9971,7 @@ export type UploadSaveBigFilePart = { @@ -9958,7 +9971,7 @@ export type UploadSaveBigFilePart = {
file_id: string,
file_part: number,
file_total_parts: number,
bytes: Uint8Array | number[]
bytes: Uint8Array
};
export type UploadGetWebFile = {
@ -9968,18 +9981,18 @@ export type UploadGetWebFile = { @@ -9968,18 +9981,18 @@ export type UploadGetWebFile = {
};
export type UploadGetCdnFile = {
file_token: Uint8Array | number[],
file_token: Uint8Array,
offset: number,
limit: number
};
export type UploadReuploadCdnFile = {
file_token: Uint8Array | number[],
request_token: Uint8Array | number[]
file_token: Uint8Array,
request_token: Uint8Array
};
export type UploadGetCdnFileHashes = {
file_token: Uint8Array | number[],
file_token: Uint8Array,
offset: number
};
@ -10350,19 +10363,19 @@ export type PhoneRequestCall = { @@ -10350,19 +10363,19 @@ export type PhoneRequestCall = {
video?: true,
user_id: InputUser,
random_id: number,
g_a_hash: Uint8Array | number[],
g_a_hash: Uint8Array,
protocol: PhoneCallProtocol
};
export type PhoneAcceptCall = {
peer: InputPhoneCall,
g_b: Uint8Array | number[],
g_b: Uint8Array,
protocol: PhoneCallProtocol
};
export type PhoneConfirmCall = {
peer: InputPhoneCall,
g_a: Uint8Array | number[],
g_a: Uint8Array,
key_fingerprint: string,
protocol: PhoneCallProtocol
};

17
src/lib/appManagers/apiUpdatesManager.ts

@ -6,7 +6,6 @@ import appPeersManager from "./appPeersManager"; @@ -6,7 +6,6 @@ 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: {
@ -63,7 +62,7 @@ export class ApiUpdatesManager { @@ -63,7 +62,7 @@ export class ApiUpdatesManager {
return true;
}
public popPendingPtsUpdate(channelID: any) {
public popPendingPtsUpdate(channelID: number) {
var curState = channelID ? this.getChannelState(channelID) : this.updatesState;
if(!curState.pendingPtsUpdates.length) {
return false;
@ -216,7 +215,7 @@ export class ApiUpdatesManager { @@ -216,7 +215,7 @@ export class ApiUpdatesManager {
// Should be first because of updateMessageID
// this.log('applying', differenceResult.other_updates.length, 'other updates')
differenceResult.other_updates.forEach((update: any) => {
differenceResult.other_updates.forEach((update) => {
switch(update._) {
case 'updateChannelTooLong':
case 'updateNewChannelMessage':
@ -229,7 +228,7 @@ export class ApiUpdatesManager { @@ -229,7 +228,7 @@ export class ApiUpdatesManager {
});
// this.log('applying', differenceResult.new_messages.length, 'new messages')
differenceResult.new_messages.forEach((apiMessage: any) => {
differenceResult.new_messages.forEach((apiMessage) => {
this.saveUpdate({
_: 'updateNewMessage',
message: apiMessage,
@ -303,13 +302,13 @@ export class ApiUpdatesManager { @@ -303,13 +302,13 @@ export class ApiUpdatesManager {
appChatsManager.saveApiChats(differenceResult.chats);
// Should be first because of updateMessageID
this.log('applying', differenceResult.other_updates.length, 'channel other updates')
differenceResult.other_updates.forEach((update: any) => {
this.log('applying', differenceResult.other_updates.length, 'channel other updates');
differenceResult.other_updates.forEach((update) => {
this.saveUpdate(update);
});
this.log('applying', differenceResult.new_messages.length, 'channel new messages')
differenceResult.new_messages.forEach((apiMessage: any) => {
this.log('applying', differenceResult.new_messages.length, 'channel new messages');
differenceResult.new_messages.forEach((apiMessage) => {
this.saveUpdate({
_: 'updateNewChannelMessage',
message: apiMessage,
@ -519,7 +518,7 @@ export class ApiUpdatesManager { @@ -519,7 +518,7 @@ export class ApiUpdatesManager {
apiManager.setUpdatesProcessor(this.processUpdateMessage.bind(this));
if(!state || !state.pts || !state.date || !state.seq) {
apiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then((stateResult: any) => {
apiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then((stateResult) => {
this.updatesState.seq = stateResult.seq;
this.updatesState.pts = stateResult.pts;
this.updatesState.date = stateResult.date;

26
src/lib/appManagers/appDocsManager.ts

@ -1,11 +1,12 @@ @@ -1,11 +1,12 @@
import {RichTextProcessor} from '../richtextprocessor';
import { isObject, getFileURL, FileURLType } from '../utils';
import { isObject, getFileURL, FileURLType, safeReplaceArrayInObject } from '../utils';
import opusDecodeController from '../opusDecodeController';
import { getFileNameByLocation } from '../bin_utils';
import appDownloadManager, { DownloadBlob } from './appDownloadManager';
import appPhotosManager from './appPhotosManager';
import { isServiceWorkerSupported } from '../config';
import { InputFileLocation, Document, PhotoSize } from '../../layer';
import referenceDatabase, { ReferenceContext } from '../mtproto/referenceDatabase';
export type MyDocument = Document.document;
@ -14,38 +15,35 @@ export type MyDocument = Document.document; @@ -14,38 +15,35 @@ export type MyDocument = Document.document;
class AppDocsManager {
private docs: {[docID: string]: MyDocument} = {};
public saveDoc(doc: Document, context?: any): MyDocument {
public saveDoc(doc: Document, context?: ReferenceContext): MyDocument {
if(doc._ == 'documentEmpty') {
return undefined;
}
const oldDoc = this.docs[doc.id];
safeReplaceArrayInObject('file_reference', oldDoc, doc);
referenceDatabase.saveContext(doc.file_reference, context);
//console.log('saveDoc', apiDoc, this.docs[apiDoc.id]);
if(this.docs[doc.id]) {
const d = this.docs[doc.id];
if(oldDoc) {
//if(doc._ != 'documentEmpty' && doc._ == d._) {
if(doc.thumbs) {
if(!d.thumbs) d.thumbs = doc.thumbs;
if(!oldDoc.thumbs) oldDoc.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;
//}
return d;
return oldDoc;
//return Object.assign(d, apiDoc, context);
//return context ? Object.assign(d, context) : d;
}
if(context) {
Object.assign(doc, context);
}
this.docs[doc.id] = doc;

41
src/lib/appManagers/appDownloadManager.ts

@ -4,6 +4,8 @@ import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePr @@ -4,6 +4,8 @@ import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePr
import type { DownloadOptions } from "../mtproto/apiFileManager";
import { getFileNameByLocation } from "../bin_utils";
import { InputFile } from "../../layer";
import referenceDatabase, {ReferenceBytes} from "../mtproto/referenceDatabase";
import appMessagesManager from "./appMessagesManager";
export type ResponseMethodBlob = 'blob';
export type ResponseMethodJson = 'json';
@ -74,7 +76,44 @@ export class AppDownloadManager { @@ -74,7 +76,44 @@ export class AppDownloadManager {
if(this.downloads.hasOwnProperty(fileName)) return this.downloads[fileName];
const deferred = this.getNewDeferred(fileName);
apiManager.downloadFile(options).then(deferred.resolve, deferred.reject);
const onError = (err: any) => {
switch(err.type) {
case 'FILE_REFERENCE_EXPIRED': {
// @ts-ignore
const bytes: ReferenceBytes = options?.location?.file_reference;
if(bytes) {
const context = referenceDatabase.getContext(bytes);
switch(context?.type) {
case 'message': {
return appMessagesManager.wrapSingleMessage(context.messageID, true).then(() => {
//console.log('FILE_REFERENCE_EXPIRED: got message', context, options, appMessagesManager.getMessage(context.messageID).media);
return tryDownload();
});
}
default: {
console.warn('FILE_REFERENCE_EXPIRED: not implemented context', context);
}
}
} else {
console.warn('FILE_REFERENCE_EXPIRED: no context for bytes:', bytes);
}
break;
}
default:
deferred.reject(err);
break;
}
};
const tryDownload = (): Promise<unknown> => {
return apiManager.downloadFile(options).then(deferred.resolve, onError);
};
tryDownload();
//console.log('Will download file:', fileName, url);
return deferred;

7
src/lib/appManagers/appImManager.ts

@ -563,7 +563,8 @@ export class AppImManager { @@ -563,7 +563,8 @@ export class AppImManager {
cancelEvent(e);
if(mediaSizes.isMobile) {
this.setPeer(0);
//this.setPeer(0);
this.selectTab(0);
} else {
const isNowOpen = document.body.classList.toggle(LEFT_COLUMN_ACTIVE_CLASSNAME);
@ -1056,6 +1057,10 @@ export class AppImManager { @@ -1056,6 +1057,10 @@ export class AppImManager {
}
const samePeer = this.peerID == peerID;
if(samePeer) { // fix for returning back
this.selectTab(1);
}
if(this.setPeerPromise && samePeer) return this.setPeerPromise;

150
src/lib/appManagers/appMessagesManager.ts

@ -24,10 +24,12 @@ import { Modify } from "../../types"; @@ -24,10 +24,12 @@ 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, DocumentAttribute, Dialog as MTDialog, MessagesDialogs, MessagesPeerDialogs } from "../../layer";
import { DialogFilter, Message, InputMessage, MethodDeclMap, MessagesFilter, PhotoSize, DocumentAttribute, Dialog as MTDialog, MessagesDialogs, MessagesPeerDialogs, MessagesMessages, MessageMedia } from "../../layer";
import referenceDatabase, { ReferenceContext } from "../mtproto/referenceDatabase";
//console.trace('include');
// TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет
// TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках
const APITIMEOUT = 0;
@ -135,7 +137,7 @@ export class DialogsStorage { @@ -135,7 +137,7 @@ export class DialogsStorage {
const mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID);
const message = appMessagesManager.getMessage(mid);
let topDate = message.date;
let topDate = (message as Message.message).date || Date.now() / 1000;
if(channelID) {
const channel = appChatsManager.getChat(channelID);
if(!topDate || channel.date && channel.date > topDate) {
@ -458,8 +460,10 @@ export class FiltersStorage { @@ -458,8 +460,10 @@ export class FiltersStorage {
}
}
type MyMessage = Message.message | Message.messageService;
export class AppMessagesManager {
public messagesStorage: any = {};
public messagesStorage: {[mid: string]: any} = {};
public groupedMessagesStorage: {[groupID: string]: any} = {}; // will be used for albums
public historiesStorage: {
[peerID: string]: HistoryStorage
@ -477,8 +481,7 @@ export class AppMessagesManager { @@ -477,8 +481,7 @@ export class AppMessagesManager {
public lastSearchResults: any = [];
public needSingleMessages: number[] = [];
public fetchSingleMessagesTimeout = 0;
private fetchSingleMessagesPromise: Promise<any> = null;
private fetchSingleMessagesPromise: Promise<void> = null;
public maxSeenID = 0;
@ -517,9 +520,9 @@ export class AppMessagesManager { @@ -517,9 +520,9 @@ export class AppMessagesManager {
$rootScope.$on('webpage_updated', (e) => {
let eventData = e.detail;
eventData.msgs.forEach((msgID: number) => {
let message = this.getMessage(msgID);
message.webpage = appWebPagesManager.getWebPage(eventData.id); // warning
eventData.msgs.forEach((msgID) => {
let message = this.getMessage(msgID) as Message.message;
(message.media as MessageMedia.messageMediaWebPage).webpage = appWebPagesManager.getWebPage(eventData.id); // warning
$rootScope.$broadcast('message_edit', {
peerID: this.getMessagePeer(message),
id: message.id,
@ -626,6 +629,7 @@ export class AppMessagesManager { @@ -626,6 +629,7 @@ export class AppMessagesManager {
peer: appPeersManager.getInputPeerByID(peerID),
id: appMessagesIDsManager.getMessageLocalID(messageID),
message: text,
// @ts-ignore
media: message.media,
entities: this.getInputEntities(entities),
no_webpage: noWebPage || undefined,
@ -2024,9 +2028,10 @@ export class AppMessagesManager { @@ -2024,9 +2028,10 @@ export class AppMessagesManager {
return Promise.all(promises);
}
public getMessage(messageID: number) {
public getMessage(messageID: number)/* : Message */ {
return this.messagesStorage[messageID] || {
_: 'messageEmpty',
id: messageID,
deleted: true,
pFlags: {out: false, unread: false}
};
@ -2245,9 +2250,9 @@ export class AppMessagesManager { @@ -2245,9 +2250,9 @@ export class AppMessagesManager {
apiMessage.viaBotID = apiMessage.via_bot_id;
}
const mediaContext = {
user_id: apiMessage.fromID,
date: apiMessage.date
const mediaContext: ReferenceContext = {
type: 'message',
messageID: mid
};
if(apiMessage.media) {
@ -2638,7 +2643,8 @@ export class AppMessagesManager { @@ -2638,7 +2643,8 @@ export class AppMessagesManager {
let topMessage = dialog.top_message;
const topPendingMessage = this.pendingTopMsgs[peerID];
if(topPendingMessage) {
if(!topMessage || this.getMessage(topPendingMessage).date > this.getMessage(topMessage).date) {
if(!topMessage
|| (this.getMessage(topPendingMessage) as MyMessage).date > (this.getMessage(topMessage) as MyMessage).date) {
dialog.top_message = topMessage = topPendingMessage;
}
}
@ -3764,11 +3770,21 @@ export class AppMessagesManager { @@ -3764,11 +3770,21 @@ export class AppMessagesManager {
for(let i = 0; i < update.messages.length; i++) {
let messageID = appMessagesIDsManager.getFullMessageID(update.messages[i], channelID);
let message = this.messagesStorage[messageID];
let message: MyMessage = this.messagesStorage[messageID];
if(message) {
let peerID = this.getMessagePeer(message);
let history = historiesUpdated[peerID] || (historiesUpdated[peerID] = {count: 0, unread: 0, msgs: {}});
if((message as Message.message).media) {
// @ts-ignore
let c = message.media.webpage || message.media;
const smth = c.photo || c.document;
if(smth.file_reference) {
referenceDatabase.deleteContext(smth.file_reference, {type: 'message', messageID});
}
}
if(!message.pFlags.out && message.pFlags.unread) {
history.unread++;
}
@ -4228,29 +4244,34 @@ export class AppMessagesManager { @@ -4228,29 +4244,34 @@ export class AppMessagesManager {
}, {
//timeout: APITIMEOUT,
noErrorBox: true
}).then((historyResult: any) => {
}).then((historyResult) => {
this.log('requestHistory result:', historyResult, maxID, limit, offset);
if(historyResult._ == 'messages.messagesNotModified') {
return historyResult;
}
appUsersManager.saveApiUsers(historyResult.users);
appChatsManager.saveApiChats(historyResult.chats);
this.saveMessages(historyResult.messages);
if(isChannel) {
apiUpdatesManager.addChannelState(-peerID, historyResult.pts);
apiUpdatesManager.addChannelState(-peerID, (historyResult as MessagesMessages.messagesChannelMessages).pts);
}
let length = historyResult.messages.length;
if(length && historyResult.messages[length - 1].deleted) {
historyResult.messages.splice(length - 1, 1);
length--;
historyResult.count--;
(historyResult as MessagesMessages.messagesMessagesSlice).count--;
}
// will load more history if last message is album grouped (because it can be not last item)
const historyStorage = this.historiesStorage[peerID];
// historyResult.messages: desc sorted
if(length && historyResult.messages[length - 1].grouped_id && (historyStorage.history.length + historyResult.messages.length) < historyResult.count) {
return this.requestHistory(peerID, historyResult.messages[length - 1].mid, 10, 0).then((_historyResult: any) => {
if(length && (historyResult.messages[length - 1] as Message.message).grouped_id
&& (historyStorage.history.length + historyResult.messages.length) < (historyResult as MessagesMessages.messagesMessagesSlice).count) {
return this.requestHistory(peerID, (historyResult.messages[length - 1] as Message.message).mid, 10, 0).then((_historyResult) => {
return historyResult;
});
}
@ -4314,59 +4335,62 @@ export class AppMessagesManager { @@ -4314,59 +4335,62 @@ export class AppMessagesManager {
return this.fetchSingleMessagesPromise;
}
const mids = this.needSingleMessages.slice();
this.needSingleMessages.length = 0;
const splitted = appMessagesIDsManager.splitMessageIDsByChannels(mids);
let promises: Promise<void>[] = [];
Object.keys(splitted.msgIDs).forEach((channelID: number | string) => {
channelID = +channelID;
const msgIDs: InputMessage[] = splitted.msgIDs[channelID].map((msgID: number) => {
return {
_: 'inputMessageID',
id: msgID
};
});
let promise: Promise<MethodDeclMap['channels.getMessages']['res'] | MethodDeclMap['messages.getMessages']['res']>;
if(channelID > 0) {
promise = apiManager.invokeApi('channels.getMessages', {
channel: appChatsManager.getChannelInput(channelID),
id: msgIDs
});
} else {
promise = apiManager.invokeApi('messages.getMessages', {
id: msgIDs
return this.fetchSingleMessagesPromise = new Promise((resolve) => {
setTimeout(() => {
const mids = this.needSingleMessages.slice();
this.needSingleMessages.length = 0;
const splitted = appMessagesIDsManager.splitMessageIDsByChannels(mids);
let promises: Promise<void>[] = [];
Object.keys(splitted.msgIDs).forEach((channelID: number | string) => {
channelID = +channelID;
const msgIDs: InputMessage[] = splitted.msgIDs[channelID].map((msgID: number) => {
return {
_: 'inputMessageID',
id: msgID
};
});
let promise: Promise<MethodDeclMap['channels.getMessages']['res'] | MethodDeclMap['messages.getMessages']['res']>;
if(channelID > 0) {
promise = apiManager.invokeApi('channels.getMessages', {
channel: appChatsManager.getChannelInput(channelID),
id: msgIDs
});
} else {
promise = apiManager.invokeApi('messages.getMessages', {
id: msgIDs
});
}
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]);
}));
});
}
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]);
}));
});
this.fetchSingleMessagesPromise = Promise.all(promises).finally(() => {
this.fetchSingleMessagesTimeout = 0;
this.fetchSingleMessagesPromise = null;
if(this.needSingleMessages.length) this.fetchSingleMessages();
Promise.all(promises).finally(() => {
this.fetchSingleMessagesPromise = null;
if(this.needSingleMessages.length) this.fetchSingleMessages();
resolve();
});
}, 0);
});
}
public wrapSingleMessage(msgID: number, overwrite = false) {
public wrapSingleMessage(msgID: number, overwrite = false): Promise<void> {
if(this.messagesStorage[msgID] && !overwrite) {
$rootScope.$broadcast('messages_downloaded', [msgID]);
return Promise.resolve();
} else if(this.needSingleMessages.indexOf(msgID) == -1) {
this.needSingleMessages.push(msgID);
if(this.fetchSingleMessagesTimeout == 0) {
this.fetchSingleMessagesTimeout = window.setTimeout(this.fetchSingleMessages.bind(this), 10);
}
return this.fetchSingleMessages();
}
}

34
src/lib/appManagers/appPhotosManager.ts

@ -1,13 +1,17 @@ @@ -1,13 +1,17 @@
import { calcImageInBox, isObject } from "../utils";
import { calcImageInBox, isObject, safeReplaceArrayInObject } from "../utils";
import { bytesFromHex, getFileNameByLocation } from "../bin_utils";
import appDownloadManager from "./appDownloadManager";
import { isSafari } from "../../helpers/userAgent";
import { FileLocation, InputFileLocation, Photo, PhotoSize } from "../../layer";
import { MyDocument } from "./appDocsManager";
import { CancellablePromise } from "../../helpers/cancellablePromise";
import referenceDatabase, { ReferenceContext } from "../mtproto/referenceDatabase";
export type MyPhoto = Photo.photo;
// TIMES = 2 DUE TO SIDEBAR AND CHAT
//let TEST_FILE_REFERENCE = "5440692274120994569", TEST_FILE_REFERENCE_TIMES = 2;
export class AppPhotosManager {
private photos: {
[id: string]: MyPhoto
@ -34,20 +38,28 @@ export class AppPhotosManager { @@ -34,20 +38,28 @@ export class AppPhotosManager {
this.windowH = document.body.scrollHeight;
}
public savePhoto(photo: Photo, context?: any) {
public savePhoto(photo: Photo, context?: ReferenceContext) {
if(photo._ == 'photoEmpty') return undefined;
if(this.photos[photo.id]) return Object.assign(this.photos[photo.id], photo);
/* if(photo.id == TEST_FILE_REFERENCE) {
console.warn('Testing FILE_REFERENCE_EXPIRED');
const bytes = [2, 67, 175, 43, 190, 0, 0, 20, 62, 95, 111, 33, 45, 99, 220, 116, 218, 11, 167, 127, 213, 18, 127, 32, 243, 202, 117, 80, 30];
//photo.file_reference = new Uint8Array(bytes);
photo.file_reference = bytes;
if(!--TEST_FILE_REFERENCE_TIMES) {
TEST_FILE_REFERENCE = '';
}
} */
/* if(context) {
Object.assign(photo, context);
} */ // warning
if(!photo.id) {
console.warn('no apiPhoto.id', photo);
} else this.photos[photo.id] = photo;
const oldPhoto = this.photos[photo.id];
safeReplaceArrayInObject('file_reference', oldPhoto, photo);
referenceDatabase.saveContext(photo.file_reference, context);
if(oldPhoto) {
return Object.assign(oldPhoto, photo);
}
return photo;
return this.photos[photo.id] = photo;
}
public choosePhotoSize(photo: MyPhoto | MyDocument, width = 0, height = 0) {

33
src/lib/appManagers/appStateManager.ts

@ -8,8 +8,10 @@ import apiUpdatesManager from './apiUpdatesManager'; @@ -8,8 +8,10 @@ import apiUpdatesManager from './apiUpdatesManager';
import { copy } from '../utils';
import { logger } from '../logger';
import type { AppStickersManager } from './appStickersManager';
import { App } from '../mtproto/mtproto_config';
const REFRESH_EVERY = 24 * 60 * 60 * 1000; // 1 day
const STATE_VERSION = App.version;
type State = Partial<{
dialogs: Dialog[],
@ -24,7 +26,8 @@ type State = Partial<{ @@ -24,7 +26,8 @@ type State = Partial<{
recentEmoji: string[],
topPeers: number[],
recentSearch: number[],
stickerSets: AppStickersManager['stickerSets']
stickerSets: AppStickersManager['stickerSets'],
version: typeof STATE_VERSION
}>;
const REFRESH_KEYS = ['dialogs', 'allDialogsLoaded', 'messages', 'contactsList', 'stateCreatedTime',
@ -45,19 +48,26 @@ export class AppStateManager { @@ -45,19 +48,26 @@ export class AppStateManager {
return this.loaded = new Promise((resolve) => {
AppStorage.get<State>('state').then((state) => {
const time = Date.now();
if((state?.stateCreatedTime ?? 0) + REFRESH_EVERY < time) {
this.log('will refresh state', state.stateCreatedTime, time);
REFRESH_KEYS.forEach(key => {
delete state[key];
});
//state = {};
if(state) {
if(state?.version != STATE_VERSION) {
state = {};
} else if((state?.stateCreatedTime ?? 0) + REFRESH_EVERY < time) {
this.log('will refresh state', state.stateCreatedTime, time);
REFRESH_KEYS.forEach(key => {
delete state[key];
});
//state = {};
}
}
// will not throw error because state can be `FALSE`
const {dialogs, allDialogsLoaded, peers, messages, contactsList, maxSeenMsgID, updates, filters} = state;
this.state = state || {};
this.state.peers = peers || {};
this.state.version = STATE_VERSION;
// ??= doesn't compiles
if(!this.state.hasOwnProperty('stateCreatedTime')) {
this.state.stateCreatedTime = Date.now();
}
@ -100,13 +110,6 @@ export class AppStateManager { @@ -100,13 +110,6 @@ export class AppStateManager {
} */
appMessagesManager.saveMessages(messages);
// FIX FILE_REFERENCE_EXPIRED KOSTIL'1999
for(let message of messages) {
if(message.media) {
appMessagesManager.wrapSingleMessage(message.mid, true);
}
}
}
if(allDialogsLoaded) {

3
src/lib/appManagers/appWebPagesManager.ts

@ -2,6 +2,7 @@ import { $rootScope, safeReplaceObject } from "../utils"; @@ -2,6 +2,7 @@ import { $rootScope, safeReplaceObject } from "../utils";
import appPhotosManager from "./appPhotosManager";
import appDocsManager from "./appDocsManager";
import { RichTextProcessor } from "../richtextprocessor";
import { ReferenceContext } from "../mtproto/referenceDatabase";
class AppWebPagesManager {
webpages: any = {};
@ -19,7 +20,7 @@ class AppWebPagesManager { @@ -19,7 +20,7 @@ class AppWebPagesManager {
});
}
public saveWebPage(apiWebPage: any, messageID?: number, mediaContext?: any) {
public saveWebPage(apiWebPage: any, messageID?: number, mediaContext?: ReferenceContext) {
if(apiWebPage.photo && apiWebPage.photo._ === 'photo') {
//appPhotosManager.savePhoto(apiWebPage.photo, mediaContext);
apiWebPage.photo = appPhotosManager.savePhoto(apiWebPage.photo, mediaContext);

29
src/lib/bin_utils.ts

@ -5,6 +5,8 @@ @@ -5,6 +5,8 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
import {str2bigInt, divInt_, int2bigInt, bigInt2str, bigInt2bytes} from '../vendor/leemon';
// @ts-ignore
import {BigInteger, SecureRandom} from 'jsbn';
import type { InputFileLocation, FileLocation } from '../layer';
@ -23,11 +25,6 @@ export function gzipUncompress(bytes: ArrayBuffer, toString?: boolean): string | @@ -23,11 +25,6 @@ export function gzipUncompress(bytes: ArrayBuffer, toString?: boolean): string |
}
/// #endif
var _logTimer = Date.now();
export function dT () {
return '[' + ((Date.now() - _logTimer) / 1000).toFixed(3) + ']'
}
export function isObject(object: any) {
return typeof(object) === 'object' && object !== null;
}
@ -294,24 +291,6 @@ export function bufferConcats(...args: any[]) { @@ -294,24 +291,6 @@ export function bufferConcats(...args: any[]) {
return tmp/* .buffer */;
}
export function longToInts(sLong: string) {
var divRem = bigStringInt(sLong).divideAndRemainder(bigint(0x100000000));
return [divRem[0].intValue(), divRem[1].intValue()];
}
export function bytesFromWords(wordArray: {words: number[] | Uint8Array | Uint32Array, sigBytes: number}) {
var words = wordArray.words;
var sigBytes = wordArray.sigBytes;
var bytes = [];
for(var i = 0; i < sigBytes; i++) {
bytes.push((words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
}
return bytes;
}
export function bytesFromWordss(input: Uint32Array) {
var o = [];
for(var i = 0; i < input.length * 4; i++) {
@ -336,10 +315,6 @@ export function bytesToWordss(input: ArrayBuffer | Uint8Array) { @@ -336,10 +315,6 @@ export function bytesToWordss(input: ArrayBuffer | Uint8Array) {
return new Uint32Array(words);
}
export function longToBytes(sLong: string) {
return bytesFromWords({words: longToInts(sLong), sigBytes: 8}).reverse();
}
export function longFromInts(high: number, low: number) {
return bigint(high).shiftLeft(32).add(bigint(low)).toString(10);
}

1
src/lib/config.ts

@ -1,4 +1,3 @@ @@ -1,4 +1,3 @@
import EventListenerBase from '../helpers/eventListenerBase';
import mediaSizes from '../helpers/mediaSizes';
/*!
* Webogram v0.7.0 - messaging web application for MTProto

29
src/lib/crypto/crypto_utils.ts

@ -7,17 +7,32 @@ import pako from 'pako/dist/pako_inflate.min.js'; @@ -7,17 +7,32 @@ import pako from 'pako/dist/pako_inflate.min.js';
import {str2bigInt, bpe, equalsInt, greater,
copy_, eGCD_, add_, rightShift_, sub_, copyInt_, isZero,
// @ts-ignore
divide_, one, bigInt2str, powMod} from 'leemon';//from 'leemon';
divide_, one, bigInt2str, powMod, bigInt2bytes} from '../../vendor/leemon';//from 'leemon';
// @ts-ignore
import {BigInteger} from 'jsbn';
import { addPadding, bytesToHex, bytesFromHex, nextRandomInt, bytesFromBigInt, dT, bytesFromWords, bytesToWordss, bytesFromWordss } from '../bin_utils';
import { addPadding, bytesToHex, bytesFromHex, nextRandomInt, bytesFromBigInt, bytesToWordss, bytesFromWordss } from '../bin_utils';
export function bytesFromLeemonBigInt(bigInt: BigInteger) {
var str = bigInt2str(bigInt, 16);
return bytesFromHex(str);
export function longToBytes(sLong: string) {
/* let perf = performance.now();
for(let i = 0; i < 1000000; ++i) {
bytesFromWords({words: longToInts(sLong), sigBytes: 8}).reverse();
}
console.log('longToBytes JSBN', sLong, performance.now() - perf);
//const bytes = bytesFromWords({words: longToInts(sLong), sigBytes: 8}).reverse();
perf = performance.now();
for(let i = 0; i < 1000000; ++i) {
bigInt2bytes(str2bigInt(sLong, 10));
}
console.log('longToBytes LEEMON', sLong, performance.now() - perf); */
const bytes = bigInt2bytes(str2bigInt(sLong, 10), false);
//console.log('longToBytes', bytes, b);
return bytes;
}
export function sha1HashSync(bytes: number[] | ArrayBuffer | Uint8Array) {
@ -225,7 +240,7 @@ export function pqPrimeLeemon(what: any) { @@ -225,7 +240,7 @@ export function pqPrimeLeemon(what: any) {
// console.log(dT(), 'done', bigInt2str(what, 10), bigInt2str(P, 10), bigInt2str(Q, 10))
return [bytesFromLeemonBigInt(P), bytesFromLeemonBigInt(Q), it];
return [bigInt2bytes(P), bigInt2bytes(Q), it];
}
export function bytesModPow(x: any, y: any, m: any) {

37
src/lib/crypto/srp.ts

@ -1,8 +1,7 @@ @@ -1,8 +1,7 @@
import { bufferConcats, bytesToHex, bytesFromHex, bufferConcat, bytesXor } from "../bin_utils";
import CryptoWorker from "../crypto/cryptoworker";
import {str2bigInt, isZero,
// @ts-ignore
bigInt2str, powMod, int2bigInt, mult, mod, sub, bitSize, negative, add, greater} from 'leemon';
bigInt2str, powMod, int2bigInt, mult, mod, sub, bitSize, negative, add, greater} from '../../vendor/leemon';
import {logger, LogLevels} from '../logger';
import { AccountPassword, PasswordKdfAlgo } from "../../layer";
@ -41,8 +40,8 @@ export async function computeSRP(password: string, state: AccountPassword) { @@ -41,8 +40,8 @@ export async function computeSRP(password: string, state: AccountPassword) {
let B = str2bigInt(bytesToHex(state.srp_B), 16);
let g = int2bigInt(algo.g, 32, 256);
log('p', bigInt2str(p, 16));
log('B', bigInt2str(B, 16));
//log('p', bigInt2str(p, 16));
//log('B', bigInt2str(B, 16));
/* if(B.compareTo(BigInteger.ZERO) < 0) {
console.error('srp_B < 0')
@ -67,7 +66,7 @@ export async function computeSRP(password: string, state: AccountPassword) { @@ -67,7 +66,7 @@ export async function computeSRP(password: string, state: AccountPassword) {
let pw_hash = await makePasswordHash(password, new Uint8Array(algo.salt1), new Uint8Array(algo.salt2));
let x = str2bigInt(bytesToHex(new Uint8Array(pw_hash)), 16);
log('computed pw_hash:', pw_hash, x, bytesToHex(new Uint8Array(pw_hash)));
//log('computed pw_hash:', pw_hash, x, bytesToHex(new Uint8Array(pw_hash)));
var padArray = function(arr: any[], len: number, fill = 0) {
return Array(len).fill(fill).concat(arr).slice(-len);
@ -77,25 +76,25 @@ export async function computeSRP(password: string, state: AccountPassword) { @@ -77,25 +76,25 @@ export async function computeSRP(password: string, state: AccountPassword) {
let gForHash = padArray(bytesFromHex(bigInt2str(g, 16)), 256); // like uint8array
let b_for_hash = padArray(bytesFromHex(bigInt2str(B, 16)), 256);
log(bytesToHex(pForHash));
/* log(bytesToHex(pForHash));
log(bytesToHex(gForHash));
log(bytesToHex(b_for_hash));
log(bytesToHex(b_for_hash)); */
let g_x = powMod(g, x, p);
log('g_x', bigInt2str(g_x, 16));
//log('g_x', bigInt2str(g_x, 16));
let k: any = await CryptoWorker.sha256Hash(bufferConcat(pForHash, gForHash));
k = str2bigInt(bytesToHex(new Uint8Array(k)), 16);
log('k', bigInt2str(k, 16));
//log('k', bigInt2str(k, 16));
// kg_x = (k * g_x) % p
let kg_x = mod(mult(k, g_x), p);
// good
log('kg_x', bigInt2str(kg_x, 16));
//log('kg_x', bigInt2str(kg_x, 16));
let is_good_mod_exp_first = (modexp: any, prime: any) => {
let diff = sub(prime, modexp);
@ -128,10 +127,10 @@ export async function computeSRP(password: string, state: AccountPassword) { @@ -128,10 +127,10 @@ export async function computeSRP(password: string, state: AccountPassword) {
//console.log('ITERATION');
log('g a p', bigInt2str(g, 16), bigInt2str(a, 16), bigInt2str(p, 16));
//log('g a p', bigInt2str(g, 16), bigInt2str(a, 16), bigInt2str(p, 16));
const A = powMod(g, a, p);
log('A MODPOW', bigInt2str(A, 16));
//log('A MODPOW', bigInt2str(A, 16));
if(is_good_mod_exp_first(A, p)) {
const a_for_hash = bytesFromHex(bigInt2str(A, 16));
@ -147,9 +146,9 @@ export async function computeSRP(password: string, state: AccountPassword) { @@ -147,9 +146,9 @@ export async function computeSRP(password: string, state: AccountPassword) {
let {a, a_for_hash, u} = await generate_and_check_random();
log('a', bigInt2str(a, 16));
/* log('a', bigInt2str(a, 16));
log('a_for_hash', bytesToHex(a_for_hash));
log('u', bigInt2str(u, 16));
log('u', bigInt2str(u, 16)); */
// g_b = (B - kg_x) % p
/* log('B - kg_x', bigInt2str(sub(B, kg_x), 16));
@ -158,26 +157,26 @@ export async function computeSRP(password: string, state: AccountPassword) { @@ -158,26 +157,26 @@ export async function computeSRP(password: string, state: AccountPassword) {
let g_b;
if(!greater(B, kg_x)) {
log('negative');
//log('negative');
g_b = add(B, p);
} else g_b = B;
g_b = mod(sub(g_b, kg_x), p);
/* let g_b = sub(B, kg_x);
if(negative(g_b)) g_b = add(g_b, p); */
log('g_b', bigInt2str(g_b, 16));
//log('g_b', bigInt2str(g_b, 16));
/* if(!is_good_mod_exp_first(g_b, p))
throw new Error('bad g_b'); */
let ux = mult(u, x);
log('u and x multiply', bigInt2str(u, 16), bigInt2str(x, 16), bigInt2str(ux, 16));
//log('u and x multiply', bigInt2str(u, 16), bigInt2str(x, 16), bigInt2str(ux, 16));
let a_ux = add(a, ux);
let S = powMod(g_b, a_ux, p);
let K = await CryptoWorker.sha256Hash(padArray(bytesFromHex(bigInt2str(S, 16)), 256));
log('K', bytesToHex(K), new Uint32Array(new Uint8Array(K).buffer));
//log('K', bytesToHex(K), new Uint32Array(new Uint8Array(K).buffer));
let h1 = await CryptoWorker.sha256Hash(pForHash);
let h2 = await CryptoWorker.sha256Hash(gForHash);
@ -201,7 +200,7 @@ export async function computeSRP(password: string, state: AccountPassword) { @@ -201,7 +200,7 @@ export async function computeSRP(password: string, state: AccountPassword) {
};
log('out', bytesToHex(out.A), bytesToHex(out.M1));
//log('out', bytesToHex(out.A), bytesToHex(out.M1));
return out;
/* console.log(gForHash, pForHash, bForHash); */

53
src/lib/lottieLoader.ts

@ -3,7 +3,7 @@ import animationIntersector from "../components/animationIntersector"; @@ -3,7 +3,7 @@ import animationIntersector from "../components/animationIntersector";
import apiManager from "./mtproto/mtprotoworker";
import EventListenerBase from "../helpers/eventListenerBase";
import mediaSizes from "../helpers/mediaSizes";
import { isApple, isSafari } from "../helpers/userAgent";
import { isAndroid, isApple, isAppleMobile, isSafari } from "../helpers/userAgent";
import RLottieWorker from 'worker-loader!./rlottie/rlottie.worker';
let convert = (value: number) => {
@ -93,7 +93,7 @@ export class RLottiePlayer extends EventListenerBase<{ @@ -93,7 +93,7 @@ export class RLottiePlayer extends EventListenerBase<{
// Skip ratio
let skipRatio: number;
if(options.skipRatio !== undefined) skipRatio = options.skipRatio;
else if(mediaSizes.isMobile && this.width < 100 && this.height < 100) {
else if((isAndroid || isAppleMobile) && this.width < 100 && this.height < 100) {
skipRatio = 0.5;
}
@ -106,15 +106,20 @@ export class RLottiePlayer extends EventListenerBase<{ @@ -106,15 +106,20 @@ export class RLottiePlayer extends EventListenerBase<{
if(options.needUpscale) {
this.width = Math.round(this.width * pixelRatio);
this.height = Math.round(this.height * pixelRatio);
} else if(pixelRatio > 1 && this.width > 100 && this.height > 100) {
if(isApple || !mediaSizes.isMobile) {
/* this.width = Math.round(this.width * (pixelRatio - 1));
this.height = Math.round(this.height * (pixelRatio - 1)); */
this.width = Math.round(this.width * pixelRatio);
this.height = Math.round(this.height * pixelRatio);
} else if(pixelRatio > 2.5) {
this.width = Math.round(this.width * (pixelRatio - 1.5));
this.height = Math.round(this.height * (pixelRatio - 1.5));
} else if(pixelRatio > 1) {
if(this.width > 100 && this.height > 100) {
if(isApple || !mediaSizes.isMobile) {
/* this.width = Math.round(this.width * (pixelRatio - 1));
this.height = Math.round(this.height * (pixelRatio - 1)); */
this.width = Math.round(this.width * pixelRatio);
this.height = Math.round(this.height * pixelRatio);
} else if(pixelRatio > 2.5) {
this.width = Math.round(this.width * (pixelRatio - 1.5));
this.height = Math.round(this.height * (pixelRatio - 1.5));
}
} else {
this.width = Math.round(this.width * Math.max(1.5, pixelRatio - 1.5));
this.height = Math.round(this.height * Math.max(1.5, pixelRatio - 1.5));
}
}
}
@ -159,12 +164,14 @@ export class RLottiePlayer extends EventListenerBase<{ @@ -159,12 +164,14 @@ export class RLottiePlayer extends EventListenerBase<{
}
public loadFromData(jsonString: string) {
this.sendQuery('loadFromData', jsonString, this.width, this.height);
this.sendQuery('loadFromData', jsonString, this.width, this.height/* , this.canvas.transferControlToOffscreen() */);
}
public play() {
if(!this.paused) return;
//return;
//console.log('RLOTTIE PLAY' + this.reqId);
this.paused = false;
@ -373,7 +380,9 @@ export class RLottiePlayer extends EventListenerBase<{ @@ -373,7 +380,9 @@ export class RLottiePlayer extends EventListenerBase<{
console.timeEnd('cache' + this.reqId); */
//console.log('cached');
//return;
/* this.el.innerHTML = '';
this.el.append(this.canvas);
return; */
this.requestFrame(0);
this.setListenerResult('ready');
@ -444,29 +453,29 @@ class QueryableWorker extends EventListenerBase<any> { @@ -444,29 +453,29 @@ class QueryableWorker extends EventListenerBase<any> {
}
public sendQuery(queryMethod: string, ...args: any[]) {
var args = Array.prototype.slice.call(arguments, 1);
if(isSafari) {
this.worker.postMessage({
'queryMethod': queryMethod,
'queryMethodArguments': args
});
} else {
var transfer = [];
for(var i = 0; i < args.length; i++) {
if(args[i] instanceof ArrayBuffer) {
transfer.push(args[i]);
//const transfer: (ArrayBuffer | OffscreenCanvas)[] = [];
const transfer: ArrayBuffer[] = [];
args.forEach(arg => {
if(arg instanceof ArrayBuffer) {
transfer.push(arg);
}
if(args[i].buffer && args[i].buffer instanceof ArrayBuffer) {
transfer.push(args[i].buffer);
if(arg.buffer && arg.buffer instanceof ArrayBuffer) {
transfer.push(arg.buffer);
}
}
});
//console.log('transfer', transfer);
this.worker.postMessage({
'queryMethod': queryMethod,
'queryMethodArguments': args
}, transfer);
}, transfer as PostMessageOptions);
}
}
}

8
src/lib/mediaPlayer.ts

@ -150,7 +150,7 @@ export class MediaProgressLine extends ProgressLine { @@ -150,7 +150,7 @@ export class MediaProgressLine extends ProgressLine {
clearTimeout(this.stopAndScrubTimeout);
}
this.stopAndScrubTimeout = setTimeout(() => {
this.stopAndScrubTimeout = window.setTimeout(() => {
!this.media.paused && this.media.pause();
this.stopAndScrubTimeout = 0;
}, 150);
@ -412,7 +412,7 @@ export default class VideoPlayer { @@ -412,7 +412,7 @@ export default class VideoPlayer {
let showControlsTimeout = 0;
const t = () => {
showControlsTimeout = setTimeout(() => {
showControlsTimeout = window.setTimeout(() => {
showControlsTimeout = 0;
player.classList.remove('show-controls');
}, 3e3);
@ -489,7 +489,7 @@ export default class VideoPlayer { @@ -489,7 +489,7 @@ export default class VideoPlayer {
video.addEventListener('play', () => {
iconVolume.style.display = 'none';
updateInterval = setInterval(() => {
updateInterval = window.setInterval(() => {
//elapsed += 0.02; // Increase with timer interval
if(video.currentTime != prevTime) {
elapsed = video.currentTime; // Update if getCurrentTime was changed
@ -555,7 +555,7 @@ export default class VideoPlayer { @@ -555,7 +555,7 @@ export default class VideoPlayer {
let prevTime = 0;
if(skin === 'circle') {
updateInterval = setInterval(() => {
updateInterval = window.setInterval(() => {
if(video.currentTime != prevTime) {
elapsed = video.currentTime; // Update if getCurrentTime was changed
prevTime = video.currentTime;

1
src/lib/mtproto/apiFileManager.ts

@ -232,6 +232,7 @@ export class ApiFileManager { @@ -232,6 +232,7 @@ export class ApiFileManager {
let resolved = false;
let cacheFileWriter: ReturnType<typeof FileManager['getFakeFileWriter']>;
let errorHandler = (error: any) => {
delete this.cachedDownloadPromises[fileName];
deferred.reject(error);
errorHandler = () => {};

25
src/lib/mtproto/authorizer.ts

@ -10,6 +10,7 @@ import { BigInteger } from "jsbn"; @@ -10,6 +10,7 @@ import { BigInteger } from "jsbn";
import CryptoWorker from "../crypto/cryptoworker";
import { logger, LogLevels } from "../logger";
//import { bigInt2str, greater, int2bigInt, one, powMod, str2bigInt, sub } from "../../vendor/leemon";
/* let fNewNonce: any = bytesFromHex('8761970c24cb2329b5b2459752c502f3057cb7e8dbab200e526e8767fdc73b3c').reverse();
let fNonce: any = bytesFromHex('b597720d11faa5914ef485c529cde414').reverse();
@ -42,8 +43,8 @@ type AuthOptions = { @@ -42,8 +43,8 @@ type AuthOptions = {
b?: number[],
g?: number,
gA?: number,
dhPrime?: number,
gA?: Uint8Array,
dhPrime?: Uint8Array,
tmpAesKey?: Uint8Array,
tmpAesIv?: Uint8Array,
@ -64,7 +65,7 @@ export class Authorizer { @@ -64,7 +65,7 @@ export class Authorizer {
private log: ReturnType<typeof logger>;
constructor() {
this.log = logger(`AUTHORIZER`/* , LogLevels.error | LogLevels.log */);
this.log = logger(`AUTHORIZER`, LogLevels.error | LogLevels.log);
}
public mtpSendPlainRequest(dcID: number, requestArray: Uint8Array) {
@ -365,8 +366,8 @@ export class Authorizer { @@ -365,8 +366,8 @@ export class Authorizer {
timeManager.applyServerTime(auth.serverTime, auth.localTime);
}
public mtpVerifyDhParams(g: number, dhPrime: any, gA: any) {
this.log('Verifying DH params');
public mtpVerifyDhParams(g: number, dhPrime: Uint8Array, gA: Uint8Array) {
this.log('Verifying DH params', g, dhPrime, gA);
var dhPrimeHex = bytesToHex(dhPrime);
if(g != 3 || dhPrimeHex !== 'c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b') {
// The verified value is from https://core.telegram.org/mtproto/security_guidelines
@ -375,13 +376,20 @@ export class Authorizer { @@ -375,13 +376,20 @@ export class Authorizer {
this.log('dhPrime cmp OK');
var gABigInt = new BigInteger(bytesToHex(gA), 16);
//const _gABigInt = str2bigInt(bytesToHex(gA), 16);
var dhPrimeBigInt = new BigInteger(dhPrimeHex, 16);
//const _dhPrimeBigInt = str2bigInt(dhPrimeHex, 16);
//this.log('gABigInt.compareTo(BigInteger.ONE) <= 0', gABigInt.compareTo(BigInteger.ONE), BigInteger.ONE.compareTo(BigInteger.ONE), greater(_gABigInt, one));
if(gABigInt.compareTo(BigInteger.ONE) <= 0) {
//if(!greater(_gABigInt, one)) {
throw new Error('[MT] DH params are not verified: gA <= 1');
}
/* this.log('gABigInt.compareTo(dhPrimeBigInt.subtract(BigInteger.ONE)) >= 0', gABigInt.compareTo(dhPrimeBigInt.subtract(BigInteger.ONE)),
greater(gABigInt, sub(_dhPrimeBigInt, one))); */
if(gABigInt.compareTo(dhPrimeBigInt.subtract(BigInteger.ONE)) >= 0) {
//if(greater(gABigInt, sub(_dhPrimeBigInt, one))) {
throw new Error('[MT] DH params are not verified: gA >= dhPrime - 1');
}
this.log('1 < gA < dhPrime-1 OK');
@ -389,8 +397,13 @@ export class Authorizer { @@ -389,8 +397,13 @@ export class Authorizer {
var two = new BigInteger(/* null */'');
two.fromInt(2);
//const _two = int2bigInt(2, 10, 0);
//this.log('_two:', bigInt2str(_two, 16), two.toString(16));
var twoPow = two.pow(2048 - 64);
//const _twoPow = powMod(_two, int2bigInt(2048 - 64, 10, 0), null);
//this.log('twoPow:', twoPow.toString(16), bigInt2str(_twoPow, 16));
// this.log('gABigInt.compareTo(twoPow) < 0');
if(gABigInt.compareTo(twoPow) < 0) {
throw new Error('[MT] DH params are not verified: gA < 2^{2048-64}');
}

5
src/lib/mtproto/networker.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import {isObject} from '../bin_utils';
import {convertToUint8Array,
bufferConcat, nextRandomInt, bytesToHex, longToBytes,
bufferConcat, nextRandomInt, bytesToHex,
bytesCmp, bigStringInt} from '../bin_utils';
import {TLDeserialization, TLSerialization} from './tl_utils';
import CryptoWorker from '../crypto/cryptoworker';
@ -15,6 +15,7 @@ import HTTP from './transports/http'; @@ -15,6 +15,7 @@ import HTTP from './transports/http';
import { logger, LogLevels } from '../logger';
import { Modes, App } from './mtproto_config';
import { InvokeApiOptions } from '../../types';
import { longToBytes } from '../crypto/crypto_utils';
//console.error('networker included!', new Error().stack);
@ -948,7 +949,7 @@ class MTPNetworker { @@ -948,7 +949,7 @@ class MTPNetworker {
clearTimeout(this.nextReqTimeout);
this.nextReqTimeout = 0;
if(delay > 0) {
this.nextReqTimeout = setTimeout(this.performScheduledRequest.bind(this), delay || 0);
this.nextReqTimeout = self.setTimeout(this.performScheduledRequest.bind(this), delay || 0);
} else {
setTimeout(this.performScheduledRequest.bind(this), 0);
}

64
src/lib/mtproto/referenceDatabase.ts

@ -1,9 +1,71 @@ @@ -1,9 +1,71 @@
import { Photo } from "../../layer";
import { deepEqual } from "../utils";
export type ReferenceContext = ReferenceContext.referenceContextProfilePhoto | ReferenceContext.referenceContextMessage;
export namespace ReferenceContext {
export type referenceContextProfilePhoto = {
type: 'profilePhoto',
peerID: number
};
export type referenceContextMessage = {
type: 'message',
messageID: number
};
}
export type ReferenceBytes = Photo.photo['file_reference'];
//type ReferenceBytes = Uint8Array;
class ReferenceDatabase {
private contexts: Map<ReferenceBytes, Set<ReferenceContext>> = new Map();
//private references: Map<ReferenceBytes, number[]> = new Map();
public saveContext(reference: ReferenceBytes, context: ReferenceContext) {
const contexts = this.contexts.get(reference) ?? new Set();
for(const _context of contexts) {
if(deepEqual(_context, context)) {
return;
}
}
contexts.add(context);
this.contexts.set(reference, contexts);
}
public getContext(reference: ReferenceBytes): ReferenceContext {
const contexts = this.contexts.get(reference);
return contexts ? contexts.values().next().value : undefined;
}
public deleteContext(reference: ReferenceBytes, context: ReferenceContext) {
const contexts = this.contexts.get(reference);
if(contexts) {
for(const _context of contexts) {
if(deepEqual(_context, context)) {
contexts.delete(_context);
if(!contexts.size) {
this.contexts.delete(reference);
}
return true;
}
}
}
return false;
}
/* public replaceReference(oldReference: ReferenceBytes, newReference: ReferenceBytes) {
const contexts = this.contexts.get(oldReference);
if(contexts) {
this.contexts.delete(oldReference);
this.contexts.set(newReference, contexts);
}
} */
}
const referenceDatabase = new ReferenceDatabase();
// @ts-ignore
if(process.env.NODE_ENV != 'production') {
(window as any).referenceDatabase = referenceDatabase;
}

12
src/lib/polyfill.ts

@ -25,12 +25,13 @@ Uint8Array.prototype.concat = function(...args: Array<Uint8Array | ArrayBuffer | @@ -25,12 +25,13 @@ Uint8Array.prototype.concat = function(...args: Array<Uint8Array | ArrayBuffer |
return bufferConcats(this, ...args);
};
Uint8Array.prototype.toString = function() {
/* Uint8Array.prototype.toString = function() {
return String.fromCharCode.apply(null, [...this]);
};
}; */
Uint8Array.prototype.toJSON = function() {
return [...this];
//return [...this];
return {type: 'bytes', value: [...this]};
};
Array.prototype.forEachReverse = function<T>(callback: (value: T, index?: number, array?: Array<T>) => void) {
@ -62,8 +63,9 @@ declare global { @@ -62,8 +63,9 @@ declare global {
hex: string;
randomize: () => Uint8Array,
concat: (...args: Array<Uint8Array | ArrayBuffer | number[]>) => Uint8Array,
toString: () => string,
toJSON: () => number[],
//toString: () => string,
//toJSON: () => number[],
toJSON: () => {type: 'bytes', value: number[]},
}
interface Array<T> {

22
src/lib/rlottie/rlottie.worker.ts

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
importScripts('rlottie-wasm.js');
//import Module, { allocate, intArrayFromString } from './rlottie-wasm';
const _Module = Module as any;
const _Module = (self as any).Module as any;
const DEFAULT_FPS = 60;
@ -11,13 +11,23 @@ export class RLottieItem { @@ -11,13 +11,23 @@ export class RLottieItem {
private frameCount = 0;
private dead = false;
//private context: OffscreenCanvasRenderingContext2D;
constructor(private reqId: number, jsString: string, private width: number, private height: number, private fps: number) {
constructor(private reqId: number, jsString: string, private width: number, private height: number, private fps: number/* , private canvas: OffscreenCanvas */) {
this.fps = Math.max(1, Math.min(60, fps || DEFAULT_FPS));
//this.context = canvas.getContext('2d');
this.init(jsString);
reply('loaded', this.reqId, this.frameCount, this.fps);
/* let frame = 0;
setInterval(() => {
if(frame >= this.frameCount) frame = 0;
let _frame = frame++;
this.render(_frame, null);
}, 1000 / this.fps); */
}
private init(jsString: string) {
@ -56,6 +66,8 @@ export class RLottieItem { @@ -56,6 +66,8 @@ export class RLottieItem {
} else {
clamped.set(data);
}
//this.context.putImageData(new ImageData(clamped, this.width, this.height), 0, 0);
reply('frame', this.reqId, frameNo, clamped);
} catch(e) {
@ -94,13 +106,13 @@ class RLottieWorker { @@ -94,13 +106,13 @@ class RLottieWorker {
const worker = new RLottieWorker();
Module.onRuntimeInitialized = function() {
_Module.onRuntimeInitialized = function() {
worker.init();
};
const items: {[reqId: string]: RLottieItem} = {};
const queryableFunctions = {
loadFromData: function(reqId: number, jsString: string, width: number, height: number) {
loadFromData: function(reqId: number, jsString: string, width: number, height: number/* , canvas: OffscreenCanvas */) {
try {
// ! WARNING, с этой проверкой не все стикеры работают, например - ДУРКА
/* if(!/"tgs":\s*?1./.test(jsString)) {
@ -112,7 +124,7 @@ const queryableFunctions = { @@ -112,7 +124,7 @@ const queryableFunctions = {
//console.log('Rendering sticker:', reqId, frameRate, 'now rendered:', Object.keys(items).length);
items[reqId] = new RLottieItem(reqId, jsString, width, height, frameRate);
items[reqId] = new RLottieItem(reqId, jsString, width, height, frameRate/* , canvas */);
} catch(e) {
console.error('Invalid file for sticker:', jsString);
reply('error', reqId, e);

24
src/lib/storage.ts

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
import { Modes } from './mtproto/mtproto_config';
import { notifySomeone, isWorker } from '../helpers/context';
import { parse, stringify } from '../helpers/json';
class ConfigStorage {
public keyPrefix = '';
@ -16,24 +17,24 @@ class ConfigStorage { @@ -16,24 +17,24 @@ class ConfigStorage {
return this.keyPrefix;
}
get(keys: any, callback: any) {
get(keys: string | string[], callback: any) {
var single = false;
if(!Array.isArray(keys)) {
keys = Array.prototype.slice.call(arguments);
callback = keys.pop();
single = keys.length == 1;
}
var result = [],
value;
var result = [];
var allFound = true;
var prefix = this.storageGetPrefix(),
i, key;
var prefix = this.storageGetPrefix();
for(i = 0; i < keys.length; i++) {
key = keys[i] = prefix + keys[i];
if(key.substr(0, 3) != 'xt_' && this.cache[key] !== undefined) {
for(let key of keys) {
key = prefix + key;
if(this.cache.hasOwnProperty(key)) {
result.push(this.cache[key]);
} else if(this.useLs) {
let value: any;
try {
value = localStorage.getItem(key);
} catch(e) {
@ -41,7 +42,7 @@ class ConfigStorage { @@ -41,7 +42,7 @@ class ConfigStorage {
}
try {
value = (value === undefined || value === null) ? false : JSON.parse(value);
value = (value === undefined || value === null) ? false : parse(value);
} catch(e) {
value = false;
}
@ -68,10 +69,7 @@ class ConfigStorage { @@ -68,10 +69,7 @@ class ConfigStorage {
value = obj[key];
key = prefix + key;
this.cache[key] = value;
value = JSON.stringify(value, (key, value) => {
if(key == 'downloaded' || (key == 'url' && value.indexOf('blob:') === 0)) return undefined;
return value;
});
value = stringify(value);
if(this.useLs) {
try {

22
src/lib/utils.ts

@ -343,6 +343,28 @@ export function safeReplaceObject(wasObject: any, newObject: any) { @@ -343,6 +343,28 @@ export function safeReplaceObject(wasObject: any, newObject: any) {
}
}
/**
* Will be used for FILE_REFERENCE_EXPIRED
* @param key
* @param wasObject
* @param newObject
*/
export function safeReplaceArrayInObject<K>(key: K, wasObject: any, newObject: any) {
if('byteLength' in newObject[key]) { // Uint8Array
newObject[key] = [...newObject[key]];
}
if(wasObject && wasObject[key] != newObject[key]) {
wasObject[key].length = newObject[key].length;
(newObject[key] as any[]).forEach((v, i) => {
wasObject[key][i] = v;
})
/* wasObject[key].set(newObject[key]); */
newObject[key] = wasObject[key];
}
}
export function numberWithCommas(x: number) {
var parts = x.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");

20
src/scripts/generate_mtproto_types.js

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
// @ts-check
const schema = require('./in/schema.json');
const additional = require('./in/schema_additional_params.json');
const schema = require(__dirname + '/in/schema.json');
const additional = require(__dirname + '/in/schema_additional_params.json');
const replace = require(__dirname + '/in/schema_replace_types.json');
const mtproto = schema.API;
@ -10,6 +11,12 @@ for(const constructor of additional) { @@ -10,6 +11,12 @@ for(const constructor of additional) {
});
const realConstructor = mtproto.constructors.find(c => c.predicate == constructor.predicate);
/* constructor.params.forEach(param => {
const index = realConstructor.params.findIndex(_param => _param.predicate == param.predicate);
if(index !== -1) {
realConstructor.params.splice(index, 1);
}
}); */
realConstructor.params.splice(realConstructor.params.length, 0, ...constructor.params);
}
@ -80,7 +87,7 @@ const processParamType = (type) => { @@ -80,7 +87,7 @@ const processParamType = (type) => {
return 'string';
case 'bytes':
return 'Uint8Array | number[]';
return 'Uint8Array';
case 'string':
return 'string';
@ -105,6 +112,10 @@ const processParams = (params, object = {}, parseBooleanFlags = true) => { @@ -105,6 +112,10 @@ const processParams = (params, object = {}, parseBooleanFlags = true) => {
name += '?';
}
if(replace[name]) {
type = replace[name];
}
const processed = processParamType(type);
if(type.includes('?true') && parseBooleanFlags) {
if(!object.pFlags) object.pFlags = {};
@ -238,4 +249,5 @@ for(const method in methodsMap) { @@ -238,4 +249,5 @@ for(const method in methodsMap) {
}
out += `}\n\n`;
require('fs').writeFileSync('./out/layer.d.ts', out);
const path = process.argv[2];
require('fs').writeFileSync((path || __dirname + '/out/') + 'layer.d.ts', out);

26
src/scripts/in/schema_additional_params.json

@ -54,4 +54,30 @@ @@ -54,4 +54,30 @@
{"name": "peerID", "type": "number"},
{"name": "folder_id", "type": "number"}
]
}, {
"predicate": "message",
"params": [
{"name": "mid", "type": "number"},
{"name": "deleted", "type": "boolean"},
{"name": "peerID", "type": "number"},
{"name": "fromID", "type": "number"},
{"name": "grouped_id", "type": "string"},
{"name": "canBeEdited", "type": "boolean"},
{"name": "unread", "type": "true"}
]
}, {
"predicate": "messageService",
"params": [
{"name": "mid", "type": "number"},
{"name": "deleted", "type": "boolean"},
{"name": "peerID", "type": "number"},
{"name": "fromID", "type": "number"},
{"name": "canBeEdited", "type": "boolean"},
{"name": "unread", "type": "true"}
]
}, {
"predicate": "messageEmpty",
"params": [
{"name": "deleted", "type": "boolean"}
]
}]

3
src/scripts/in/schema_replace_types.json

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
{
"file_reference": "Uint8Array | number[]"
}

29
src/scss/partials/_chat.scss

@ -249,15 +249,24 @@ @@ -249,15 +249,24 @@
padding: 0 .5rem;
flex: 0 0 auto;
@include respond-to(handhelds) {
padding-bottom: .5rem;
}
@include respond-to(not-handhelds) {
padding-left: 1rem;
padding-right: 1rem;
padding-bottom: 21px;
}
@include respond-to(handhelds) {
padding-bottom: .5rem;
}
@include respond-to(esg-bottom) {
padding-bottom: .5rem;
.btn-circle {
height: 46px;
width: 46px;
}
}
}
/* @include respond-to(handhelds) {
@ -313,7 +322,7 @@ @@ -313,7 +322,7 @@
align-self: flex-end;
z-index: 2;
}
#btn-send {
color: #9e9e9e;
@ -551,11 +560,15 @@ @@ -551,11 +560,15 @@
position: relative;
z-index: 3;
@include respond-to(handhelds) {
min-height: 46px;
padding: .5px .5rem;
}
}
@include respond-to(esg-bottom) {
min-height: 46px;
padding: .5px .5rem;
}
&:after {
content: '';
@ -697,7 +710,7 @@ @@ -697,7 +710,7 @@
}
html.no-touch &:hover {
background-color: rgba(112, 117, 121, 0.08);
background-color: var(--color-gray-hover);
}
&-border {

4
src/scss/partials/_chatBubble.scss

@ -1776,11 +1776,11 @@ poll-element { @@ -1776,11 +1776,11 @@ poll-element {
}
}
&-line {
/* &-line {
use {
}
}
} */
}
avatar-element {

16
src/scss/partials/_chatlist.scss

@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
input {
--border-width: 1px;
background-color: rgba(112, 117, 121, .08);
background-color: var(--color-gray-hover);
height: 40px;
border-radius: 22px;
border: var(--border-width) solid transparent;
@ -42,7 +42,7 @@ @@ -42,7 +42,7 @@
&:focus {
--border-width: 2px;
background-color: rgba(112, 117, 121, 0);
background-color: transparent;
border: 2px solid $button-primary-background;
& + .tgico {
@ -151,13 +151,19 @@ @@ -151,13 +151,19 @@
}
html.no-touch &:hover {
background: rgba(112, 117, 121, .08);
background: var(--color-gray-hover);
}
}
li.active, li.menu-open {
li.menu-open {
> .rp {
background: rgba(112, 117, 121, 0.08);
background: var(--color-gray-hover);
}
}
@include respond-to(not-handhelds) {
li.active > .rp {
background: var(--color-gray-hover);
}
}

7
src/scss/partials/_emojiDropdown.scss

@ -188,7 +188,7 @@ @@ -188,7 +188,7 @@
}
html.no-touch &:hover {
background-color: rgba(112, 117, 121, .08);
background-color: var(--color-gray-hover);
}
}
}
@ -226,7 +226,7 @@ @@ -226,7 +226,7 @@
> .grid-item {
html.no-touch &:hover {
border-radius: 12px;
background-color: rgba(112, 117, 121, .08);
background-color: var(--color-gray-hover);
}
/* &:nth-child(5n+5) {
@ -243,6 +243,7 @@ @@ -243,6 +243,7 @@
> img {
animation: fadeIn .2s ease forwards;
object-fit: contain;
}
}
}
@ -310,7 +311,7 @@ @@ -310,7 +311,7 @@
&.active {
&:not(.tgico-recent) {
background-color: rgba(112, 117, 121, .08);
background-color: var(--color-gray-hover);
}
}

2
src/scss/partials/_leftSidebar.scss

@ -371,7 +371,7 @@ @@ -371,7 +371,7 @@
}
html.no-touch &:hover {
background: rgba(112, 117, 121, 0.08);
background: var(--color-gray-hover);
cursor: pointer;
}

2
src/scss/partials/_rightSidebar.scss

@ -605,7 +605,7 @@ @@ -605,7 +605,7 @@
&:hover {
border-radius: 12px;
background-color: rgba(112, 117, 121, 0.08);
background-color: var(--color-gray-hover);
}
img {

2
src/scss/partials/_selector.scss

@ -45,7 +45,7 @@ @@ -45,7 +45,7 @@
&-user {
color: #000;
background-color: rgba(112, 117, 121, 0.08);
background-color: var(--color-gray-hover);
font-size: 16px;
padding: 0 17px 0px 0px;
line-height: 31px;

2
src/scss/partials/_slider.scss

@ -38,7 +38,7 @@ $slider-time: .25s; @@ -38,7 +38,7 @@ $slider-time: .25s;
transition: background-color .15s ease-in-out;
&:hover {
background-color: rgba(112, 117, 121, .08);
background-color: var(--color-gray-hover);
}
}

2
src/scss/partials/popups/_popup.scss

@ -102,7 +102,7 @@ @@ -102,7 +102,7 @@
overflow: hidden;
html.no-touch &:hover {
background-color: rgba(112, 117, 121, 0.08);
background-color: var(--color-gray-hover);
}
& + button {

2
src/scss/partials/popups/_stickers.scss

@ -68,7 +68,7 @@ @@ -68,7 +68,7 @@
&:hover {
border-radius: 12px;
background-color: rgba(112, 117, 121, 0.08);
background-color: var(--color-gray-hover);
}
img {

11
src/scss/style.scss

@ -65,10 +65,15 @@ $floating-left-sidebar: 925px; @@ -65,10 +65,15 @@ $floating-left-sidebar: 925px;
@else if $media == esg-top { // topbar + chat input + margin bottom + height of ESG
@media only screen and (min-height: 570px) and (min-width: $small-screen + 1) { @content; }
}
@else if $media == esg-bottom {
@media only screen and (max-height: 569px) { @content; }
}
}
:root {
--color-gray: #c4c9cc;
--color-gray-hover: rgba(112, 117, 121, .08);
--layer-transition: .2s ease-in-out;
//--layer-transition: .3s cubic-bezier(.33, 1, .68, 1);
//--layer-transition: none;
@ -315,7 +320,7 @@ input, textarea { @@ -315,7 +320,7 @@ input, textarea {
}
html.no-touch &:hover {
background-color: rgba(112, 117, 121, .08);
background-color: var(--color-gray-hover);
}
}
@ -798,7 +803,7 @@ hr { @@ -798,7 +803,7 @@ hr {
grid-template-columns: calc(26px + 2rem) 1fr 50px;
html.no-touch &:hover {
background-color: rgba(112, 117, 121, .08);
background-color: var(--color-gray-hover);
}
}
@ -920,7 +925,7 @@ input:focus, button:focus { @@ -920,7 +925,7 @@ input:focus, button:focus {
pointer-events: all !important;
&:not(.btn-primary).menu-open {
background-color: rgba(112, 117, 121, 0.08);
background-color: var(--color-gray-hover);
}
}

2136
src/vendor/leemon.ts vendored

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save