Browse Source

Pre-lags fix

master
Eduard Kuzmenko 4 years ago
parent
commit
cd04dc6b34
  1. 1822
      package-lock.json
  2. 53
      package.json
  3. 23
      server.cert
  4. 20
      server.js
  5. 28
      server.key
  6. 162
      src/components/bubbleGroups.ts
  7. 111
      src/components/lazyLoadQueue.ts
  8. 143
      src/components/misc.ts
  9. 17
      src/components/pageIm.ts
  10. 11
      src/components/pageSignIn.ts
  11. 15
      src/components/preloader.ts
  12. 65
      src/components/scrollable.ts
  13. 102
      src/components/wrappers.ts
  14. 94
      src/lib/appManagers/appDialogsManager.ts
  15. 15
      src/lib/appManagers/appDocsManager.ts
  16. 784
      src/lib/appManagers/appImManager.ts
  17. 1
      src/lib/appManagers/appMediaViewer.ts
  18. 27
      src/lib/appManagers/appMessagesManager.ts
  19. 140
      src/lib/appManagers/appPhotosManager.ts
  20. 105
      src/lib/appManagers/appSidebarRight.ts
  21. 134
      src/lib/appManagers/appWebPagesManager.ts
  22. 23
      src/lib/appManagers/appWebpManager.ts
  23. 7
      src/lib/bin_utils.ts
  24. 223
      src/lib/crypto/crypto.js
  25. 201
      src/lib/crypto/crypto_utils.ts
  26. 56
      src/lib/crypto/cryptoworker.ts
  27. 42
      src/lib/idb.ts
  28. 4
      src/lib/lottie.ts
  29. 78
      src/lib/lottieLoader.ts
  30. 89
      src/lib/mtproto/apiFileManager.ts
  31. 3
      src/lib/mtproto/authorizer.ts
  32. 7
      src/lib/mtproto/mtproto.ts
  33. 6
      src/lib/mtproto/networker.ts
  34. 7
      src/lib/polyfill.ts
  35. 28
      src/lib/webp.ts
  36. 20
      src/scss/partials/_chat.scss
  37. 2
      src/scss/partials/_rightSIdebar.scss
  38. 119
      src/scss/style.scss
  39. 8
      src/tests/lol.test.ts
  40. 5
      tsconfig.json
  41. 6
      webpack.common.js

1822
package-lock.json generated

File diff suppressed because it is too large Load Diff

53
package.json

@ -5,7 +5,8 @@ @@ -5,7 +5,8 @@
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --open --config webpack.dev.js",
"serve": "webpack-dev-server --open --config webpack.prod.js",
"start-production": "webpack-dev-server --open --config webpack.prod.js",
"serve-serve": "webpack-serve --compress --port 9001 --host localhost --no-watch --static ./public --config webpack.prod.js",
"build": "webpack --config webpack.prod.js",
"test": "jest --config=jest.config.js",
"profile": "webpack --profile --json > stats.json --config webpack.prod.js"
@ -14,52 +15,48 @@ @@ -14,52 +15,48 @@
"license": "ISC",
"dependencies": {
"jsbn": "^1.1.0",
"materialize-css": "^1.0.0",
"roboto-fontface": "^0.10.0",
"rusha": "^0.8.13",
"tdweb": "^1.5.0",
"webpack-dev-server": "^3.9.0"
"webpack-dev-server": "^3.10.3"
},
"devDependencies": {
"@babel/core": "^7.8.3",
"@babel/preset-env": "^7.8.3",
"@babel/preset-typescript": "^7.8.3",
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.5",
"@babel/preset-typescript": "^7.9.0",
"@cryptography/sha1": "^0.1.0",
"@types/aes-js": "^3.1.0",
"@cryptography/sha256": "^0.2.0",
"@types/aes-js": "^3.1.1",
"@types/chrome": "0.0.91",
"@types/crypto-js": "^3.1.43",
"@types/jest": "^24.9.0",
"@types/materialize-css": "^1.0.6",
"@types/overlayscrollbars": "^1.9.0",
"@types/jest": "^24.9.1",
"aes-js": "^3.1.2",
"babel-jest": "^24.9.0",
"compression": "^1.7.4",
"compression-webpack-plugin": "^3.1.0",
"css-loader": "^3.2.0",
"css-loader": "^3.5.1",
"express": "^4.17.1",
"fastdom": "^1.0.9",
"file-loader": "^4.3.0",
"html-webpack-plugin": "^3.2.0",
"install": "^0.13.0",
"jest": "^24.9.0",
"leemon": "^6.2.0",
"lottie-web": "^5.6.4",
"lottie-web": "^5.6.8",
"node-sass": "^4.13.1",
"npm": "^6.14.1",
"npm": "^6.14.4",
"offscreen-canvas": "^0.1.1",
"on-build-webpack": "^0.1.0",
"overlayscrollbars": "^1.10.0",
"pako": "^1.0.10",
"overlayscrollbars": "^1.12.0",
"pako": "^1.0.11",
"resolve-url-loader": "^3.1.1",
"sass-loader": "^8.0.0",
"streamsaver": "^2.0.3",
"style-loader": "^1.0.0",
"sass-loader": "^8.0.2",
"streamsaver": "^2.0.4",
"style-loader": "^1.1.3",
"ts-jest": "^24.3.0",
"ts-loader": "^6.2.1",
"typescript": "^3.7.2",
"url-loader": "^2.2.0",
"web-streams-polyfill": "^2.0.6",
"ts-loader": "^6.2.2",
"typescript": "^3.8.3",
"url-loader": "^2.3.0",
"web-streams-polyfill": "^2.1.0",
"webp-hero": "0.0.0-dev.24",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11",
"webpack-merge": "^4.2.2",
"worker-loader": "^2.0.0"
}

23
server.cert

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDwDCCAqigAwIBAgIJAOqEYb8IH1NQMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDEaMBgGCSqGSIb3DQEJ
ARYLcXdlQGFzZC5jb20wHhcNMjAwNDEwMTc0OTU3WhcNMjAwNTEwMTc0OTU3WjB1
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QxGjAYBgkq
hkiG9w0BCQEWC3F3ZUBhc2QuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAxxzMhd/vxT84pEcRIIZwYKe6C00OaRZq8VErgNoOQVr8hejj7FueGjAu
io841OcJjUHCgD7Bda/cu5FLs0ATxAkBow7K9+Qzx3Gnl87ZKkxlt4xFZ6mWvDiY
hovliUL+I+ll29l2cF2PXhl8q/VEsOl0+M45d5rzT/qy3PD3H/5CwqCVnovCXe5H
BEFCf82qYAEe+J/w5KjtiqgT4fx/n0XNXES0KO/ot2PWyjGnzs+V52yf2eChmfIr
Ae7+ykRM60YwIR3HHvCiVO5XtEqB3x207ADwcQGpC0oOvuj8YB8Jj76QUfLm559/
pNjkZR07jMReX6Pzuuf7bEsQvfkMmwIDAQABo1MwUTAdBgNVHQ4EFgQUpn8Hwa3D
qFgdzvB62FYygrvZSlcwHwYDVR0jBBgwFoAUpn8Hwa3DqFgdzvB62FYygrvZSlcw
DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAQYb7MFbTQ7i6bwoc
j6jQ67S3RkK1svqXVgoqjYHY69BEhO94vNQZeOgTJWRk1YjKfTwQAdLru8U2BjtA
gh76OiMsTXhnhOv/TPGBRFUwPt2BSY5G/aa6ZJ0XB/exsOo0z/34k9IIGCO3g/g6
BpphITMjOJ8LbA8v0PBeHQD9d9u1ViuafgzvD6WwvmAkDKhGXDMyYDn5zjGo6L+w
rjmGjOn4c0rJAxPGEMABaGusuxhRFrFLxolohru3jSvvjjZ/zbXo3V6bR+Ta2IIC
cewd/gPI0odv1ObXNwdI/Dvu/1ymxcg8aVgjuMHtonkOAfxw9ghIuDfNtbV0Nb15
SEzY5w==
-----END CERTIFICATE-----

20
server.js

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
const compression = require('compression');
const express = require('express');
const https = require('https');
const fs = require('fs');
const app = express();
app.use(compression());
app.use(express.static('public'));
app.get('/', (req, res) => {
res.sendFile(__dirname + '/public/index.html');
});
https.createServer({
key: fs.readFileSync(__dirname + '/server.key'),
cert: fs.readFileSync(__dirname + '/server.cert')
}, app).listen(9001, () => {
console.log('Listening...');
});

28
server.key

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHHMyF3+/FPzik
RxEghnBgp7oLTQ5pFmrxUSuA2g5BWvyF6OPsW54aMC6KjzjU5wmNQcKAPsF1r9y7
kUuzQBPECQGjDsr35DPHcaeXztkqTGW3jEVnqZa8OJiGi+WJQv4j6WXb2XZwXY9e
GXyr9USw6XT4zjl3mvNP+rLc8Pcf/kLCoJWei8Jd7kcEQUJ/zapgAR74n/DkqO2K
qBPh/H+fRc1cRLQo7+i3Y9bKMafOz5XnbJ/Z4KGZ8isB7v7KREzrRjAhHcce8KJU
7le0SoHfHbTsAPBxAakLSg6+6PxgHwmPvpBR8ubnn3+k2ORlHTuMxF5fo/O65/ts
SxC9+QybAgMBAAECggEAdWzciU9p3k/MncVzqlTezYHdTHDjQMKBy1Ntbo4qvgxk
xKx2Tpwxf4xOxlR01cpzbaUMigl4mmleqhekJ1Bw17ngB0PgG5Wvm73BctwAYtuv
WTIWdG4lgVd3TFIQyoSB0LgC5Ec5fEcRGBO73MXG/vaPj3Q/m/P77n0RIw/RDkIg
mZjPOAeVpaJxRInVgpEAfzPAB+yPyk1qxHzbBzQziyzZLxycF26vlHcGWbjWeOBA
0CLVwAHeoctLpxPYSTlEc1PCZvn0Lm4kMFuuUnf03Tc/nlvGD0iQ2s6ThdUs2dNV
uie5JzrgsZuRPQ2vxYGLAZZI4455HA+gENvQQd160QKBgQDsAwtAF1S4fAiuR9hY
Nh5PEM3MRb9FMaRN5p4PdinnCmkymrUoW/LKotyU7ASwKnmALUzQ0eUoIjvRC85B
rW5NpuK/GjURZYd6+AzrqoWcnuDe7ugj0G1U0yyBbtMbp+KELB30UMJexV7ovHPM
rCi5mz1EUy0SUTj1CyWEazco5wKBgQDX+b3xu3gtOxbDi0hGJ1u/TAvJGndHXDBK
yzaY0I8BIbPC6IWTiWkCImuogFoV7TruLpGJJa9QDzhBi2mkRGvS6XWDKEoRe/tb
l8BhGFbNqdDyMb6tNh8k8cpYSDLlzMgK1/5S6gUsr5jWA0fFegXRcexYmj0r+ibN
5AP1mZrELQKBgQCE5SXlnf2XsEgXEt+QtFCWxuiLWM7eQJi7QNvJ6winT2ZzF0hh
BH2PeutodAojxJcMBPYXM8mssrIqAVLQCr9svEc7wp8VP61tIdXsseVwjsoi3jYb
TJbzx8Fs1KHNFdjoAguP8hWw1cSemtc97cc01GRIX+mmQdQnr3IdwV2bCwKBgCiV
cl7pRmThdJ6cHqGoJbJlvNU2VvGe3ig/1WuTzTt+NMRMY0VdDdFr3GUWcVcrc+Zr
88ccwLu/kGeopdpLTSOd4QobWQe+D3afpnPYWf9diLjqJhVwVRvhH4/FSWMrPu/i
tJSqCvzhpkuY5DS0gEFiMfJYUWRhJkeMMD5HdfClAoGBANTEaSrZhmw2HgdXD4iE
sscOyh84+kJG77T+0B5jj+Oa5laulRwIjxZfndoSFlspQ+z1hDE9iqWGv+qd5u0u
yWWf6LB88hBAcLTg82AUuPTQXsOtkzBeb1Ny+xoKCL73nGcHCydoih0AA8+4oQWT
ecQb07xZUbaNB5HKRzcmalf6
-----END PRIVATE KEY-----

162
src/components/bubbleGroups.ts

@ -0,0 +1,162 @@ @@ -0,0 +1,162 @@
import { generatePathData } from "../lib/utils";
export default class BubbleGroups {
bubblesByGroups: Array<{timestamp: number, fromID: number, mid: number, group: HTMLDivElement[]}> = []; // map to group
groups: Array<HTMLDivElement[]> = [];
//updateRAFs: Map<HTMLDivElement[], number> = new Map();
newGroupDiff = 120;
removeBubble(bubble: HTMLDivElement, mid: number) {
let details = this.bubblesByGroups.findAndSplice(g => g.mid == mid);
if(details && details.group.length) {
details.group.findAndSplice(d => d == bubble);
if(!details.group.length) {
this.groups.findAndSplice(g => g == details.group);
}
}
}
addBubble(bubble: HTMLDivElement, message: any, reverse: boolean) {
let timestamp = message.date;
let fromID = message.fromID;
let group: HTMLDivElement[];
// try to find added
//this.removeBubble(message.mid);
if(this.bubblesByGroups.length) {
if(reverse) {
let g = this.bubblesByGroups[0];
if(g.fromID == fromID && (g.timestamp - timestamp) < this.newGroupDiff) {
group = g.group;
group.unshift(bubble);
} else {
this.groups.unshift(group = [bubble]);
}
} else {
let g = this.bubblesByGroups[this.bubblesByGroups.length - 1];
if(g.fromID == fromID && (timestamp - g.timestamp) < this.newGroupDiff) {
group = g.group;
group.push(bubble);
} else {
this.groups.push(group = [bubble]);
}
}
} else {
this.groups.push(group = [bubble]);
}
//console.log('addBubble', bubble, message.mid, fromID, reverse, group);
this.bubblesByGroups[reverse ? 'unshift' : 'push']({timestamp, fromID, mid: message.mid, group});
this.updateGroup(group, reverse);
}
setClipIfNeeded(bubble: HTMLDivElement, remove = false) {
//console.log('setClipIfNeeded', bubble, remove);
if(bubble.classList.contains('is-message-empty')/* && !bubble.classList.contains('is-reply') */
&& (bubble.classList.contains('photo') || bubble.classList.contains('video'))) {
let container = bubble.querySelector('.bubble__media-container') as SVGSVGElement;
//console.log('setClipIfNeeded', bubble, remove, container);
if(!container) return;
Array.from(container.children).forEach(object => {
if(object instanceof SVGDefsElement) return;
if(remove) {
object.removeAttributeNS(null, 'clip-path');
} else {
let clipID = container.dataset.clipID;
let path = container.firstElementChild.firstElementChild.lastElementChild as SVGPathElement;
let width = +object.getAttributeNS(null, 'width');
let height = +object.getAttributeNS(null, 'height');
let isOut = bubble.classList.contains('is-out');
let isReply = bubble.classList.contains('is-reply');
let d = '';
//console.log('setClipIfNeeded', object, width, height, isOut);
let tr: number, tl: number;
if(bubble.classList.contains('forwarded') || isReply) {
tr = tl = 0;
} else if(isOut) {
tr = bubble.classList.contains('is-group-first') ? 12 : 6;
tl = 12;
} else {
tr = 12;
tl = bubble.classList.contains('is-group-first') ? 12 : 6;
}
if(isOut) {
d = generatePathData(0, 0, width - 9, height, tl, tr, 0, 12);
} else {
d = generatePathData(9, 0, width - 9, height, tl, tr, 12, 0);
}
path.setAttributeNS(null, 'd', d);
object.setAttributeNS(null, 'clip-path', 'url(#' + clipID + ')');
}
});
}
}
updateGroup(group: HTMLDivElement[], reverse = false) {
/* if(this.updateRAFs.has(group)) {
window.cancelAnimationFrame(this.updateRAFs.get(group));
this.updateRAFs.delete(group);
} */
//this.updateRAFs.set(group, window.requestAnimationFrame(() => {
//this.updateRAFs.delete(group);
if(!group.length) {
return;
}
let first = group[0];
//appImManager.scrollPosition.prepareFor(reverse ? 'up' : 'down');
//console.log('updateGroup', group, first);
if(group.length == 1) {
first.classList.add('is-group-first', 'is-group-last');
this.setClipIfNeeded(first);
return;
} else {
first.classList.remove('is-group-last');
first.classList.add('is-group-first');
this.setClipIfNeeded(first, true);
}
let length = group.length - 1;
for(let i = 1; i < length; ++i) {
let bubble = group[i];
bubble.classList.remove('is-group-last', 'is-group-first');
this.setClipIfNeeded(bubble, true);
}
let last = group[group.length - 1];
last.classList.remove('is-group-first');
last.classList.add('is-group-last');
this.setClipIfNeeded(last);
//appImManager.scrollPosition.restore();
//}));
}
updateGroupByMessageID(mid: number) {
let details = this.bubblesByGroups.find(g => g.mid == mid);
if(details) {
this.updateGroup(details.group);
}
}
cleanup() {
this.bubblesByGroups = [];
/* for(let value of this.updateRAFs.values()) {
window.cancelAnimationFrame(value);
}
this.updateRAFs.clear(); */
}
}

111
src/components/lazyLoadQueue.ts

@ -1,11 +1,20 @@ @@ -1,11 +1,20 @@
import { isElementInViewport } from "../lib/utils";
type LazyLoadElement = {
div: HTMLDivElement,
load: () => Promise<void>,
wasSeen?: boolean
};
export default class LazyLoadQueue {
private lazyLoadMedia: Array<{div: HTMLDivElement, load: () => Promise<void>, wasSeen?: boolean}> = [];
private lazyLoadMedia: Array<LazyLoadElement> = [];
private loadingMedia = 0;
private tempID = 0;
constructor(private parallelLimit = 0) {
private lockPromise: Promise<void> = null;
private unlockResolve: () => void = null;
constructor(private parallelLimit = 5) {
}
@ -15,27 +24,34 @@ export default class LazyLoadQueue { @@ -15,27 +24,34 @@ export default class LazyLoadQueue {
this.loadingMedia = 0;
}
public length() {
return this.lazyLoadMedia.length + this.loadingMedia;
}
public lock() {
if(this.lockPromise) return;
this.lockPromise = new Promise((resolve, reject) => {
this.unlockResolve = resolve;
});
}
public unlock() {
if(!this.unlockResolve) return;
this.lockPromise = null;
this.unlockResolve();
this.unlockResolve = null;
}
public async processQueue(id?: number) {
if(this.parallelLimit > 0 && this.loadingMedia >= this.parallelLimit) return;
let item: {div: HTMLDivElement, load: () => Promise<void>, wasSeen?: boolean};
let item: LazyLoadElement;
let index: number;
/* if(id) item = this.lazyLoadMedia.splice(id, 1) as any;
else item = this.lazyLoadMedia.pop(); */
if(id !== undefined) item = this.lazyLoadMedia.splice(id, 1)[0];
else {
index = this.lazyLoadMedia.findIndex(i => isElementInViewport(i.div));
if(index !== -1) {
item = this.lazyLoadMedia.splice(index, 1)[0];
} else {
//index = this.lazyLoadMedia.findIndex(i => i.wasSeen);
//if(index !== -1) {
//item = this.lazyLoadMedia.splice(index, 1)[0];
/*} else {
item = this.lazyLoadMedia.pop();
} */
item = this.lazyLoadMedia.findAndSplice(i => isElementInViewport(i.div));
if(!item) {
let length = this.lazyLoadMedia.length;
for(index = length - 1; index >= 0; --index) {
if(this.lazyLoadMedia[index].wasSeen) {
@ -51,7 +67,16 @@ export default class LazyLoadQueue { @@ -51,7 +67,16 @@ export default class LazyLoadQueue {
let tempID = this.tempID;
console.log('lazyLoadQueue: will load media', this.lockPromise);
try {
if(this.lockPromise) {
let perf = performance.now();
await this.lockPromise;
console.log('lazyLoadQueue: waited lock:', performance.now() - perf);
}
await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
await item.load();
} catch(err) {
console.error('loadMediaQueue error:', err, item, id, index);
@ -61,6 +86,8 @@ export default class LazyLoadQueue { @@ -61,6 +86,8 @@ export default class LazyLoadQueue {
this.loadingMedia--;
}
console.log('lazyLoadQueue: loaded media');
if(this.lazyLoadMedia.length) {
this.processQueue();
}
@ -68,31 +95,9 @@ export default class LazyLoadQueue { @@ -68,31 +95,9 @@ export default class LazyLoadQueue {
}
public check(id?: number) {
/* if(id !== undefined) {
let {div, load} = this.lazyLoadMedia[id];
if(isElementInViewport(div)) {
//console.log('will load div by id:', div, div.getBoundingClientRect());
load();
this.lazyLoadMedia.splice(id, 1);
}
return;
}
let length = this.lazyLoadMedia.length;
for(let i = length - 1; i >= 0; --i) {
let {div, load} = this.lazyLoadMedia[i];
if(isElementInViewport(div)) {
console.log('will load div:', div);
load();
this.lazyLoadMedia.splice(i, 1);
}
} */
if(id !== undefined) {
let {div} = this.lazyLoadMedia[id];
if(isElementInViewport(div)) {
let {div, wasSeen} = this.lazyLoadMedia[id];
if(!wasSeen && isElementInViewport(div)) {
//console.log('will load div by id:', div, div.getBoundingClientRect());
this.lazyLoadMedia[id].wasSeen = true;
this.processQueue(id);
@ -103,31 +108,25 @@ export default class LazyLoadQueue { @@ -103,31 +108,25 @@ export default class LazyLoadQueue {
let length = this.lazyLoadMedia.length;
for(let i = length - 1; i >= 0; --i) {
let {div} = this.lazyLoadMedia[i];
let {div, wasSeen} = this.lazyLoadMedia[i];
if(isElementInViewport(div)) {
if(!wasSeen && isElementInViewport(div)) {
//console.log('will load div:', div);
this.lazyLoadMedia[i].wasSeen = true;
this.processQueue(i);
//this.lazyLoadMedia.splice(i, 1);
}
}
/* this.lazyLoadMedia = this.lazyLoadMedia.filter(({div, load}) => {
if(isElementInViewport(div)) {
//console.log('will load div:', div, div.getBoundingClientRect());
load();
return false;
}
return true;
}); */
}
public push(el: {div: HTMLDivElement, load: () => Promise<void>, wasSeen?: boolean}) {
el.wasSeen = false;
public push(el: LazyLoadElement) {
let id = this.lazyLoadMedia.push(el) - 1;
this.check(id);
if(el.wasSeen) {
this.processQueue(id);
} else {
el.wasSeen = false;
this.check(id);
}
}
}

143
src/components/misc.ts

@ -1,55 +1,95 @@ @@ -1,55 +1,95 @@
import apiManager from "../lib/mtproto/apiManager";
import { whichChild, findUpTag } from "../lib/utils";
let onRippleClick = function(this: HTMLElement, e: MouseEvent) {
var $circle = this.firstElementChild as HTMLSpanElement;
var rect = this.parentElement.getBoundingClientRect();
var x = e.clientX - rect.left; //x position within the element.
var y = e.clientY - rect.top;
/* var x = e.pageX - this.parentElement.offsetLeft;
var y = e.pageY - this.parentElement.offsetTop - this.parentElement.scrollHeight; */
$circle.style.top = y + 'px';
$circle.style.left = x + 'px';
this.classList.add('active');
//console.log('onrippleclick', e/* e.pageY, this.parentElement.offsetTop */);
};
export function ripple(elem: Element) {
/* elem.addEventListener('click', function(e) {
var $circle = elem.querySelector('.c-ripple__circle') as HTMLSpanElement;
var x = e.pageX - elem.offsetLeft;
var y = e.pageY - elem.offsetTop;
$circle.style.top = y + 'px';
$circle.style.left = x + 'px';
elem.classList.add('active');
}); */
let rippleClickID = 0;
export function ripple(elem: HTMLElement, callback: (id: number) => Promise<boolean | void> = () => Promise.resolve(), onEnd: (id: number) => void = null) {
let r = document.createElement('div');
r.classList.add('c-ripple');
let span = document.createElement('span');
span.classList.add('c-ripple__circle');
r.append(span);
elem.append(r);
elem.addEventListener('mousedown', (e) => {
let startTime = Date.now();
let span = document.createElement('span');
let clickID = rippleClickID++;
let handler = () => {
let elapsedTime = Date.now() - startTime;
if(elapsedTime < 700) {
let delay = Math.max(700 - elapsedTime, 350);
setTimeout(() => span.classList.add('hiding'), Math.max(delay - 350, 0));
setTimeout(() => {
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
span.remove();
if(onEnd) onEnd(clickID);
}, delay);
} else {
span.classList.add('hiding');
setTimeout(() => {
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
span.remove();
if(onEnd) onEnd(clickID);
}, 350);
}
};
callback && callback(clickID);
/* callback().then((bad) => {
if(bad) {
span.remove();
return;
} */
//console.log('ripple after promise', Date.now() - startTime);
//console.log('ripple tooSlow:', tooSlow);
/* if(tooSlow) {
span.remove();
return;
} */
window.requestAnimationFrame(() => {
span.classList.add('c-ripple__circle');
let rect = r.getBoundingClientRect();
let clickX = e.clientX - rect.left;
let clickY = e.clientY - rect.top;
let size: number, clickPos: number;
if(rect.width > rect.height) {
size = rect.width;
clickPos = clickX;
} else {
size = rect.height;
clickPos = clickY;
}
let offsetFromCenter = clickPos > (size / 2) ? size - clickPos : clickPos;
size = size - offsetFromCenter;
size *= 1.1;
// center of circle
let x = clickX - size / 2;
let y = clickY - size / 2;
//console.log('ripple click', offsetFromCenter, size, clickX, clickY);
span.style.width = span.style.height = size + 'px';
span.style.left = x + 'px';
span.style.top = y + 'px';
r.addEventListener('click', onRippleClick);
let onEnd = () => {
r.classList.remove('active');
};
for(let type of ['animationend', 'webkitAnimationEnd', 'oanimationend', 'MSAnimationEnd']) {
r.addEventListener(type, onEnd);
}
r.append(span);
//r.classList.add('active');
//handler();
});
//});
window.addEventListener('mouseup', () => {
//console.time('appImManager: pre render start');
handler();
}, {once: true});
});
}
export function putPreloader(elem: Element, returnDiv = false) {
@ -156,17 +196,6 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement, @@ -156,17 +196,6 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement,
});
}
export function getNearestDc() {
return apiManager.invokeApi('help.getNearestDc').then((nearestDcResult: any) => {
if(nearestDcResult.nearest_dc != nearestDcResult.this_dc) {
//MTProto.apiManager.baseDcID = nearestDcResult.nearest_dc;
apiManager.getNetworker(nearestDcResult.nearest_dc);
}
return nearestDcResult;
});
}
export function formatPhoneNumber(str: string) {
str = str.replace(/\D/g, '');
let phoneCode = str.slice(0, 6);

17
src/components/pageIm.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
//import { appImManager, appMessagesManager, appDialogsManager, apiUpdatesManager, appUsersManager } from "../lib/services";
import { openBtnMenu } from "./misc";
import {stackBlurImage} from '../lib/StackBlur';
//import {stackBlurImage} from '../lib/StackBlur';
import appSidebarLeft from "../lib/appManagers/appSidebarLeft";
export default () => import('../lib/services').then(services => {
@ -130,7 +130,7 @@ export default () => import('../lib/services').then(services => { @@ -130,7 +130,7 @@ export default () => import('../lib/services').then(services => {
}
}); */
fetch('assets/img/camomile.jpg')
/* fetch('assets/img/camomile.jpg')
.then(res => res.blob())
.then(blob => {
let img = new Image();
@ -139,7 +139,11 @@ export default () => import('../lib/services').then(services => { @@ -139,7 +139,11 @@ export default () => import('../lib/services').then(services => {
img.onload = () => {
let id = 'chat-background-canvas';
var canvas = document.getElementById(id) as HTMLCanvasElement;
URL.revokeObjectURL(url);
//URL.revokeObjectURL(url);
let elements = ['.chat-container'].map(selector => {
return document.querySelector(selector) as HTMLDivElement;
});
stackBlurImage(img, id, 15, 0);
@ -147,13 +151,12 @@ export default () => import('../lib/services').then(services => { @@ -147,13 +151,12 @@ export default () => import('../lib/services').then(services => {
//let dataUrl = canvas.toDataURL('image/jpeg', 1);
let dataUrl = URL.createObjectURL(blob);
[/* '.chat-background', '#chat-closed' */'.chat-container'].forEach(selector => {
let bg = document.querySelector(selector) as HTMLDivElement;
bg.style.backgroundImage = 'url(' + dataUrl + ')';
elements.forEach(el => {
el.style.backgroundImage = 'url(' + dataUrl + ')';
});
}, 'image/jpeg', 1);
};
});
}); */
/* toggleEmoticons.onclick = (e) => {
if(!emoticonsDropdown) {

11
src/components/pageSignIn.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { putPreloader, getNearestDc, formatPhoneNumber } from "./misc";
import { putPreloader, formatPhoneNumber } from "./misc";
import Scrollable from './scrollable';
import {RichTextProcessor} from '../lib/richtextprocessor';
import * as Config from '../lib/config';
@ -237,7 +237,14 @@ export default () => { @@ -237,7 +237,14 @@ export default () => {
});
let tryAgain = () => {
getNearestDc().then((nearestDcResult: any) => {
apiManager.invokeApi('help.getNearestDc').then((nearestDcResult: any) => {
if(nearestDcResult.nearest_dc != nearestDcResult.this_dc) {
//MTProto.apiManager.baseDcID = nearestDcResult.nearest_dc;
apiManager.getNetworker(nearestDcResult.nearest_dc);
}
return nearestDcResult;
}).then((nearestDcResult: any) => {
let country = countries.find((c) => c.code == nearestDcResult.country);
if(country) {
if(!selectCountryCode.value.length && !telEl.value.length) {

15
src/components/preloader.ts

@ -7,6 +7,7 @@ export default class ProgressivePreloader { @@ -7,6 +7,7 @@ export default class ProgressivePreloader {
private progress = 0;
private promise: CancellablePromise<any> = null;
private tempID = 0;
private detached = true;
constructor(elem?: Element, private cancelable = true) {
this.preloader = document.createElement('div');
this.preloader.classList.add('preloader-container');
@ -68,7 +69,12 @@ export default class ProgressivePreloader { @@ -68,7 +69,12 @@ export default class ProgressivePreloader {
this.setProgress(0);
}
elem.append(this.preloader);
this.detached = false;
window.requestAnimationFrame(() => {
if(this.detached) return;
this.detached = false;
elem.append(this.preloader);
});
/* let isIn = isInDOM(this.preloader);
if(isIn && this.progress != this.defaultProgress) {
@ -83,8 +89,13 @@ export default class ProgressivePreloader { @@ -83,8 +89,13 @@ export default class ProgressivePreloader {
}
public detach() {
this.detached = true;
if(this.preloader.parentElement) {
this.preloader.parentElement.removeChild(this.preloader);
window.requestAnimationFrame(() => {
if(!this.detached) return;
this.detached = true;
this.preloader.parentElement.removeChild(this.preloader);
});
}
}

65
src/components/scrollable.ts

@ -90,11 +90,16 @@ export default class Scrollable { @@ -90,11 +90,16 @@ export default class Scrollable {
private disableHoverTimeout: number = 0;
private log: ReturnType<typeof logger>;
private debug = true;
private debug = false;
private measureMutex: CancellablePromise<void>;
private prependLocked = false;
private appendLocked = false;
private prependFragment: DocumentFragment = null;
private appendFragment: DocumentFragment = null;
private prependFragmentId = 0;
private appendFragmentId = 0;
constructor(public el: HTMLElement, axis: 'y' | 'x' = 'y', public splitOffset = 300, logPrefix = '', public appendTo = el, public onScrollOffset = splitOffset) {
this.container = document.createElement('div');
@ -194,11 +199,11 @@ export default class Scrollable { @@ -194,11 +199,11 @@ export default class Scrollable {
el.append(this.container);
setTimeout(() => {
window.requestAnimationFrame(() => {
// @ts-ignore
this.size = this.container[this.clientSize];
this.resize();
}, 0);
});
this.container.parentElement.append(this.thumb);
}
@ -763,7 +768,8 @@ export default class Scrollable { @@ -763,7 +768,8 @@ export default class Scrollable {
}
} else {
this.appendTo.prepend(element);
this.onScroll();
this.visibleElements.unshift({element, height: 0});
//this.onScroll();
}
//this.onScroll();
@ -812,11 +818,52 @@ export default class Scrollable { @@ -812,11 +818,52 @@ export default class Scrollable {
}
} else {
this.appendTo.append(element);
this.onScroll();
this.visibleElements.push({element, height: 0});
//this.onScroll();
}
//this.onScroll();
}
public prependByBatch(element: HTMLElement) {
let perf = performance.now();
let fragment = this.prependFragment ?? (this.prependFragment = document.createDocumentFragment());
fragment.prepend(element);
if(this.prependFragmentId) window.cancelAnimationFrame(this.prependFragmentId);
this.prependFragmentId = window.requestAnimationFrame(() => {
this.prependFragment = null;
this.prependFragmentId = 0;
for(let length = fragment.childElementCount, i = length - 1; i >= 0; --i) {
let element = fragment.children[i];
this.visibleElements.unshift({element, height: 0});
}
this.log('prependByBatch perf:', performance.now() - perf, fragment.childElementCount);
this.appendTo.prepend(fragment);
//this.onScroll();
});
}
public appendByBatch(element: HTMLElement) {
let fragment = this.appendFragment ?? (this.appendFragment = document.createDocumentFragment());
fragment.append(element);
if(this.appendFragmentId) window.cancelAnimationFrame(this.appendFragmentId);
this.appendFragmentId = window.requestAnimationFrame(() => {
this.appendFragment = null;
this.appendFragmentId = 0;
for(let i = 0, length = fragment.childElementCount; i < length; ++i) {
let element = fragment.children[i];
this.visibleElements.push({element, height: 0});
}
this.appendTo.append(fragment);
//this.onScroll();
});
}
public contains(element: Element) {
if(!this.splitUp) {
@ -985,9 +1032,9 @@ export default class Scrollable { @@ -985,9 +1032,9 @@ export default class Scrollable {
}
set scrollTop(y: number) {
fastdom.mutate(() => {
//fastdom.mutate(() => {
this.container.scrollTop = y;
});
//});
}
get scrollTop() {
@ -997,6 +1044,10 @@ export default class Scrollable { @@ -997,6 +1044,10 @@ export default class Scrollable {
get scrollHeight() {
return this.container.scrollHeight;
}
get innerHeight() {
return this.size;
}
get parentElement() {
return this.container.parentElement;

102
src/components/wrappers.ts

@ -42,9 +42,7 @@ export type MTPhotoSize = { @@ -42,9 +42,7 @@ export type MTPhotoSize = {
size?: number,
type?: string, // i, m, x, y, w by asc
location?: any,
bytes?: Uint8Array, // if type == 'i'
preloaded?: boolean // custom added
bytes?: Uint8Array // if type == 'i'
};
export function wrapVideo(this: AppImManager, doc: MTDocument, container: HTMLDivElement, message: any, justLoader = true, preloader?: ProgressivePreloader, controls = true, round = false, boxWidth = 380, boxHeight = 380, withTail = false, isOut = false) {
@ -129,7 +127,7 @@ export function wrapVideo(this: AppImManager, doc: MTDocument, container: HTMLDi @@ -129,7 +127,7 @@ export function wrapVideo(this: AppImManager, doc: MTDocument, container: HTMLDi
};
if(doc.type == 'gif' || true) { // extra fix
return this.loadMediaQueuePush(loadVideo);
return this.lazyLoadQueue.push({div: container, load: loadVideo, wasSeen: true});
} /* else { // if video
let load = () => appPhotosManager.preloadPhoto(doc).then((blob) => {
if((this.peerID ? this.peerID : this.currentMessageID) != peerID) {
@ -430,46 +428,24 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement { @@ -430,46 +428,24 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
function wrapMediaWithTail(photo: any, message: {mid: number, message: string}, container: HTMLDivElement, boxWidth: number, boxHeight: number, isOut: boolean) {
let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.classList.add('bubble__media-container', isOut ? 'is-out' : 'is-in');
let image = document.createElementNS("http://www.w3.org/2000/svg", "image");
svg.append(image);
let size = appPhotosManager.setAttachmentSize(photo.type ? photo : photo.id, svg, boxWidth, boxHeight);
let image = svg.firstElementChild as SVGImageElement || document.createElementNS("http://www.w3.org/2000/svg", "image");
let size = appPhotosManager.setAttachmentSize(photo.type ? photo : photo.id, svg, boxWidth, boxHeight, false);
let width = +svg.getAttributeNS(null, 'width');
let height = +svg.getAttributeNS(null, 'height');
//container.style.width = (width - 9) + 'px'; // maybe return
let clipID = 'clip' + message.mid;
svg.dataset.clipID = clipID;
//image.setAttributeNS(null, 'clip-path', `url(#${clipID})`);
if(!svg.contains(image)) {
image.setAttributeNS(null, 'width', '' + width);
image.setAttributeNS(null, 'height', '' + height);
svg.append(image);
}
let defs = document.createElementNS("http://www.w3.org/2000/svg", 'defs');
let clipPathHTML: string = '';/* = `
<use href="#message-tail" transform="translate(${width - 2}, ${height}) scale(-1, -1)"></use>
<rect width="${width - 9}" height="${height}" rx="12"></rect>
`; */
//window.getComputedStyle(container).getPropertyValue('border-radius');
let clipPathHTML: string = '';
if(message.message) {
//clipPathHTML += `<rect width="${width}" height="${height}"></rect>`;
} else {
/* if(isOut) {
clipPathHTML += `
<use href="#message-tail" transform="translate(${width - 2}, ${height}) scale(-1, -1)"></use>
<path d="${generatePathData(0, 0, width - 9, height, 6, 0, 12, 12)}" />
`;
} else {
clipPathHTML += `
<use href="#message-tail" transform="translate(0, ${height}) scale(1, -1)"></use>
<path d="${generatePathData(0, 0, width - 9, height, 12, 12, 0, 6)}" />
`;
} */
if(isOut) {
clipPathHTML += `
<use href="#message-tail" transform="translate(${width - 2}, ${height}) scale(-1, -1)"></use>
@ -482,18 +458,7 @@ function wrapMediaWithTail(photo: any, message: {mid: number, message: string}, @@ -482,18 +458,7 @@ function wrapMediaWithTail(photo: any, message: {mid: number, message: string},
`;
}
}
/* if(isOut) { // top-right, bottom-right
clipPathHTML += `
<rect class="br-tr" width="12" height="12" x="${width - 9 - 12}" y="0"></rect>
<rect class="br-br" width="12" height="12" x="${width - 9 - 12}" y="${height - 12}"></rect>
`
} else { // top-left, bottom-left
clipPathHTML += `
<rect class="br-tl" width="12" height="12" x="0" y="0"></rect>
<rect class="br-bl" width="12" height="12" x="0" y="${height - 12}"></rect>
`;
} */
defs.innerHTML = `<clipPath id="${clipID}">${clipPathHTML}</clipPath>`;
svg.prepend(defs);
@ -502,15 +467,17 @@ function wrapMediaWithTail(photo: any, message: {mid: number, message: string}, @@ -502,15 +467,17 @@ function wrapMediaWithTail(photo: any, message: {mid: number, message: string},
return image;
}
export function wrapPhoto(this: AppImManager, photo: any, message: any, container: HTMLDivElement, boxWidth = 380, boxHeight = 380, withTail = true, isOut = false) {
export async function wrapPhoto(this: AppImManager, photoID: string, message: any, container: HTMLDivElement, boxWidth = 380, boxHeight = 380, withTail = true, isOut = false) {
let peerID = this.peerID;
let photo = appPhotosManager.getPhoto(photoID);
let size: MTPhotoSize;
let image: SVGImageElement | HTMLImageElement;
if(withTail) {
image = wrapMediaWithTail(photo, message, container, boxWidth, boxHeight, isOut);
} else {
size = appPhotosManager.setAttachmentSize(photo.id, container, boxWidth, boxHeight);
} else { // means webpage's preview
size = appPhotosManager.setAttachmentSize(photoID, container, boxWidth, boxHeight, false);
image = container.firstElementChild as HTMLImageElement || new Image();
@ -518,12 +485,17 @@ export function wrapPhoto(this: AppImManager, photo: any, message: any, containe @@ -518,12 +485,17 @@ export function wrapPhoto(this: AppImManager, photo: any, message: any, containe
container.appendChild(image);
}
}
let preloader = new ProgressivePreloader(container, false);
console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
let preloader: ProgressivePreloader;
if(!photo.downloaded) preloader = new ProgressivePreloader(container, false);
let load = () => {
let promise = appPhotosManager.preloadPhoto(photo.id, size);
let promise = appPhotosManager.preloadPhoto(photoID, size);
preloader.attach(container, true, promise);
if(preloader) {
preloader.attach(container, true, promise);
}
return promise.then((blob) => {
if(this.peerID != peerID) {
@ -541,11 +513,15 @@ export function wrapPhoto(this: AppImManager, photo: any, message: any, containe @@ -541,11 +513,15 @@ export function wrapPhoto(this: AppImManager, photo: any, message: any, containe
/////////console.log('wrapPhoto', load, container, image);
return this.loadMediaQueue ? this.loadMediaQueuePush(load) : load();
return photo.downloaded ? load() : this.lazyLoadQueue.push({div: container, load: load, wasSeen: true});
}
export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: () => boolean, lazyLoadQueue?: LazyLoadQueue, group?: string, canvas?: boolean, play = false, onlyThumb = false) {
let stickerType = doc.mime_type == "application/x-tgsticker" ? 2 : (doc.mime_type == "image/webp" ? 1 : 0);
if(stickerType == 2 && !LottieLoader.loaded) {
LottieLoader.loadLottie();
}
if(!stickerType) {
console.error('wrong doc for wrapSticker!', doc, div);
@ -601,17 +577,21 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -601,17 +577,21 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
//console.log('loaded sticker:', blob, div);
if(middleware && !middleware()) return;
if(div.firstElementChild) {
/* if(div.firstElementChild) {
div.firstElementChild.remove();
}
} */
if(stickerType == 2) {
const reader = new FileReader();
reader.addEventListener('loadend', async(e) => {
console.time('decompress sticker' + doc.id);
console.time('render sticker' + doc.id);
// @ts-ignore
const text = e.srcElement.result;
let json = await CryptoWorker.gzipUncompress<string>(text, true);
console.timeEnd('decompress sticker' + doc.id);
let animation = await LottieLoader.loadAnimation({
container: div,
@ -620,6 +600,12 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -620,6 +600,12 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
animationData: JSON.parse(json),
renderer: canvas ? 'canvas' : 'svg'
}, group);
console.timeEnd('render sticker' + doc.id);
if(div.firstElementChild && div.firstElementChild.tagName != 'CANVAS') {
div.firstElementChild.remove();
}
if(!canvas) {
div.addEventListener('mouseover', (e) => {
@ -663,7 +649,11 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: ( @@ -663,7 +649,11 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
} else if(stickerType == 1) {
let img = new Image();
appWebpManager.polyfillImage(img, blob);
appWebpManager.polyfillImage(img, blob).then(() => {
if(div.firstElementChild && div.firstElementChild != img) {
div.firstElementChild.remove();
}
});
//img.src = URL.createObjectURL(blob);

94
src/lib/appManagers/appDialogsManager.ts

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import apiManager from "../mtproto/apiManager";
import apiFileManager from '../mtproto/apiFileManager';
import { $rootScope, findUpTag, langPack, findUpClassName } from "../utils";
import appImManager from "./appImManager";
import { $rootScope, langPack, findUpClassName } from "../utils";
import appImManager, { AppImManager } from "./appImManager";
import appPeersManager from './appPeersManager';
import appMessagesManager from "./appMessagesManager";
import appUsersManager from "./appUsersManager";
@ -38,6 +38,10 @@ export class AppDialogsManager { @@ -38,6 +38,10 @@ export class AppDialogsManager {
public savedAvatarURLs: {[peerID: number]: string} = {};
private rippleCallback: (value?: boolean | PromiseLike<boolean>) => void = null;
private lastClickID = 0;
private lastGoodClickID = 0;
constructor() {
this.pinnedDelimiter = document.createElement('div');
this.pinnedDelimiter.classList.add('pinned-delimiter');
@ -52,6 +56,13 @@ export class AppDialogsManager { @@ -52,6 +56,13 @@ export class AppDialogsManager {
this.myID = userAuth ? userAuth.id : 0;
});
$rootScope.$on('history_request', () => { // will call at history request api or cache RENDERED!
if(this.rippleCallback) {
this.rippleCallback();
this.rippleCallback = null;
}
});
//let chatClosedDiv = document.getElementById('chat-closed');
this.setListClickListener(this.chatList);
@ -60,6 +71,8 @@ export class AppDialogsManager { @@ -60,6 +71,8 @@ export class AppDialogsManager {
public setListClickListener(list: HTMLUListElement, onFound?: () => void) {
list.addEventListener('click', (e: Event) => {
//return;
console.log('dialogs click list');
let target = e.target as HTMLElement;
let elem = target.classList.contains('rp') ? target : findUpClassName(target, 'rp');
@ -69,10 +82,15 @@ export class AppDialogsManager { @@ -69,10 +82,15 @@ export class AppDialogsManager {
elem = elem.parentElement;
if(this.lastActiveListElement) {
let samePeer = this.lastActiveListElement == elem;
if(this.lastActiveListElement && !samePeer) {
this.lastActiveListElement.classList.remove('active');
}
let startTime = Date.now();
let result: ReturnType<AppImManager['setPeer']>;
//console.log('appDialogsManager: lock lazyLoadQueue');
if(elem) {
/* if(chatClosedDiv) {
chatClosedDiv.style.display = 'none';
@ -81,14 +99,46 @@ export class AppDialogsManager { @@ -81,14 +99,46 @@ export class AppDialogsManager {
if(onFound) onFound();
let peerID = +elem.getAttribute('data-peerID');
let lastMsgID = +elem.getAttribute('data-mid');
appImManager.setPeer(peerID, lastMsgID);
elem.classList.add('active');
this.lastActiveListElement = elem;
let lastMsgID = +elem.dataset.mid;
if(!samePeer) {
elem.classList.add('active');
this.lastActiveListElement = elem;
}
result = appImManager.setPeer(peerID, lastMsgID, false, true);
if(result instanceof Promise) {
this.lastGoodClickID = this.lastClickID;
appImManager.lazyLoadQueue.lock();
}
} else /* if(chatClosedDiv) */ {
appImManager.setPeer(0);
result = appImManager.setPeer(0);
//chatClosedDiv.style.display = '';
}
/* if(!(result instanceof Promise)) { // if click on same dialog
this.rippleCallback();
this.rippleCallback = null;
} */
/* promise.then(() => {
appImManager.lazyLoadQueue.unlock();
}); */
/* promise.then(() => {
let length = appImManager.lazyLoadQueue.length();
console.log('pre ripple callback', length);
if(length) {
setTimeout(() => {
this.rippleCallback();
}, length * 25);
} else {
let elapsedTime = Date.now() - startTime;
this.rippleCallback(elapsedTime > 200);
}
}); */
});
}
@ -110,6 +160,7 @@ export class AppDialogsManager { @@ -110,6 +160,7 @@ export class AppDialogsManager {
div.style.backgroundColor = '';
div.classList.add('tgico-savedmessages');
div.classList.remove('tgico-avatar_deletedaccount');
return true;
}
@ -171,7 +222,10 @@ export class AppDialogsManager { @@ -171,7 +222,10 @@ export class AppDialogsManager {
div.innerHTML = '';
//div.style.fontSize = '0'; // need
//div.style.backgroundColor = '';
div.append(img);
window.requestAnimationFrame(() => {
div.append(img);
});
return true;
}
@ -364,7 +418,8 @@ export class AppDialogsManager { @@ -364,7 +418,8 @@ export class AppDialogsManager {
str = sender.first_name || sender.last_name || sender.username;
}
senderBold.innerText = str + ': ';
//senderBold.innerText = str + ': ';
senderBold.innerHTML = RichTextProcessor.wrapRichText(str, {noLinebreakers: true}) + ': ';
//console.log(sender, senderBold.innerText);
dom.lastMessageSpan.prepend(senderBold);
} //////// else console.log('no sender', lastMessage, peerID);
@ -505,12 +560,25 @@ export class AppDialogsManager { @@ -505,12 +560,25 @@ export class AppDialogsManager {
//captionDiv.append(titleSpan);
//captionDiv.append(span);
let paddingDiv = document.createElement('div');
paddingDiv.classList.add('rp');
paddingDiv.append(avatarDiv, captionDiv);
ripple(paddingDiv);
ripple(paddingDiv, (id) => {
console.log('dialogs click element');
this.lastClickID = id;
return new Promise((resolve, reject) => {
this.rippleCallback = resolve;
//setTimeout(() => resolve(), 100);
//window.requestAnimationFrame(() => window.requestAnimationFrame(() => resolve()));
});
}, (id) => {
//console.log('appDialogsManager: ripple onEnd called!');
if(id == this.lastGoodClickID) {
appImManager.lazyLoadQueue.unlock();
}
});
let li = document.createElement('li');
li.append(paddingDiv);

15
src/lib/appManagers/appDocsManager.ts

@ -6,17 +6,16 @@ import { CancellablePromise } from '../polyfill'; @@ -6,17 +6,16 @@ import { CancellablePromise } from '../polyfill';
class AppDocsManager {
private docs: any = {};
constructor() {
}
public saveDoc(apiDoc: /* MTDocument */any, context?: any) {
console.log('saveDoc', apiDoc, this.docs[apiDoc.id]);
if(this.docs[apiDoc.id]) return this.docs[apiDoc.id];
this.docs[apiDoc.id] = apiDoc;
if(context) {
/* if(context) {
Object.assign(apiDoc, context);
}
} */
if(apiDoc.thumb && apiDoc.thumb._ == 'photoCachedSize') {
apiFileManager.saveSmallFile(apiDoc.thumb.location, apiDoc.thumb.bytes);
@ -125,6 +124,8 @@ class AppDocsManager { @@ -125,6 +124,8 @@ class AppDocsManager {
if(apiDoc._ == 'documentEmpty') {
apiDoc.size = 0;
}
return apiDoc;
}
public getDoc(docID: string) {

784
src/lib/appManagers/appImManager.ts

File diff suppressed because it is too large Load Diff

1
src/lib/appManagers/appMediaViewer.ts

@ -5,7 +5,6 @@ import appMessagesManager from "./appMessagesManager"; @@ -5,7 +5,6 @@ import appMessagesManager from "./appMessagesManager";
import { RichTextProcessor } from "../richtextprocessor";
import { logger } from "../polyfill";
import ProgressivePreloader from "../../components/preloader";
import { wrapVideo } from "../../components/wrappers";
import { findUpClassName, $rootScope, generatePathData } from "../utils";
import appDocsManager from "./appDocsManager";
import { wrapPlayer } from "../ckin";

27
src/lib/appManagers/appMessagesManager.ts

@ -21,13 +21,20 @@ import apiManager from "../mtproto/apiManager"; @@ -21,13 +21,20 @@ import apiManager from "../mtproto/apiManager";
import appWebPagesManager from "./appWebPagesManager";
import { CancellablePromise, deferredPromise } from "../polyfill";
type HistoryStorage = {
export type HistoryStorage = {
count: number | null,
history: number[],
pending: number[],
readPromise?: any
};
export type HistoryResult = {
count: number,
history: number[],
unreadOffset: number,
unreadSkip: boolean
};
export class AppMessagesManager {
public messagesStorage: any = {};
public messagesForHistory: any = {};
@ -1137,7 +1144,7 @@ export class AppMessagesManager { @@ -1137,7 +1144,7 @@ export class AppMessagesManager {
if(apiMessage.media.ttl_seconds) {
apiMessage.media = {_: 'messageMediaUnsupportedWeb'};
} else {
appDocsManager.saveDoc(apiMessage.media.document, mediaContext);
apiMessage.media.document = appDocsManager.saveDoc(apiMessage.media.document, mediaContext); // 11.04.2020 warning
}
break;
case 'messageMediaWebPage':
@ -3017,12 +3024,7 @@ export class AppMessagesManager { @@ -3017,12 +3024,7 @@ export class AppMessagesManager {
});
}
public getHistory(peerID: number, maxID = 0, limit = 0, backLimit?: number, prerendered?: number): Promise<{
count: number,
history: number[],
unreadOffset: number,
unreadSkip: boolean
}> {
public getHistory(peerID: number, maxID = 0, limit = 0, backLimit?: number, prerendered?: number) {
if(this.migratedFromTo[peerID]) {
peerID = this.migratedFromTo[peerID];
}
@ -3095,10 +3097,12 @@ export class AppMessagesManager { @@ -3095,10 +3097,12 @@ export class AppMessagesManager {
} else {
limit = limit || (offset ? 20 : (prerendered || 5));
}
var history = historyStorage.history.slice(offset, offset + limit);
if(!maxID && historyStorage.pending.length) {
history = historyStorage.pending.slice().concat(history);
}
return this.wrapHistoryResult(peerID, {
count: historyStorage.count,
history: history,
@ -3230,7 +3234,7 @@ export class AppMessagesManager { @@ -3230,7 +3234,7 @@ export class AppMessagesManager {
});
}
public wrapHistoryResult(peerID: number, result: any) {
public wrapHistoryResult(peerID: number, result: HistoryResult) {
var unreadOffset = result.unreadOffset;
if(unreadOffset) {
var i;
@ -3243,7 +3247,8 @@ export class AppMessagesManager { @@ -3243,7 +3247,8 @@ export class AppMessagesManager {
}
}
}
return Promise.resolve(result);
return result;
//return Promise.resolve(result);
}
public requestHistory(peerID: number, maxID: number, limit: number, offset = 0) {
@ -3251,6 +3256,8 @@ export class AppMessagesManager { @@ -3251,6 +3256,8 @@ export class AppMessagesManager {
//console.trace('requestHistory', peerID, maxID, limit, offset);
$rootScope.$broadcast('history_request');
return apiManager.invokeApi('messages.getHistory', {
peer: AppPeersManager.getInputPeerByID(peerID),
offset_id: maxID ? appMessagesIDsManager.getMessageLocalID(maxID) : 0,

140
src/lib/appManagers/appPhotosManager.ts

@ -5,10 +5,6 @@ import { bytesFromHex } from "../bin_utils"; @@ -5,10 +5,6 @@ import { bytesFromHex } from "../bin_utils";
import { MTPhotoSize } from "../../components/wrappers";
import apiFileManager from "../mtproto/apiFileManager";
import apiManager from "../mtproto/apiManager";
//import { MTPhotoSize } from "../../components/misc";
//import fastdom from "fastdom";
//import 'fastdom/fastdom-strict'; // exclude in production
type MTPhoto = {
_: 'photo',
@ -20,7 +16,9 @@ type MTPhoto = { @@ -20,7 +16,9 @@ type MTPhoto = {
date: number,
sizes: Array<MTPhotoSize>,
dc_id: number,
user_id: number
user_id: number,
downloaded?: boolean
};
export class AppPhotosManager {
@ -35,36 +33,32 @@ export class AppPhotosManager { @@ -35,36 +33,32 @@ export class AppPhotosManager {
constructor() {
window.addEventListener('resize', (e) => {
//fastdom.measure(() => {
this.windowW = document.body.scrollWidth;
this.windowH = document.body.scrollHeight;
//});
//console.log(`Set windowW, windowH: ${this.windowW}x${this.windowH}`);
});
//fastdom.measure(() => {
console.log('measure works');
this.windowW = document.body.scrollWidth;
this.windowH = document.body.scrollHeight;
//});
}
public savePhoto(apiPhoto: any, context?: any) {
if(context) {
Object.assign(apiPhoto, context);
}
public savePhoto(photo: MTPhoto, context?: any) {
if(this.photos[photo.id]) return this.photos[photo.id];
/* if(context) {
Object.assign(photo, context);
} */ // warning
if(!apiPhoto.id) {
console.warn('no apiPhoto.id', apiPhoto);
} else this.photos[apiPhoto.id] = apiPhoto;
if(!photo.id) {
console.warn('no apiPhoto.id', photo);
} else this.photos[photo.id] = photo;
if(!('sizes' in apiPhoto)) return;
if(!('sizes' in photo)) return;
apiPhoto.sizes.forEach((photoSize: any) => {
photo.sizes.forEach((photoSize: any) => {
if(photoSize._ == 'photoCachedSize') {
apiFileManager.saveSmallFile(photoSize.location, photoSize.bytes);
console.log('clearing photo cached size', apiPhoto);
console.log('clearing photo cached size', photo);
// Memory
photoSize.size = photoSize.bytes.length;
@ -72,6 +66,25 @@ export class AppPhotosManager { @@ -72,6 +66,25 @@ export class AppPhotosManager {
photoSize._ = 'photoSize';
}
});
/* if(!photo.downloaded) {
photo.downloaded = apiFileManager.isFileExists({
_: 'inputPhotoFileLocation',
id: photo.id,
access_hash: photo.access_hash,
file_reference: photo.file_reference
});
// apiFileManager.isFileExists({
// _: 'inputPhotoFileLocation',
// id: photo.id,
// access_hash: photo.access_hash,
// file_reference: photo.file_reference
// }).then(downloaded => {
// photo.downloaded = downloaded;
// });
} */
return photo;
}
public choosePhotoSize(photo: any, width = 0, height = 0) {
@ -140,9 +153,6 @@ export class AppPhotosManager { @@ -140,9 +153,6 @@ export class AppPhotosManager {
}
public setAttachmentPreview(bytes: Uint8Array, element: HTMLElement | SVGSVGElement, isSticker = false, background = false) {
//image.src = "data:image/jpeg;base64," + bytesToBase64(photo.sizes[0].bytes);
//photo.sizes[0].bytes = new Uint8Array([...photo.sizes[0].bytes].reverse());
let arr: Uint8Array;
if(!isSticker) {
arr = AppPhotosManager.jf.concat(bytes.slice(3), AppPhotosManager.Df);
@ -157,12 +167,18 @@ export class AppPhotosManager { @@ -157,12 +167,18 @@ export class AppPhotosManager {
let blob = new Blob([arr], {type: "image/jpeg"});
if(background) {
element.style.backgroundImage = 'url(' + URL.createObjectURL(blob) + ')';
let url = URL.createObjectURL(blob);
let img = new Image();
img.src = url;
img.onload = () => {
element.style.backgroundImage = 'url(' + url + ')';
};
//element.style.backgroundImage = 'url(' + url + ')';
} else {
if(element instanceof SVGSVGElement) {
let image = document.createElementNS("http://www.w3.org/2000/svg", "image");
let image = element.firstElementChild as SVGImageElement || document.createElementNS("http://www.w3.org/2000/svg", "image");
image.setAttributeNS(null, 'href', URL.createObjectURL(blob));
//image.setAttributeNS(null, 'preserveAspectRatio', 'xMinYMin slice');
element.append(image);
} else {
let image = new Image();
@ -189,7 +205,7 @@ export class AppPhotosManager { @@ -189,7 +205,7 @@ export class AppPhotosManager {
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
let sizes = photo.sizes || photo.thumbs;
if(sizes && sizes[0].bytes) {
if((!photo.downloaded || isSticker) && sizes && sizes[0].bytes) {
this.setAttachmentPreview(sizes[0].bytes, element, isSticker);
}
@ -221,7 +237,7 @@ export class AppPhotosManager { @@ -221,7 +237,7 @@ export class AppPhotosManager {
return photoSize;
}
public async preloadPhoto(photoID: any, photoSize?: MTPhotoSize): Promise<Blob> {
public preloadPhoto(photoID: any, photoSize?: MTPhotoSize): Promise<Blob> {
let photo: any = null;
if(typeof(photoID) === 'string') {
@ -239,8 +255,6 @@ export class AppPhotosManager { @@ -239,8 +255,6 @@ export class AppPhotosManager {
}
if(photoSize && photoSize._ != 'photoSizeEmpty') {
photoSize.preloaded = true;
// maybe it's a thumb
let isPhoto = photoSize.size && photo.access_hash && photo.file_reference;
let location = isPhoto ? {
@ -250,34 +264,29 @@ export class AppPhotosManager { @@ -250,34 +264,29 @@ export class AppPhotosManager {
file_reference: photo.file_reference,
thumb_size: photoSize.type
} : photoSize.location;
/* if(overwrite) {
await apiFileManager.deleteFile(location);
console.log('Photos deleted file!');
} */
let promise: Promise<Blob>;
if(isPhoto/* && photoSize.size >= 1e6 */) {
//console.log('Photos downloadFile exec', photo);
/* let promise = apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
let blob = await promise;
if(blob.size < photoSize.size && overwrite) {
await apiFileManager.deleteFile(location);
console.log('Photos deleted file!');
return apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
}
return blob; */
return apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
promise = apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
} else {
//console.log('Photos downloadSmallFile exec', photo, location);
return apiFileManager.downloadSmallFile(location);
promise = apiFileManager.downloadSmallFile(location);
}
if(typeof(photoID) === 'string') {
promise.then(() => {
this.photos[photoID].downloaded = true;
});
}
return promise;
} else return Promise.reject('no photoSize');
}
public getPhoto(photoID: string) {
return this.photos[photoID] || {_: 'photoEmpty'};
return this.photos[photoID] || {_: 'photoEmpty'} as unknown as Partial<MTPhoto>;
}
public wrapForHistory(photoID: string, options: any = {}) {
@ -311,36 +320,7 @@ export class AppPhotosManager { @@ -311,36 +320,7 @@ export class AppPhotosManager {
return photo;
}
/* public wrapForFull(photoID: string) {
var photo = this.wrapForHistory(photoID);
var fullWidth = document.body.scrollWidth - (Config.Mobile ? 0 : 32);
var fullHeight = document.body.scrollHeight - (Config.Mobile ? 0 : 116);
if (!Config.Mobile && fullWidth > 800) {
fullWidth -= 208;
}
var fullPhotoSize = this.choosePhotoSize(photo, fullWidth, fullHeight);
var full: any = {};
full.width = fullWidth;
full.height = fullHeight;
if (fullPhotoSize && fullPhotoSize._ != 'photoSizeEmpty') {
var wh = calcImageInBox(fullPhotoSize.w, fullPhotoSize.h, fullWidth, fullHeight, true);
full.width = wh.w;
full.height = wh.h;
full.modalWidth = Math.max(full.width, Math.min(400, fullWidth));
full.location = fullPhotoSize.location;
full.size = fullPhotoSize.size;
}
photo.full = full;
return photo;
} */
public downloadPhoto(photoID: string) {
var photo = this.photos[photoID];
var ext = 'jpg';

105
src/lib/appManagers/appSidebarRight.ts

@ -289,17 +289,24 @@ class AppSidebarRight { @@ -289,17 +289,24 @@ class AppSidebarRight {
let div = document.createElement('div');
//console.log(message, photo);
let sizes = media.sizes || media.thumbs;
if(sizes && sizes[0].bytes) {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, div, false, true);
} /* else {
this.log('no stripped size', message, media);
} */
let isPhoto = media._ == 'photo';
let photo = isPhoto ? appPhotosManager.getPhoto(media.id) : null;
if(!photo || !photo.downloaded) {
//this.log('inputMessagesFilterPhotoVideo', message, media, photo, div);
let sizes = media.sizes || media.thumbs;
if(sizes && sizes[0].bytes) {
appPhotosManager.setAttachmentPreview(sizes[0].bytes, div, false, true);
} /* else {
this.log('no stripped size', message, media);
} */
}
//this.log('inputMessagesFilterPhotoVideo', message, media);
let load = () => appPhotosManager.preloadPhoto(media, appPhotosManager.choosePhotoSize(media, 200, 200))
let load = () => appPhotosManager.preloadPhoto(isPhoto ? media.id : media, appPhotosManager.choosePhotoSize(media, 200, 200))
.then((blob) => {
if($rootScope.selectedPeerID != peerID) {
this.log.warn('peer changed');
@ -308,13 +315,20 @@ class AppSidebarRight { @@ -308,13 +315,20 @@ class AppSidebarRight {
let url = URL.createObjectURL(blob);
this.urlsToRevoke.push(url);
let img = new Image();
img.src = url;
img.onload = () => {
div.style.backgroundImage = 'url(' + url + ')';
};
div.style.backgroundImage = 'url(' + url + ')';
//div.style.backgroundImage = 'url(' + url + ')';
});
div.dataset.mid = '' + message.mid;
this.lazyLoadQueueSidebar.push({div, load});
if(photo && photo.downloaded) load();
else this.lazyLoadQueueSidebar.push({div, load});
this.lastSharedMediaDiv.append(div);
if(this.lastSharedMediaDiv.childElementCount == 3) {
@ -444,9 +458,9 @@ class AppSidebarRight { @@ -444,9 +458,9 @@ class AppSidebarRight {
}
if(elemsToAppend.length) {
window.requestAnimationFrame(() => {
//window.requestAnimationFrame(() => {
elemsToAppend.forEach(el => this.scroll.append(el));
});
//});
}
if(sharedMediaDiv) {
@ -477,28 +491,37 @@ class AppSidebarRight { @@ -477,28 +491,37 @@ class AppSidebarRight {
this.lastSharedMediaDiv = document.createElement('div');
//this.log('fillProfileElements');
this.profileContentEl.parentElement.scrollTop = 0;
this.profileElements.bio.style.display = 'none';
this.profileElements.phone.style.display = 'none';
this.profileElements.username.style.display = 'none';
this.profileElements.notificationsRow.style.display = '';
this.profileElements.notificationsCheckbox.checked = true;
this.profileElements.notificationsStatus.innerText = 'Enabled';
window.requestAnimationFrame(() => {
this.profileContentEl.parentElement.scrollTop = 0;
this.profileElements.bio.style.display = 'none';
this.profileElements.phone.style.display = 'none';
this.profileElements.username.style.display = 'none';
this.profileElements.notificationsRow.style.display = '';
this.profileElements.notificationsCheckbox.checked = true;
this.profileElements.notificationsStatus.innerText = 'Enabled';
Object.keys(this.sharedMedia).forEach(key => {
this.sharedMedia[key].innerHTML = '';
let parent = this.sharedMedia[key].parentElement;
if(!parent.querySelector('.preloader')) {
putPreloader(parent, true);
}
});
this.savedVirtualStates = {};
this.prevTabID = -1;
this.scroll.setVirtualContainer(null);
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
this.loadSidebarMedia(true);
});
this.mediaDivsByIDs = {};
this.lazyLoadQueueSidebar.clear();
Object.keys(this.sharedMedia).forEach(key => {
this.sharedMedia[key].innerHTML = '';
let parent = this.sharedMedia[key].parentElement;
if(!parent.querySelector('.preloader')) {
putPreloader(parent, true);
}
});
this.urlsToRevoke.forEach(url => {
URL.revokeObjectURL(url);
});
@ -509,20 +532,18 @@ class AppSidebarRight { @@ -509,20 +532,18 @@ class AppSidebarRight {
this.cleared[type] = true;
});
this.savedVirtualStates = {};
this.prevTabID = -1;
this.scroll.setVirtualContainer(null);
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
let setText = (text: string, el: HTMLDivElement) => {
el.style.display = '';
if(el.childElementCount > 1) {
el.firstElementChild.remove();
}
let p = document.createElement('p');
p.innerHTML = text;
el.prepend(p);
window.requestAnimationFrame(() => {
if(el.childElementCount > 1) {
el.firstElementChild.remove();
}
let p = document.createElement('p');
p.innerHTML = text;
el.prepend(p);
el.style.display = '';
});
};
// username

134
src/lib/appManagers/appWebPagesManager.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { $rootScope, safeReplaceObject, calcImageInBox, encodeEntities, copy } from "../utils";
import { $rootScope, safeReplaceObject, copy } from "../utils";
import appPhotosManager from "./appPhotosManager";
import appDocsManager from "./appDocsManager";
import { RichTextProcessor } from "../richtextprocessor";
@ -11,80 +11,86 @@ class AppWebPagesManager { @@ -11,80 +11,86 @@ class AppWebPagesManager {
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
let update = e.detail;
switch (update._) {
switch(update._) {
case 'updateWebPage':
this.saveWebPage(update.webpage)
break
this.saveWebPage(update.webpage);
break;
}
})
});
}
saveWebPage(apiWebPage: any, messageID?: number, mediaContext?: any) {
if (apiWebPage.photo && apiWebPage.photo._ === 'photo') {
appPhotosManager.savePhoto(apiWebPage.photo, mediaContext)
if(apiWebPage.photo && apiWebPage.photo._ === 'photo') {
appPhotosManager.savePhoto(apiWebPage.photo, mediaContext);
} else {
delete apiWebPage.photo
delete apiWebPage.photo;
}
if (apiWebPage.document && apiWebPage.document._ === 'document') {
appDocsManager.saveDoc(apiWebPage.document, mediaContext)
if(apiWebPage.document && apiWebPage.document._ === 'document') {
apiWebPage.document = appDocsManager.saveDoc(apiWebPage.document, mediaContext); // warning 11.04.2020
} else {
if (apiWebPage.type == 'document') {
delete apiWebPage.type
if(apiWebPage.type == 'document') {
delete apiWebPage.type;
}
delete apiWebPage.document
delete apiWebPage.document;
}
var siteName = apiWebPage.site_name
var shortTitle = apiWebPage.title || apiWebPage.author || siteName || ''
if (siteName && shortTitle == siteName) {
delete apiWebPage.site_name
var siteName = apiWebPage.site_name;
var shortTitle = apiWebPage.title || apiWebPage.author || siteName || '';
if(siteName && shortTitle == siteName) {
delete apiWebPage.site_name;
}
if (shortTitle.length > 100) {
shortTitle = shortTitle.substr(0, 80) + '...'
if(shortTitle.length > 100) {
shortTitle = shortTitle.substr(0, 80) + '...';
}
apiWebPage.rTitle = RichTextProcessor.wrapRichText(shortTitle, {noLinks: true, noLinebreaks: true})
var contextHashtag = ''
if (siteName == 'GitHub') {
var matches = apiWebPage.url.match(/(https?:\/\/github\.com\/[^\/]+\/[^\/]+)/)
if (matches) {
contextHashtag = matches[0] + '/issues/{1}'
apiWebPage.rTitle = RichTextProcessor.wrapRichText(shortTitle, {noLinks: true, noLinebreaks: true});
var contextHashtag = '';
if(siteName == 'GitHub') {
var matches = apiWebPage.url.match(/(https?:\/\/github\.com\/[^\/]+\/[^\/]+)/);
if(matches) {
contextHashtag = matches[0] + '/issues/{1}';
}
}
// delete apiWebPage.description
var shortDescriptionText = (apiWebPage.description || '')
if (shortDescriptionText.length > 180) {
shortDescriptionText = shortDescriptionText.substr(0, 150).replace(/(\n|\s)+$/, '') + '...'
var shortDescriptionText = (apiWebPage.description || '');
if(shortDescriptionText.length > 180) {
shortDescriptionText = shortDescriptionText.substr(0, 150).replace(/(\n|\s)+$/, '') + '...';
}
apiWebPage.rDescription = RichTextProcessor.wrapRichText(shortDescriptionText, {
contextSite: siteName || 'external',
contextHashtag: contextHashtag
});
if (apiWebPage.type != 'photo' &&
if(apiWebPage.type != 'photo' &&
apiWebPage.type != 'video' &&
apiWebPage.type != 'gif' &&
apiWebPage.type != 'document' &&
!apiWebPage.description &&
apiWebPage.photo) {
apiWebPage.type = 'photo'
apiWebPage.type = 'photo';
}
if (messageID) {
if (this.pendingWebPages[apiWebPage.id] === undefined) {
this.pendingWebPages[apiWebPage.id] = {}
if(messageID) {
if(this.pendingWebPages[apiWebPage.id] === undefined) {
this.pendingWebPages[apiWebPage.id] = {};
}
this.pendingWebPages[apiWebPage.id][messageID] = true
this.webpages[apiWebPage.id] = apiWebPage
this.pendingWebPages[apiWebPage.id][messageID] = true;
this.webpages[apiWebPage.id] = apiWebPage;
}
if (this.webpages[apiWebPage.id] === undefined) {
this.webpages[apiWebPage.id] = apiWebPage
if(this.webpages[apiWebPage.id] === undefined) {
this.webpages[apiWebPage.id] = apiWebPage;
} else {
safeReplaceObject(this.webpages[apiWebPage.id], apiWebPage)
safeReplaceObject(this.webpages[apiWebPage.id], apiWebPage);
}
if (!messageID && this.pendingWebPages[apiWebPage.id] !== undefined) {
var msgs = []
if(!messageID && this.pendingWebPages[apiWebPage.id] !== undefined) {
var msgs = [];
for(let msgID in this.pendingWebPages[apiWebPage.id]) {
msgs.push(msgID);
}
@ -92,58 +98,22 @@ class AppWebPagesManager { @@ -92,58 +98,22 @@ class AppWebPagesManager {
$rootScope.$broadcast('webpage_updated', {
id: apiWebPage.id,
msgs: msgs
})
});
}
}
wrapForHistory (webPageID: number) {
var webPage = copy(this.webpages[webPageID]) || {_: 'webPageEmpty'}
wrapForHistory(webPageID: number) {
var webPage = copy(this.webpages[webPageID]) || {_: 'webPageEmpty'};
if (webPage.photo && webPage.photo.id) {
webPage.photo = appPhotosManager.wrapForHistory(webPage.photo.id, {website: webPage.type != 'photo' && webPage.type != 'video'})
if(webPage.photo && webPage.photo.id) {
webPage.photo = appPhotosManager.wrapForHistory(webPage.photo.id, {website: webPage.type != 'photo' && webPage.type != 'video'});
}
/* if (webPage.document && webPage.document.id) {
webPage.document = appDocsManager.wrapForHistory(webPage.document.id)
} */ // warning
return webPage
}
wrapForFull (webPageID: number) {
var webPage = this.wrapForHistory(webPageID)
if (!webPage.embed_url) {
return webPage
}
var fullWidth = window.innerWidth;
var fullHeight = window.innerHeight;
var full: any = {
width: fullWidth,
height: fullHeight
}
if (!webPage.embed_width || !webPage.embed_height) {
full.height = full.width = Math.min(fullWidth, fullHeight)
} else {
var wh = calcImageInBox(webPage.embed_width, webPage.embed_height, fullWidth, fullHeight)
full.width = wh.w
full.height = wh.h
}
var embedTag = Config.Modes.chrome_packed ? 'webview' : 'iframe'
var embedType = webPage.embed_type != 'iframe' ? webPage.embed_type || 'text/html' : 'text/html'
var embedHtml = '<' + embedTag + ' src="' + encodeEntities(webPage.embed_url) + '" type="' + encodeEntities(embedType) + '" frameborder="0" border="0" webkitallowfullscreen mozallowfullscreen allowfullscreen width="' + full.width + '" height="' + full.height + '" style="width: ' + full.width + 'px; height: ' + full.height + 'px;"></' + embedTag + '>'
full.html = embedHtml;
webPage.full = full
return webPage
return webPage;
}
}

23
src/lib/appManagers/appWebpManager.ts

@ -5,7 +5,7 @@ class AppWebpManager { @@ -5,7 +5,7 @@ class AppWebpManager {
public webpMachine: any = null;
public loaded: Promise<void>;
public busyPromise: Promise<string>;
public queue: {bytes: Uint8Array, img: HTMLImageElement}[] = [];
public queue: {bytes: Uint8Array, img: HTMLImageElement, callback: () => void}[] = [];
//public worker: any;
public webpSupport: Promise<boolean> = null;
@ -56,7 +56,7 @@ class AppWebpManager { @@ -56,7 +56,7 @@ class AppWebpManager {
this.busyPromise = Promise.resolve('');
let {img, bytes} = this.queue.pop();
let {img, bytes, callback} = this.queue.pop();
if(!this.loaded) {
this.loadWebpHero();
@ -66,6 +66,7 @@ class AppWebpManager { @@ -66,6 +66,7 @@ class AppWebpManager {
this.busyPromise = this.convert(bytes);
img.src = await this.busyPromise;
callback();
this.busyPromise = null;
@ -97,15 +98,17 @@ class AppWebpManager { @@ -97,15 +98,17 @@ class AppWebpManager {
return;
}
const reader = new FileReader();
reader.addEventListener('loadend', async(e) => {
// @ts-ignore
let bytes = new Uint8Array(e.srcElement.result);
this.queue.push({bytes, img});
this.processQueue();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.addEventListener('loadend', (e) => {
// @ts-ignore
let bytes = new Uint8Array(e.srcElement.result);
this.queue.push({bytes, img, callback: resolve});
this.processQueue();
});
reader.readAsArrayBuffer(blob);
});
reader.readAsArrayBuffer(blob);
}
}

7
src/lib/bin_utils.ts

@ -10,13 +10,6 @@ import {BigInteger, SecureRandom} from 'jsbn'; @@ -10,13 +10,6 @@ import {BigInteger, SecureRandom} from 'jsbn';
// @ts-ignore
import pako from 'pako/dist/pako_inflate.min.js';
//import { dT } from './utils.js';
/* import './closure_long.js';
//var goog = window ? window.goog as any : this.goog;
var goog = typeof(window) !== 'undefined' ? window.goog : this.goog; */
var _logTimer = (new Date()).getTime()
export function dT () {
return '[' + (((new Date()).getTime() - _logTimer) / 1000).toFixed(3) + ']'

223
src/lib/crypto/crypto.js

@ -716,14 +716,11 @@ var CryptoJS /* = this.CryptoJS = globalThis.CryptoJS */ = CryptoJS || (function @@ -716,14 +716,11 @@ var CryptoJS /* = this.CryptoJS = globalThis.CryptoJS */ = CryptoJS || (function
*/
var C_algo = C.algo = {};
return C;
}(Math));
module.exports = CryptoJS;
(function (undefined) {
// X-64
// Shortcuts
var C = CryptoJS;
var C_lib = C.lib;
var Base = C_lib.Base;
var X32WordArray = C_lib.WordArray;
@ -1004,20 +1001,11 @@ module.exports = CryptoJS; @@ -1004,20 +1001,11 @@ module.exports = CryptoJS;
return clone;
}
});
}());
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
/**
* Cipher core components.
*/
CryptoJS.lib.Cipher || (function (undefined) {
// CIPHER
// Shortcuts
var C = CryptoJS;
var C_lib = C.lib;
var Base = C_lib.Base;
var WordArray = C_lib.WordArray;
@ -1979,20 +1967,13 @@ CryptoJS.lib.Cipher || (function (undefined) { @@ -1979,20 +1967,13 @@ CryptoJS.lib.Cipher || (function (undefined) {
return plaintext;
}
});
}());
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
(function () {
// AES
// Shortcuts
var C = CryptoJS;
var C_lib = C.lib;
var BlockCipher = C_lib.BlockCipher;
var C_algo = C.algo;
@ -2196,192 +2177,8 @@ code.google.com/p/crypto-js/wiki/License @@ -2196,192 +2177,8 @@ code.google.com/p/crypto-js/wiki/License
* var plaintext = CryptoJS.AES.decrypt(ciphertext, key, cfg);
*/
C.AES = BlockCipher._createHelper(AES);
}());
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
(function (Math) {
// Shortcuts
var C = CryptoJS;
var C_lib = C.lib;
var WordArray = C_lib.WordArray;
var Hasher = C_lib.Hasher;
var C_algo = C.algo;
// Initialization and round constants tables
var H = [];
var K = [];
// Compute constants
(function () {
function isPrime(n) {
var sqrtN = Math.sqrt(n);
for (var factor = 2; factor <= sqrtN; factor++) {
if (!(n % factor)) {
return false;
}
}
return true;
}
function getFractionalBits(n) {
return ((n - (n | 0)) * 0x100000000) | 0;
}
var n = 2;
var nPrime = 0;
while (nPrime < 64) {
if (isPrime(n)) {
if (nPrime < 8) {
H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2));
}
K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));
nPrime++;
}
n++;
}
}());
// Reusable object
var W = [];
/**
* SHA-256 hash algorithm.
*/
var SHA256 = C_algo.SHA256 = Hasher.extend({
_doReset: function () {
this._hash = new WordArray.init(H.slice(0));
},
_doProcessBlock: function (M, offset) {
// Shortcut
var H = this._hash.words;
// Working variables
var a = H[0];
var b = H[1];
var c = H[2];
var d = H[3];
var e = H[4];
var f = H[5];
var g = H[6];
var h = H[7];
// Computation
for (var i = 0; i < 64; i++) {
if (i < 16) {
W[i] = M[offset + i] | 0;
} else {
var gamma0x = W[i - 15];
var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^
((gamma0x << 14) | (gamma0x >>> 18)) ^
(gamma0x >>> 3);
var gamma1x = W[i - 2];
var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
((gamma1x << 13) | (gamma1x >>> 19)) ^
(gamma1x >>> 10);
W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];
}
var ch = (e & f) ^ (~e & g);
var maj = (a & b) ^ (a & c) ^ (b & c);
var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22));
var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25));
var t1 = h + sigma1 + ch + K[i] + W[i];
var t2 = sigma0 + maj;
h = g;
g = f;
f = e;
e = (d + t1) | 0;
d = c;
c = b;
b = a;
a = (t1 + t2) | 0;
}
// Intermediate hash value
H[0] = (H[0] + a) | 0;
H[1] = (H[1] + b) | 0;
H[2] = (H[2] + c) | 0;
H[3] = (H[3] + d) | 0;
H[4] = (H[4] + e) | 0;
H[5] = (H[5] + f) | 0;
H[6] = (H[6] + g) | 0;
H[7] = (H[7] + h) | 0;
},
_doFinalize: function () {
// Shortcuts
var data = this._data;
var dataWords = data.words;
var nBitsTotal = this._nDataBytes * 8;
var nBitsLeft = data.sigBytes * 8;
// Add padding
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;
data.sigBytes = dataWords.length * 4;
// Hash final blocks
this._process();
// Return final computed hash
return this._hash;
},
clone: function () {
var clone = Hasher.clone.call(this);
clone._hash = this._hash.clone();
return clone;
}
});
/**
* Shortcut function to the hasher's object interface.
*
* @param {WordArray|string} message The message to hash.
*
* @return {WordArray} The hash.
*
* @static
*
* @example
*
* var hash = CryptoJS.SHA256('message');
* var hash = CryptoJS.SHA256(wordArray);
*/
C.SHA256 = Hasher._createHelper(SHA256);
/**
* Shortcut function to the HMAC's object interface.
*
* @param {WordArray|string} message The message to hash.
* @param {WordArray|string} key The secret key.
*
* @return {WordArray} The HMAC.
*
* @static
*
* @example
*
* var hmac = CryptoJS.HmacSHA256(message, key);
*/
C.HmacSHA256 = Hasher._createHmacHelper(SHA256);
return C;
}(Math));
module.exports = CryptoJS;

201
src/lib/crypto/crypto_utils.ts

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
import sha1 from '@cryptography/sha1';
import sha256 from '@cryptography/sha256';
import {str2bigInt, bpe, equalsInt, greater,
copy_, eGCD_, add_, rightShift_, sub_, copyInt_, isZero,
@ -16,6 +17,21 @@ export function bytesFromLeemonBigInt(bigInt: BigInteger) { @@ -16,6 +17,21 @@ export function bytesFromLeemonBigInt(bigInt: BigInteger) {
return bytesFromHex(str);
}
export function bytesToWordss(input: ArrayBuffer | Uint8Array) {
let bytes: Uint8Array;
if(input instanceof ArrayBuffer) bytes = new Uint8Array(input);
else bytes = input;
var len = bytes.length;
var words: number[] = [];
var i;
for(i = 0; i < len; i++) {
words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);
}
return new Uint32Array(words);
}
export function bytesToWords(bytes: any) {
if(bytes instanceof ArrayBuffer) {
bytes = new Uint8Array(bytes);
@ -46,23 +62,30 @@ export function sha1HashSync(bytes: number[] | ArrayBuffer | Uint8Array) { @@ -46,23 +62,30 @@ export function sha1HashSync(bytes: number[] | ArrayBuffer | Uint8Array) {
return new Uint8Array(hashBytes);
}
export function sha256HashSync(bytes: any) {
// console.log(dT(), 'SHA-2 hash start', bytes.byteLength || bytes.length)
var hashWords = CryptoJS.SHA256(bytesToWords(bytes));
// console.log(dT(), 'SHA-2 hash finish')
export function sha256HashSync(bytes: Uint8Array | ArrayBuffer) {
//console.log(dT(), 'SHA-256 hash start');
var hashBytes = bytesFromWords(hashWords);
let words = bytesToWordss(bytes);
let hash = sha256(words);
return hashBytes;
// bytesFromWords below
var o = [];
for(var i = 0; i < hash.length * 4; i++) {
o.push((hash[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
}
//console.log(dT(), 'SHA-256 hash finish');
return o;
}
export function aesEncryptSync(bytes: any, keyBytes: any, ivBytes: any, _mode = 'IGE') {
export function aesEncryptSync(bytes: any, keyBytes: any, ivBytes: any) {
// console.log(dT(), 'AES encrypt start', len/*, bytesToHex(keyBytes), bytesToHex(ivBytes)*/)
// console.log('aes before padding bytes:', bytesToHex(bytes));
bytes = addPadding(bytes);
// console.log('aes after padding bytes:', bytesToHex(bytes));
let mode = CryptoJS.mode[_mode];
let mode = CryptoJS.mode.IGE;
let encryptedWords = CryptoJS.AES.encrypt(bytesToWords(bytes), bytesToWords(keyBytes), {
iv: bytesToWords(ivBytes),
@ -76,9 +99,9 @@ export function aesEncryptSync(bytes: any, keyBytes: any, ivBytes: any, _mode = @@ -76,9 +99,9 @@ export function aesEncryptSync(bytes: any, keyBytes: any, ivBytes: any, _mode =
return encryptedBytes;
}
export function aesDecryptSync(encryptedBytes: any, keyBytes: any, ivBytes: any, _mode = 'IGE') {
export function aesDecryptSync(encryptedBytes: any, keyBytes: any, ivBytes: any) {
let mode = CryptoJS.mode[_mode];
let mode = CryptoJS.mode.IGE;
// console.log(dT(), 'AES decrypt start', encryptedBytes.length)
var decryptedWords = CryptoJS.AES.decrypt({ciphertext: bytesToWords(encryptedBytes)}, bytesToWords(keyBytes), {
iv: bytesToWords(ivBytes),
@ -98,11 +121,6 @@ export function rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes @@ -98,11 +121,6 @@ export function rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes
bytes = addPadding(bytes, 255);
/* var N = new BigInteger(publicKey.modulus, 16);
var E = new BigInteger(publicKey.exponent, 16);
var X = new BigInteger(bytes);
var encryptedBigInt = X.modPowInt(E, N),
encryptedBytes = bytesFromBigInt(encryptedBigInt, 256); */
var N = str2bigInt(publicKey.modulus, 16);
var E = str2bigInt(publicKey.exponent, 16);
var X = str2bigInt(bytesToHex(bytes), 16);
@ -110,7 +128,7 @@ export function rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes @@ -110,7 +128,7 @@ export function rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes
var encryptedBigInt = powMod(X, E, N);
var encryptedBytes = bytesFromHex(bigInt2str(encryptedBigInt, 16));
console.log(dT(), 'RSA encrypt finish'/* , encryptedBytes *//* , bigInt2str(encryptedBigInt, 16) */);
console.log(dT(), 'RSA encrypt finish');
return encryptedBytes;
}
@ -172,162 +190,11 @@ export function pqPrimeFactorization(pqBytes: any) { @@ -172,162 +190,11 @@ export function pqPrimeFactorization(pqBytes: any) {
console.error('Pq leemon Exception', e);
}
/* if(result === false && what.bitLength() <= 64) {
console.time('PQ long');
try {
result = pqPrimeLong(goog.math.Long.fromString(what.toString(16), 16));
} catch (e) {
console.error('Pq long Exception', e);
}
console.timeEnd('PQ long');
}
// console.log(result)
if(result === false) {
console.time('pq BigInt');
result = pqPrimeBigInteger(what);
console.timeEnd('pq BigInt');
} */
console.log(dT(), 'PQ finish');
return result;
}
/* export function pqPrimeBigInteger(what: any) {
var it = 0,
g;
for(var i = 0; i < 3; i++) {
var q = (nextRandomInt(128) & 15) + 17;
var x = bigint(nextRandomInt(1000000000) + 1);
var y = x.clone();
var lim = 1 << (i + 18);
for(var j = 1; j < lim; j++) {
++it;
var a = x.clone();
var b = x.clone();
var c = bigint(q);
while(!b.equals(BigInteger.ZERO)) {
if(!b.and(BigInteger.ONE).equals(BigInteger.ZERO)) {
c = c.add(a);
if(c.compareTo(what) > 0) {
c = c.subtract(what);
}
}
a = a.add(a);
if(a.compareTo(what) > 0) {
a = a.subtract(what);
}
b = b.shiftRight(1);
}
x = c.clone();
var z = x.compareTo(y) < 0 ? y.subtract(x) : x.subtract(y);
g = z.gcd(what);
if(!g.equals(BigInteger.ONE)) {
break;
}
if((j & (j - 1)) == 0) {
y = x.clone();
}
}
if(g.compareTo(BigInteger.ONE) > 0) {
break;
}
}
var f = what.divide(g), P, Q;
if(g.compareTo(f) > 0) {
P = f;
Q = g;
} else {
P = g;
Q = f;
}
return [bytesFromBigInt(P), bytesFromBigInt(Q), it];
} */
/* export function gcdLong(a: any, b: any) {
while(a.notEquals(goog.math.Long.ZERO) && b.notEquals(goog.math.Long.ZERO)) {
while(b.and(goog.math.Long.ONE).equals(goog.math.Long.ZERO)) {
b = b.shiftRight(1);
}
while(a.and(goog.math.Long.ONE).equals(goog.math.Long.ZERO)) {
a = a.shiftRight(1);
}
if(a.compare(b) > 0) {
a = a.subtract(b);
} else {
b = b.subtract(a);
}
}
return b.equals(goog.math.Long.ZERO) ? a : b;
} */
/* export function pqPrimeLong(what: any) {
var it = 0,
g;
for (var i = 0; i < 3; i++) {
var q = goog.math.Long.fromInt((nextRandomInt(128) & 15) + 17);
var x = goog.math.Long.fromInt(nextRandomInt(1000000000) + 1);
var y = x;
var lim = 1 << (i + 18);
for(var j = 1; j < lim; j++) {
++it;
var a = x;
var b = x;
var c = q;
while(b.notEquals(goog.math.Long.ZERO)) {
if(b.and(goog.math.Long.ONE).notEquals(goog.math.Long.ZERO)) {
c = c.add(a);
if (c.compare(what) > 0) {
c = c.subtract(what);
}
}
a = a.add(a);
if(a.compare(what) > 0) {
a = a.subtract(what);
}
b = b.shiftRight(1);
}
x = c;
var z = x.compare(y) < 0 ? y.subtract(x) : x.subtract(y);
g = gcdLong(z, what);
if(g.notEquals(goog.math.Long.ONE)) {
break;
}
if((j & (j - 1)) == 0) {
y = x;
}
}
if(g.compare(goog.math.Long.ONE) > 0) {
break;
}
}
var f = what.div(g), P, Q;
if(g.compare(f) > 0) {
P = f;
Q = g;
} else {
P = g;
Q = f;
}
return [bytesFromHex(P.toString(16)), bytesFromHex(Q.toString(16)), it];
} */
export function pqPrimeLeemon(what: any) {
var minBits = 64;
var minLen = Math.ceil(minBits / bpe) + 1;

56
src/lib/crypto/cryptoworker.ts

@ -22,11 +22,6 @@ class CryptoWorker { @@ -22,11 +22,6 @@ class CryptoWorker {
private pending: Array<Task> = [];
private debug = false;
// @ts-ignore
// private webCrypto = Config.Modes.webcrypto && window.crypto && (window.crypto.subtle || window.crypto.webkitSubtle); /* || window.msCrypto && window.msCrypto.subtle*/
// private useSha1Crypto = this.webCrypto && this.webCrypto.digest !== undefined;
// private useSha256Crypto = this.webCrypto && this.webCrypto.digest !== undefined;
constructor() {
console.log(dT(), 'CW constructor');
@ -96,57 +91,18 @@ class CryptoWorker { @@ -96,57 +91,18 @@ class CryptoWorker {
//if(this.webWorker) {
return this.performTaskWorker<Uint8Array>('sha1-hash', bytes);
//}
/* if(this.useSha1Crypto) {
console.error('usesha1crypto');
// We don't use buffer since typedArray.subarray(...).buffer gives the whole buffer and not sliced one. webCrypto.digest supports typed array
return new Promise((resolve, reject) => {
var bytesTyped = Array.isArray(bytes) ? convertToUint8Array(bytes) : bytes;
// console.log(dT(), 'Native sha1 start')
this.webCrypto.digest({name: 'SHA-1'}, bytesTyped).then((digest: ArrayBuffer) => {
// console.log(dT(), 'Native sha1 done')
resolve(digest);
}, (e: ErrorEvent) => {
console.error('Crypto digest error', e);
this.useSha1Crypto = false;
resolve(sha1HashSync(bytes));
})
});
}
return Promise.resolve(sha1HashSync(bytes)); */
}
public sha256Hash(bytes: any) {
//if(this.webWorker) {
return this.performTaskWorker<number[]>('sha256-hash', bytes);
//}
/* if(this.useSha256Crypto) {
return new Promise((resolve, reject) => {
var bytesTyped = Array.isArray(bytes) ? convertToUint8Array(bytes) : bytes;
// console.log(dT(), 'Native sha1 start')
this.webCrypto.digest({name: 'SHA-256'}, bytesTyped).then((digest: ArrayBuffer) => {
// console.log(dT(), 'Native sha1 done')
resolve(digest);
}, (e: ErrorEvent) => {
console.error('Crypto digest error', e);
this.useSha256Crypto = false;
resolve(sha256HashSync(bytes));
})
});
}
return Promise.resolve(sha256HashSync(bytes)); */
}
public pbkdf2(buffer: Uint8Array, salt: Uint8Array, iterations: number) {
//if(this.webWorker) {
return this.performTaskWorker<ArrayBuffer>('pbkdf2', buffer, salt, iterations);
//}
//return hash_pbkdf2(buffer, salt, iterations);
}
public aesEncrypt(bytes: any, keyBytes: any, ivBytes: any) {
@ -154,8 +110,6 @@ class CryptoWorker { @@ -154,8 +110,6 @@ class CryptoWorker {
return this.performTaskWorker<ArrayBuffer>('aes-encrypt', convertToArrayBuffer(bytes),
convertToArrayBuffer(keyBytes), convertToArrayBuffer(ivBytes));
//}
//return Promise.resolve<ArrayBuffer>(convertToArrayBuffer(aesEncryptSync(bytes, keyBytes, ivBytes)));
}
public aesDecrypt(encryptedBytes: any, keyBytes: any, ivBytes: any): Promise<ArrayBuffer> {
@ -164,8 +118,6 @@ class CryptoWorker { @@ -164,8 +118,6 @@ class CryptoWorker {
encryptedBytes, keyBytes, ivBytes)
.then(bytes => convertToArrayBuffer(bytes));
//}
//return Promise.resolve<ArrayBuffer>(convertToArrayBuffer(aesDecryptSync(encryptedBytes, keyBytes, ivBytes)));
}
public rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes: any): Promise<number[]> {
@ -178,29 +130,21 @@ class CryptoWorker { @@ -178,29 +130,21 @@ class CryptoWorker {
//if(this.webWorker) {
return this.performTaskWorker<[number[], number[], number]>('factorize', bytes);
//}
//return Promise.resolve(pqPrimeFactorization(bytes));
}
public modPow(x: any, y: any, m: any) {
//if(this.webWorker) {
return this.performTaskWorker<number[]>('mod-pow', x, y, m);
//}
//return Promise.resolve(bytesModPow(x, y, m));
}
public gzipUncompress<T>(bytes: ArrayBuffer, toString?: boolean) {
//if(this.webWorker) {
return this.performTaskWorker<T>('unzip', bytes, toString);
//}
//return Promise.resolve(gzipUncompress(bytes, toString) as T);
}
}
const cryptoWorker = new CryptoWorker();
(window as any).CryptoWorker = cryptoWorker;
export default cryptoWorker;

42
src/lib/idb.ts

@ -254,6 +254,48 @@ class IdbFileStorage { @@ -254,6 +254,48 @@ class IdbFileStorage {
});
}
public getAllKeys(): Promise<Array<string>> {
console.time('getAllEntries');
return this.openDatabase().then((db) => {
// @ts-ignore
var objectStore = db.transaction([this.dbStoreName], IDBTransaction.READ || 'readonly')
.objectStore(this.dbStoreName);
var request = objectStore.getAllKeys();
return new Promise((resolve, reject) => {
request.onsuccess = function(event) {
// @ts-ignore
var result = event.target.result;
resolve(result);
console.timeEnd('getAllEntries');
}
request.onerror = reject;
});
});
}
public isFileExists(fileName: string): Promise<boolean> {
console.time('isFileExists');
return this.openDatabase().then((db) => {
// @ts-ignore
var objectStore = db.transaction([this.dbStoreName], IDBTransaction.READ || 'readonly')
.objectStore(this.dbStoreName);
var request = objectStore.openCursor(fileName);
return new Promise((resolve, reject) => {
request.onsuccess = function(event) {
// @ts-ignore
var cursor = event.target.result;
resolve(!!cursor);
console.timeEnd('isFileExists');
}
request.onerror = reject;
});
});
}
public getFileWriter(fileName: string, mimeType: string) {
var fakeWriter = FileManager.getFakeFileWriter(mimeType, (blob: any) => {
this.saveFile(fileName, blob);

4
src/lib/lottie.ts

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
// @ts-ignore
import LottiePlayer from "lottie-web/build/player/lottie_canvas.min.js";
(window as any).lottie = LottiePlayer;

78
src/lib/lottieLoader.ts

@ -1,14 +1,11 @@ @@ -1,14 +1,11 @@
//import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web";
// @ts-ignore
import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web/build/player/lottie_canvas.min.js";
//import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web/build/player/lottie_light.min.js";
import { isElementInViewport, isInDOM } from "./utils";
import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web/build/player/lottie.d";
class LottieLoader {
public lottie: /* any */ typeof LottiePlayer = null;
private lottie: /* any */ typeof LottiePlayer = null;
private animations: {
[group: string]: {
animation: AnimationItem,
animation: /* any */AnimationItem,
container: HTMLDivElement,
paused: boolean,
autoplay: boolean,
@ -16,6 +13,28 @@ class LottieLoader { @@ -16,6 +13,28 @@ class LottieLoader {
}[]
} = {};
private debug = false;
public loaded: Promise<void>;
private lastTimeLoad = 0;
private waitingTimeouts = 0;
public loadLottie() {
if(this.loaded) return this.loaded;
this.loaded = new Promise((resolve, reject) => {
(window as any).lottieLoaded = () => {
console.log('lottie loaded');
this.lottie = (window as any).lottie;
resolve();
};
let sc = document.createElement('script');
sc.src = 'npm.lottie-web.chunk.js';
sc.async = true;
sc.onload = (window as any).lottieLoaded;
document.body.appendChild(sc);
});
}
public checkAnimations(blurred?: boolean, group?: string, destroy = false) {
let groups = group ? [group] : Object.keys(this.animations);
@ -33,6 +52,13 @@ class LottieLoader { @@ -33,6 +52,13 @@ class LottieLoader {
for(let i = length - 1; i >= 0; --i) {
let {animation, container, paused, autoplay, canvas} = animations[i];
if(destroy && !isInDOM(container)) {
this.debug && console.log('destroy animation');
animation.destroy();
animations.splice(i, 1);
continue;
}
if(canvas) {
let c = container.firstElementChild as HTMLCanvasElement;
if(!c.height && !c.width && isElementInViewport(container)) {
@ -41,16 +67,9 @@ class LottieLoader { @@ -41,16 +67,9 @@ class LottieLoader {
}
}
if(destroy && !isInDOM(container)) {
this.debug && console.log('destroy animation');
animation.destroy();
animations.splice(i, 1);
continue;
}
if(!autoplay) continue;
if(!isElementInViewport(container) || blurred) {
if(blurred || !isElementInViewport(container)) {
if(!paused) {
this.debug && console.log('pause animation', isElementInViewport(container), container);
animation.pause();
@ -66,16 +85,10 @@ class LottieLoader { @@ -66,16 +85,10 @@ class LottieLoader {
}
public async loadAnimation(params: /* any */AnimationConfigWithPath | AnimationConfigWithData, group = '') {
/* if(!this.lottie) {
this.lottie = (await import(
'lottie-web')).default;
this.lottie.setQuality('low');
} */
//params.autoplay = false;
params.renderer = 'canvas';
params.rendererSettings = {
//context: canvasContext, // the canvas context
//context: context, // the canvas context
//preserveAspectRatio: 'xMinYMin slice', // Supports the same options as the svg element's preserveAspectRatio property
clearCanvas: true,
progressiveLoad: true, // Boolean, only svg renderer, loads dom elements when needed. Might speed up initialization for large number of elements.
@ -83,12 +96,27 @@ class LottieLoader { @@ -83,12 +96,27 @@ class LottieLoader {
};
if(!this.lottie) {
this.lottie = LottiePlayer;
//this.lottie.setQuality('low');
this.lottie.setQuality(10);
if(!this.loaded) this.loadLottie();
await this.loaded;
this.lottie.setQuality('low');
//this.lottie.setQuality(10);
}
let time = Date.now();
let diff = time - this.lastTimeLoad;
let delay = 150;
if(diff < delay) {
delay *= ++this.waitingTimeouts;
console.log('lottieloader delay:', delay);
//await new Promise((resolve) => setTimeout(resolve, delay));
this.waitingTimeouts--;
}
let animation = this.lottie.loadAnimation(params);
this.lastTimeLoad = Date.now();
if(!this.animations[group]) this.animations[group] = [];
this.animations[group].push({
animation,
@ -101,7 +129,7 @@ class LottieLoader { @@ -101,7 +129,7 @@ class LottieLoader {
if(params.autoplay) {
this.checkAnimations();
}
return animation;
}

89
src/lib/mtproto/apiFileManager.ts

@ -6,8 +6,6 @@ import apiManager from "./apiManager"; @@ -6,8 +6,6 @@ import apiManager from "./apiManager";
import { logger, deferredPromise, CancellablePromise } from "../polyfill";
export class ApiFileManager {
public cachedFs = false;
public cachedFsPromise = false;
public cachedSavePromises: {
[fileName: string]: Promise<Blob>
} = {};
@ -18,6 +16,9 @@ export class ApiFileManager { @@ -18,6 +16,9 @@ export class ApiFileManager {
[fileName: string]: any
} = {};
/* public indexedKeys: Set<string> = new Set();
private keysLoaded = false; */
public downloadPulls: {
[x: string]: Array<{
cb: () => Promise<unknown>,
@ -93,8 +94,8 @@ export class ApiFileManager { @@ -93,8 +94,8 @@ export class ApiFileManager {
var fileName = (location.file_name as string || '').split('.');
var ext: string = fileName[fileName.length - 1] || '';
if(location.stickerType == 1 /* && !WebpManager.isWebpSupported() */) { // warning
ext += 'webp'; /* 'png'; */
if(location.stickerType == 1) {
ext += 'webp';
} else if(location.stickerType == 2) {
ext += 'tgs';
}
@ -109,13 +110,13 @@ export class ApiFileManager { @@ -109,13 +110,13 @@ export class ApiFileManager {
var ext: string = 'jpg';
if(location.stickerType == 1) {
ext = 'webp'/* WebpManager.isWebpSupported() ? 'webp' : 'png'*/;
ext = 'webp';
} else if(location.stickerType == 2) {
ext += 'tgs';
}
if(location.volume_id) {
return location.volume_id + '_' + location.local_id /* + '_' + location.secret */ + '.' + ext;
return location.volume_id + '_' + location.local_id + '.' + ext;
} else {
return location.id + '_' + location.access_hash + '.' + ext;
}
@ -138,19 +139,16 @@ export class ApiFileManager { @@ -138,19 +139,16 @@ export class ApiFileManager {
}
public getFileStorage(): typeof IdbFileStorage {
if(!Config.Modes.memory_only) {
/* if(TmpfsFileStorage.isAvailable()) {
return TmpfsFileStorage;
} */
if(IdbFileStorage.isAvailable()) {
return IdbFileStorage;
}
}
return IdbFileStorage/* MemoryFileStorage */;
return IdbFileStorage;
}
/* public isFileExists(location: any) {
var fileName = this.getFileName(location);
return this.cachedDownloads[fileName] || this.indexedKeys.has(fileName);
//return this.cachedDownloads[fileName] || this.indexedKeys.has(fileName) ? Promise.resolve(true) : this.getFileStorage().isFileExists(fileName);
} */
public saveSmallFile(location: any, bytes: Uint8Array) {
var fileName = this.getFileName(location);
@ -172,6 +170,10 @@ export class ApiFileManager { @@ -172,6 +170,10 @@ export class ApiFileManager {
return Promise.reject({type: 'BROWSER_BLOB_NOT_SUPPORTED'});
}
/* if(!this.keysLoaded) {
this.getIndexedKeys();
} */
//this.log('downloadSmallFile', location, options);
let dcID = options.dcID || location.dc_id;
@ -243,6 +245,14 @@ export class ApiFileManager { @@ -243,6 +245,14 @@ export class ApiFileManager {
return fileStorage.getFile(fileName, size);
}
/* public getIndexedKeys() {
this.keysLoaded = true;
this.getFileStorage().getAllKeys().then(keys => {
this.indexedKeys.clear();
this.indexedKeys = new Set(keys);
});
} */
public downloadFile(dcID: number, location: any, size: number, options: {
mimeType?: string,
dcID?: number,
@ -253,14 +263,8 @@ export class ApiFileManager { @@ -253,14 +263,8 @@ export class ApiFileManager {
return Promise.reject({type: 'BROWSER_BLOB_NOT_SUPPORTED'});
}
/* var processSticker = false;
if(location.sticker && !WebpManager.isWebpSupported()) {
if(options.toFileEntry || size > 524288) {
delete location.sticker;
} else {
processSticker = true;
options.mime = 'image/png';
}
/* if(!this.keysLoaded) {
this.getIndexedKeys();
} */
// this.log('Dload file', dcID, location, size)
@ -273,8 +277,6 @@ export class ApiFileManager { @@ -273,8 +277,6 @@ export class ApiFileManager {
if(cachedPromise) {
if(toFileEntry) {
/* let blob = await cachedPromise;
return FileManager.copy(blob, toFileEntry) as Promise<Blob>; */
return cachedPromise.then((blob: any) => {
return FileManager.copy(blob, toFileEntry);
});
@ -283,13 +285,6 @@ export class ApiFileManager { @@ -283,13 +285,6 @@ export class ApiFileManager {
//this.log('downloadFile cachedPromise');
if(size) {
/* let blob = await cachedPromise;
if(blob.size < size) {
this.log('downloadFile need to deleteFile, wrong size:', blob.size, size);
await this.deleteFile(location);
} else {
return cachedPromise;
} */
return cachedPromise.then((blob: Blob) => {
if(blob.size < size) {
this.log('downloadFile need to deleteFile, wrong size:', blob.size, size);
@ -300,7 +295,6 @@ export class ApiFileManager { @@ -300,7 +295,6 @@ export class ApiFileManager {
return this.downloadFile(dcID, location, size, options);
});
} else {
//return cachedPromise;
return blob;
}
});
@ -309,13 +303,8 @@ export class ApiFileManager { @@ -309,13 +303,8 @@ export class ApiFileManager {
}
}
//this.log('arriba');
//var deferred = $q.defer()
let deferred = deferredPromise<Blob>();
//return;
var canceled = false;
var resolved = false;
var mimeType = options.mimeType || 'image/jpeg',
@ -345,7 +334,6 @@ export class ApiFileManager { @@ -345,7 +334,6 @@ export class ApiFileManager {
} else {
deferred.resolve(this.cachedDownloads[fileName] = blob);
}
//}, () => {
}).catch(() => {
//this.log('not i wanted');
//var fileWriterPromise = toFileEntry ? FileManager.getFileWriter(toFileEntry) : fileStorage.getFileWriter(fileName, mimeType);
@ -496,7 +484,6 @@ export class ApiFileManager { @@ -496,7 +484,6 @@ export class ApiFileManager {
var totalParts = Math.ceil(fileSize / partSize);
var fileID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)];
//var deferred = $q.defer();
var _part = 0,
resultInputFile = {
@ -507,23 +494,6 @@ export class ApiFileManager { @@ -507,23 +494,6 @@ export class ApiFileManager {
md5_checksum: ''
};
/* let deferred: {
then?: any,
resolve?: (input: typeof resultInputFile) => void,
reject?: (error: any) => void,
promise?: any,
cancel?: () => void,
notify?: (details: {done: number, total: number}) => void
} = {
};
deferred.promise = new Promise<typeof resultInputFile>((resolve, reject) => {
deferred.resolve = resolve;
deferred.reject = reject;
}); */
let deferredHelper: {
resolve?: (input: typeof resultInputFile) => void,
reject?: (error: any) => void,
@ -538,7 +508,6 @@ export class ApiFileManager { @@ -538,7 +508,6 @@ export class ApiFileManager {
deferredHelper.resolve = resolve;
deferredHelper.reject = reject;
//return Promise.resolve();
});
Object.assign(deferred, deferredHelper);

3
src/lib/mtproto/authorizer.ts

@ -408,7 +408,8 @@ export class Authorizer { @@ -408,7 +408,8 @@ export class Authorizer {
var gBytes = bytesFromHex(auth.g.toString(16));
auth.b = new Array(256);
MTProto.secureRandom.nextBytes(auth.b);
auth.b = [...new Uint8Array(auth.b.length).randomize()];
//MTProto.secureRandom.nextBytes(auth.b);
try {
var gB = await CryptoWorker.modPow(gBytes, auth.b, auth.dhPrime);

7
src/lib/mtproto/mtproto.ts

@ -54,14 +54,9 @@ export class TelegramMeWebService { @@ -54,14 +54,9 @@ export class TelegramMeWebService {
export const telegramMeWebService = new TelegramMeWebService();
// @ts-ignore
import {SecureRandom} from 'jsbn';
export namespace MTProto {
//$($window).on('click keydown', rng_seed_time); // WARNING!
export const secureRandom = new SecureRandom();
export const passwordManager = PasswordManager;
export const dcConfigurator = DcConfigurator;
export const rsaKeysManager = RSAKeysManager;
@ -73,4 +68,4 @@ export namespace MTProto { @@ -73,4 +68,4 @@ export namespace MTProto {
export const serverTimeManager = ServerTimeManager;
}
//(window as any).MTProto = MTProto;
(window as any).MTProto = MTProto;

6
src/lib/mtproto/networker.ts

@ -128,7 +128,8 @@ class MTPNetworker { @@ -128,7 +128,8 @@ class MTPNetworker {
this.seqNo = 0;
this.prevSessionID = this.sessionID;
this.sessionID = new Array(8);
MTProto.secureRandom.nextBytes(this.sessionID);
this.sessionID = [...new Uint8Array(this.sessionID.length).randomize()];
//MTProto.secureRandom.nextBytes(this.sessionID);
}
public setupMobileSleep() {
@ -789,7 +790,8 @@ class MTPNetworker { @@ -789,7 +790,8 @@ class MTPNetworker {
var paddingLength = (16 - (data.offset % 16)) + 16 * (1 + nextRandomInt(5));
var padding = new Array(paddingLength);
MTProto.secureRandom.nextBytes(padding);
padding = [...new Uint8Array(padding.length).randomize()];
//MTProto.secureRandom.nextBytes(padding);
var dataWithPadding = bufferConcat(dataBuffer, padding);
// this.log('Adding padding', dataBuffer, padding, dataWithPadding)

7
src/lib/polyfill.ts

@ -1,5 +1,8 @@ @@ -1,5 +1,8 @@
import { bytesToHex, bytesFromHex, dT, bufferConcats } from "./bin_utils";
import { MTProto } from "./mtproto/mtproto";
// @ts-ignore
import {SecureRandom} from 'jsbn';
export const secureRandom = new SecureRandom();
export function logger(prefix: string) {
function Log(...args: any[]) {
@ -69,7 +72,7 @@ Object.defineProperty(Uint8Array.prototype, 'hex', { @@ -69,7 +72,7 @@ Object.defineProperty(Uint8Array.prototype, 'hex', {
});
Uint8Array.prototype.randomize = function() {
MTProto.secureRandom.nextBytes(this);
secureRandom.nextBytes(this);
return this;
};

28
src/lib/webp.ts

@ -1,4 +1,3 @@ @@ -1,4 +1,3 @@
import {Webp} from "webp-hero/libwebp/dist/webp.js"
import {detectWebpSupport} from "webp-hero/dist/detect-webp-support.js"
@ -30,22 +29,21 @@ export class WebpMachine { @@ -30,22 +29,21 @@ export class WebpMachine {
* Decode raw webp data into a png data url
*/
async decode(webpData: Uint8Array): Promise<string> {
if (this.busy) throw new WebpMachineError("cannot decode when already busy")
this.busy = true
if(this.busy) throw new WebpMachineError("cannot decode when already busy");
this.busy = true;
try {
await relax()
const canvas = document.createElement("canvas")
this.webp.setCanvas(canvas)
this.webp.webpToSdl(webpData, webpData.length)
this.busy = false
return canvas.toDataURL()
}
catch (error) {
this.busy = false
error.name = WebpMachineError.name
error.message = `failed to decode webp image: ${error.message}`
throw error
await relax();
const canvas = document.createElement("canvas");
this.webp.setCanvas(canvas);
this.webp.webpToSdl(webpData, webpData.length);
this.busy = false;
return canvas.toDataURL();
} catch(error) {
this.busy = false;
error.name = WebpMachineError.name;
error.message = `failed to decode webp image: ${error.message}`;
throw error;
}
}
}

20
src/scss/partials/_chat.scss

@ -7,7 +7,8 @@ @@ -7,7 +7,8 @@
overflow: hidden;
flex-direction: column;
position: relative;
background-image: url('../../../public/assets/img/camomile.jpg');
/* background-image: url('../../../public/assets/img/camomile.jpg'); */
background-image: url('../../../public/assets/img/camomile_blurred.jpg');
background-size: cover;
background-position: center center;
@ -120,6 +121,8 @@ @@ -120,6 +121,8 @@
bottom: 0;
left: 0;
display: flex; // for end
/* display: flex;
flex-direction: column;
justify-content: flex-end; */
@ -155,6 +158,7 @@ @@ -155,6 +158,7 @@
box-sizing: border-box;
/* min-height: 100%; */
justify-content: flex-end;
flex: 1;
&.is-chat {
.is-in .bubble__container {
@ -237,6 +241,10 @@ @@ -237,6 +241,10 @@
&.service {
display: block;
padding: 1rem 0;
.bubble__container {
max-width: 100%;
}
}
&.forwarded {
@ -264,8 +272,12 @@ @@ -264,8 +272,12 @@
}
}
/* &.is-group-first {
padding-top: 10px;
} */
&.is-group-last {
padding-bottom: 2.5px;
padding-bottom: 5px;
}
&:not(.forwarded) {
@ -328,6 +340,7 @@ @@ -328,6 +340,7 @@
height: 18px;
width: 18px;
margin: 0 .05rem;
vertical-align: bottom;
}
span.emoji {
@ -738,7 +751,8 @@ @@ -738,7 +751,8 @@
&__container > .name {
/* padding: .2675rem .6rem 0 .6rem; */
padding: .32rem .6rem 0 .6rem;
/* padding: .32rem .6rem 0 .6rem; */
padding: 5px .6rem 0 .6rem;
font-weight: 500;
/* padding-bottom: 4px; */
color: $darkblue;

2
src/scss/partials/_rightSIdebar.scss

@ -185,7 +185,7 @@ @@ -185,7 +185,7 @@
background-position: center center;
display: flex;
background-color: #cecece;
//background-color: #000;
justify-content: center;
align-items: center;

119
src/scss/style.scss

@ -29,8 +29,8 @@ $bg: #ffffff; @@ -29,8 +29,8 @@ $bg: #ffffff;
$text-size: 16px;
$time-size: 12px;
//$large-screen: 1680px;
$large-screen: 16800px;
$large-screen: 1680px;
//$large-screen: 16800px;
@import "partials/ico";
@import "partials/chatlist";
@ -66,15 +66,6 @@ button, input, optgroup, select, textarea, html { @@ -66,15 +66,6 @@ button, input, optgroup, select, textarea, html {
//min-width: 100%;
margin: 0 auto;
max-width: $large-screen;
@media (min-width: $large-screen) {
border-top-width: 0;
border-bottom-width: 0;
border-left-width: 1px;
border-right-width: 1px;
border-style: solid;
border-color: #DADCE0;
}
}
.disable-hover,
@ -336,84 +327,47 @@ input { @@ -336,84 +327,47 @@ input {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: transparent;
border-radius: inherit;
}
.c-ripple__circle {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
opacity: 0;
width: 0;
height: 0;
border-radius: 50%;
/* background: rgba(255, 255, 255, 0.25); */
background: rgba(112, 117, 121, .08);
}
.c-ripple.active .c-ripple__circle {
//-webkit-animation: a-ripple 750ms ease-in;
//animation: a-ripple 750ms ease-in-out;
will-change: padding-bottom, width, opacity;
-webkit-animation: a-ripple 625ms ease-in-out;
animation: a-ripple 625ms ease-in-out;
}
/**
* Animation: Ripple
* --------------------------------------------------
*/
@-webkit-keyframes a-ripple {
0% {
opacity: 0;
//opacity: 1;
}
25% {
opacity: 1;
&__circle {
background-color: rgba(0,0,0,.08);
display: block;
position: absolute;
transform: scale(0);
border-radius: 50%;
animation: ripple-effect .7s forwards;
transition: .35s opacity;
overflow: hidden;
}
100% {
width: 200%;
padding-bottom: 200%;
&__circle.hiding {
opacity: 0;
}
}
@keyframes a-ripple {
@keyframes ripple-effect {
0% {
opacity: 0;
//opacity: 1;
//transform: translate(-50%, -50%) scale(0);
transform: scale(0);
}
25% {
/* 50% {
opacity: 1;
}
100% {
width: 200%;
padding-bottom: 200%;
opacity: 0;
}
}
} */
/* .ripple {
background-color: rgba(112, 117, 121, .08);
width: 1rem;
height: 1rem;
position: absolute;
border-radius: 50%;
transform: translateX(-100%) translateY(-100%);
mix-blend-mode: screen;
animation: ripple 750ms ease-out forwards;
to {
transform: scale(2);
//transform: translate(-50%, -50%) scale(2);
//opacity: 0;
}
}
@keyframes ripple {
0% { transform: translate(-100%, -100%); }
80% { transform: translate(-100%, -100%) scale(50); }
100% { transform: translate(-100%, -100%) scale(50); }
} */
.document {
padding-left: 4.5rem;
height: 70px;
@ -810,6 +764,10 @@ input { @@ -810,6 +764,10 @@ input {
font-size: 24px;
line-height: 1;
}
.scrollable {
position: relative;
}
}
.phone-code {
@ -1313,7 +1271,7 @@ span.popup-close { @@ -1313,7 +1271,7 @@ span.popup-close {
}
}
/* div.scrollable::-webkit-scrollbar {
div.scrollable::-webkit-scrollbar {
width: 0;
height: 0;
}
@ -1330,7 +1288,7 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -1330,7 +1288,7 @@ div.scrollable::-webkit-scrollbar-thumb {
}
::-webkit-scrollbar-corner {
background-color: transparent;
} */
}
.scrollable {
width: 100%;
@ -1395,7 +1353,7 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -1395,7 +1353,7 @@ div.scrollable::-webkit-scrollbar-thumb {
transition-duration: .2s;
transition-timing-function: ease-in-out;
display: none;
//display: none;
border-radius: $border-radius;
z-index: 2;
@ -1622,6 +1580,15 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -1622,6 +1580,15 @@ div.scrollable::-webkit-scrollbar-thumb {
stroke: $button-primary-background;
}
}
@media (min-width: $large-screen) {
border-top-width: 0;
border-bottom-width: 0;
border-left-width: 1px;
border-right-width: 1px;
border-style: solid;
border-color: #DADCE0;
}
}
.page-password {

8
src/tests/lol.test.ts

@ -3,7 +3,7 @@ import { bytesFromHex, bytesFromArrayBuffer } from '../lib/bin_utils'; @@ -3,7 +3,7 @@ import { bytesFromHex, bytesFromArrayBuffer } from '../lib/bin_utils';
test('factorize', () => {
for(let i = 0; i < 10; ++i) {
CryptoWorker.factorize(new Uint8Array([20, 149, 30, 137, 202, 169, 105, 69])).then((pAndQ: any) => {
CryptoWorker.factorize(new Uint8Array([20, 149, 30, 137, 202, 169, 105, 69])).then(pAndQ => {
pAndQ.pop();
expect(pAndQ).toEqual([[59, 165, 190, 67], [88, 86, 117, 215]]);
});
@ -25,6 +25,12 @@ test('sha1', () => { @@ -25,6 +25,12 @@ test('sha1', () => {
});
});
test('sha256', () => {
CryptoWorker.sha256Hash(new Uint8Array([112, 20, 211, 20, 106, 249, 203, 252, 39, 107, 106, 194, 63, 60, 13, 130, 51, 78, 107, 6, 110, 156, 214, 65, 205, 10, 30, 150, 79, 10, 145, 194, 232, 240, 127, 55, 146, 103, 248, 227, 160, 172, 30, 153, 122, 189, 110, 162, 33, 86, 174, 117])).then(bytes => {
expect(bytes).toEqual(new Uint8Array([158, 59, 39, 247, 130, 244, 235, 160, 16, 249, 34, 114, 67, 171, 203, 208, 187, 72, 217, 106, 253, 62, 195, 242, 52, 118, 99, 72, 221, 29, 203, 95]));
});
});
test('pbkdf2', () => {
/* const crypto = require('crypto');

5
tsconfig.json

@ -65,9 +65,10 @@ @@ -65,9 +65,10 @@
"node_modules",
"public",
"coverage",
/* "./src/lib/StackBlur.js",
"./src/lib/ckin.js",
"./src/lib/StackBlur.js",
"./src/lib/*.js",
"./src/*.js",
"*.js", */
"*.js",
]
}

6
webpack.common.js

@ -54,7 +54,8 @@ module.exports = { @@ -54,7 +54,8 @@ module.exports = {
//entry: './src/index.ts',
entry: {
index: './src/index.ts',
webp: './src/lib/webp.ts'
webp: './src/lib/webp.ts',
lottie: './src/lib/lottie.ts'
},
/* entry: {
index: './src/index.ts',
@ -72,6 +73,7 @@ module.exports = { @@ -72,6 +73,7 @@ module.exports = {
contentBase: path.join(__dirname, 'public'),
watchContentBase: true,
compress: true,
http2: true,
port: 9000
},
@ -93,7 +95,7 @@ module.exports = { @@ -93,7 +95,7 @@ module.exports = {
minifyURLs: true
}, */
chunks: "all",
excludeChunks: ['npm.webp-hero']
excludeChunks: ['npm.webp-hero', 'npm.lottie-web']
})
],
};

Loading…
Cancel
Save