Pre-lags fix
This commit is contained in:
parent
60e03cf691
commit
04d5f9f3fb
File diff suppressed because it is too large
Load Diff
53
package.json
53
package.json
|
@ -5,7 +5,8 @@
|
|||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --open --config webpack.dev.js",
|
||||
"serve": "webpack-dev-server --open --config webpack.prod.js",
|
||||
"start-production": "webpack-dev-server --open --config webpack.prod.js",
|
||||
"serve-serve": "webpack-serve --compress --port 9001 --host localhost --no-watch --static ./public --config webpack.prod.js",
|
||||
"build": "webpack --config webpack.prod.js",
|
||||
"test": "jest --config=jest.config.js",
|
||||
"profile": "webpack --profile --json > stats.json --config webpack.prod.js"
|
||||
|
@ -14,52 +15,48 @@
|
|||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"jsbn": "^1.1.0",
|
||||
"materialize-css": "^1.0.0",
|
||||
"roboto-fontface": "^0.10.0",
|
||||
"rusha": "^0.8.13",
|
||||
"tdweb": "^1.5.0",
|
||||
"webpack-dev-server": "^3.9.0"
|
||||
"webpack-dev-server": "^3.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.8.3",
|
||||
"@babel/preset-env": "^7.8.3",
|
||||
"@babel/preset-typescript": "^7.8.3",
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/preset-env": "^7.9.5",
|
||||
"@babel/preset-typescript": "^7.9.0",
|
||||
"@cryptography/sha1": "^0.1.0",
|
||||
"@types/aes-js": "^3.1.0",
|
||||
"@cryptography/sha256": "^0.2.0",
|
||||
"@types/aes-js": "^3.1.1",
|
||||
"@types/chrome": "0.0.91",
|
||||
"@types/crypto-js": "^3.1.43",
|
||||
"@types/jest": "^24.9.0",
|
||||
"@types/materialize-css": "^1.0.6",
|
||||
"@types/overlayscrollbars": "^1.9.0",
|
||||
"@types/jest": "^24.9.1",
|
||||
"aes-js": "^3.1.2",
|
||||
"babel-jest": "^24.9.0",
|
||||
"compression": "^1.7.4",
|
||||
"compression-webpack-plugin": "^3.1.0",
|
||||
"css-loader": "^3.2.0",
|
||||
"css-loader": "^3.5.1",
|
||||
"express": "^4.17.1",
|
||||
"fastdom": "^1.0.9",
|
||||
"file-loader": "^4.3.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"install": "^0.13.0",
|
||||
"jest": "^24.9.0",
|
||||
"leemon": "^6.2.0",
|
||||
"lottie-web": "^5.6.4",
|
||||
"lottie-web": "^5.6.8",
|
||||
"node-sass": "^4.13.1",
|
||||
"npm": "^6.14.1",
|
||||
"npm": "^6.14.4",
|
||||
"offscreen-canvas": "^0.1.1",
|
||||
"on-build-webpack": "^0.1.0",
|
||||
"overlayscrollbars": "^1.10.0",
|
||||
"pako": "^1.0.10",
|
||||
"overlayscrollbars": "^1.12.0",
|
||||
"pako": "^1.0.11",
|
||||
"resolve-url-loader": "^3.1.1",
|
||||
"sass-loader": "^8.0.0",
|
||||
"streamsaver": "^2.0.3",
|
||||
"style-loader": "^1.0.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
"streamsaver": "^2.0.4",
|
||||
"style-loader": "^1.1.3",
|
||||
"ts-jest": "^24.3.0",
|
||||
"ts-loader": "^6.2.1",
|
||||
"typescript": "^3.7.2",
|
||||
"url-loader": "^2.2.0",
|
||||
"web-streams-polyfill": "^2.0.6",
|
||||
"ts-loader": "^6.2.2",
|
||||
"typescript": "^3.8.3",
|
||||
"url-loader": "^2.3.0",
|
||||
"web-streams-polyfill": "^2.1.0",
|
||||
"webp-hero": "0.0.0-dev.24",
|
||||
"webpack": "^4.41.2",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"webpack": "^4.42.1",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-merge": "^4.2.2",
|
||||
"worker-loader": "^2.0.0"
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
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.
|
@ -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.
|
@ -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
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
|
@ -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-----
|
|
@ -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...');
|
||||
});
|
|
@ -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-----
|
|
@ -0,0 +1,162 @@
|
|||
import { generatePathData } from "../lib/utils";
|
||||
|
||||
export default class BubbleGroups {
|
||||
bubblesByGroups: Array<{timestamp: number, fromID: number, mid: number, group: HTMLDivElement[]}> = []; // map to group
|
||||
groups: Array<HTMLDivElement[]> = [];
|
||||
//updateRAFs: Map<HTMLDivElement[], number> = new Map();
|
||||
newGroupDiff = 120;
|
||||
|
||||
removeBubble(bubble: HTMLDivElement, mid: number) {
|
||||
let details = this.bubblesByGroups.findAndSplice(g => g.mid == mid);
|
||||
if(details && details.group.length) {
|
||||
details.group.findAndSplice(d => d == bubble);
|
||||
if(!details.group.length) {
|
||||
this.groups.findAndSplice(g => g == details.group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addBubble(bubble: HTMLDivElement, message: any, reverse: boolean) {
|
||||
let timestamp = message.date;
|
||||
let fromID = message.fromID;
|
||||
let group: HTMLDivElement[];
|
||||
|
||||
// try to find added
|
||||
//this.removeBubble(message.mid);
|
||||
|
||||
if(this.bubblesByGroups.length) {
|
||||
if(reverse) {
|
||||
let g = this.bubblesByGroups[0];
|
||||
if(g.fromID == fromID && (g.timestamp - timestamp) < this.newGroupDiff) {
|
||||
group = g.group;
|
||||
group.unshift(bubble);
|
||||
} else {
|
||||
this.groups.unshift(group = [bubble]);
|
||||
}
|
||||
} else {
|
||||
let g = this.bubblesByGroups[this.bubblesByGroups.length - 1];
|
||||
if(g.fromID == fromID && (timestamp - g.timestamp) < this.newGroupDiff) {
|
||||
group = g.group;
|
||||
group.push(bubble);
|
||||
} else {
|
||||
this.groups.push(group = [bubble]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.groups.push(group = [bubble]);
|
||||
}
|
||||
|
||||
//console.log('addBubble', bubble, message.mid, fromID, reverse, group);
|
||||
|
||||
this.bubblesByGroups[reverse ? 'unshift' : 'push']({timestamp, fromID, mid: message.mid, group});
|
||||
this.updateGroup(group, reverse);
|
||||
}
|
||||
|
||||
setClipIfNeeded(bubble: HTMLDivElement, remove = false) {
|
||||
//console.log('setClipIfNeeded', bubble, remove);
|
||||
if(bubble.classList.contains('is-message-empty')/* && !bubble.classList.contains('is-reply') */
|
||||
&& (bubble.classList.contains('photo') || bubble.classList.contains('video'))) {
|
||||
let container = bubble.querySelector('.bubble__media-container') as SVGSVGElement;
|
||||
//console.log('setClipIfNeeded', bubble, remove, container);
|
||||
if(!container) return;
|
||||
|
||||
Array.from(container.children).forEach(object => {
|
||||
if(object instanceof SVGDefsElement) return;
|
||||
|
||||
if(remove) {
|
||||
object.removeAttributeNS(null, 'clip-path');
|
||||
} else {
|
||||
let clipID = container.dataset.clipID;
|
||||
let path = container.firstElementChild.firstElementChild.lastElementChild as SVGPathElement;
|
||||
let width = +object.getAttributeNS(null, 'width');
|
||||
let height = +object.getAttributeNS(null, 'height');
|
||||
let isOut = bubble.classList.contains('is-out');
|
||||
let isReply = bubble.classList.contains('is-reply');
|
||||
let d = '';
|
||||
|
||||
//console.log('setClipIfNeeded', object, width, height, isOut);
|
||||
|
||||
let tr: number, tl: number;
|
||||
if(bubble.classList.contains('forwarded') || isReply) {
|
||||
tr = tl = 0;
|
||||
} else if(isOut) {
|
||||
tr = bubble.classList.contains('is-group-first') ? 12 : 6;
|
||||
tl = 12;
|
||||
} else {
|
||||
tr = 12;
|
||||
tl = bubble.classList.contains('is-group-first') ? 12 : 6;
|
||||
}
|
||||
|
||||
if(isOut) {
|
||||
d = generatePathData(0, 0, width - 9, height, tl, tr, 0, 12);
|
||||
} else {
|
||||
d = generatePathData(9, 0, width - 9, height, tl, tr, 12, 0);
|
||||
}
|
||||
|
||||
path.setAttributeNS(null, 'd', d);
|
||||
object.setAttributeNS(null, 'clip-path', 'url(#' + clipID + ')');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateGroup(group: HTMLDivElement[], reverse = false) {
|
||||
/* if(this.updateRAFs.has(group)) {
|
||||
window.cancelAnimationFrame(this.updateRAFs.get(group));
|
||||
this.updateRAFs.delete(group);
|
||||
} */
|
||||
|
||||
//this.updateRAFs.set(group, window.requestAnimationFrame(() => {
|
||||
//this.updateRAFs.delete(group);
|
||||
|
||||
if(!group.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let first = group[0];
|
||||
|
||||
//appImManager.scrollPosition.prepareFor(reverse ? 'up' : 'down');
|
||||
|
||||
//console.log('updateGroup', group, first);
|
||||
|
||||
if(group.length == 1) {
|
||||
first.classList.add('is-group-first', 'is-group-last');
|
||||
this.setClipIfNeeded(first);
|
||||
return;
|
||||
} else {
|
||||
first.classList.remove('is-group-last');
|
||||
first.classList.add('is-group-first');
|
||||
this.setClipIfNeeded(first, true);
|
||||
}
|
||||
|
||||
let length = group.length - 1;
|
||||
for(let i = 1; i < length; ++i) {
|
||||
let bubble = group[i];
|
||||
bubble.classList.remove('is-group-last', 'is-group-first');
|
||||
this.setClipIfNeeded(bubble, true);
|
||||
}
|
||||
|
||||
let last = group[group.length - 1];
|
||||
last.classList.remove('is-group-first');
|
||||
last.classList.add('is-group-last');
|
||||
this.setClipIfNeeded(last);
|
||||
|
||||
//appImManager.scrollPosition.restore();
|
||||
//}));
|
||||
}
|
||||
|
||||
updateGroupByMessageID(mid: number) {
|
||||
let details = this.bubblesByGroups.find(g => g.mid == mid);
|
||||
if(details) {
|
||||
this.updateGroup(details.group);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.bubblesByGroups = [];
|
||||
/* for(let value of this.updateRAFs.values()) {
|
||||
window.cancelAnimationFrame(value);
|
||||
}
|
||||
this.updateRAFs.clear(); */
|
||||
}
|
||||
}
|
|
@ -1,11 +1,20 @@
|
|||
import { isElementInViewport } from "../lib/utils";
|
||||
|
||||
type LazyLoadElement = {
|
||||
div: HTMLDivElement,
|
||||
load: () => Promise<void>,
|
||||
wasSeen?: boolean
|
||||
};
|
||||
|
||||
export default class LazyLoadQueue {
|
||||
private lazyLoadMedia: Array<{div: HTMLDivElement, load: () => Promise<void>, wasSeen?: boolean}> = [];
|
||||
private lazyLoadMedia: Array<LazyLoadElement> = [];
|
||||
private loadingMedia = 0;
|
||||
private tempID = 0;
|
||||
|
||||
constructor(private parallelLimit = 0) {
|
||||
private lockPromise: Promise<void> = null;
|
||||
private unlockResolve: () => void = null;
|
||||
|
||||
constructor(private parallelLimit = 5) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -15,27 +24,34 @@ export default class LazyLoadQueue {
|
|||
this.loadingMedia = 0;
|
||||
}
|
||||
|
||||
public length() {
|
||||
return this.lazyLoadMedia.length + this.loadingMedia;
|
||||
}
|
||||
|
||||
public lock() {
|
||||
if(this.lockPromise) return;
|
||||
this.lockPromise = new Promise((resolve, reject) => {
|
||||
this.unlockResolve = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
public unlock() {
|
||||
if(!this.unlockResolve) return;
|
||||
this.lockPromise = null;
|
||||
this.unlockResolve();
|
||||
this.unlockResolve = null;
|
||||
}
|
||||
|
||||
public async processQueue(id?: number) {
|
||||
if(this.parallelLimit > 0 && this.loadingMedia >= this.parallelLimit) return;
|
||||
|
||||
let item: {div: HTMLDivElement, load: () => Promise<void>, wasSeen?: boolean};
|
||||
let item: LazyLoadElement;
|
||||
let index: number;
|
||||
/* if(id) item = this.lazyLoadMedia.splice(id, 1) as any;
|
||||
else item = this.lazyLoadMedia.pop(); */
|
||||
|
||||
if(id !== undefined) item = this.lazyLoadMedia.splice(id, 1)[0];
|
||||
else {
|
||||
index = this.lazyLoadMedia.findIndex(i => isElementInViewport(i.div));
|
||||
if(index !== -1) {
|
||||
item = this.lazyLoadMedia.splice(index, 1)[0];
|
||||
} else {
|
||||
//index = this.lazyLoadMedia.findIndex(i => i.wasSeen);
|
||||
//if(index !== -1) {
|
||||
//item = this.lazyLoadMedia.splice(index, 1)[0];
|
||||
/*} else {
|
||||
item = this.lazyLoadMedia.pop();
|
||||
} */
|
||||
|
||||
item = this.lazyLoadMedia.findAndSplice(i => isElementInViewport(i.div));
|
||||
if(!item) {
|
||||
let length = this.lazyLoadMedia.length;
|
||||
for(index = length - 1; index >= 0; --index) {
|
||||
if(this.lazyLoadMedia[index].wasSeen) {
|
||||
|
@ -51,7 +67,16 @@ export default class LazyLoadQueue {
|
|||
|
||||
let tempID = this.tempID;
|
||||
|
||||
console.log('lazyLoadQueue: will load media', this.lockPromise);
|
||||
|
||||
try {
|
||||
if(this.lockPromise) {
|
||||
let perf = performance.now();
|
||||
await this.lockPromise;
|
||||
console.log('lazyLoadQueue: waited lock:', performance.now() - perf);
|
||||
}
|
||||
|
||||
await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
|
||||
await item.load();
|
||||
} catch(err) {
|
||||
console.error('loadMediaQueue error:', err, item, id, index);
|
||||
|
@ -61,6 +86,8 @@ export default class LazyLoadQueue {
|
|||
this.loadingMedia--;
|
||||
}
|
||||
|
||||
console.log('lazyLoadQueue: loaded media');
|
||||
|
||||
if(this.lazyLoadMedia.length) {
|
||||
this.processQueue();
|
||||
}
|
||||
|
@ -68,31 +95,9 @@ export default class LazyLoadQueue {
|
|||
}
|
||||
|
||||
public check(id?: number) {
|
||||
/* if(id !== undefined) {
|
||||
let {div, load} = this.lazyLoadMedia[id];
|
||||
if(isElementInViewport(div)) {
|
||||
//console.log('will load div by id:', div, div.getBoundingClientRect());
|
||||
load();
|
||||
this.lazyLoadMedia.splice(id, 1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let length = this.lazyLoadMedia.length;
|
||||
for(let i = length - 1; i >= 0; --i) {
|
||||
let {div, load} = this.lazyLoadMedia[i];
|
||||
|
||||
if(isElementInViewport(div)) {
|
||||
console.log('will load div:', div);
|
||||
load();
|
||||
this.lazyLoadMedia.splice(i, 1);
|
||||
}
|
||||
} */
|
||||
|
||||
if(id !== undefined) {
|
||||
let {div} = this.lazyLoadMedia[id];
|
||||
if(isElementInViewport(div)) {
|
||||
let {div, wasSeen} = this.lazyLoadMedia[id];
|
||||
if(!wasSeen && isElementInViewport(div)) {
|
||||
//console.log('will load div by id:', div, div.getBoundingClientRect());
|
||||
this.lazyLoadMedia[id].wasSeen = true;
|
||||
this.processQueue(id);
|
||||
|
@ -103,31 +108,25 @@ export default class LazyLoadQueue {
|
|||
|
||||
let length = this.lazyLoadMedia.length;
|
||||
for(let i = length - 1; i >= 0; --i) {
|
||||
let {div} = this.lazyLoadMedia[i];
|
||||
let {div, wasSeen} = this.lazyLoadMedia[i];
|
||||
|
||||
if(isElementInViewport(div)) {
|
||||
if(!wasSeen && isElementInViewport(div)) {
|
||||
//console.log('will load div:', div);
|
||||
this.lazyLoadMedia[i].wasSeen = true;
|
||||
this.processQueue(i);
|
||||
//this.lazyLoadMedia.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* this.lazyLoadMedia = this.lazyLoadMedia.filter(({div, load}) => {
|
||||
if(isElementInViewport(div)) {
|
||||
//console.log('will load div:', div, div.getBoundingClientRect());
|
||||
load();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}); */
|
||||
}
|
||||
|
||||
public push(el: {div: HTMLDivElement, load: () => Promise<void>, wasSeen?: boolean}) {
|
||||
el.wasSeen = false;
|
||||
public push(el: LazyLoadElement) {
|
||||
let id = this.lazyLoadMedia.push(el) - 1;
|
||||
|
||||
this.check(id);
|
||||
|
||||
if(el.wasSeen) {
|
||||
this.processQueue(id);
|
||||
} else {
|
||||
el.wasSeen = false;
|
||||
this.check(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,55 +1,95 @@
|
|||
import apiManager from "../lib/mtproto/apiManager";
|
||||
import { whichChild, findUpTag } from "../lib/utils";
|
||||
|
||||
let onRippleClick = function(this: HTMLElement, e: MouseEvent) {
|
||||
var $circle = this.firstElementChild as HTMLSpanElement;
|
||||
|
||||
var rect = this.parentElement.getBoundingClientRect();
|
||||
var x = e.clientX - rect.left; //x position within the element.
|
||||
var y = e.clientY - rect.top;
|
||||
|
||||
/* var x = e.pageX - this.parentElement.offsetLeft;
|
||||
var y = e.pageY - this.parentElement.offsetTop - this.parentElement.scrollHeight; */
|
||||
|
||||
$circle.style.top = y + 'px';
|
||||
$circle.style.left = x + 'px';
|
||||
|
||||
this.classList.add('active');
|
||||
|
||||
//console.log('onrippleclick', e/* e.pageY, this.parentElement.offsetTop */);
|
||||
};
|
||||
|
||||
export function ripple(elem: Element) {
|
||||
/* elem.addEventListener('click', function(e) {
|
||||
var $circle = elem.querySelector('.c-ripple__circle') as HTMLSpanElement;
|
||||
|
||||
var x = e.pageX - elem.offsetLeft;
|
||||
var y = e.pageY - elem.offsetTop;
|
||||
|
||||
$circle.style.top = y + 'px';
|
||||
$circle.style.left = x + 'px';
|
||||
|
||||
elem.classList.add('active');
|
||||
}); */
|
||||
|
||||
let rippleClickID = 0;
|
||||
export function ripple(elem: HTMLElement, callback: (id: number) => Promise<boolean | void> = () => Promise.resolve(), onEnd: (id: number) => void = null) {
|
||||
let r = document.createElement('div');
|
||||
r.classList.add('c-ripple');
|
||||
|
||||
let span = document.createElement('span');
|
||||
span.classList.add('c-ripple__circle');
|
||||
|
||||
r.append(span);
|
||||
|
||||
elem.append(r);
|
||||
|
||||
elem.addEventListener('mousedown', (e) => {
|
||||
let startTime = Date.now();
|
||||
let span = document.createElement('span');
|
||||
|
||||
let clickID = rippleClickID++;
|
||||
|
||||
let handler = () => {
|
||||
let elapsedTime = Date.now() - startTime;
|
||||
if(elapsedTime < 700) {
|
||||
let delay = Math.max(700 - elapsedTime, 350);
|
||||
setTimeout(() => span.classList.add('hiding'), Math.max(delay - 350, 0));
|
||||
|
||||
setTimeout(() => {
|
||||
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
|
||||
span.remove();
|
||||
if(onEnd) onEnd(clickID);
|
||||
}, delay);
|
||||
} else {
|
||||
span.classList.add('hiding');
|
||||
setTimeout(() => {
|
||||
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
|
||||
span.remove();
|
||||
if(onEnd) onEnd(clickID);
|
||||
}, 350);
|
||||
}
|
||||
};
|
||||
|
||||
callback && callback(clickID);
|
||||
|
||||
/* callback().then((bad) => {
|
||||
if(bad) {
|
||||
span.remove();
|
||||
return;
|
||||
} */
|
||||
|
||||
//console.log('ripple after promise', Date.now() - startTime);
|
||||
//console.log('ripple tooSlow:', tooSlow);
|
||||
/* if(tooSlow) {
|
||||
span.remove();
|
||||
return;
|
||||
} */
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
span.classList.add('c-ripple__circle');
|
||||
let rect = r.getBoundingClientRect();
|
||||
|
||||
let clickX = e.clientX - rect.left;
|
||||
let clickY = e.clientY - rect.top;
|
||||
|
||||
let size: number, clickPos: number;
|
||||
if(rect.width > rect.height) {
|
||||
size = rect.width;
|
||||
clickPos = clickX;
|
||||
} else {
|
||||
size = rect.height;
|
||||
clickPos = clickY;
|
||||
}
|
||||
|
||||
let offsetFromCenter = clickPos > (size / 2) ? size - clickPos : clickPos;
|
||||
size = size - offsetFromCenter;
|
||||
size *= 1.1;
|
||||
|
||||
// center of circle
|
||||
let x = clickX - size / 2;
|
||||
let y = clickY - size / 2;
|
||||
|
||||
//console.log('ripple click', offsetFromCenter, size, clickX, clickY);
|
||||
|
||||
span.style.width = span.style.height = size + 'px';
|
||||
span.style.left = x + 'px';
|
||||
span.style.top = y + 'px';
|
||||
|
||||
r.addEventListener('click', onRippleClick);
|
||||
|
||||
let onEnd = () => {
|
||||
r.classList.remove('active');
|
||||
};
|
||||
|
||||
for(let type of ['animationend', 'webkitAnimationEnd', 'oanimationend', 'MSAnimationEnd']) {
|
||||
r.addEventListener(type, onEnd);
|
||||
}
|
||||
r.append(span);
|
||||
//r.classList.add('active');
|
||||
//handler();
|
||||
});
|
||||
//});
|
||||
|
||||
window.addEventListener('mouseup', () => {
|
||||
//console.time('appImManager: pre render start');
|
||||
handler();
|
||||
}, {once: true});
|
||||
});
|
||||
}
|
||||
|
||||
export function putPreloader(elem: Element, returnDiv = false) {
|
||||
|
@ -156,17 +196,6 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement,
|
|||
});
|
||||
}
|
||||
|
||||
export function getNearestDc() {
|
||||
return apiManager.invokeApi('help.getNearestDc').then((nearestDcResult: any) => {
|
||||
if(nearestDcResult.nearest_dc != nearestDcResult.this_dc) {
|
||||
//MTProto.apiManager.baseDcID = nearestDcResult.nearest_dc;
|
||||
apiManager.getNetworker(nearestDcResult.nearest_dc);
|
||||
}
|
||||
|
||||
return nearestDcResult;
|
||||
});
|
||||
}
|
||||
|
||||
export function formatPhoneNumber(str: string) {
|
||||
str = str.replace(/\D/g, '');
|
||||
let phoneCode = str.slice(0, 6);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//import { appImManager, appMessagesManager, appDialogsManager, apiUpdatesManager, appUsersManager } from "../lib/services";
|
||||
import { openBtnMenu } from "./misc";
|
||||
import {stackBlurImage} from '../lib/StackBlur';
|
||||
//import {stackBlurImage} from '../lib/StackBlur';
|
||||
import appSidebarLeft from "../lib/appManagers/appSidebarLeft";
|
||||
|
||||
export default () => import('../lib/services').then(services => {
|
||||
|
@ -130,7 +130,7 @@ export default () => import('../lib/services').then(services => {
|
|||
}
|
||||
}); */
|
||||
|
||||
fetch('assets/img/camomile.jpg')
|
||||
/* fetch('assets/img/camomile.jpg')
|
||||
.then(res => res.blob())
|
||||
.then(blob => {
|
||||
let img = new Image();
|
||||
|
@ -139,7 +139,11 @@ export default () => import('../lib/services').then(services => {
|
|||
img.onload = () => {
|
||||
let id = 'chat-background-canvas';
|
||||
var canvas = document.getElementById(id) as HTMLCanvasElement;
|
||||
URL.revokeObjectURL(url);
|
||||
//URL.revokeObjectURL(url);
|
||||
|
||||
let elements = ['.chat-container'].map(selector => {
|
||||
return document.querySelector(selector) as HTMLDivElement;
|
||||
});
|
||||
|
||||
stackBlurImage(img, id, 15, 0);
|
||||
|
||||
|
@ -147,13 +151,12 @@ export default () => import('../lib/services').then(services => {
|
|||
//let dataUrl = canvas.toDataURL('image/jpeg', 1);
|
||||
let dataUrl = URL.createObjectURL(blob);
|
||||
|
||||
[/* '.chat-background', '#chat-closed' */'.chat-container'].forEach(selector => {
|
||||
let bg = document.querySelector(selector) as HTMLDivElement;
|
||||
bg.style.backgroundImage = 'url(' + dataUrl + ')';
|
||||
elements.forEach(el => {
|
||||
el.style.backgroundImage = 'url(' + dataUrl + ')';
|
||||
});
|
||||
}, 'image/jpeg', 1);
|
||||
};
|
||||
});
|
||||
}); */
|
||||
|
||||
/* toggleEmoticons.onclick = (e) => {
|
||||
if(!emoticonsDropdown) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { putPreloader, getNearestDc, formatPhoneNumber } from "./misc";
|
||||
import { putPreloader, formatPhoneNumber } from "./misc";
|
||||
import Scrollable from './scrollable';
|
||||
import {RichTextProcessor} from '../lib/richtextprocessor';
|
||||
import * as Config from '../lib/config';
|
||||
|
@ -237,7 +237,14 @@ export default () => {
|
|||
});
|
||||
|
||||
let tryAgain = () => {
|
||||
getNearestDc().then((nearestDcResult: any) => {
|
||||
apiManager.invokeApi('help.getNearestDc').then((nearestDcResult: any) => {
|
||||
if(nearestDcResult.nearest_dc != nearestDcResult.this_dc) {
|
||||
//MTProto.apiManager.baseDcID = nearestDcResult.nearest_dc;
|
||||
apiManager.getNetworker(nearestDcResult.nearest_dc);
|
||||
}
|
||||
|
||||
return nearestDcResult;
|
||||
}).then((nearestDcResult: any) => {
|
||||
let country = countries.find((c) => c.code == nearestDcResult.country);
|
||||
if(country) {
|
||||
if(!selectCountryCode.value.length && !telEl.value.length) {
|
||||
|
|
|
@ -7,6 +7,7 @@ export default class ProgressivePreloader {
|
|||
private progress = 0;
|
||||
private promise: CancellablePromise<any> = null;
|
||||
private tempID = 0;
|
||||
private detached = true;
|
||||
constructor(elem?: Element, private cancelable = true) {
|
||||
this.preloader = document.createElement('div');
|
||||
this.preloader.classList.add('preloader-container');
|
||||
|
@ -68,7 +69,12 @@ export default class ProgressivePreloader {
|
|||
this.setProgress(0);
|
||||
}
|
||||
|
||||
elem.append(this.preloader);
|
||||
this.detached = false;
|
||||
window.requestAnimationFrame(() => {
|
||||
if(this.detached) return;
|
||||
this.detached = false;
|
||||
elem.append(this.preloader);
|
||||
});
|
||||
/* let isIn = isInDOM(this.preloader);
|
||||
|
||||
if(isIn && this.progress != this.defaultProgress) {
|
||||
|
@ -83,8 +89,13 @@ export default class ProgressivePreloader {
|
|||
}
|
||||
|
||||
public detach() {
|
||||
this.detached = true;
|
||||
if(this.preloader.parentElement) {
|
||||
this.preloader.parentElement.removeChild(this.preloader);
|
||||
window.requestAnimationFrame(() => {
|
||||
if(!this.detached) return;
|
||||
this.detached = true;
|
||||
this.preloader.parentElement.removeChild(this.preloader);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -90,11 +90,16 @@ export default class Scrollable {
|
|||
private disableHoverTimeout: number = 0;
|
||||
|
||||
private log: ReturnType<typeof logger>;
|
||||
private debug = true;
|
||||
private debug = false;
|
||||
|
||||
private measureMutex: CancellablePromise<void>;
|
||||
private prependLocked = false;
|
||||
private appendLocked = false;
|
||||
|
||||
private prependFragment: DocumentFragment = null;
|
||||
private appendFragment: DocumentFragment = null;
|
||||
private prependFragmentId = 0;
|
||||
private appendFragmentId = 0;
|
||||
|
||||
constructor(public el: HTMLElement, axis: 'y' | 'x' = 'y', public splitOffset = 300, logPrefix = '', public appendTo = el, public onScrollOffset = splitOffset) {
|
||||
this.container = document.createElement('div');
|
||||
|
@ -194,11 +199,11 @@ export default class Scrollable {
|
|||
|
||||
el.append(this.container);
|
||||
|
||||
setTimeout(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
// @ts-ignore
|
||||
this.size = this.container[this.clientSize];
|
||||
this.resize();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
this.container.parentElement.append(this.thumb);
|
||||
}
|
||||
|
@ -763,7 +768,8 @@ export default class Scrollable {
|
|||
}
|
||||
} else {
|
||||
this.appendTo.prepend(element);
|
||||
this.onScroll();
|
||||
this.visibleElements.unshift({element, height: 0});
|
||||
//this.onScroll();
|
||||
}
|
||||
|
||||
//this.onScroll();
|
||||
|
@ -812,11 +818,52 @@ export default class Scrollable {
|
|||
}
|
||||
} else {
|
||||
this.appendTo.append(element);
|
||||
this.onScroll();
|
||||
this.visibleElements.push({element, height: 0});
|
||||
//this.onScroll();
|
||||
}
|
||||
|
||||
//this.onScroll();
|
||||
}
|
||||
|
||||
public prependByBatch(element: HTMLElement) {
|
||||
let perf = performance.now();
|
||||
let fragment = this.prependFragment ?? (this.prependFragment = document.createDocumentFragment());
|
||||
fragment.prepend(element);
|
||||
|
||||
if(this.prependFragmentId) window.cancelAnimationFrame(this.prependFragmentId);
|
||||
this.prependFragmentId = window.requestAnimationFrame(() => {
|
||||
this.prependFragment = null;
|
||||
this.prependFragmentId = 0;
|
||||
|
||||
for(let length = fragment.childElementCount, i = length - 1; i >= 0; --i) {
|
||||
let element = fragment.children[i];
|
||||
this.visibleElements.unshift({element, height: 0});
|
||||
}
|
||||
|
||||
this.log('prependByBatch perf:', performance.now() - perf, fragment.childElementCount);
|
||||
this.appendTo.prepend(fragment);
|
||||
//this.onScroll();
|
||||
});
|
||||
}
|
||||
|
||||
public appendByBatch(element: HTMLElement) {
|
||||
let fragment = this.appendFragment ?? (this.appendFragment = document.createDocumentFragment());
|
||||
fragment.append(element);
|
||||
|
||||
if(this.appendFragmentId) window.cancelAnimationFrame(this.appendFragmentId);
|
||||
this.appendFragmentId = window.requestAnimationFrame(() => {
|
||||
this.appendFragment = null;
|
||||
this.appendFragmentId = 0;
|
||||
|
||||
for(let i = 0, length = fragment.childElementCount; i < length; ++i) {
|
||||
let element = fragment.children[i];
|
||||
this.visibleElements.push({element, height: 0});
|
||||
}
|
||||
|
||||
this.appendTo.append(fragment);
|
||||
//this.onScroll();
|
||||
});
|
||||
}
|
||||
|
||||
public contains(element: Element) {
|
||||
if(!this.splitUp) {
|
||||
|
@ -985,9 +1032,9 @@ export default class Scrollable {
|
|||
}
|
||||
|
||||
set scrollTop(y: number) {
|
||||
fastdom.mutate(() => {
|
||||
//fastdom.mutate(() => {
|
||||
this.container.scrollTop = y;
|
||||
});
|
||||
//});
|
||||
}
|
||||
|
||||
get scrollTop() {
|
||||
|
@ -997,6 +1044,10 @@ export default class Scrollable {
|
|||
get scrollHeight() {
|
||||
return this.container.scrollHeight;
|
||||
}
|
||||
|
||||
get innerHeight() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
get parentElement() {
|
||||
return this.container.parentElement;
|
||||
|
|
|
@ -42,9 +42,7 @@ export type MTPhotoSize = {
|
|||
size?: number,
|
||||
type?: string, // i, m, x, y, w by asc
|
||||
location?: any,
|
||||
bytes?: Uint8Array, // if type == 'i'
|
||||
|
||||
preloaded?: boolean // custom added
|
||||
bytes?: Uint8Array // if type == 'i'
|
||||
};
|
||||
|
||||
export function wrapVideo(this: AppImManager, doc: MTDocument, container: HTMLDivElement, message: any, justLoader = true, preloader?: ProgressivePreloader, controls = true, round = false, boxWidth = 380, boxHeight = 380, withTail = false, isOut = false) {
|
||||
|
@ -129,7 +127,7 @@ export function wrapVideo(this: AppImManager, doc: MTDocument, container: HTMLDi
|
|||
};
|
||||
|
||||
if(doc.type == 'gif' || true) { // extra fix
|
||||
return this.loadMediaQueuePush(loadVideo);
|
||||
return this.lazyLoadQueue.push({div: container, load: loadVideo, wasSeen: true});
|
||||
} /* else { // if video
|
||||
let load = () => appPhotosManager.preloadPhoto(doc).then((blob) => {
|
||||
if((this.peerID ? this.peerID : this.currentMessageID) != peerID) {
|
||||
|
@ -430,46 +428,24 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
|
|||
function wrapMediaWithTail(photo: any, message: {mid: number, message: string}, container: HTMLDivElement, boxWidth: number, boxHeight: number, isOut: boolean) {
|
||||
let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svg.classList.add('bubble__media-container', isOut ? 'is-out' : 'is-in');
|
||||
|
||||
let image = document.createElementNS("http://www.w3.org/2000/svg", "image");
|
||||
svg.append(image);
|
||||
|
||||
let size = appPhotosManager.setAttachmentSize(photo.type ? photo : photo.id, svg, boxWidth, boxHeight);
|
||||
let image = svg.firstElementChild as SVGImageElement || document.createElementNS("http://www.w3.org/2000/svg", "image");
|
||||
let size = appPhotosManager.setAttachmentSize(photo.type ? photo : photo.id, svg, boxWidth, boxHeight, false);
|
||||
|
||||
let width = +svg.getAttributeNS(null, 'width');
|
||||
let height = +svg.getAttributeNS(null, 'height');
|
||||
|
||||
//container.style.width = (width - 9) + 'px'; // maybe return
|
||||
|
||||
|
||||
let clipID = 'clip' + message.mid;
|
||||
svg.dataset.clipID = clipID;
|
||||
//image.setAttributeNS(null, 'clip-path', `url(#${clipID})`);
|
||||
if(!svg.contains(image)) {
|
||||
image.setAttributeNS(null, 'width', '' + width);
|
||||
image.setAttributeNS(null, 'height', '' + height);
|
||||
svg.append(image);
|
||||
}
|
||||
|
||||
let defs = document.createElementNS("http://www.w3.org/2000/svg", 'defs');
|
||||
let clipPathHTML: string = '';/* = `
|
||||
<use href="#message-tail" transform="translate(${width - 2}, ${height}) scale(-1, -1)"></use>
|
||||
<rect width="${width - 9}" height="${height}" rx="12"></rect>
|
||||
`; */
|
||||
|
||||
//window.getComputedStyle(container).getPropertyValue('border-radius');
|
||||
let clipPathHTML: string = '';
|
||||
|
||||
if(message.message) {
|
||||
//clipPathHTML += `<rect width="${width}" height="${height}"></rect>`;
|
||||
} else {
|
||||
/* if(isOut) {
|
||||
clipPathHTML += `
|
||||
<use href="#message-tail" transform="translate(${width - 2}, ${height}) scale(-1, -1)"></use>
|
||||
<path d="${generatePathData(0, 0, width - 9, height, 6, 0, 12, 12)}" />
|
||||
`;
|
||||
} else {
|
||||
clipPathHTML += `
|
||||
<use href="#message-tail" transform="translate(0, ${height}) scale(1, -1)"></use>
|
||||
<path d="${generatePathData(0, 0, width - 9, height, 12, 12, 0, 6)}" />
|
||||
`;
|
||||
} */
|
||||
if(isOut) {
|
||||
clipPathHTML += `
|
||||
<use href="#message-tail" transform="translate(${width - 2}, ${height}) scale(-1, -1)"></use>
|
||||
|
@ -482,18 +458,7 @@ function wrapMediaWithTail(photo: any, message: {mid: number, message: string},
|
|||
`;
|
||||
}
|
||||
}
|
||||
|
||||
/* if(isOut) { // top-right, bottom-right
|
||||
clipPathHTML += `
|
||||
<rect class="br-tr" width="12" height="12" x="${width - 9 - 12}" y="0"></rect>
|
||||
<rect class="br-br" width="12" height="12" x="${width - 9 - 12}" y="${height - 12}"></rect>
|
||||
`
|
||||
} else { // top-left, bottom-left
|
||||
clipPathHTML += `
|
||||
<rect class="br-tl" width="12" height="12" x="0" y="0"></rect>
|
||||
<rect class="br-bl" width="12" height="12" x="0" y="${height - 12}"></rect>
|
||||
`;
|
||||
} */
|
||||
|
||||
defs.innerHTML = `<clipPath id="${clipID}">${clipPathHTML}</clipPath>`;
|
||||
|
||||
svg.prepend(defs);
|
||||
|
@ -502,15 +467,17 @@ function wrapMediaWithTail(photo: any, message: {mid: number, message: string},
|
|||
return image;
|
||||
}
|
||||
|
||||
export function wrapPhoto(this: AppImManager, photo: any, message: any, container: HTMLDivElement, boxWidth = 380, boxHeight = 380, withTail = true, isOut = false) {
|
||||
export async function wrapPhoto(this: AppImManager, photoID: string, message: any, container: HTMLDivElement, boxWidth = 380, boxHeight = 380, withTail = true, isOut = false) {
|
||||
let peerID = this.peerID;
|
||||
|
||||
|
||||
let photo = appPhotosManager.getPhoto(photoID);
|
||||
|
||||
let size: MTPhotoSize;
|
||||
let image: SVGImageElement | HTMLImageElement;
|
||||
if(withTail) {
|
||||
image = wrapMediaWithTail(photo, message, container, boxWidth, boxHeight, isOut);
|
||||
} else {
|
||||
size = appPhotosManager.setAttachmentSize(photo.id, container, boxWidth, boxHeight);
|
||||
} else { // means webpage's preview
|
||||
size = appPhotosManager.setAttachmentSize(photoID, container, boxWidth, boxHeight, false);
|
||||
|
||||
image = container.firstElementChild as HTMLImageElement || new Image();
|
||||
|
||||
|
@ -518,12 +485,17 @@ export function wrapPhoto(this: AppImManager, photo: any, message: any, containe
|
|||
container.appendChild(image);
|
||||
}
|
||||
}
|
||||
|
||||
let preloader = new ProgressivePreloader(container, false);
|
||||
|
||||
console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
|
||||
|
||||
let preloader: ProgressivePreloader;
|
||||
if(!photo.downloaded) preloader = new ProgressivePreloader(container, false);
|
||||
let load = () => {
|
||||
let promise = appPhotosManager.preloadPhoto(photo.id, size);
|
||||
let promise = appPhotosManager.preloadPhoto(photoID, size);
|
||||
|
||||
preloader.attach(container, true, promise);
|
||||
if(preloader) {
|
||||
preloader.attach(container, true, promise);
|
||||
}
|
||||
|
||||
return promise.then((blob) => {
|
||||
if(this.peerID != peerID) {
|
||||
|
@ -541,11 +513,15 @@ export function wrapPhoto(this: AppImManager, photo: any, message: any, containe
|
|||
|
||||
/////////console.log('wrapPhoto', load, container, image);
|
||||
|
||||
return this.loadMediaQueue ? this.loadMediaQueuePush(load) : load();
|
||||
return photo.downloaded ? load() : this.lazyLoadQueue.push({div: container, load: load, wasSeen: true});
|
||||
}
|
||||
|
||||
export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: () => boolean, lazyLoadQueue?: LazyLoadQueue, group?: string, canvas?: boolean, play = false, onlyThumb = false) {
|
||||
let stickerType = doc.mime_type == "application/x-tgsticker" ? 2 : (doc.mime_type == "image/webp" ? 1 : 0);
|
||||
|
||||
if(stickerType == 2 && !LottieLoader.loaded) {
|
||||
LottieLoader.loadLottie();
|
||||
}
|
||||
|
||||
if(!stickerType) {
|
||||
console.error('wrong doc for wrapSticker!', doc, div);
|
||||
|
@ -601,17 +577,21 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
|
|||
//console.log('loaded sticker:', blob, div);
|
||||
if(middleware && !middleware()) return;
|
||||
|
||||
if(div.firstElementChild) {
|
||||
/* if(div.firstElementChild) {
|
||||
div.firstElementChild.remove();
|
||||
}
|
||||
} */
|
||||
|
||||
if(stickerType == 2) {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.addEventListener('loadend', async(e) => {
|
||||
console.time('decompress sticker' + doc.id);
|
||||
console.time('render sticker' + doc.id);
|
||||
// @ts-ignore
|
||||
const text = e.srcElement.result;
|
||||
let json = await CryptoWorker.gzipUncompress<string>(text, true);
|
||||
|
||||
console.timeEnd('decompress sticker' + doc.id);
|
||||
|
||||
let animation = await LottieLoader.loadAnimation({
|
||||
container: div,
|
||||
|
@ -620,6 +600,12 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
|
|||
animationData: JSON.parse(json),
|
||||
renderer: canvas ? 'canvas' : 'svg'
|
||||
}, group);
|
||||
|
||||
console.timeEnd('render sticker' + doc.id);
|
||||
|
||||
if(div.firstElementChild && div.firstElementChild.tagName != 'CANVAS') {
|
||||
div.firstElementChild.remove();
|
||||
}
|
||||
|
||||
if(!canvas) {
|
||||
div.addEventListener('mouseover', (e) => {
|
||||
|
@ -663,7 +649,11 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
|
|||
} else if(stickerType == 1) {
|
||||
let img = new Image();
|
||||
|
||||
appWebpManager.polyfillImage(img, blob);
|
||||
appWebpManager.polyfillImage(img, blob).then(() => {
|
||||
if(div.firstElementChild && div.firstElementChild != img) {
|
||||
div.firstElementChild.remove();
|
||||
}
|
||||
});
|
||||
|
||||
//img.src = URL.createObjectURL(blob);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import apiManager from "../mtproto/apiManager";
|
||||
import apiFileManager from '../mtproto/apiFileManager';
|
||||
import { $rootScope, findUpTag, langPack, findUpClassName } from "../utils";
|
||||
import appImManager from "./appImManager";
|
||||
import { $rootScope, langPack, findUpClassName } from "../utils";
|
||||
import appImManager, { AppImManager } from "./appImManager";
|
||||
import appPeersManager from './appPeersManager';
|
||||
import appMessagesManager from "./appMessagesManager";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
|
@ -38,6 +38,10 @@ export class AppDialogsManager {
|
|||
|
||||
public savedAvatarURLs: {[peerID: number]: string} = {};
|
||||
|
||||
private rippleCallback: (value?: boolean | PromiseLike<boolean>) => void = null;
|
||||
private lastClickID = 0;
|
||||
private lastGoodClickID = 0;
|
||||
|
||||
constructor() {
|
||||
this.pinnedDelimiter = document.createElement('div');
|
||||
this.pinnedDelimiter.classList.add('pinned-delimiter');
|
||||
|
@ -52,6 +56,13 @@ export class AppDialogsManager {
|
|||
this.myID = userAuth ? userAuth.id : 0;
|
||||
});
|
||||
|
||||
$rootScope.$on('history_request', () => { // will call at history request api or cache RENDERED!
|
||||
if(this.rippleCallback) {
|
||||
this.rippleCallback();
|
||||
this.rippleCallback = null;
|
||||
}
|
||||
});
|
||||
|
||||
//let chatClosedDiv = document.getElementById('chat-closed');
|
||||
|
||||
this.setListClickListener(this.chatList);
|
||||
|
@ -60,6 +71,8 @@ export class AppDialogsManager {
|
|||
|
||||
public setListClickListener(list: HTMLUListElement, onFound?: () => void) {
|
||||
list.addEventListener('click', (e: Event) => {
|
||||
//return;
|
||||
console.log('dialogs click list');
|
||||
let target = e.target as HTMLElement;
|
||||
let elem = target.classList.contains('rp') ? target : findUpClassName(target, 'rp');
|
||||
|
||||
|
@ -69,10 +82,15 @@ export class AppDialogsManager {
|
|||
|
||||
elem = elem.parentElement;
|
||||
|
||||
if(this.lastActiveListElement) {
|
||||
let samePeer = this.lastActiveListElement == elem;
|
||||
|
||||
if(this.lastActiveListElement && !samePeer) {
|
||||
this.lastActiveListElement.classList.remove('active');
|
||||
}
|
||||
|
||||
let startTime = Date.now();
|
||||
let result: ReturnType<AppImManager['setPeer']>;
|
||||
//console.log('appDialogsManager: lock lazyLoadQueue');
|
||||
if(elem) {
|
||||
/* if(chatClosedDiv) {
|
||||
chatClosedDiv.style.display = 'none';
|
||||
|
@ -81,14 +99,46 @@ export class AppDialogsManager {
|
|||
if(onFound) onFound();
|
||||
|
||||
let peerID = +elem.getAttribute('data-peerID');
|
||||
let lastMsgID = +elem.getAttribute('data-mid');
|
||||
appImManager.setPeer(peerID, lastMsgID);
|
||||
elem.classList.add('active');
|
||||
this.lastActiveListElement = elem;
|
||||
let lastMsgID = +elem.dataset.mid;
|
||||
|
||||
if(!samePeer) {
|
||||
elem.classList.add('active');
|
||||
this.lastActiveListElement = elem;
|
||||
}
|
||||
|
||||
result = appImManager.setPeer(peerID, lastMsgID, false, true);
|
||||
|
||||
if(result instanceof Promise) {
|
||||
this.lastGoodClickID = this.lastClickID;
|
||||
appImManager.lazyLoadQueue.lock();
|
||||
}
|
||||
} else /* if(chatClosedDiv) */ {
|
||||
appImManager.setPeer(0);
|
||||
result = appImManager.setPeer(0);
|
||||
//chatClosedDiv.style.display = '';
|
||||
}
|
||||
|
||||
/* if(!(result instanceof Promise)) { // if click on same dialog
|
||||
this.rippleCallback();
|
||||
this.rippleCallback = null;
|
||||
} */
|
||||
|
||||
/* promise.then(() => {
|
||||
appImManager.lazyLoadQueue.unlock();
|
||||
}); */
|
||||
|
||||
/* promise.then(() => {
|
||||
let length = appImManager.lazyLoadQueue.length();
|
||||
console.log('pre ripple callback', length);
|
||||
|
||||
if(length) {
|
||||
setTimeout(() => {
|
||||
this.rippleCallback();
|
||||
}, length * 25);
|
||||
} else {
|
||||
let elapsedTime = Date.now() - startTime;
|
||||
this.rippleCallback(elapsedTime > 200);
|
||||
}
|
||||
}); */
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -110,6 +160,7 @@ export class AppDialogsManager {
|
|||
|
||||
div.style.backgroundColor = '';
|
||||
div.classList.add('tgico-savedmessages');
|
||||
div.classList.remove('tgico-avatar_deletedaccount');
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -171,7 +222,10 @@ export class AppDialogsManager {
|
|||
div.innerHTML = '';
|
||||
//div.style.fontSize = '0'; // need
|
||||
//div.style.backgroundColor = '';
|
||||
div.append(img);
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
div.append(img);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -364,7 +418,8 @@ export class AppDialogsManager {
|
|||
str = sender.first_name || sender.last_name || sender.username;
|
||||
}
|
||||
|
||||
senderBold.innerText = str + ': ';
|
||||
//senderBold.innerText = str + ': ';
|
||||
senderBold.innerHTML = RichTextProcessor.wrapRichText(str, {noLinebreakers: true}) + ': ';
|
||||
//console.log(sender, senderBold.innerText);
|
||||
dom.lastMessageSpan.prepend(senderBold);
|
||||
} //////// else console.log('no sender', lastMessage, peerID);
|
||||
|
@ -505,12 +560,25 @@ export class AppDialogsManager {
|
|||
//captionDiv.append(titleSpan);
|
||||
//captionDiv.append(span);
|
||||
|
||||
|
||||
|
||||
let paddingDiv = document.createElement('div');
|
||||
paddingDiv.classList.add('rp');
|
||||
paddingDiv.append(avatarDiv, captionDiv);
|
||||
ripple(paddingDiv);
|
||||
|
||||
ripple(paddingDiv, (id) => {
|
||||
console.log('dialogs click element');
|
||||
this.lastClickID = id;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rippleCallback = resolve;
|
||||
//setTimeout(() => resolve(), 100);
|
||||
//window.requestAnimationFrame(() => window.requestAnimationFrame(() => resolve()));
|
||||
});
|
||||
}, (id) => {
|
||||
//console.log('appDialogsManager: ripple onEnd called!');
|
||||
if(id == this.lastGoodClickID) {
|
||||
appImManager.lazyLoadQueue.unlock();
|
||||
}
|
||||
});
|
||||
|
||||
let li = document.createElement('li');
|
||||
li.append(paddingDiv);
|
||||
|
|
|
@ -6,17 +6,16 @@ import { CancellablePromise } from '../polyfill';
|
|||
|
||||
class AppDocsManager {
|
||||
private docs: any = {};
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public saveDoc(apiDoc: /* MTDocument */any, context?: any) {
|
||||
console.log('saveDoc', apiDoc, this.docs[apiDoc.id]);
|
||||
if(this.docs[apiDoc.id]) return this.docs[apiDoc.id];
|
||||
|
||||
this.docs[apiDoc.id] = apiDoc;
|
||||
|
||||
if(context) {
|
||||
/* if(context) {
|
||||
Object.assign(apiDoc, context);
|
||||
}
|
||||
} */
|
||||
|
||||
if(apiDoc.thumb && apiDoc.thumb._ == 'photoCachedSize') {
|
||||
apiFileManager.saveSmallFile(apiDoc.thumb.location, apiDoc.thumb.bytes);
|
||||
|
@ -125,6 +124,8 @@ class AppDocsManager {
|
|||
if(apiDoc._ == 'documentEmpty') {
|
||||
apiDoc.size = 0;
|
||||
}
|
||||
|
||||
return apiDoc;
|
||||
}
|
||||
|
||||
public getDoc(docID: string) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,7 +5,6 @@ import appMessagesManager from "./appMessagesManager";
|
|||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
import { logger } from "../polyfill";
|
||||
import ProgressivePreloader from "../../components/preloader";
|
||||
import { wrapVideo } from "../../components/wrappers";
|
||||
import { findUpClassName, $rootScope, generatePathData } from "../utils";
|
||||
import appDocsManager from "./appDocsManager";
|
||||
import { wrapPlayer } from "../ckin";
|
||||
|
|
|
@ -21,13 +21,20 @@ import apiManager from "../mtproto/apiManager";
|
|||
import appWebPagesManager from "./appWebPagesManager";
|
||||
import { CancellablePromise, deferredPromise } from "../polyfill";
|
||||
|
||||
type HistoryStorage = {
|
||||
export type HistoryStorage = {
|
||||
count: number | null,
|
||||
history: number[],
|
||||
pending: number[],
|
||||
readPromise?: any
|
||||
};
|
||||
|
||||
export type HistoryResult = {
|
||||
count: number,
|
||||
history: number[],
|
||||
unreadOffset: number,
|
||||
unreadSkip: boolean
|
||||
};
|
||||
|
||||
export class AppMessagesManager {
|
||||
public messagesStorage: any = {};
|
||||
public messagesForHistory: any = {};
|
||||
|
@ -1137,7 +1144,7 @@ export class AppMessagesManager {
|
|||
if(apiMessage.media.ttl_seconds) {
|
||||
apiMessage.media = {_: 'messageMediaUnsupportedWeb'};
|
||||
} else {
|
||||
appDocsManager.saveDoc(apiMessage.media.document, mediaContext);
|
||||
apiMessage.media.document = appDocsManager.saveDoc(apiMessage.media.document, mediaContext); // 11.04.2020 warning
|
||||
}
|
||||
break;
|
||||
case 'messageMediaWebPage':
|
||||
|
@ -3017,12 +3024,7 @@ export class AppMessagesManager {
|
|||
});
|
||||
}
|
||||
|
||||
public getHistory(peerID: number, maxID = 0, limit = 0, backLimit?: number, prerendered?: number): Promise<{
|
||||
count: number,
|
||||
history: number[],
|
||||
unreadOffset: number,
|
||||
unreadSkip: boolean
|
||||
}> {
|
||||
public getHistory(peerID: number, maxID = 0, limit = 0, backLimit?: number, prerendered?: number) {
|
||||
if(this.migratedFromTo[peerID]) {
|
||||
peerID = this.migratedFromTo[peerID];
|
||||
}
|
||||
|
@ -3095,10 +3097,12 @@ export class AppMessagesManager {
|
|||
} else {
|
||||
limit = limit || (offset ? 20 : (prerendered || 5));
|
||||
}
|
||||
|
||||
var history = historyStorage.history.slice(offset, offset + limit);
|
||||
if(!maxID && historyStorage.pending.length) {
|
||||
history = historyStorage.pending.slice().concat(history);
|
||||
}
|
||||
|
||||
return this.wrapHistoryResult(peerID, {
|
||||
count: historyStorage.count,
|
||||
history: history,
|
||||
|
@ -3230,7 +3234,7 @@ export class AppMessagesManager {
|
|||
});
|
||||
}
|
||||
|
||||
public wrapHistoryResult(peerID: number, result: any) {
|
||||
public wrapHistoryResult(peerID: number, result: HistoryResult) {
|
||||
var unreadOffset = result.unreadOffset;
|
||||
if(unreadOffset) {
|
||||
var i;
|
||||
|
@ -3243,7 +3247,8 @@ export class AppMessagesManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
return result;
|
||||
//return Promise.resolve(result);
|
||||
}
|
||||
|
||||
public requestHistory(peerID: number, maxID: number, limit: number, offset = 0) {
|
||||
|
@ -3251,6 +3256,8 @@ export class AppMessagesManager {
|
|||
|
||||
//console.trace('requestHistory', peerID, maxID, limit, offset);
|
||||
|
||||
$rootScope.$broadcast('history_request');
|
||||
|
||||
return apiManager.invokeApi('messages.getHistory', {
|
||||
peer: AppPeersManager.getInputPeerByID(peerID),
|
||||
offset_id: maxID ? appMessagesIDsManager.getMessageLocalID(maxID) : 0,
|
||||
|
|
|
@ -5,10 +5,6 @@ import { bytesFromHex } from "../bin_utils";
|
|||
import { MTPhotoSize } from "../../components/wrappers";
|
||||
import apiFileManager from "../mtproto/apiFileManager";
|
||||
import apiManager from "../mtproto/apiManager";
|
||||
//import { MTPhotoSize } from "../../components/misc";
|
||||
|
||||
//import fastdom from "fastdom";
|
||||
//import 'fastdom/fastdom-strict'; // exclude in production
|
||||
|
||||
type MTPhoto = {
|
||||
_: 'photo',
|
||||
|
@ -20,7 +16,9 @@ type MTPhoto = {
|
|||
date: number,
|
||||
sizes: Array<MTPhotoSize>,
|
||||
dc_id: number,
|
||||
user_id: number
|
||||
user_id: number,
|
||||
|
||||
downloaded?: boolean
|
||||
};
|
||||
|
||||
export class AppPhotosManager {
|
||||
|
@ -35,36 +33,32 @@ export class AppPhotosManager {
|
|||
|
||||
constructor() {
|
||||
window.addEventListener('resize', (e) => {
|
||||
//fastdom.measure(() => {
|
||||
this.windowW = document.body.scrollWidth;
|
||||
this.windowH = document.body.scrollHeight;
|
||||
//});
|
||||
//console.log(`Set windowW, windowH: ${this.windowW}x${this.windowH}`);
|
||||
});
|
||||
|
||||
//fastdom.measure(() => {
|
||||
console.log('measure works');
|
||||
this.windowW = document.body.scrollWidth;
|
||||
this.windowH = document.body.scrollHeight;
|
||||
//});
|
||||
}
|
||||
|
||||
public savePhoto(apiPhoto: any, context?: any) {
|
||||
if(context) {
|
||||
Object.assign(apiPhoto, context);
|
||||
}
|
||||
public savePhoto(photo: MTPhoto, context?: any) {
|
||||
if(this.photos[photo.id]) return this.photos[photo.id];
|
||||
|
||||
/* if(context) {
|
||||
Object.assign(photo, context);
|
||||
} */ // warning
|
||||
|
||||
if(!apiPhoto.id) {
|
||||
console.warn('no apiPhoto.id', apiPhoto);
|
||||
} else this.photos[apiPhoto.id] = apiPhoto;
|
||||
if(!photo.id) {
|
||||
console.warn('no apiPhoto.id', photo);
|
||||
} else this.photos[photo.id] = photo;
|
||||
|
||||
if(!('sizes' in apiPhoto)) return;
|
||||
if(!('sizes' in photo)) return;
|
||||
|
||||
apiPhoto.sizes.forEach((photoSize: any) => {
|
||||
photo.sizes.forEach((photoSize: any) => {
|
||||
if(photoSize._ == 'photoCachedSize') {
|
||||
apiFileManager.saveSmallFile(photoSize.location, photoSize.bytes);
|
||||
|
||||
console.log('clearing photo cached size', apiPhoto);
|
||||
console.log('clearing photo cached size', photo);
|
||||
|
||||
// Memory
|
||||
photoSize.size = photoSize.bytes.length;
|
||||
|
@ -72,6 +66,25 @@ export class AppPhotosManager {
|
|||
photoSize._ = 'photoSize';
|
||||
}
|
||||
});
|
||||
|
||||
/* if(!photo.downloaded) {
|
||||
photo.downloaded = apiFileManager.isFileExists({
|
||||
_: 'inputPhotoFileLocation',
|
||||
id: photo.id,
|
||||
access_hash: photo.access_hash,
|
||||
file_reference: photo.file_reference
|
||||
});
|
||||
// apiFileManager.isFileExists({
|
||||
// _: 'inputPhotoFileLocation',
|
||||
// id: photo.id,
|
||||
// access_hash: photo.access_hash,
|
||||
// file_reference: photo.file_reference
|
||||
// }).then(downloaded => {
|
||||
// photo.downloaded = downloaded;
|
||||
// });
|
||||
} */
|
||||
|
||||
return photo;
|
||||
}
|
||||
|
||||
public choosePhotoSize(photo: any, width = 0, height = 0) {
|
||||
|
@ -140,9 +153,6 @@ export class AppPhotosManager {
|
|||
}
|
||||
|
||||
public setAttachmentPreview(bytes: Uint8Array, element: HTMLElement | SVGSVGElement, isSticker = false, background = false) {
|
||||
//image.src = "data:image/jpeg;base64," + bytesToBase64(photo.sizes[0].bytes);
|
||||
//photo.sizes[0].bytes = new Uint8Array([...photo.sizes[0].bytes].reverse());
|
||||
|
||||
let arr: Uint8Array;
|
||||
if(!isSticker) {
|
||||
arr = AppPhotosManager.jf.concat(bytes.slice(3), AppPhotosManager.Df);
|
||||
|
@ -157,12 +167,18 @@ export class AppPhotosManager {
|
|||
let blob = new Blob([arr], {type: "image/jpeg"});
|
||||
|
||||
if(background) {
|
||||
element.style.backgroundImage = 'url(' + URL.createObjectURL(blob) + ')';
|
||||
let url = URL.createObjectURL(blob);
|
||||
let img = new Image();
|
||||
img.src = url;
|
||||
img.onload = () => {
|
||||
element.style.backgroundImage = 'url(' + url + ')';
|
||||
};
|
||||
|
||||
//element.style.backgroundImage = 'url(' + url + ')';
|
||||
} else {
|
||||
if(element instanceof SVGSVGElement) {
|
||||
let image = document.createElementNS("http://www.w3.org/2000/svg", "image");
|
||||
let image = element.firstElementChild as SVGImageElement || document.createElementNS("http://www.w3.org/2000/svg", "image");
|
||||
image.setAttributeNS(null, 'href', URL.createObjectURL(blob));
|
||||
//image.setAttributeNS(null, 'preserveAspectRatio', 'xMinYMin slice');
|
||||
element.append(image);
|
||||
} else {
|
||||
let image = new Image();
|
||||
|
@ -189,7 +205,7 @@ export class AppPhotosManager {
|
|||
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
|
||||
|
||||
let sizes = photo.sizes || photo.thumbs;
|
||||
if(sizes && sizes[0].bytes) {
|
||||
if((!photo.downloaded || isSticker) && sizes && sizes[0].bytes) {
|
||||
this.setAttachmentPreview(sizes[0].bytes, element, isSticker);
|
||||
}
|
||||
|
||||
|
@ -221,7 +237,7 @@ export class AppPhotosManager {
|
|||
return photoSize;
|
||||
}
|
||||
|
||||
public async preloadPhoto(photoID: any, photoSize?: MTPhotoSize): Promise<Blob> {
|
||||
public preloadPhoto(photoID: any, photoSize?: MTPhotoSize): Promise<Blob> {
|
||||
let photo: any = null;
|
||||
|
||||
if(typeof(photoID) === 'string') {
|
||||
|
@ -239,8 +255,6 @@ export class AppPhotosManager {
|
|||
}
|
||||
|
||||
if(photoSize && photoSize._ != 'photoSizeEmpty') {
|
||||
photoSize.preloaded = true;
|
||||
|
||||
// maybe it's a thumb
|
||||
let isPhoto = photoSize.size && photo.access_hash && photo.file_reference;
|
||||
let location = isPhoto ? {
|
||||
|
@ -250,34 +264,29 @@ export class AppPhotosManager {
|
|||
file_reference: photo.file_reference,
|
||||
thumb_size: photoSize.type
|
||||
} : photoSize.location;
|
||||
|
||||
/* if(overwrite) {
|
||||
await apiFileManager.deleteFile(location);
|
||||
console.log('Photos deleted file!');
|
||||
} */
|
||||
|
||||
|
||||
let promise: Promise<Blob>;
|
||||
|
||||
if(isPhoto/* && photoSize.size >= 1e6 */) {
|
||||
//console.log('Photos downloadFile exec', photo);
|
||||
/* let promise = apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
|
||||
|
||||
let blob = await promise;
|
||||
if(blob.size < photoSize.size && overwrite) {
|
||||
await apiFileManager.deleteFile(location);
|
||||
console.log('Photos deleted file!');
|
||||
return apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
|
||||
}
|
||||
|
||||
return blob; */
|
||||
return apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
|
||||
promise = apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
|
||||
} else {
|
||||
//console.log('Photos downloadSmallFile exec', photo, location);
|
||||
return apiFileManager.downloadSmallFile(location);
|
||||
promise = apiFileManager.downloadSmallFile(location);
|
||||
}
|
||||
|
||||
if(typeof(photoID) === 'string') {
|
||||
promise.then(() => {
|
||||
this.photos[photoID].downloaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
return promise;
|
||||
} else return Promise.reject('no photoSize');
|
||||
}
|
||||
|
||||
public getPhoto(photoID: string) {
|
||||
return this.photos[photoID] || {_: 'photoEmpty'};
|
||||
return this.photos[photoID] || {_: 'photoEmpty'} as unknown as Partial<MTPhoto>;
|
||||
}
|
||||
|
||||
public wrapForHistory(photoID: string, options: any = {}) {
|
||||
|
@ -311,36 +320,7 @@ export class AppPhotosManager {
|
|||
|
||||
return photo;
|
||||
}
|
||||
|
||||
/* public wrapForFull(photoID: string) {
|
||||
var photo = this.wrapForHistory(photoID);
|
||||
var fullWidth = document.body.scrollWidth - (Config.Mobile ? 0 : 32);
|
||||
var fullHeight = document.body.scrollHeight - (Config.Mobile ? 0 : 116);
|
||||
if (!Config.Mobile && fullWidth > 800) {
|
||||
fullWidth -= 208;
|
||||
}
|
||||
var fullPhotoSize = this.choosePhotoSize(photo, fullWidth, fullHeight);
|
||||
var full: any = {};
|
||||
|
||||
full.width = fullWidth;
|
||||
full.height = fullHeight;
|
||||
|
||||
if (fullPhotoSize && fullPhotoSize._ != 'photoSizeEmpty') {
|
||||
var wh = calcImageInBox(fullPhotoSize.w, fullPhotoSize.h, fullWidth, fullHeight, true);
|
||||
full.width = wh.w;
|
||||
full.height = wh.h;
|
||||
|
||||
full.modalWidth = Math.max(full.width, Math.min(400, fullWidth));
|
||||
|
||||
full.location = fullPhotoSize.location;
|
||||
full.size = fullPhotoSize.size;
|
||||
}
|
||||
|
||||
photo.full = full;
|
||||
|
||||
return photo;
|
||||
} */
|
||||
|
||||
|
||||
public downloadPhoto(photoID: string) {
|
||||
var photo = this.photos[photoID];
|
||||
var ext = 'jpg';
|
||||
|
|
|
@ -289,17 +289,24 @@ class AppSidebarRight {
|
|||
|
||||
let div = document.createElement('div');
|
||||
//console.log(message, photo);
|
||||
|
||||
let sizes = media.sizes || media.thumbs;
|
||||
if(sizes && sizes[0].bytes) {
|
||||
appPhotosManager.setAttachmentPreview(sizes[0].bytes, div, false, true);
|
||||
} /* else {
|
||||
this.log('no stripped size', message, media);
|
||||
} */
|
||||
|
||||
let isPhoto = media._ == 'photo';
|
||||
|
||||
let photo = isPhoto ? appPhotosManager.getPhoto(media.id) : null;
|
||||
if(!photo || !photo.downloaded) {
|
||||
//this.log('inputMessagesFilterPhotoVideo', message, media, photo, div);
|
||||
|
||||
let sizes = media.sizes || media.thumbs;
|
||||
if(sizes && sizes[0].bytes) {
|
||||
appPhotosManager.setAttachmentPreview(sizes[0].bytes, div, false, true);
|
||||
} /* else {
|
||||
this.log('no stripped size', message, media);
|
||||
} */
|
||||
}
|
||||
|
||||
//this.log('inputMessagesFilterPhotoVideo', message, media);
|
||||
|
||||
let load = () => appPhotosManager.preloadPhoto(media, appPhotosManager.choosePhotoSize(media, 200, 200))
|
||||
let load = () => appPhotosManager.preloadPhoto(isPhoto ? media.id : media, appPhotosManager.choosePhotoSize(media, 200, 200))
|
||||
.then((blob) => {
|
||||
if($rootScope.selectedPeerID != peerID) {
|
||||
this.log.warn('peer changed');
|
||||
|
@ -308,13 +315,20 @@ class AppSidebarRight {
|
|||
|
||||
let url = URL.createObjectURL(blob);
|
||||
this.urlsToRevoke.push(url);
|
||||
|
||||
let img = new Image();
|
||||
img.src = url;
|
||||
img.onload = () => {
|
||||
div.style.backgroundImage = 'url(' + url + ')';
|
||||
};
|
||||
|
||||
div.style.backgroundImage = 'url(' + url + ')';
|
||||
//div.style.backgroundImage = 'url(' + url + ')';
|
||||
});
|
||||
|
||||
div.dataset.mid = '' + message.mid;
|
||||
|
||||
this.lazyLoadQueueSidebar.push({div, load});
|
||||
if(photo && photo.downloaded) load();
|
||||
else this.lazyLoadQueueSidebar.push({div, load});
|
||||
|
||||
this.lastSharedMediaDiv.append(div);
|
||||
if(this.lastSharedMediaDiv.childElementCount == 3) {
|
||||
|
@ -444,9 +458,9 @@ class AppSidebarRight {
|
|||
}
|
||||
|
||||
if(elemsToAppend.length) {
|
||||
window.requestAnimationFrame(() => {
|
||||
//window.requestAnimationFrame(() => {
|
||||
elemsToAppend.forEach(el => this.scroll.append(el));
|
||||
});
|
||||
//});
|
||||
}
|
||||
|
||||
if(sharedMediaDiv) {
|
||||
|
@ -477,28 +491,37 @@ class AppSidebarRight {
|
|||
this.lastSharedMediaDiv = document.createElement('div');
|
||||
|
||||
//this.log('fillProfileElements');
|
||||
|
||||
this.profileContentEl.parentElement.scrollTop = 0;
|
||||
this.profileElements.bio.style.display = 'none';
|
||||
this.profileElements.phone.style.display = 'none';
|
||||
this.profileElements.username.style.display = 'none';
|
||||
this.profileElements.notificationsRow.style.display = '';
|
||||
this.profileElements.notificationsCheckbox.checked = true;
|
||||
this.profileElements.notificationsStatus.innerText = 'Enabled';
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
this.profileContentEl.parentElement.scrollTop = 0;
|
||||
this.profileElements.bio.style.display = 'none';
|
||||
this.profileElements.phone.style.display = 'none';
|
||||
this.profileElements.username.style.display = 'none';
|
||||
this.profileElements.notificationsRow.style.display = '';
|
||||
this.profileElements.notificationsCheckbox.checked = true;
|
||||
this.profileElements.notificationsStatus.innerText = 'Enabled';
|
||||
|
||||
Object.keys(this.sharedMedia).forEach(key => {
|
||||
this.sharedMedia[key].innerHTML = '';
|
||||
|
||||
let parent = this.sharedMedia[key].parentElement;
|
||||
if(!parent.querySelector('.preloader')) {
|
||||
putPreloader(parent, true);
|
||||
}
|
||||
});
|
||||
|
||||
this.savedVirtualStates = {};
|
||||
this.prevTabID = -1;
|
||||
this.scroll.setVirtualContainer(null);
|
||||
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
|
||||
|
||||
this.loadSidebarMedia(true);
|
||||
});
|
||||
|
||||
this.mediaDivsByIDs = {};
|
||||
|
||||
this.lazyLoadQueueSidebar.clear();
|
||||
|
||||
Object.keys(this.sharedMedia).forEach(key => {
|
||||
this.sharedMedia[key].innerHTML = '';
|
||||
|
||||
let parent = this.sharedMedia[key].parentElement;
|
||||
if(!parent.querySelector('.preloader')) {
|
||||
putPreloader(parent, true);
|
||||
}
|
||||
});
|
||||
|
||||
this.urlsToRevoke.forEach(url => {
|
||||
URL.revokeObjectURL(url);
|
||||
});
|
||||
|
@ -509,20 +532,18 @@ class AppSidebarRight {
|
|||
this.cleared[type] = true;
|
||||
});
|
||||
|
||||
this.savedVirtualStates = {};
|
||||
this.prevTabID = -1;
|
||||
this.scroll.setVirtualContainer(null);
|
||||
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
|
||||
|
||||
let setText = (text: string, el: HTMLDivElement) => {
|
||||
el.style.display = '';
|
||||
if(el.childElementCount > 1) {
|
||||
el.firstElementChild.remove();
|
||||
}
|
||||
|
||||
let p = document.createElement('p');
|
||||
p.innerHTML = text;
|
||||
el.prepend(p);
|
||||
window.requestAnimationFrame(() => {
|
||||
if(el.childElementCount > 1) {
|
||||
el.firstElementChild.remove();
|
||||
}
|
||||
|
||||
let p = document.createElement('p');
|
||||
p.innerHTML = text;
|
||||
el.prepend(p);
|
||||
|
||||
el.style.display = '';
|
||||
});
|
||||
};
|
||||
|
||||
// username
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { $rootScope, safeReplaceObject, calcImageInBox, encodeEntities, copy } from "../utils";
|
||||
import { $rootScope, safeReplaceObject, copy } from "../utils";
|
||||
import appPhotosManager from "./appPhotosManager";
|
||||
import appDocsManager from "./appDocsManager";
|
||||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
|
@ -11,80 +11,86 @@ class AppWebPagesManager {
|
|||
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
|
||||
let update = e.detail;
|
||||
|
||||
switch (update._) {
|
||||
switch(update._) {
|
||||
case 'updateWebPage':
|
||||
this.saveWebPage(update.webpage)
|
||||
break
|
||||
this.saveWebPage(update.webpage);
|
||||
break;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
saveWebPage(apiWebPage: any, messageID?: number, mediaContext?: any) {
|
||||
if (apiWebPage.photo && apiWebPage.photo._ === 'photo') {
|
||||
appPhotosManager.savePhoto(apiWebPage.photo, mediaContext)
|
||||
if(apiWebPage.photo && apiWebPage.photo._ === 'photo') {
|
||||
appPhotosManager.savePhoto(apiWebPage.photo, mediaContext);
|
||||
} else {
|
||||
delete apiWebPage.photo
|
||||
delete apiWebPage.photo;
|
||||
}
|
||||
if (apiWebPage.document && apiWebPage.document._ === 'document') {
|
||||
appDocsManager.saveDoc(apiWebPage.document, mediaContext)
|
||||
|
||||
if(apiWebPage.document && apiWebPage.document._ === 'document') {
|
||||
apiWebPage.document = appDocsManager.saveDoc(apiWebPage.document, mediaContext); // warning 11.04.2020
|
||||
} else {
|
||||
if (apiWebPage.type == 'document') {
|
||||
delete apiWebPage.type
|
||||
if(apiWebPage.type == 'document') {
|
||||
delete apiWebPage.type;
|
||||
}
|
||||
delete apiWebPage.document
|
||||
|
||||
delete apiWebPage.document;
|
||||
}
|
||||
|
||||
var siteName = apiWebPage.site_name
|
||||
var shortTitle = apiWebPage.title || apiWebPage.author || siteName || ''
|
||||
if (siteName && shortTitle == siteName) {
|
||||
delete apiWebPage.site_name
|
||||
var siteName = apiWebPage.site_name;
|
||||
var shortTitle = apiWebPage.title || apiWebPage.author || siteName || '';
|
||||
if(siteName && shortTitle == siteName) {
|
||||
delete apiWebPage.site_name;
|
||||
}
|
||||
if (shortTitle.length > 100) {
|
||||
shortTitle = shortTitle.substr(0, 80) + '...'
|
||||
|
||||
if(shortTitle.length > 100) {
|
||||
shortTitle = shortTitle.substr(0, 80) + '...';
|
||||
}
|
||||
apiWebPage.rTitle = RichTextProcessor.wrapRichText(shortTitle, {noLinks: true, noLinebreaks: true})
|
||||
var contextHashtag = ''
|
||||
if (siteName == 'GitHub') {
|
||||
var matches = apiWebPage.url.match(/(https?:\/\/github\.com\/[^\/]+\/[^\/]+)/)
|
||||
if (matches) {
|
||||
contextHashtag = matches[0] + '/issues/{1}'
|
||||
|
||||
apiWebPage.rTitle = RichTextProcessor.wrapRichText(shortTitle, {noLinks: true, noLinebreaks: true});
|
||||
var contextHashtag = '';
|
||||
if(siteName == 'GitHub') {
|
||||
var matches = apiWebPage.url.match(/(https?:\/\/github\.com\/[^\/]+\/[^\/]+)/);
|
||||
if(matches) {
|
||||
contextHashtag = matches[0] + '/issues/{1}';
|
||||
}
|
||||
}
|
||||
|
||||
// delete apiWebPage.description
|
||||
var shortDescriptionText = (apiWebPage.description || '')
|
||||
if (shortDescriptionText.length > 180) {
|
||||
shortDescriptionText = shortDescriptionText.substr(0, 150).replace(/(\n|\s)+$/, '') + '...'
|
||||
var shortDescriptionText = (apiWebPage.description || '');
|
||||
if(shortDescriptionText.length > 180) {
|
||||
shortDescriptionText = shortDescriptionText.substr(0, 150).replace(/(\n|\s)+$/, '') + '...';
|
||||
}
|
||||
apiWebPage.rDescription = RichTextProcessor.wrapRichText(shortDescriptionText, {
|
||||
contextSite: siteName || 'external',
|
||||
contextHashtag: contextHashtag
|
||||
});
|
||||
|
||||
if (apiWebPage.type != 'photo' &&
|
||||
if(apiWebPage.type != 'photo' &&
|
||||
apiWebPage.type != 'video' &&
|
||||
apiWebPage.type != 'gif' &&
|
||||
apiWebPage.type != 'document' &&
|
||||
!apiWebPage.description &&
|
||||
apiWebPage.photo) {
|
||||
apiWebPage.type = 'photo'
|
||||
apiWebPage.type = 'photo';
|
||||
}
|
||||
|
||||
if (messageID) {
|
||||
if (this.pendingWebPages[apiWebPage.id] === undefined) {
|
||||
this.pendingWebPages[apiWebPage.id] = {}
|
||||
if(messageID) {
|
||||
if(this.pendingWebPages[apiWebPage.id] === undefined) {
|
||||
this.pendingWebPages[apiWebPage.id] = {};
|
||||
}
|
||||
this.pendingWebPages[apiWebPage.id][messageID] = true
|
||||
this.webpages[apiWebPage.id] = apiWebPage
|
||||
|
||||
this.pendingWebPages[apiWebPage.id][messageID] = true;
|
||||
this.webpages[apiWebPage.id] = apiWebPage;
|
||||
}
|
||||
|
||||
if (this.webpages[apiWebPage.id] === undefined) {
|
||||
this.webpages[apiWebPage.id] = apiWebPage
|
||||
if(this.webpages[apiWebPage.id] === undefined) {
|
||||
this.webpages[apiWebPage.id] = apiWebPage;
|
||||
} else {
|
||||
safeReplaceObject(this.webpages[apiWebPage.id], apiWebPage)
|
||||
safeReplaceObject(this.webpages[apiWebPage.id], apiWebPage);
|
||||
}
|
||||
|
||||
if (!messageID && this.pendingWebPages[apiWebPage.id] !== undefined) {
|
||||
var msgs = []
|
||||
if(!messageID && this.pendingWebPages[apiWebPage.id] !== undefined) {
|
||||
var msgs = [];
|
||||
for(let msgID in this.pendingWebPages[apiWebPage.id]) {
|
||||
msgs.push(msgID);
|
||||
}
|
||||
|
@ -92,58 +98,22 @@ class AppWebPagesManager {
|
|||
$rootScope.$broadcast('webpage_updated', {
|
||||
id: apiWebPage.id,
|
||||
msgs: msgs
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
wrapForHistory (webPageID: number) {
|
||||
var webPage = copy(this.webpages[webPageID]) || {_: 'webPageEmpty'}
|
||||
wrapForHistory(webPageID: number) {
|
||||
var webPage = copy(this.webpages[webPageID]) || {_: 'webPageEmpty'};
|
||||
|
||||
if (webPage.photo && webPage.photo.id) {
|
||||
webPage.photo = appPhotosManager.wrapForHistory(webPage.photo.id, {website: webPage.type != 'photo' && webPage.type != 'video'})
|
||||
if(webPage.photo && webPage.photo.id) {
|
||||
webPage.photo = appPhotosManager.wrapForHistory(webPage.photo.id, {website: webPage.type != 'photo' && webPage.type != 'video'});
|
||||
}
|
||||
|
||||
/* if (webPage.document && webPage.document.id) {
|
||||
webPage.document = appDocsManager.wrapForHistory(webPage.document.id)
|
||||
} */ // warning
|
||||
|
||||
return webPage
|
||||
}
|
||||
|
||||
wrapForFull (webPageID: number) {
|
||||
var webPage = this.wrapForHistory(webPageID)
|
||||
|
||||
if (!webPage.embed_url) {
|
||||
return webPage
|
||||
}
|
||||
|
||||
var fullWidth = window.innerWidth;
|
||||
var fullHeight = window.innerHeight;
|
||||
|
||||
var full: any = {
|
||||
width: fullWidth,
|
||||
height: fullHeight
|
||||
}
|
||||
|
||||
if (!webPage.embed_width || !webPage.embed_height) {
|
||||
full.height = full.width = Math.min(fullWidth, fullHeight)
|
||||
} else {
|
||||
var wh = calcImageInBox(webPage.embed_width, webPage.embed_height, fullWidth, fullHeight)
|
||||
full.width = wh.w
|
||||
full.height = wh.h
|
||||
}
|
||||
|
||||
var embedTag = Config.Modes.chrome_packed ? 'webview' : 'iframe'
|
||||
|
||||
var embedType = webPage.embed_type != 'iframe' ? webPage.embed_type || 'text/html' : 'text/html'
|
||||
|
||||
var embedHtml = '<' + embedTag + ' src="' + encodeEntities(webPage.embed_url) + '" type="' + encodeEntities(embedType) + '" frameborder="0" border="0" webkitallowfullscreen mozallowfullscreen allowfullscreen width="' + full.width + '" height="' + full.height + '" style="width: ' + full.width + 'px; height: ' + full.height + 'px;"></' + embedTag + '>'
|
||||
|
||||
full.html = embedHtml;
|
||||
|
||||
webPage.full = full
|
||||
|
||||
return webPage
|
||||
return webPage;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ class AppWebpManager {
|
|||
public webpMachine: any = null;
|
||||
public loaded: Promise<void>;
|
||||
public busyPromise: Promise<string>;
|
||||
public queue: {bytes: Uint8Array, img: HTMLImageElement}[] = [];
|
||||
public queue: {bytes: Uint8Array, img: HTMLImageElement, callback: () => void}[] = [];
|
||||
//public worker: any;
|
||||
public webpSupport: Promise<boolean> = null;
|
||||
|
||||
|
@ -56,7 +56,7 @@ class AppWebpManager {
|
|||
|
||||
this.busyPromise = Promise.resolve('');
|
||||
|
||||
let {img, bytes} = this.queue.pop();
|
||||
let {img, bytes, callback} = this.queue.pop();
|
||||
|
||||
if(!this.loaded) {
|
||||
this.loadWebpHero();
|
||||
|
@ -66,6 +66,7 @@ class AppWebpManager {
|
|||
|
||||
this.busyPromise = this.convert(bytes);
|
||||
img.src = await this.busyPromise;
|
||||
callback();
|
||||
|
||||
this.busyPromise = null;
|
||||
|
||||
|
@ -97,15 +98,17 @@ class AppWebpManager {
|
|||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('loadend', async(e) => {
|
||||
// @ts-ignore
|
||||
let bytes = new Uint8Array(e.srcElement.result);
|
||||
|
||||
this.queue.push({bytes, img});
|
||||
this.processQueue();
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('loadend', (e) => {
|
||||
// @ts-ignore
|
||||
let bytes = new Uint8Array(e.srcElement.result);
|
||||
|
||||
this.queue.push({bytes, img, callback: resolve});
|
||||
this.processQueue();
|
||||
});
|
||||
reader.readAsArrayBuffer(blob);
|
||||
});
|
||||
reader.readAsArrayBuffer(blob);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,13 +10,6 @@ import {BigInteger, SecureRandom} from 'jsbn';
|
|||
// @ts-ignore
|
||||
import pako from 'pako/dist/pako_inflate.min.js';
|
||||
|
||||
//import { dT } from './utils.js';
|
||||
|
||||
/* import './closure_long.js';
|
||||
//var goog = window ? window.goog as any : this.goog;
|
||||
var goog = typeof(window) !== 'undefined' ? window.goog : this.goog; */
|
||||
|
||||
|
||||
var _logTimer = (new Date()).getTime()
|
||||
export function dT () {
|
||||
return '[' + (((new Date()).getTime() - _logTimer) / 1000).toFixed(3) + ']'
|
||||
|
|
|
@ -716,14 +716,11 @@ var CryptoJS /* = this.CryptoJS = globalThis.CryptoJS */ = CryptoJS || (function
|
|||
*/
|
||||
var C_algo = C.algo = {};
|
||||
|
||||
return C;
|
||||
}(Math));
|
||||
|
||||
module.exports = CryptoJS;
|
||||
|
||||
(function (undefined) {
|
||||
|
||||
// X-64
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var Base = C_lib.Base;
|
||||
var X32WordArray = C_lib.WordArray;
|
||||
|
@ -1004,20 +1001,11 @@ module.exports = CryptoJS;
|
|||
return clone;
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
||||
/*
|
||||
CryptoJS v3.1.2
|
||||
code.google.com/p/crypto-js
|
||||
(c) 2009-2013 by Jeff Mott. All rights reserved.
|
||||
code.google.com/p/crypto-js/wiki/License
|
||||
*/
|
||||
/**
|
||||
* Cipher core components.
|
||||
*/
|
||||
CryptoJS.lib.Cipher || (function (undefined) {
|
||||
|
||||
// CIPHER
|
||||
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var Base = C_lib.Base;
|
||||
var WordArray = C_lib.WordArray;
|
||||
|
@ -1979,20 +1967,13 @@ CryptoJS.lib.Cipher || (function (undefined) {
|
|||
return plaintext;
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
CryptoJS v3.1.2
|
||||
code.google.com/p/crypto-js
|
||||
(c) 2009-2013 by Jeff Mott. All rights reserved.
|
||||
code.google.com/p/crypto-js/wiki/License
|
||||
*/
|
||||
(function () {
|
||||
|
||||
// AES
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var BlockCipher = C_lib.BlockCipher;
|
||||
var C_algo = C.algo;
|
||||
|
@ -2196,192 +2177,8 @@ code.google.com/p/crypto-js/wiki/License
|
|||
* var plaintext = CryptoJS.AES.decrypt(ciphertext, key, cfg);
|
||||
*/
|
||||
C.AES = BlockCipher._createHelper(AES);
|
||||
}());
|
||||
|
||||
|
||||
|
||||
/*
|
||||
CryptoJS v3.1.2
|
||||
code.google.com/p/crypto-js
|
||||
(c) 2009-2013 by Jeff Mott. All rights reserved.
|
||||
code.google.com/p/crypto-js/wiki/License
|
||||
*/
|
||||
(function (Math) {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var Hasher = C_lib.Hasher;
|
||||
var C_algo = C.algo;
|
||||
|
||||
// Initialization and round constants tables
|
||||
var H = [];
|
||||
var K = [];
|
||||
|
||||
// Compute constants
|
||||
(function () {
|
||||
function isPrime(n) {
|
||||
var sqrtN = Math.sqrt(n);
|
||||
for (var factor = 2; factor <= sqrtN; factor++) {
|
||||
if (!(n % factor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getFractionalBits(n) {
|
||||
return ((n - (n | 0)) * 0x100000000) | 0;
|
||||
}
|
||||
|
||||
var n = 2;
|
||||
var nPrime = 0;
|
||||
while (nPrime < 64) {
|
||||
if (isPrime(n)) {
|
||||
if (nPrime < 8) {
|
||||
H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2));
|
||||
}
|
||||
K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));
|
||||
|
||||
nPrime++;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
}());
|
||||
|
||||
// Reusable object
|
||||
var W = [];
|
||||
|
||||
/**
|
||||
* SHA-256 hash algorithm.
|
||||
*/
|
||||
var SHA256 = C_algo.SHA256 = Hasher.extend({
|
||||
_doReset: function () {
|
||||
this._hash = new WordArray.init(H.slice(0));
|
||||
},
|
||||
|
||||
_doProcessBlock: function (M, offset) {
|
||||
// Shortcut
|
||||
var H = this._hash.words;
|
||||
|
||||
// Working variables
|
||||
var a = H[0];
|
||||
var b = H[1];
|
||||
var c = H[2];
|
||||
var d = H[3];
|
||||
var e = H[4];
|
||||
var f = H[5];
|
||||
var g = H[6];
|
||||
var h = H[7];
|
||||
|
||||
// Computation
|
||||
for (var i = 0; i < 64; i++) {
|
||||
if (i < 16) {
|
||||
W[i] = M[offset + i] | 0;
|
||||
} else {
|
||||
var gamma0x = W[i - 15];
|
||||
var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^
|
||||
((gamma0x << 14) | (gamma0x >>> 18)) ^
|
||||
(gamma0x >>> 3);
|
||||
|
||||
var gamma1x = W[i - 2];
|
||||
var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
|
||||
((gamma1x << 13) | (gamma1x >>> 19)) ^
|
||||
(gamma1x >>> 10);
|
||||
|
||||
W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];
|
||||
}
|
||||
|
||||
var ch = (e & f) ^ (~e & g);
|
||||
var maj = (a & b) ^ (a & c) ^ (b & c);
|
||||
|
||||
var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22));
|
||||
var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25));
|
||||
|
||||
var t1 = h + sigma1 + ch + K[i] + W[i];
|
||||
var t2 = sigma0 + maj;
|
||||
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = (d + t1) | 0;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = (t1 + t2) | 0;
|
||||
}
|
||||
|
||||
// Intermediate hash value
|
||||
H[0] = (H[0] + a) | 0;
|
||||
H[1] = (H[1] + b) | 0;
|
||||
H[2] = (H[2] + c) | 0;
|
||||
H[3] = (H[3] + d) | 0;
|
||||
H[4] = (H[4] + e) | 0;
|
||||
H[5] = (H[5] + f) | 0;
|
||||
H[6] = (H[6] + g) | 0;
|
||||
H[7] = (H[7] + h) | 0;
|
||||
},
|
||||
|
||||
_doFinalize: function () {
|
||||
// Shortcuts
|
||||
var data = this._data;
|
||||
var dataWords = data.words;
|
||||
|
||||
var nBitsTotal = this._nDataBytes * 8;
|
||||
var nBitsLeft = data.sigBytes * 8;
|
||||
|
||||
// Add padding
|
||||
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;
|
||||
data.sigBytes = dataWords.length * 4;
|
||||
|
||||
// Hash final blocks
|
||||
this._process();
|
||||
|
||||
// Return final computed hash
|
||||
return this._hash;
|
||||
},
|
||||
|
||||
clone: function () {
|
||||
var clone = Hasher.clone.call(this);
|
||||
clone._hash = this._hash.clone();
|
||||
|
||||
return clone;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Shortcut function to the hasher's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
*
|
||||
* @return {WordArray} The hash.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hash = CryptoJS.SHA256('message');
|
||||
* var hash = CryptoJS.SHA256(wordArray);
|
||||
*/
|
||||
C.SHA256 = Hasher._createHelper(SHA256);
|
||||
|
||||
/**
|
||||
* Shortcut function to the HMAC's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
* @param {WordArray|string} key The secret key.
|
||||
*
|
||||
* @return {WordArray} The HMAC.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmac = CryptoJS.HmacSHA256(message, key);
|
||||
*/
|
||||
C.HmacSHA256 = Hasher._createHmacHelper(SHA256);
|
||||
return C;
|
||||
}(Math));
|
||||
|
||||
module.exports = CryptoJS;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import sha1 from '@cryptography/sha1';
|
||||
import sha256 from '@cryptography/sha256';
|
||||
|
||||
import {str2bigInt, bpe, equalsInt, greater,
|
||||
copy_, eGCD_, add_, rightShift_, sub_, copyInt_, isZero,
|
||||
|
@ -16,6 +17,21 @@ export function bytesFromLeemonBigInt(bigInt: BigInteger) {
|
|||
return bytesFromHex(str);
|
||||
}
|
||||
|
||||
export function bytesToWordss(input: ArrayBuffer | Uint8Array) {
|
||||
let bytes: Uint8Array;
|
||||
if(input instanceof ArrayBuffer) bytes = new Uint8Array(input);
|
||||
else bytes = input;
|
||||
|
||||
var len = bytes.length;
|
||||
var words: number[] = [];
|
||||
var i;
|
||||
for(i = 0; i < len; i++) {
|
||||
words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);
|
||||
}
|
||||
|
||||
return new Uint32Array(words);
|
||||
}
|
||||
|
||||
export function bytesToWords(bytes: any) {
|
||||
if(bytes instanceof ArrayBuffer) {
|
||||
bytes = new Uint8Array(bytes);
|
||||
|
@ -46,23 +62,30 @@ export function sha1HashSync(bytes: number[] | ArrayBuffer | Uint8Array) {
|
|||
return new Uint8Array(hashBytes);
|
||||
}
|
||||
|
||||
export function sha256HashSync(bytes: any) {
|
||||
// console.log(dT(), 'SHA-2 hash start', bytes.byteLength || bytes.length)
|
||||
var hashWords = CryptoJS.SHA256(bytesToWords(bytes));
|
||||
// console.log(dT(), 'SHA-2 hash finish')
|
||||
export function sha256HashSync(bytes: Uint8Array | ArrayBuffer) {
|
||||
//console.log(dT(), 'SHA-256 hash start');
|
||||
|
||||
var hashBytes = bytesFromWords(hashWords);
|
||||
let words = bytesToWordss(bytes);
|
||||
let hash = sha256(words);
|
||||
|
||||
return hashBytes;
|
||||
// bytesFromWords below
|
||||
var o = [];
|
||||
for(var i = 0; i < hash.length * 4; i++) {
|
||||
o.push((hash[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
|
||||
}
|
||||
|
||||
//console.log(dT(), 'SHA-256 hash finish');
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
export function aesEncryptSync(bytes: any, keyBytes: any, ivBytes: any, _mode = 'IGE') {
|
||||
export function aesEncryptSync(bytes: any, keyBytes: any, ivBytes: any) {
|
||||
// console.log(dT(), 'AES encrypt start', len/*, bytesToHex(keyBytes), bytesToHex(ivBytes)*/)
|
||||
// console.log('aes before padding bytes:', bytesToHex(bytes));
|
||||
bytes = addPadding(bytes);
|
||||
// console.log('aes after padding bytes:', bytesToHex(bytes));
|
||||
|
||||
let mode = CryptoJS.mode[_mode];
|
||||
let mode = CryptoJS.mode.IGE;
|
||||
|
||||
let encryptedWords = CryptoJS.AES.encrypt(bytesToWords(bytes), bytesToWords(keyBytes), {
|
||||
iv: bytesToWords(ivBytes),
|
||||
|
@ -76,9 +99,9 @@ export function aesEncryptSync(bytes: any, keyBytes: any, ivBytes: any, _mode =
|
|||
return encryptedBytes;
|
||||
}
|
||||
|
||||
export function aesDecryptSync(encryptedBytes: any, keyBytes: any, ivBytes: any, _mode = 'IGE') {
|
||||
export function aesDecryptSync(encryptedBytes: any, keyBytes: any, ivBytes: any) {
|
||||
|
||||
let mode = CryptoJS.mode[_mode];
|
||||
let mode = CryptoJS.mode.IGE;
|
||||
// console.log(dT(), 'AES decrypt start', encryptedBytes.length)
|
||||
var decryptedWords = CryptoJS.AES.decrypt({ciphertext: bytesToWords(encryptedBytes)}, bytesToWords(keyBytes), {
|
||||
iv: bytesToWords(ivBytes),
|
||||
|
@ -98,11 +121,6 @@ export function rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes
|
|||
|
||||
bytes = addPadding(bytes, 255);
|
||||
|
||||
/* var N = new BigInteger(publicKey.modulus, 16);
|
||||
var E = new BigInteger(publicKey.exponent, 16);
|
||||
var X = new BigInteger(bytes);
|
||||
var encryptedBigInt = X.modPowInt(E, N),
|
||||
encryptedBytes = bytesFromBigInt(encryptedBigInt, 256); */
|
||||
var N = str2bigInt(publicKey.modulus, 16);
|
||||
var E = str2bigInt(publicKey.exponent, 16);
|
||||
var X = str2bigInt(bytesToHex(bytes), 16);
|
||||
|
@ -110,7 +128,7 @@ export function rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes
|
|||
var encryptedBigInt = powMod(X, E, N);
|
||||
var encryptedBytes = bytesFromHex(bigInt2str(encryptedBigInt, 16));
|
||||
|
||||
console.log(dT(), 'RSA encrypt finish'/* , encryptedBytes *//* , bigInt2str(encryptedBigInt, 16) */);
|
||||
console.log(dT(), 'RSA encrypt finish');
|
||||
|
||||
return encryptedBytes;
|
||||
}
|
||||
|
@ -172,162 +190,11 @@ export function pqPrimeFactorization(pqBytes: any) {
|
|||
console.error('Pq leemon Exception', e);
|
||||
}
|
||||
|
||||
/* if(result === false && what.bitLength() <= 64) {
|
||||
console.time('PQ long');
|
||||
try {
|
||||
result = pqPrimeLong(goog.math.Long.fromString(what.toString(16), 16));
|
||||
} catch (e) {
|
||||
console.error('Pq long Exception', e);
|
||||
}
|
||||
console.timeEnd('PQ long');
|
||||
}
|
||||
// console.log(result)
|
||||
|
||||
if(result === false) {
|
||||
console.time('pq BigInt');
|
||||
result = pqPrimeBigInteger(what);
|
||||
console.timeEnd('pq BigInt');
|
||||
} */
|
||||
|
||||
console.log(dT(), 'PQ finish');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* export function pqPrimeBigInteger(what: any) {
|
||||
var it = 0,
|
||||
g;
|
||||
for(var i = 0; i < 3; i++) {
|
||||
var q = (nextRandomInt(128) & 15) + 17;
|
||||
var x = bigint(nextRandomInt(1000000000) + 1);
|
||||
var y = x.clone();
|
||||
var lim = 1 << (i + 18);
|
||||
|
||||
for(var j = 1; j < lim; j++) {
|
||||
++it;
|
||||
var a = x.clone();
|
||||
var b = x.clone();
|
||||
var c = bigint(q);
|
||||
|
||||
while(!b.equals(BigInteger.ZERO)) {
|
||||
if(!b.and(BigInteger.ONE).equals(BigInteger.ZERO)) {
|
||||
c = c.add(a);
|
||||
if(c.compareTo(what) > 0) {
|
||||
c = c.subtract(what);
|
||||
}
|
||||
}
|
||||
a = a.add(a);
|
||||
if(a.compareTo(what) > 0) {
|
||||
a = a.subtract(what);
|
||||
}
|
||||
b = b.shiftRight(1);
|
||||
}
|
||||
|
||||
x = c.clone();
|
||||
var z = x.compareTo(y) < 0 ? y.subtract(x) : x.subtract(y);
|
||||
g = z.gcd(what);
|
||||
if(!g.equals(BigInteger.ONE)) {
|
||||
break;
|
||||
}
|
||||
if((j & (j - 1)) == 0) {
|
||||
y = x.clone();
|
||||
}
|
||||
}
|
||||
if(g.compareTo(BigInteger.ONE) > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var f = what.divide(g), P, Q;
|
||||
|
||||
if(g.compareTo(f) > 0) {
|
||||
P = f;
|
||||
Q = g;
|
||||
} else {
|
||||
P = g;
|
||||
Q = f;
|
||||
}
|
||||
|
||||
return [bytesFromBigInt(P), bytesFromBigInt(Q), it];
|
||||
} */
|
||||
|
||||
/* export function gcdLong(a: any, b: any) {
|
||||
while(a.notEquals(goog.math.Long.ZERO) && b.notEquals(goog.math.Long.ZERO)) {
|
||||
while(b.and(goog.math.Long.ONE).equals(goog.math.Long.ZERO)) {
|
||||
b = b.shiftRight(1);
|
||||
}
|
||||
|
||||
while(a.and(goog.math.Long.ONE).equals(goog.math.Long.ZERO)) {
|
||||
a = a.shiftRight(1);
|
||||
}
|
||||
|
||||
if(a.compare(b) > 0) {
|
||||
a = a.subtract(b);
|
||||
} else {
|
||||
b = b.subtract(a);
|
||||
}
|
||||
}
|
||||
|
||||
return b.equals(goog.math.Long.ZERO) ? a : b;
|
||||
} */
|
||||
|
||||
/* export function pqPrimeLong(what: any) {
|
||||
var it = 0,
|
||||
g;
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var q = goog.math.Long.fromInt((nextRandomInt(128) & 15) + 17);
|
||||
var x = goog.math.Long.fromInt(nextRandomInt(1000000000) + 1);
|
||||
var y = x;
|
||||
var lim = 1 << (i + 18);
|
||||
|
||||
for(var j = 1; j < lim; j++) {
|
||||
++it;
|
||||
var a = x;
|
||||
var b = x;
|
||||
var c = q;
|
||||
|
||||
while(b.notEquals(goog.math.Long.ZERO)) {
|
||||
if(b.and(goog.math.Long.ONE).notEquals(goog.math.Long.ZERO)) {
|
||||
c = c.add(a);
|
||||
if (c.compare(what) > 0) {
|
||||
c = c.subtract(what);
|
||||
}
|
||||
}
|
||||
a = a.add(a);
|
||||
if(a.compare(what) > 0) {
|
||||
a = a.subtract(what);
|
||||
}
|
||||
b = b.shiftRight(1);
|
||||
}
|
||||
|
||||
x = c;
|
||||
var z = x.compare(y) < 0 ? y.subtract(x) : x.subtract(y);
|
||||
g = gcdLong(z, what);
|
||||
if(g.notEquals(goog.math.Long.ONE)) {
|
||||
break;
|
||||
}
|
||||
if((j & (j - 1)) == 0) {
|
||||
y = x;
|
||||
}
|
||||
}
|
||||
if(g.compare(goog.math.Long.ONE) > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var f = what.div(g), P, Q;
|
||||
|
||||
if(g.compare(f) > 0) {
|
||||
P = f;
|
||||
Q = g;
|
||||
} else {
|
||||
P = g;
|
||||
Q = f;
|
||||
}
|
||||
|
||||
return [bytesFromHex(P.toString(16)), bytesFromHex(Q.toString(16)), it];
|
||||
} */
|
||||
|
||||
export function pqPrimeLeemon(what: any) {
|
||||
var minBits = 64;
|
||||
var minLen = Math.ceil(minBits / bpe) + 1;
|
||||
|
|
|
@ -22,11 +22,6 @@ class CryptoWorker {
|
|||
private pending: Array<Task> = [];
|
||||
private debug = false;
|
||||
|
||||
// @ts-ignore
|
||||
// private webCrypto = Config.Modes.webcrypto && window.crypto && (window.crypto.subtle || window.crypto.webkitSubtle); /* || window.msCrypto && window.msCrypto.subtle*/
|
||||
// private useSha1Crypto = this.webCrypto && this.webCrypto.digest !== undefined;
|
||||
// private useSha256Crypto = this.webCrypto && this.webCrypto.digest !== undefined;
|
||||
|
||||
constructor() {
|
||||
console.log(dT(), 'CW constructor');
|
||||
|
||||
|
@ -96,57 +91,18 @@ class CryptoWorker {
|
|||
//if(this.webWorker) {
|
||||
return this.performTaskWorker<Uint8Array>('sha1-hash', bytes);
|
||||
//}
|
||||
|
||||
/* if(this.useSha1Crypto) {
|
||||
console.error('usesha1crypto');
|
||||
// We don't use buffer since typedArray.subarray(...).buffer gives the whole buffer and not sliced one. webCrypto.digest supports typed array
|
||||
return new Promise((resolve, reject) => {
|
||||
var bytesTyped = Array.isArray(bytes) ? convertToUint8Array(bytes) : bytes;
|
||||
// console.log(dT(), 'Native sha1 start')
|
||||
|
||||
this.webCrypto.digest({name: 'SHA-1'}, bytesTyped).then((digest: ArrayBuffer) => {
|
||||
// console.log(dT(), 'Native sha1 done')
|
||||
resolve(digest);
|
||||
}, (e: ErrorEvent) => {
|
||||
console.error('Crypto digest error', e);
|
||||
this.useSha1Crypto = false;
|
||||
resolve(sha1HashSync(bytes));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(sha1HashSync(bytes)); */
|
||||
}
|
||||
|
||||
public sha256Hash(bytes: any) {
|
||||
//if(this.webWorker) {
|
||||
return this.performTaskWorker<number[]>('sha256-hash', bytes);
|
||||
//}
|
||||
|
||||
/* if(this.useSha256Crypto) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var bytesTyped = Array.isArray(bytes) ? convertToUint8Array(bytes) : bytes;
|
||||
// console.log(dT(), 'Native sha1 start')
|
||||
this.webCrypto.digest({name: 'SHA-256'}, bytesTyped).then((digest: ArrayBuffer) => {
|
||||
// console.log(dT(), 'Native sha1 done')
|
||||
resolve(digest);
|
||||
}, (e: ErrorEvent) => {
|
||||
console.error('Crypto digest error', e);
|
||||
this.useSha256Crypto = false;
|
||||
resolve(sha256HashSync(bytes));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(sha256HashSync(bytes)); */
|
||||
}
|
||||
|
||||
public pbkdf2(buffer: Uint8Array, salt: Uint8Array, iterations: number) {
|
||||
//if(this.webWorker) {
|
||||
return this.performTaskWorker<ArrayBuffer>('pbkdf2', buffer, salt, iterations);
|
||||
//}
|
||||
|
||||
//return hash_pbkdf2(buffer, salt, iterations);
|
||||
}
|
||||
|
||||
public aesEncrypt(bytes: any, keyBytes: any, ivBytes: any) {
|
||||
|
@ -154,8 +110,6 @@ class CryptoWorker {
|
|||
return this.performTaskWorker<ArrayBuffer>('aes-encrypt', convertToArrayBuffer(bytes),
|
||||
convertToArrayBuffer(keyBytes), convertToArrayBuffer(ivBytes));
|
||||
//}
|
||||
|
||||
//return Promise.resolve<ArrayBuffer>(convertToArrayBuffer(aesEncryptSync(bytes, keyBytes, ivBytes)));
|
||||
}
|
||||
|
||||
public aesDecrypt(encryptedBytes: any, keyBytes: any, ivBytes: any): Promise<ArrayBuffer> {
|
||||
|
@ -164,8 +118,6 @@ class CryptoWorker {
|
|||
encryptedBytes, keyBytes, ivBytes)
|
||||
.then(bytes => convertToArrayBuffer(bytes));
|
||||
//}
|
||||
|
||||
//return Promise.resolve<ArrayBuffer>(convertToArrayBuffer(aesDecryptSync(encryptedBytes, keyBytes, ivBytes)));
|
||||
}
|
||||
|
||||
public rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes: any): Promise<number[]> {
|
||||
|
@ -178,29 +130,21 @@ class CryptoWorker {
|
|||
//if(this.webWorker) {
|
||||
return this.performTaskWorker<[number[], number[], number]>('factorize', bytes);
|
||||
//}
|
||||
|
||||
//return Promise.resolve(pqPrimeFactorization(bytes));
|
||||
}
|
||||
|
||||
public modPow(x: any, y: any, m: any) {
|
||||
//if(this.webWorker) {
|
||||
return this.performTaskWorker<number[]>('mod-pow', x, y, m);
|
||||
//}
|
||||
|
||||
//return Promise.resolve(bytesModPow(x, y, m));
|
||||
}
|
||||
|
||||
public gzipUncompress<T>(bytes: ArrayBuffer, toString?: boolean) {
|
||||
//if(this.webWorker) {
|
||||
return this.performTaskWorker<T>('unzip', bytes, toString);
|
||||
//}
|
||||
|
||||
//return Promise.resolve(gzipUncompress(bytes, toString) as T);
|
||||
}
|
||||
}
|
||||
|
||||
const cryptoWorker = new CryptoWorker();
|
||||
|
||||
(window as any).CryptoWorker = cryptoWorker;
|
||||
|
||||
export default cryptoWorker;
|
||||
|
|
|
@ -254,6 +254,48 @@ class IdbFileStorage {
|
|||
});
|
||||
}
|
||||
|
||||
public getAllKeys(): Promise<Array<string>> {
|
||||
console.time('getAllEntries');
|
||||
return this.openDatabase().then((db) => {
|
||||
// @ts-ignore
|
||||
var objectStore = db.transaction([this.dbStoreName], IDBTransaction.READ || 'readonly')
|
||||
.objectStore(this.dbStoreName);
|
||||
var request = objectStore.getAllKeys();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request.onsuccess = function(event) {
|
||||
// @ts-ignore
|
||||
var result = event.target.result;
|
||||
resolve(result);
|
||||
console.timeEnd('getAllEntries');
|
||||
}
|
||||
|
||||
request.onerror = reject;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public isFileExists(fileName: string): Promise<boolean> {
|
||||
console.time('isFileExists');
|
||||
return this.openDatabase().then((db) => {
|
||||
// @ts-ignore
|
||||
var objectStore = db.transaction([this.dbStoreName], IDBTransaction.READ || 'readonly')
|
||||
.objectStore(this.dbStoreName);
|
||||
var request = objectStore.openCursor(fileName);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request.onsuccess = function(event) {
|
||||
// @ts-ignore
|
||||
var cursor = event.target.result;
|
||||
resolve(!!cursor);
|
||||
console.timeEnd('isFileExists');
|
||||
}
|
||||
|
||||
request.onerror = reject;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getFileWriter(fileName: string, mimeType: string) {
|
||||
var fakeWriter = FileManager.getFakeFileWriter(mimeType, (blob: any) => {
|
||||
this.saveFile(fileName, blob);
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
// @ts-ignore
|
||||
import LottiePlayer from "lottie-web/build/player/lottie_canvas.min.js";
|
||||
|
||||
(window as any).lottie = LottiePlayer;
|
|
@ -1,14 +1,11 @@
|
|||
//import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web";
|
||||
// @ts-ignore
|
||||
import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web/build/player/lottie_canvas.min.js";
|
||||
//import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web/build/player/lottie_light.min.js";
|
||||
import { isElementInViewport, isInDOM } from "./utils";
|
||||
import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web/build/player/lottie.d";
|
||||
|
||||
class LottieLoader {
|
||||
public lottie: /* any */ typeof LottiePlayer = null;
|
||||
private lottie: /* any */ typeof LottiePlayer = null;
|
||||
private animations: {
|
||||
[group: string]: {
|
||||
animation: AnimationItem,
|
||||
animation: /* any */AnimationItem,
|
||||
container: HTMLDivElement,
|
||||
paused: boolean,
|
||||
autoplay: boolean,
|
||||
|
@ -16,6 +13,28 @@ class LottieLoader {
|
|||
}[]
|
||||
} = {};
|
||||
private debug = false;
|
||||
public loaded: Promise<void>;
|
||||
private lastTimeLoad = 0;
|
||||
private waitingTimeouts = 0;
|
||||
|
||||
public loadLottie() {
|
||||
if(this.loaded) return this.loaded;
|
||||
|
||||
this.loaded = new Promise((resolve, reject) => {
|
||||
(window as any).lottieLoaded = () => {
|
||||
console.log('lottie loaded');
|
||||
this.lottie = (window as any).lottie;
|
||||
resolve();
|
||||
};
|
||||
|
||||
let sc = document.createElement('script');
|
||||
sc.src = 'npm.lottie-web.chunk.js';
|
||||
sc.async = true;
|
||||
sc.onload = (window as any).lottieLoaded;
|
||||
|
||||
document.body.appendChild(sc);
|
||||
});
|
||||
}
|
||||
|
||||
public checkAnimations(blurred?: boolean, group?: string, destroy = false) {
|
||||
let groups = group ? [group] : Object.keys(this.animations);
|
||||
|
@ -33,6 +52,13 @@ class LottieLoader {
|
|||
for(let i = length - 1; i >= 0; --i) {
|
||||
let {animation, container, paused, autoplay, canvas} = animations[i];
|
||||
|
||||
if(destroy && !isInDOM(container)) {
|
||||
this.debug && console.log('destroy animation');
|
||||
animation.destroy();
|
||||
animations.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(canvas) {
|
||||
let c = container.firstElementChild as HTMLCanvasElement;
|
||||
if(!c.height && !c.width && isElementInViewport(container)) {
|
||||
|
@ -41,16 +67,9 @@ class LottieLoader {
|
|||
}
|
||||
}
|
||||
|
||||
if(destroy && !isInDOM(container)) {
|
||||
this.debug && console.log('destroy animation');
|
||||
animation.destroy();
|
||||
animations.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!autoplay) continue;
|
||||
|
||||
if(!isElementInViewport(container) || blurred) {
|
||||
if(blurred || !isElementInViewport(container)) {
|
||||
if(!paused) {
|
||||
this.debug && console.log('pause animation', isElementInViewport(container), container);
|
||||
animation.pause();
|
||||
|
@ -66,16 +85,10 @@ class LottieLoader {
|
|||
}
|
||||
|
||||
public async loadAnimation(params: /* any */AnimationConfigWithPath | AnimationConfigWithData, group = '') {
|
||||
/* if(!this.lottie) {
|
||||
this.lottie = (await import(
|
||||
'lottie-web')).default;
|
||||
this.lottie.setQuality('low');
|
||||
} */
|
||||
|
||||
//params.autoplay = false;
|
||||
params.renderer = 'canvas';
|
||||
params.rendererSettings = {
|
||||
//context: canvasContext, // the canvas context
|
||||
//context: context, // the canvas context
|
||||
//preserveAspectRatio: 'xMinYMin slice', // Supports the same options as the svg element's preserveAspectRatio property
|
||||
clearCanvas: true,
|
||||
progressiveLoad: true, // Boolean, only svg renderer, loads dom elements when needed. Might speed up initialization for large number of elements.
|
||||
|
@ -83,12 +96,27 @@ class LottieLoader {
|
|||
};
|
||||
|
||||
if(!this.lottie) {
|
||||
this.lottie = LottiePlayer;
|
||||
//this.lottie.setQuality('low');
|
||||
this.lottie.setQuality(10);
|
||||
if(!this.loaded) this.loadLottie();
|
||||
await this.loaded;
|
||||
|
||||
this.lottie.setQuality('low');
|
||||
//this.lottie.setQuality(10);
|
||||
}
|
||||
|
||||
let time = Date.now();
|
||||
let diff = time - this.lastTimeLoad;
|
||||
let delay = 150;
|
||||
if(diff < delay) {
|
||||
delay *= ++this.waitingTimeouts;
|
||||
console.log('lottieloader delay:', delay);
|
||||
//await new Promise((resolve) => setTimeout(resolve, delay));
|
||||
this.waitingTimeouts--;
|
||||
}
|
||||
|
||||
let animation = this.lottie.loadAnimation(params);
|
||||
|
||||
this.lastTimeLoad = Date.now();
|
||||
|
||||
if(!this.animations[group]) this.animations[group] = [];
|
||||
this.animations[group].push({
|
||||
animation,
|
||||
|
@ -101,7 +129,7 @@ class LottieLoader {
|
|||
if(params.autoplay) {
|
||||
this.checkAnimations();
|
||||
}
|
||||
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,6 @@ import apiManager from "./apiManager";
|
|||
import { logger, deferredPromise, CancellablePromise } from "../polyfill";
|
||||
|
||||
export class ApiFileManager {
|
||||
public cachedFs = false;
|
||||
public cachedFsPromise = false;
|
||||
public cachedSavePromises: {
|
||||
[fileName: string]: Promise<Blob>
|
||||
} = {};
|
||||
|
@ -18,6 +16,9 @@ export class ApiFileManager {
|
|||
[fileName: string]: any
|
||||
} = {};
|
||||
|
||||
/* public indexedKeys: Set<string> = new Set();
|
||||
private keysLoaded = false; */
|
||||
|
||||
public downloadPulls: {
|
||||
[x: string]: Array<{
|
||||
cb: () => Promise<unknown>,
|
||||
|
@ -93,8 +94,8 @@ export class ApiFileManager {
|
|||
var fileName = (location.file_name as string || '').split('.');
|
||||
var ext: string = fileName[fileName.length - 1] || '';
|
||||
|
||||
if(location.stickerType == 1 /* && !WebpManager.isWebpSupported() */) { // warning
|
||||
ext += 'webp'; /* 'png'; */
|
||||
if(location.stickerType == 1) {
|
||||
ext += 'webp';
|
||||
} else if(location.stickerType == 2) {
|
||||
ext += 'tgs';
|
||||
}
|
||||
|
@ -109,13 +110,13 @@ export class ApiFileManager {
|
|||
|
||||
var ext: string = 'jpg';
|
||||
if(location.stickerType == 1) {
|
||||
ext = 'webp'/* WebpManager.isWebpSupported() ? 'webp' : 'png'*/;
|
||||
ext = 'webp';
|
||||
} else if(location.stickerType == 2) {
|
||||
ext += 'tgs';
|
||||
}
|
||||
|
||||
if(location.volume_id) {
|
||||
return location.volume_id + '_' + location.local_id /* + '_' + location.secret */ + '.' + ext;
|
||||
return location.volume_id + '_' + location.local_id + '.' + ext;
|
||||
} else {
|
||||
return location.id + '_' + location.access_hash + '.' + ext;
|
||||
}
|
||||
|
@ -138,19 +139,16 @@ export class ApiFileManager {
|
|||
}
|
||||
|
||||
public getFileStorage(): typeof IdbFileStorage {
|
||||
if(!Config.Modes.memory_only) {
|
||||
/* if(TmpfsFileStorage.isAvailable()) {
|
||||
return TmpfsFileStorage;
|
||||
} */
|
||||
|
||||
if(IdbFileStorage.isAvailable()) {
|
||||
return IdbFileStorage;
|
||||
}
|
||||
}
|
||||
|
||||
return IdbFileStorage/* MemoryFileStorage */;
|
||||
return IdbFileStorage;
|
||||
}
|
||||
|
||||
/* public isFileExists(location: any) {
|
||||
var fileName = this.getFileName(location);
|
||||
|
||||
return this.cachedDownloads[fileName] || this.indexedKeys.has(fileName);
|
||||
//return this.cachedDownloads[fileName] || this.indexedKeys.has(fileName) ? Promise.resolve(true) : this.getFileStorage().isFileExists(fileName);
|
||||
} */
|
||||
|
||||
public saveSmallFile(location: any, bytes: Uint8Array) {
|
||||
var fileName = this.getFileName(location);
|
||||
|
||||
|
@ -172,6 +170,10 @@ export class ApiFileManager {
|
|||
return Promise.reject({type: 'BROWSER_BLOB_NOT_SUPPORTED'});
|
||||
}
|
||||
|
||||
/* if(!this.keysLoaded) {
|
||||
this.getIndexedKeys();
|
||||
} */
|
||||
|
||||
//this.log('downloadSmallFile', location, options);
|
||||
|
||||
let dcID = options.dcID || location.dc_id;
|
||||
|
@ -243,6 +245,14 @@ export class ApiFileManager {
|
|||
return fileStorage.getFile(fileName, size);
|
||||
}
|
||||
|
||||
/* public getIndexedKeys() {
|
||||
this.keysLoaded = true;
|
||||
this.getFileStorage().getAllKeys().then(keys => {
|
||||
this.indexedKeys.clear();
|
||||
this.indexedKeys = new Set(keys);
|
||||
});
|
||||
} */
|
||||
|
||||
public downloadFile(dcID: number, location: any, size: number, options: {
|
||||
mimeType?: string,
|
||||
dcID?: number,
|
||||
|
@ -253,14 +263,8 @@ export class ApiFileManager {
|
|||
return Promise.reject({type: 'BROWSER_BLOB_NOT_SUPPORTED'});
|
||||
}
|
||||
|
||||
/* var processSticker = false;
|
||||
if(location.sticker && !WebpManager.isWebpSupported()) {
|
||||
if(options.toFileEntry || size > 524288) {
|
||||
delete location.sticker;
|
||||
} else {
|
||||
processSticker = true;
|
||||
options.mime = 'image/png';
|
||||
}
|
||||
/* if(!this.keysLoaded) {
|
||||
this.getIndexedKeys();
|
||||
} */
|
||||
|
||||
// this.log('Dload file', dcID, location, size)
|
||||
|
@ -273,8 +277,6 @@ export class ApiFileManager {
|
|||
|
||||
if(cachedPromise) {
|
||||
if(toFileEntry) {
|
||||
/* let blob = await cachedPromise;
|
||||
return FileManager.copy(blob, toFileEntry) as Promise<Blob>; */
|
||||
return cachedPromise.then((blob: any) => {
|
||||
return FileManager.copy(blob, toFileEntry);
|
||||
});
|
||||
|
@ -283,13 +285,6 @@ export class ApiFileManager {
|
|||
//this.log('downloadFile cachedPromise');
|
||||
|
||||
if(size) {
|
||||
/* let blob = await cachedPromise;
|
||||
if(blob.size < size) {
|
||||
this.log('downloadFile need to deleteFile, wrong size:', blob.size, size);
|
||||
await this.deleteFile(location);
|
||||
} else {
|
||||
return cachedPromise;
|
||||
} */
|
||||
return cachedPromise.then((blob: Blob) => {
|
||||
if(blob.size < size) {
|
||||
this.log('downloadFile need to deleteFile, wrong size:', blob.size, size);
|
||||
|
@ -300,7 +295,6 @@ export class ApiFileManager {
|
|||
return this.downloadFile(dcID, location, size, options);
|
||||
});
|
||||
} else {
|
||||
//return cachedPromise;
|
||||
return blob;
|
||||
}
|
||||
});
|
||||
|
@ -309,13 +303,8 @@ export class ApiFileManager {
|
|||
}
|
||||
}
|
||||
|
||||
//this.log('arriba');
|
||||
|
||||
//var deferred = $q.defer()
|
||||
let deferred = deferredPromise<Blob>();
|
||||
|
||||
//return;
|
||||
|
||||
var canceled = false;
|
||||
var resolved = false;
|
||||
var mimeType = options.mimeType || 'image/jpeg',
|
||||
|
@ -345,7 +334,6 @@ export class ApiFileManager {
|
|||
} else {
|
||||
deferred.resolve(this.cachedDownloads[fileName] = blob);
|
||||
}
|
||||
//}, () => {
|
||||
}).catch(() => {
|
||||
//this.log('not i wanted');
|
||||
//var fileWriterPromise = toFileEntry ? FileManager.getFileWriter(toFileEntry) : fileStorage.getFileWriter(fileName, mimeType);
|
||||
|
@ -496,7 +484,6 @@ export class ApiFileManager {
|
|||
var totalParts = Math.ceil(fileSize / partSize);
|
||||
|
||||
var fileID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)];
|
||||
//var deferred = $q.defer();
|
||||
|
||||
var _part = 0,
|
||||
resultInputFile = {
|
||||
|
@ -507,23 +494,6 @@ export class ApiFileManager {
|
|||
md5_checksum: ''
|
||||
};
|
||||
|
||||
/* let deferred: {
|
||||
then?: any,
|
||||
resolve?: (input: typeof resultInputFile) => void,
|
||||
reject?: (error: any) => void,
|
||||
promise?: any,
|
||||
|
||||
cancel?: () => void,
|
||||
notify?: (details: {done: number, total: number}) => void
|
||||
} = {
|
||||
|
||||
};
|
||||
|
||||
deferred.promise = new Promise<typeof resultInputFile>((resolve, reject) => {
|
||||
deferred.resolve = resolve;
|
||||
deferred.reject = reject;
|
||||
}); */
|
||||
|
||||
let deferredHelper: {
|
||||
resolve?: (input: typeof resultInputFile) => void,
|
||||
reject?: (error: any) => void,
|
||||
|
@ -538,7 +508,6 @@ export class ApiFileManager {
|
|||
|
||||
deferredHelper.resolve = resolve;
|
||||
deferredHelper.reject = reject;
|
||||
//return Promise.resolve();
|
||||
});
|
||||
Object.assign(deferred, deferredHelper);
|
||||
|
||||
|
|
|
@ -408,7 +408,8 @@ export class Authorizer {
|
|||
var gBytes = bytesFromHex(auth.g.toString(16));
|
||||
|
||||
auth.b = new Array(256);
|
||||
MTProto.secureRandom.nextBytes(auth.b);
|
||||
auth.b = [...new Uint8Array(auth.b.length).randomize()];
|
||||
//MTProto.secureRandom.nextBytes(auth.b);
|
||||
|
||||
try {
|
||||
var gB = await CryptoWorker.modPow(gBytes, auth.b, auth.dhPrime);
|
||||
|
|
|
@ -54,14 +54,9 @@ export class TelegramMeWebService {
|
|||
|
||||
export const telegramMeWebService = new TelegramMeWebService();
|
||||
|
||||
// @ts-ignore
|
||||
import {SecureRandom} from 'jsbn';
|
||||
|
||||
export namespace MTProto {
|
||||
//$($window).on('click keydown', rng_seed_time); // WARNING!
|
||||
|
||||
export const secureRandom = new SecureRandom();
|
||||
|
||||
export const passwordManager = PasswordManager;
|
||||
export const dcConfigurator = DcConfigurator;
|
||||
export const rsaKeysManager = RSAKeysManager;
|
||||
|
@ -73,4 +68,4 @@ export namespace MTProto {
|
|||
export const serverTimeManager = ServerTimeManager;
|
||||
}
|
||||
|
||||
//(window as any).MTProto = MTProto;
|
||||
(window as any).MTProto = MTProto;
|
||||
|
|
|
@ -128,7 +128,8 @@ class MTPNetworker {
|
|||
this.seqNo = 0;
|
||||
this.prevSessionID = this.sessionID;
|
||||
this.sessionID = new Array(8);
|
||||
MTProto.secureRandom.nextBytes(this.sessionID);
|
||||
this.sessionID = [...new Uint8Array(this.sessionID.length).randomize()];
|
||||
//MTProto.secureRandom.nextBytes(this.sessionID);
|
||||
}
|
||||
|
||||
public setupMobileSleep() {
|
||||
|
@ -789,7 +790,8 @@ class MTPNetworker {
|
|||
|
||||
var paddingLength = (16 - (data.offset % 16)) + 16 * (1 + nextRandomInt(5));
|
||||
var padding = new Array(paddingLength);
|
||||
MTProto.secureRandom.nextBytes(padding);
|
||||
padding = [...new Uint8Array(padding.length).randomize()];
|
||||
//MTProto.secureRandom.nextBytes(padding);
|
||||
|
||||
var dataWithPadding = bufferConcat(dataBuffer, padding);
|
||||
// this.log('Adding padding', dataBuffer, padding, dataWithPadding)
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import { bytesToHex, bytesFromHex, dT, bufferConcats } from "./bin_utils";
|
||||
import { MTProto } from "./mtproto/mtproto";
|
||||
// @ts-ignore
|
||||
import {SecureRandom} from 'jsbn';
|
||||
|
||||
export const secureRandom = new SecureRandom();
|
||||
|
||||
export function logger(prefix: string) {
|
||||
function Log(...args: any[]) {
|
||||
|
@ -69,7 +72,7 @@ Object.defineProperty(Uint8Array.prototype, 'hex', {
|
|||
});
|
||||
|
||||
Uint8Array.prototype.randomize = function() {
|
||||
MTProto.secureRandom.nextBytes(this);
|
||||
secureRandom.nextBytes(this);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import {Webp} from "webp-hero/libwebp/dist/webp.js"
|
||||
import {detectWebpSupport} from "webp-hero/dist/detect-webp-support.js"
|
||||
|
||||
|
@ -30,22 +29,21 @@ export class WebpMachine {
|
|||
* Decode raw webp data into a png data url
|
||||
*/
|
||||
async decode(webpData: Uint8Array): Promise<string> {
|
||||
if (this.busy) throw new WebpMachineError("cannot decode when already busy")
|
||||
this.busy = true
|
||||
if(this.busy) throw new WebpMachineError("cannot decode when already busy");
|
||||
this.busy = true;
|
||||
|
||||
try {
|
||||
await relax()
|
||||
const canvas = document.createElement("canvas")
|
||||
this.webp.setCanvas(canvas)
|
||||
this.webp.webpToSdl(webpData, webpData.length)
|
||||
this.busy = false
|
||||
return canvas.toDataURL()
|
||||
}
|
||||
catch (error) {
|
||||
this.busy = false
|
||||
error.name = WebpMachineError.name
|
||||
error.message = `failed to decode webp image: ${error.message}`
|
||||
throw error
|
||||
await relax();
|
||||
const canvas = document.createElement("canvas");
|
||||
this.webp.setCanvas(canvas);
|
||||
this.webp.webpToSdl(webpData, webpData.length);
|
||||
this.busy = false;
|
||||
return canvas.toDataURL();
|
||||
} catch(error) {
|
||||
this.busy = false;
|
||||
error.name = WebpMachineError.name;
|
||||
error.message = `failed to decode webp image: ${error.message}`;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
background-image: url('../../../public/assets/img/camomile.jpg');
|
||||
/* background-image: url('../../../public/assets/img/camomile.jpg'); */
|
||||
background-image: url('../../../public/assets/img/camomile_blurred.jpg');
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
|
||||
|
@ -120,6 +121,8 @@
|
|||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
display: flex; // for end
|
||||
|
||||
/* display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end; */
|
||||
|
@ -155,6 +158,7 @@
|
|||
box-sizing: border-box;
|
||||
/* min-height: 100%; */
|
||||
justify-content: flex-end;
|
||||
flex: 1;
|
||||
|
||||
&.is-chat {
|
||||
.is-in .bubble__container {
|
||||
|
@ -237,6 +241,10 @@
|
|||
&.service {
|
||||
display: block;
|
||||
padding: 1rem 0;
|
||||
|
||||
.bubble__container {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.forwarded {
|
||||
|
@ -264,8 +272,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* &.is-group-first {
|
||||
padding-top: 10px;
|
||||
} */
|
||||
|
||||
&.is-group-last {
|
||||
padding-bottom: 2.5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
&:not(.forwarded) {
|
||||
|
@ -328,6 +340,7 @@
|
|||
height: 18px;
|
||||
width: 18px;
|
||||
margin: 0 .05rem;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
span.emoji {
|
||||
|
@ -738,7 +751,8 @@
|
|||
|
||||
&__container > .name {
|
||||
/* padding: .2675rem .6rem 0 .6rem; */
|
||||
padding: .32rem .6rem 0 .6rem;
|
||||
/* padding: .32rem .6rem 0 .6rem; */
|
||||
padding: 5px .6rem 0 .6rem;
|
||||
font-weight: 500;
|
||||
/* padding-bottom: 4px; */
|
||||
color: $darkblue;
|
||||
|
|
|
@ -185,7 +185,7 @@
|
|||
background-position: center center;
|
||||
|
||||
display: flex;
|
||||
background-color: #cecece;
|
||||
//background-color: #000;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ $bg: #ffffff;
|
|||
$text-size: 16px;
|
||||
$time-size: 12px;
|
||||
|
||||
//$large-screen: 1680px;
|
||||
$large-screen: 16800px;
|
||||
$large-screen: 1680px;
|
||||
//$large-screen: 16800px;
|
||||
|
||||
@import "partials/ico";
|
||||
@import "partials/chatlist";
|
||||
|
@ -66,15 +66,6 @@ button, input, optgroup, select, textarea, html {
|
|||
//min-width: 100%;
|
||||
margin: 0 auto;
|
||||
max-width: $large-screen;
|
||||
|
||||
@media (min-width: $large-screen) {
|
||||
border-top-width: 0;
|
||||
border-bottom-width: 0;
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #DADCE0;
|
||||
}
|
||||
}
|
||||
|
||||
.disable-hover,
|
||||
|
@ -336,83 +327,46 @@ input {
|
|||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border-radius: inherit;
|
||||
|
||||
&__circle {
|
||||
background-color: rgba(0,0,0,.08);
|
||||
display: block;
|
||||
position: absolute;
|
||||
transform: scale(0);
|
||||
border-radius: 50%;
|
||||
animation: ripple-effect .7s forwards;
|
||||
transition: .35s opacity;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__circle.hiding {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.c-ripple__circle {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: 50%;
|
||||
/* background: rgba(255, 255, 255, 0.25); */
|
||||
background: rgba(112, 117, 121, .08);
|
||||
}
|
||||
.c-ripple.active .c-ripple__circle {
|
||||
//-webkit-animation: a-ripple 750ms ease-in;
|
||||
//animation: a-ripple 750ms ease-in-out;
|
||||
will-change: padding-bottom, width, opacity;
|
||||
-webkit-animation: a-ripple 625ms ease-in-out;
|
||||
animation: a-ripple 625ms ease-in-out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Animation: Ripple
|
||||
* --------------------------------------------------
|
||||
*/
|
||||
@-webkit-keyframes a-ripple {
|
||||
@keyframes ripple-effect {
|
||||
0% {
|
||||
opacity: 0;
|
||||
//opacity: 1;
|
||||
//transform: translate(-50%, -50%) scale(0);
|
||||
transform: scale(0);
|
||||
}
|
||||
25% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
width: 200%;
|
||||
padding-bottom: 200%;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes a-ripple {
|
||||
0% {
|
||||
opacity: 0;
|
||||
//opacity: 1;
|
||||
}
|
||||
25% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
width: 200%;
|
||||
padding-bottom: 200%;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* .ripple {
|
||||
background-color: rgba(112, 117, 121, .08);
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
transform: translateX(-100%) translateY(-100%);
|
||||
mix-blend-mode: screen;
|
||||
animation: ripple 750ms ease-out forwards;
|
||||
}
|
||||
/* 50% {
|
||||
opacity: 1;
|
||||
} */
|
||||
|
||||
@keyframes ripple {
|
||||
0% { transform: translate(-100%, -100%); }
|
||||
80% { transform: translate(-100%, -100%) scale(50); }
|
||||
100% { transform: translate(-100%, -100%) scale(50); }
|
||||
} */
|
||||
to {
|
||||
transform: scale(2);
|
||||
//transform: translate(-50%, -50%) scale(2);
|
||||
//opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.document {
|
||||
padding-left: 4.5rem;
|
||||
|
@ -810,6 +764,10 @@ input {
|
|||
font-size: 24px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.phone-code {
|
||||
|
@ -1313,7 +1271,7 @@ span.popup-close {
|
|||
}
|
||||
}
|
||||
|
||||
/* div.scrollable::-webkit-scrollbar {
|
||||
div.scrollable::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
@ -1330,7 +1288,7 @@ div.scrollable::-webkit-scrollbar-thumb {
|
|||
}
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
} */
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
width: 100%;
|
||||
|
@ -1395,7 +1353,7 @@ div.scrollable::-webkit-scrollbar-thumb {
|
|||
transition-duration: .2s;
|
||||
transition-timing-function: ease-in-out;
|
||||
|
||||
display: none;
|
||||
//display: none;
|
||||
|
||||
border-radius: $border-radius;
|
||||
z-index: 2;
|
||||
|
@ -1622,6 +1580,15 @@ div.scrollable::-webkit-scrollbar-thumb {
|
|||
stroke: $button-primary-background;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $large-screen) {
|
||||
border-top-width: 0;
|
||||
border-bottom-width: 0;
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #DADCE0;
|
||||
}
|
||||
}
|
||||
|
||||
.page-password {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { bytesFromHex, bytesFromArrayBuffer } from '../lib/bin_utils';
|
|||
|
||||
test('factorize', () => {
|
||||
for(let i = 0; i < 10; ++i) {
|
||||
CryptoWorker.factorize(new Uint8Array([20, 149, 30, 137, 202, 169, 105, 69])).then((pAndQ: any) => {
|
||||
CryptoWorker.factorize(new Uint8Array([20, 149, 30, 137, 202, 169, 105, 69])).then(pAndQ => {
|
||||
pAndQ.pop();
|
||||
expect(pAndQ).toEqual([[59, 165, 190, 67], [88, 86, 117, 215]]);
|
||||
});
|
||||
|
@ -25,6 +25,12 @@ test('sha1', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('sha256', () => {
|
||||
CryptoWorker.sha256Hash(new Uint8Array([112, 20, 211, 20, 106, 249, 203, 252, 39, 107, 106, 194, 63, 60, 13, 130, 51, 78, 107, 6, 110, 156, 214, 65, 205, 10, 30, 150, 79, 10, 145, 194, 232, 240, 127, 55, 146, 103, 248, 227, 160, 172, 30, 153, 122, 189, 110, 162, 33, 86, 174, 117])).then(bytes => {
|
||||
expect(bytes).toEqual(new Uint8Array([158, 59, 39, 247, 130, 244, 235, 160, 16, 249, 34, 114, 67, 171, 203, 208, 187, 72, 217, 106, 253, 62, 195, 242, 52, 118, 99, 72, 221, 29, 203, 95]));
|
||||
});
|
||||
});
|
||||
|
||||
test('pbkdf2', () => {
|
||||
/* const crypto = require('crypto');
|
||||
|
||||
|
|
|
@ -65,9 +65,10 @@
|
|||
"node_modules",
|
||||
"public",
|
||||
"coverage",
|
||||
/* "./src/lib/StackBlur.js",
|
||||
"./src/lib/ckin.js",
|
||||
"./src/lib/StackBlur.js",
|
||||
"./src/lib/*.js",
|
||||
"./src/*.js",
|
||||
"*.js", */
|
||||
"*.js",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -54,7 +54,8 @@ module.exports = {
|
|||
//entry: './src/index.ts',
|
||||
entry: {
|
||||
index: './src/index.ts',
|
||||
webp: './src/lib/webp.ts'
|
||||
webp: './src/lib/webp.ts',
|
||||
lottie: './src/lib/lottie.ts'
|
||||
},
|
||||
/* entry: {
|
||||
index: './src/index.ts',
|
||||
|
@ -72,6 +73,7 @@ module.exports = {
|
|||
contentBase: path.join(__dirname, 'public'),
|
||||
watchContentBase: true,
|
||||
compress: true,
|
||||
http2: true,
|
||||
port: 9000
|
||||
},
|
||||
|
||||
|
@ -93,7 +95,7 @@ module.exports = {
|
|||
minifyURLs: true
|
||||
}, */
|
||||
chunks: "all",
|
||||
excludeChunks: ['npm.webp-hero']
|
||||
excludeChunks: ['npm.webp-hero', 'npm.lottie-web']
|
||||
})
|
||||
],
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue