Pre-lags fix

This commit is contained in:
Eduard Kuzmenko 2020-04-14 18:46:31 +03:00
parent 60e03cf691
commit 04d5f9f3fb
67 changed files with 2615 additions and 2354 deletions

1822
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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"
}

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -7,9 +7,13 @@
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- <link rel="manifest" href="/site.webmanifest"> -->
<link rel="apple-touch-icon" href="icon.png">
<!-- Place favicon.ico in the root directory -->
<link rel="apple-touch-icon" sizes="180x180" href="assets/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="assets/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/img/favicon-16x16.png">
<link rel="manifest" href="site.webmanifest">
<link rel="mask-icon" href="assets/img/safari-pinned-tab.svg" color="#64a3f0">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<style>
/* cyrillic */
@ -191,6 +195,11 @@
</div>
</div>
<div class="whole valign-wrapper page-chats" style="display: none;">
<svg style="position: absolute; top: -10000px; left: -10000px;">
<defs id="svg-defs">
<path id="message-tail" d="M1.00002881,1.03679295e-14 L7,0 L7,17 C6.8069969,14.1607017 6.12380234,11.2332513 4.95041634,8.21764872 C4.04604748,5.89342034 2.50413132,3.73337411 0.324667862,1.73751004 L0.324652538,1.73752677 C-0.0826597201,1.36452676 -0.110475289,0.731958677 0.262524727,0.324646419 C0.451952959,0.117792698 0.719544377,1.0985861e-14 1.00002881,1.04360964e-14 Z"></path>
</defs>
</svg>
<div class="overlays">
<div class="media-viewer">
<div class="media-viewer-author">
@ -323,7 +332,7 @@
</div>
</div>
</div>
<div id="bubbles">
<div id="bubbles" class="scrolled-down">
<div id="bubbles-inner"></div>
<div id="bubbles-go-down" class="tgico-down z-depth-1 rp" style="display: none;"></div>
</div>
@ -411,6 +420,7 @@
<button class="btn-circle z-depth-1 btn-icon tgico-microphone2" id="btn-send"></button>
</div>
</div>
<!-- <div class="profile-container sidebar sidebar-right" style="width: 25%;"> -->
<div class="profile-container sidebar sidebar-right">
<div class="sidebar-header">
<button class="btn-icon rp tgico-close sidebar-close-button"></button>
@ -426,56 +436,50 @@
</div>
</div>
<div class="profile-content">
<div class="profile-avatar user-avatar"></div>
<div class="profile-name"></div>
<div class="profile-subtitle"></div>
<div class="profile-row profile-row-bio tgico-info">
<p></p>
<p class="profile-row-label">Bio</p>
<div class="profile-content-wrapper">
<div class="profile-avatar user-avatar"></div>
<div class="profile-name"></div>
<div class="profile-subtitle"></div>
<div class="profile-row profile-row-bio tgico-info">
<p></p>
<p class="profile-row-label">Bio</p>
</div>
<div class="profile-row profile-row-username tgico-username">
<p></p>
<p class="profile-row-label">Username</p>
</div>
<div class="profile-row profile-row-phone tgico-phone">
<p></p>
<p class="profile-row-label">Phone</p>
</div>
<div class="profile-row profile-row-notifications">
<label>
<input type="checkbox" id="profile-notifications" checked="checked">
<span>Notifications</span>
</label>
<p class="profile-row-label">Enabled</p>
</div>
<ul class="profile-tabs menu-horizontal">
<li class="profile-tabs-members rp" style="display: none;">Members</li>
<li class="profile-tabs-media rp">Media</li>
<li class="profile-tabs-docs rp">Docs</li>
<li class="profile-tabs-links rp">Links</li>
<li class="profile-tabs-audio rp">Audio</li>
</ul>
</div>
<div class="profile-row profile-row-username tgico-username">
<p></p>
<p class="profile-row-label">Username</p>
</div>
<div class="profile-row profile-row-phone tgico-phone">
<p></p>
<p class="profile-row-label">Phone</p>
</div>
<div class="profile-row profile-row-notifications">
<label>
<input type="checkbox" id="profile-notifications" checked="checked">
<span>Notifications</span>
</label>
<p class="profile-row-label">Enabled</p>
</div>
<ul class="profile-tabs menu-horizontal">
<li class="profile-tabs-members rp" style="display: none;">Members</li>
<li class="profile-tabs-media rp">Media</li>
<li class="profile-tabs-docs rp">Docs</li>
<li class="profile-tabs-links rp">Links</li>
<li class="profile-tabs-audio rp">Audio</li>
</ul>
<div class="content-container">
<div class="profile-tabs-content tabs-container">
<div class="content-members-container"><div id="content-members">1</div></div>
<div class="content-members-container"><div id="content-members"></div></div>
<div class="content-media-container"><div id="content-media"></div></div>
<div class="content-docs-container"><div id="content-docs"></div></div>
<div class="content-links-container"><div id="content-links">4</div></div>
<div class="content-audio-container"><div id="content-audio">5</div></div>
<div class="content-links-container"><div id="content-links"></div></div>
<div class="content-audio-container"><div id="content-audio"></div></div>
</div>
</div>
</div>
</div>
</div>
<!-- <script src="bundle.js"></script> -->
<!-- <script src="npm.aes-js.chunk.js"></script>
<script src="npm.jsbn.chunk.js"></script>
<script src="npm.leemon.chunk.js"></script>
<script src="npm.lottie-web.chunk.js"></script>
<script src="vendors~main.chunk.js"></script>
<script src="main.bundle.js"></script> -->
</html>
<script type="text/javascript" src="npm.aes-js.chunk.js"></script><script type="text/javascript" src="npm.jsbn.chunk.js"></script><script type="text/javascript" src="npm.leemon.chunk.js"></script><script type="text/javascript" src="npm.lottie-web.chunk.js"></script><script type="text/javascript" src="npm.web-streams-polyfill.chunk.js"></script><script type="text/javascript" src="vendors~index.chunk.js"></script><script type="text/javascript" src="index.bundle.js"></script><script type="text/javascript" src="webp.bundle.js"></script>
<script type="text/javascript" src="npm.aes-js.chunk.js"></script><script type="text/javascript" src="npm.jsbn.chunk.js"></script><script type="text/javascript" src="npm.leemon.chunk.js"></script><script type="text/javascript" src="npm.web-streams-polyfill.chunk.js"></script><script type="text/javascript" src="vendors~index.chunk.js"></script><script type="text/javascript" src="index.bundle.js"></script><script type="text/javascript" src="webp.bundle.js"></script><script type="text/javascript" src="lottie.bundle.js"></script>

Binary file not shown.

1
public/lottie.bundle.js Normal file
View File

@ -0,0 +1 @@
!function(e){function t(t){for(var n,i,l=t[0],f=t[1],a=t[2],p=0,s=[];p<l.length;p++)i=l[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(c&&c(t);s.length;)s.shift()();return u.push.apply(u,a||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,l=1;l<r.length;l++){var f=r[l];0!==o[f]&&(n=!1)}n&&(u.splice(t--,1),e=i(i.s=r[0]))}return e}var n={},o={1:0},u=[];function i(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.m=e,i.c=n,i.d=function(e,t,r){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(i.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)i.d(r,n,function(t){return e[t]}.bind(null,n));return r},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="";var l=window.webpackJsonp=window.webpackJsonp||[],f=l.push.bind(l);l.push=t,l=l.slice();for(var a=0;a<l.length;a++)t(l[a]);var c=f;u.push([93,5]),r()}({93:function(e,t,r){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const o=n(r(94));window.lottie=o.default}});

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

23
server.cert Normal file
View 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
View 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
View 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-----

View 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(); */
}
}

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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) {

View File

@ -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);
});
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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";

View File

@ -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,

View File

@ -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';

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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) + ']'

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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
View File

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

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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;
};

View File

@ -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;
}
}
}

View File

@ -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;

View File

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

View File

@ -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 {

View File

@ -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');

View File

@ -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",
]
}

View File

@ -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']
})
],
};