Pre-lags fix
This commit is contained in:
parent
34cc14d3f5
commit
cd04dc6b34
1822
package-lock.json
generated
1822
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
53
package.json
53
package.json
@ -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 @@
|
||||
"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
Normal file
23
server.cert
Normal file
@ -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
Normal file
20
server.js
Normal file
@ -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
Normal file
28
server.key
Normal file
@ -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
Normal file
162
src/components/bubbleGroups.ts
Normal file
@ -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(); */
|
||||
}
|
||||
}
|
@ -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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
this.loadingMedia--;
|
||||
}
|
||||
|
||||
console.log('lazyLoadQueue: loaded media');
|
||||
|
||||
if(this.lazyLoadMedia.length) {
|
||||
this.processQueue();
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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 => {
|
||||
}
|
||||
}); */
|
||||
|
||||
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 => {
|
||||
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 => {
|
||||
//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) {
|
||||
|
@ -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 () => {
|
||||
});
|
||||
|
||||
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) {
|
||||
|
@ -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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
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 {
|
||||
}
|
||||
} 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 {
|
||||
}
|
||||
} 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 {
|
||||
}
|
||||
|
||||
set scrollTop(y: number) {
|
||||
fastdom.mutate(() => {
|
||||
//fastdom.mutate(() => {
|
||||
this.container.scrollTop = y;
|
||||
});
|
||||
//});
|
||||
}
|
||||
|
||||
get scrollTop() {
|
||||
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
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 {
|
||||
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},
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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},
|
||||
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
|
||||
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
|
||||
|
||||
/////////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?: (
|
||||
//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?: (
|
||||
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?: (
|
||||
} 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);
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
|
||||
div.style.backgroundColor = '';
|
||||
div.classList.add('tgico-savedmessages');
|
||||
div.classList.remove('tgico-avatar_deletedaccount');
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
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 {
|
||||
//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);
|
||||
|
@ -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 {
|
||||
if(apiDoc._ == 'documentEmpty') {
|
||||
apiDoc.size = 0;
|
||||
}
|
||||
|
||||
return apiDoc;
|
||||
}
|
||||
|
||||
public getDoc(docID: string) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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";
|
||||
|
@ -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 {
|
||||
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 {
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
} 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 {
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
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 {
|
||||
|
||||
//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,
|
||||
|
@ -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 = {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
//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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
|
||||
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';
|
||||
|
@ -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 {
|
||||
|
||||
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 {
|
||||
}
|
||||
|
||||
if(elemsToAppend.length) {
|
||||
window.requestAnimationFrame(() => {
|
||||
//window.requestAnimationFrame(() => {
|
||||
elemsToAppend.forEach(el => this.scroll.append(el));
|
||||
});
|
||||
//});
|
||||
}
|
||||
|
||||
if(sharedMediaDiv) {
|
||||
@ -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 {
|
||||
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
|
||||
|
@ -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 {
|
||||
$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 {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
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 {
|
||||
|
||||
this.busyPromise = this.convert(bytes);
|
||||
img.src = await this.busyPromise;
|
||||
callback();
|
||||
|
||||
this.busyPromise = null;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) + ']'
|
||||
|
@ -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;
|
||||
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) {
|
||||
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
|
||||
* 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;
|
||||
|
@ -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) {
|
||||
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) {
|
||||
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 =
|
||||
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
|
||||
|
||||
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
|
||||
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) {
|
||||
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;
|
||||
|
@ -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 {
|
||||
//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 {
|
||||
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 {
|
||||
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 {
|
||||
//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;
|
||||
|
@ -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
Normal file
4
src/lib/lottie.ts
Normal file
@ -0,0 +1,4 @@
|
||||
// @ts-ignore
|
||||
import LottiePlayer from "lottie-web/build/player/lottie_canvas.min.js";
|
||||
|
||||
(window as any).lottie = LottiePlayer;
|
@ -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 {
|
||||
}[]
|
||||
} = {};
|
||||
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 {
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
};
|
||||
|
||||
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 {
|
||||
if(params.autoplay) {
|
||||
this.checkAnimations();
|
||||
}
|
||||
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
[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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
//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 {
|
||||
return this.downloadFile(dcID, location, size, options);
|
||||
});
|
||||
} else {
|
||||
//return cachedPromise;
|
||||
return blob;
|
||||
}
|
||||
});
|
||||
@ -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 {
|
||||
} 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 {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
deferredHelper.resolve = resolve;
|
||||
deferredHelper.reject = reject;
|
||||
//return Promise.resolve();
|
||||
});
|
||||
Object.assign(deferred, deferredHelper);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
export const serverTimeManager = ServerTimeManager;
|
||||
}
|
||||
|
||||
//(window as any).MTProto = MTProto;
|
||||
(window as any).MTProto = MTProto;
|
||||
|
@ -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 {
|
||||
|
||||
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)
|
||||
|
@ -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', {
|
||||
});
|
||||
|
||||
Uint8Array.prototype.randomize = function() {
|
||||
MTProto.secureRandom.nextBytes(this);
|
||||
secureRandom.nextBytes(this);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -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 {
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 @@
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
display: flex; // for end
|
||||
|
||||
/* display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end; */
|
||||
@ -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 @@
|
||||
&.service {
|
||||
display: block;
|
||||
padding: 1rem 0;
|
||||
|
||||
.bubble__container {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.forwarded {
|
||||
@ -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 @@
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
margin: 0 .05rem;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
span.emoji {
|
||||
@ -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;
|
||||
|
@ -185,7 +185,7 @@
|
||||
background-position: center center;
|
||||
|
||||
display: flex;
|
||||
background-color: #cecece;
|
||||
//background-color: #000;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
|
@ -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 {
|
||||
//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,83 +327,46 @@ input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border-radius: inherit;
|
||||
|
||||
&__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;
|
||||
}
|
||||
|
||||
&__circle.hiding {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.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 {
|
||||
@keyframes ripple-effect {
|
||||
0% {
|
||||
opacity: 0;
|
||||
//opacity: 1;
|
||||
//transform: translate(-50%, -50%) scale(0);
|
||||
transform: scale(0);
|
||||
}
|
||||
25% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
width: 200%;
|
||||
padding-bottom: 200%;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes a-ripple {
|
||||
0% {
|
||||
opacity: 0;
|
||||
//opacity: 1;
|
||||
}
|
||||
25% {
|
||||
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;
|
||||
}
|
||||
/* 50% {
|
||||
opacity: 1;
|
||||
} */
|
||||
|
||||
@keyframes ripple {
|
||||
0% { transform: translate(-100%, -100%); }
|
||||
80% { transform: translate(-100%, -100%) scale(50); }
|
||||
100% { transform: translate(-100%, -100%) scale(50); }
|
||||
} */
|
||||
to {
|
||||
transform: scale(2);
|
||||
//transform: translate(-50%, -50%) scale(2);
|
||||
//opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.document {
|
||||
padding-left: 4.5rem;
|
||||
@ -810,6 +764,10 @@ input {
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.phone-code {
|
||||
@ -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 {
|
||||
}
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
} */
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
width: 100%;
|
||||
@ -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 {
|
||||
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 {
|
||||
|
@ -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', () => {
|
||||
});
|
||||
});
|
||||
|
||||
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');
|
||||
|
||||
|
@ -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",
|
||||
]
|
||||
}
|
||||
|
@ -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 = {
|
||||
contentBase: path.join(__dirname, 'public'),
|
||||
watchContentBase: true,
|
||||
compress: true,
|
||||
http2: true,
|
||||
port: 9000
|
||||
},
|
||||
|
||||
@ -93,7 +95,7 @@ module.exports = {
|
||||
minifyURLs: true
|
||||
}, */
|
||||
chunks: "all",
|
||||
excludeChunks: ['npm.webp-hero']
|
||||
excludeChunks: ['npm.webp-hero', 'npm.lottie-web']
|
||||
})
|
||||
],
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user