New stickers (rlottie)

Media viewer:
1) fix arrows
2) forward
3) item opening
QR-code 2FA login fix
Forward dialogs search (not ready yet)
Webpages square and vertical layouts
This commit is contained in:
morethanwords 2020-05-30 09:44:54 +03:00
parent d1e3827c12
commit b4f16e9244
52 changed files with 2110 additions and 839 deletions

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -151,7 +151,7 @@
<label for="password">Password</label>
<span class="toggle-visible tgico-eye1"></span>
</div>
<button class="btn-primary">NEXT</button>
<button class="btn-primary rp">NEXT</button>
</div>
</div>
</div>
@ -185,11 +185,11 @@
<h6>Drag to Reposition</h6>
</div>
<div class="crop"></div>
<button class="btn-primary btn-circle btn-crop btn-icon tgico-check z-depth-1"></button>
<button class="btn-primary rp btn-circle btn-crop btn-icon tgico-check z-depth-1"></button>
<input type="file" style="display: none;" />
</div>
</div>
<div class="whole valign-wrapper page-chats" style="display: none;">
<div class="whole page-chats" style="display: none;" id="page-chats">
<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>
@ -241,7 +241,7 @@
<div class="popup-header">
<span class="btn-icon popup-close tgico-close"></span>
<div class="popup-title">Send Photo</div>
<button class="btn-primary">SEND</button>
<button class="btn-primary rp">SEND</button>
</div>
<div class="popup-photo"></div>
<div class="input-field">
@ -257,9 +257,9 @@
<div class="sidebar-header__btn-container">
<div class="btn-icon tgico-menu btn-menu-toggle rp sidebar-tools-button active">
<div class="btn-menu bottom-right">
<div class="btn-menu-item menu-new-group tgico-newgroup rp">New Group</div>
<div class="btn-menu-item menu-newGroup tgico-newgroup rp">New Group</div>
<div class="btn-menu-item menu-contacts tgico-user rp">Contacts</div>
<div class="btn-menu-item menu-archive tgico-archive rp">Archived <span class="archived-count"></span></div>
<div class="btn-menu-item menu-archived tgico-archive rp">Archived <span class="archived-count"></span></div>
<div class="btn-menu-item menu-saved tgico-savedmessages rp">Saved</div>
<div class="btn-menu-item menu-settings tgico-settings rp">Settings</div>
<div class="btn-menu-item menu-help tgico-help rp">Help</div>
@ -281,7 +281,7 @@
<div class="btn-menu top-left">
<div class="btn-menu-item menu-channel tgico-newchannel rp">New Channel</div>
<div class="btn-menu-item menu-group tgico-newgroup rp">New Group</div>
<div class="btn-menu-item menu-private-chat tgico-newprivate rp">New Private Chat</div>
<div class="btn-menu-item menu-privateChat tgico-newprivate rp">New Private Chat</div>
</div>
</button>
</div>
@ -301,7 +301,7 @@
<div class="sidebar-header">
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
<div class="input-search">
<input type="text" placeholder="Search" id="contacts-search" />
<input type="text" placeholder="Search" id="contacts-search" autocomplete="ASKLXmQQr52" />
<span class="tgico tgico-search"></span>
</div>
</div>
@ -643,4 +643,4 @@
</div>
</div>
</html>
<script type="text/javascript" src="npm.jsbn.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>
<script type="text/javascript" src="npm.jsbn.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>

Binary file not shown.

View File

@ -197,43 +197,32 @@
<path id="poll-line" d="M4.47,5.33v13.6c0,4.97,4.03,9,9,9h458.16"/>
</defs>
</svg>
<div class="overlays">
<div class="media-viewer">
<div class="media-viewer-author">
<avatar-element class="media-viewer-userpic"></avatar-element>
<div class="media-viewer-name"></div>
<div class="media-viewer-date"></div>
</div>
<div class="media-viewer-buttons">
<div class="media-viewer-delete-button btn-icon tgico-delete rp rps"></div>
<div class="media-viewer-forward-button btn-icon tgico-forward rp rps"></div>
<div class="media-viewer-download-button btn-icon tgico-download rp rps"></div>
<div class="media-viewer-close-button btn-icon tgico-close rp rps"></div>
</div>
<div class="media-viewer-content">
<div class="media-viewer-stub"></div>
<div class="media-viewer-container">
<div class="media-viewer-switcher-left">
<span class="tgico-down media-viewer-prev-button"></span>
</div>
<div class="media-viewer-switcher-right">
<span class="tgico-down media-viewer-next-button"></span>
</div>
<div class="media-viewer-media"></div>
<div class="media-viewer-whole">
<div class="overlays">
<div class="media-viewer">
<div class="media-viewer-author">
<avatar-element class="media-viewer-userpic"></avatar-element>
<div class="media-viewer-name"></div>
<div class="media-viewer-date"></div>
</div>
<div class="media-viewer-buttons">
<div class="btn-icon tgico-delete menu-delete rp"></div>
<div class="btn-icon tgico-forward menu-forward rp"></div>
<div class="btn-icon tgico-download menu-download rp"></div>
<div class="btn-icon tgico-close menu-close rp"></div>
</div>
<div class="media-viewer-content">
<div class="media-viewer-stub"></div>
<div class="media-viewer-container">
<div class="media-viewer-media"></div>
</div>
<div class="media-viewer-caption"></div>
</div>
<div class="media-viewer-caption"></div>
</div>
</div>
</div>
<div class="media-viewer-mover" style="display: none;"></div>
<div class="popup popup-delete-message">
<div class="popup-container z-depth-1">
<div class="popup-header">Do you want to delete this message?</div>
<div class="popup-buttons">
<button class="btn popup-delete-both danger rp"></button>
<button class="btn popup-delete-me danger rp"></button>
<button class="btn popup-close rp">CANCEL</button>
</div>
<div class="media-viewer-switchers">
<div class="media-viewer-switcher-left menu-prev"><span class="tgico-down media-viewer-prev-button"></span></div>
<div class="media-viewer-switcher-right menu-next"><span class="tgico-down media-viewer-next-button"></span></div>
</div>
</div>
<div class="popup popup-send-photo">

View File

@ -1 +0,0 @@
!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([87,3]),r()}({87: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(88));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.

1
public/pako-inflate.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

1
public/rlottie-wasm.js Normal file

File diff suppressed because one or more lines are too long

BIN
public/rlottie-wasm.wasm Normal file

Binary file not shown.

View File

@ -0,0 +1,310 @@
importScripts('rlottie-wasm.js');
//importScripts('pako-inflate.min.js');
function RLottieItem(reqId, jsString, width, height, fps) {
this.stringOnWasmHeap = null;
this.handle = null;
this.frameCount = 0;
this.curFrame = 0;
this.interval = 0;
this.reqId = reqId;
this.width = width;
this.height = height;
this.fps = Math.max(1, Math.min(60, fps || 60));
this.dead = false;
this.paused = true;
this.speed = 1;
this.direction = 1;
this.init(jsString, width, height);
this.play();
}
RLottieItem.prototype.mainLoop = function() {
if(this.dead) return;
this.render(this.curFrame++);
if(this.curFrame >= this.frameCount) {
this.curFrame = 0;
}
};
RLottieItem.prototype.mainLoopBackwards = function() {
if(this.dead) return;
this.render(this.curFrame--);
if(this.curFrame < 0) {
this.curFrame = this.frameCount - 1;
}
};
RLottieItem.prototype.init = function(jsString) {
try {
this.handle = RLottieWorker.Api.init();
/* var lengthBytes = lengthBytesUTF8(jsString) + 1;
this.stringOnWasmHeap = _malloc(lengthBytes);
stringToUTF8(jsString, this.stringOnWasmHeap, lengthBytes); */
this.stringOnWasmHeap = allocate(intArrayFromString(jsString), 'i8', 0);
//Module._free(this.stringOnWasmHeap);
/* var data = new Uint8ClampedArray(Module.HEAP8.buffer, this.handle - 4, 28);
console.warn('lottie handle:', this.handle, 'string:', this.stringOnWasmHeap, data);
console.warn('lottie will free:', this.stringOnWasmHeap);
Module._free(this.stringOnWasmHeap); */
/* var buffer = RLottieWorker.Api.buffer(this.handle);
console.warn('buffer after clean:', buffer); */
this.frameCount = RLottieWorker.Api.loadFromData(this.handle, this.stringOnWasmHeap);
console.warn('lottie frameCount:', this.frameCount, this.handle);
RLottieWorker.Api.resize(this.handle, this.width, this.height);
} catch(e) {
console.error('init RLottieItem error:', e);
}
};
RLottieItem.prototype.render = function(frameNo) {
if(this.dead) return;
//return;
try {
RLottieWorker.Api.render(this.handle, frameNo);
//this.dead = true;
//this.pause();
//return;
var bufferPointer = RLottieWorker.Api.buffer(this.handle);
var data = new Uint8ClampedArray(Module.HEAP8.buffer, bufferPointer, this.width * this.height * 4);
var buffer = new Uint8ClampedArray(data);
reply('frame', this.reqId, frameNo, buffer, this.width, this.height);
} catch(e) {
console.error('Render error:', e);
this.dead = true;
this.pause();
}
};
RLottieItem.prototype.setMainLoop = function() {
clearInterval(this.interval);
var frInterval = 1000 / this.fps * this.speed;
if(this.direction == 1) {
this.interval = setInterval(this.mainLoop.bind(this), frInterval);
this.mainLoop();
} else {
this.interval = setInterval(this.mainLoopBackwards.bind(this), frInterval);
this.mainLoopBackwards();
}
};
RLottieItem.prototype.play = function() {
if(this.dead || !this.paused) return;
this.setMainLoop();
this.paused = false;
};
RLottieItem.prototype.pause = function() {
if(this.dead || this.paused) return;
clearInterval(this.interval);
this.paused = true;
};
RLottieItem.prototype.stop = function() {
if(this.dead) return;
this.pause();
this.curFrame = this.direction == 1 ? 0 : this.frameCount;
};
RLottieItem.prototype.restart = function() {
if(this.dead) return;
this.stop();
this.play();
};
RLottieItem.prototype.destroy = function() {
this.dead = true;
this.pause();
//Module._free(this.handle);
console.warn('lottie will destroy:', this.handle);
RLottieWorker.Api.destroy(this.handle);
/* var data = new Uint8ClampedArray(Module.HEAP8.buffer, this.handle - 4, 20);
console.warn('lottie after destroy:', this.reqId, 'string:', data); */
};
var RLottieWorker = (function() {
var worker = {};
worker.Api = {};
//worker.lottieHandle = null;
function initApi() {
/* worker.Api = {
init: Module.cwrap('lottie_init', '', []),
destroy: Module.cwrap('lottie_destroy', '', ['number']),
resize: Module.cwrap('lottie_resize', '', ['number', 'number', 'number']),
buffer: Module.cwrap('lottie_buffer', 'number', ['number']),
frameCount: Module.cwrap('lottie_frame_count', 'number', ['number']),
render: Module.cwrap('lottie_render', '', ['number', 'number']),
loadFromData: Module.cwrap('lottie_load_from_data', 'number', ['number', 'number']),
}; */
worker.Api = {
init: Module.cwrap('lottie_init', '', []),
destroy: Module.cwrap('lottie_destroy', '', ['number']),
resize: Module.cwrap('lottie_resize', '', ['number', 'number', 'number']),
buffer: Module.cwrap('lottie_buffer', 'number', ['number']),
render: Module.cwrap('lottie_render', '', ['number', 'number']),
loadFromData: Module.cwrap('lottie_load_from_data', 'number', ['number', 'number']),
};
}
worker.init = function() {
initApi();
reply('ready');
};
/* worker.loadSticker = function(url, callback) {
getUrlContent(url, function(err, data) {
if (err) {
return console.warn('Can\'t fetch file ' + url, err);
}
try {
var json = pako.inflate(data, {to: 'string'});
var json_parsed = JSON.parse(json);
if (!json_parsed.tgs) {
throw new Error('Invalid file');
}
} catch (e) {
return console.warn('Invalid file ' + url);
}
callback(json, json_parsed.fr);
});
} */
return worker;
}());
Module.onRuntimeInitialized = function() {
RLottieWorker.init();
};
var items = {};
var queryableFunctions = {
/* loadSticker: function(reqId, url, width, height) {
RLottieWorker.loadSticker(url, function(json, fr) {
var frames = RLottieWorker.renderFrames(reqId, json, width, height, fr);
});
}, */
loadFromData: function(reqId, jsString, width, height) {
try {
var json_parsed = jsString;//JSON.parse(jsString);
if(!json_parsed.tgs) {
throw new Error('Invalid file');
}
items[reqId] = new RLottieItem(reqId, JSON.stringify(jsString), width, height, json_parsed.fr);
} catch(e) {}
},
destroy: function(reqId) {
items[reqId].destroy();
delete items[reqId];
},
play: function(reqId) {
items[reqId].play();
},
pause: function(reqId) {
items[reqId].pause();
},
stop: function(reqId) {
items[reqId].stop();
},
restart: function(reqId) {
items[reqId].restart();
},
setSpeed: function(reqId, speed) {
items[reqId].speed = speed;
items[reqId].setMainLoop();
},
setDirection: function(reqId, direction) {
items[reqId].direction = direction;
items[reqId].setMainLoop();
}
/* renderFrames: function(reqId, jsString, width, height) {
try {
var json_parsed = jsString;//JSON.parse(jsString);
if(!json_parsed.tgs) {
throw new Error('Invalid file');
}
RLottieWorker.renderFrames(reqId, JSON.stringify(jsString), width, height, json_parsed.fr);
// reply('result', reqId, width, height, frames);
} catch(e) {}
} */
};
function defaultReply(message) {
// your default PUBLIC function executed only when main page calls the queryableWorker.postMessage() method directly
// do something
}
function reply() {
if(arguments.length < 1) {
throw new TypeError('reply - not enough arguments');
}
var transfer = [], args = Array.prototype.slice.call(arguments, 1);
for(var i = 0; i < args.length; i++) {
if(args[i] instanceof ArrayBuffer) {
transfer.push(args[i]);
}
if(args[i].buffer && args[i].buffer instanceof ArrayBuffer) {
transfer.push(args[i].buffer);
}
}
postMessage({ 'queryMethodListener': arguments[0], 'queryMethodArguments': args }, transfer);
}
onmessage = function(oEvent) {
if(oEvent.data instanceof Object && oEvent.data.hasOwnProperty('queryMethod') && oEvent.data.hasOwnProperty('queryMethodArguments')) {
queryableFunctions[oEvent.data.queryMethod].apply(self, oEvent.data.queryMethodArguments);
} else {
defaultReply(oEvent.data);
}
};
/* function getUrlContent(path, callback) {
try {
var xhr = new XMLHttpRequest();
xhr.open('GET', path, true);
if ('responseType' in xhr) {
xhr.responseType = 'arraybuffer';
}
if (xhr.overrideMimeType) {
xhr.overrideMimeType('text/plain; charset=x-user-defined');
}
xhr.onreadystatechange = function (event) {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 0) {
callback(null, xhr.response || xhr.responseText);
} else {
callback(new Error('Ajax error: ' + this.status + ' ' + this.statusText));
}
}
};
xhr.send();
} catch (e) {
callback(new Error(e));
}
}; */

233
public/rlottie.worker.js Normal file
View File

@ -0,0 +1,233 @@
importScripts('rlottie-wasm.js');
//importScripts('pako-inflate.min.js');
function RLottieItem(reqId, jsString, width, height, fps) {
this.stringOnWasmHeap = null;
this.handle = null;
this.frameCount = 0;
this.reqId = reqId;
this.width = width;
this.height = height;
this.fps = Math.max(1, Math.min(60, fps || 60));
this.dead = false;
this.init(jsString, width, height);
this.render(0);
reply('loaded', this.reqId, this.frameCount, this.fps);
}
RLottieItem.prototype.init = function(jsString) {
try {
this.handle = RLottieWorker.Api.init();
/* var lengthBytes = lengthBytesUTF8(jsString) + 1;
this.stringOnWasmHeap = _malloc(lengthBytes);
stringToUTF8(jsString, this.stringOnWasmHeap, lengthBytes); */
this.stringOnWasmHeap = allocate(intArrayFromString(jsString), 'i8', 0);
//Module._free(this.stringOnWasmHeap);
/* var data = new Uint8ClampedArray(Module.HEAP8.buffer, this.handle - 4, 28);
console.warn('lottie handle:', this.handle, 'string:', this.stringOnWasmHeap, data);
console.warn('lottie will free:', this.stringOnWasmHeap);
Module._free(this.stringOnWasmHeap); */
/* var buffer = RLottieWorker.Api.buffer(this.handle);
console.warn('buffer after clean:', buffer); */
this.frameCount = RLottieWorker.Api.loadFromData(this.handle, this.stringOnWasmHeap);
//console.warn('lottie frameCount:', this.frameCount, this.handle);
RLottieWorker.Api.resize(this.handle, this.width, this.height);
} catch(e) {
console.error('init RLottieItem error:', e);
}
};
RLottieItem.prototype.render = function(frameNo) {
if(this.dead) return;
//return;
if(this.frameCount < frameNo || frameNo < 0) {
return;
}
try {
RLottieWorker.Api.render(this.handle, frameNo);
//this.dead = true;
//this.pause();
//return;
var bufferPointer = RLottieWorker.Api.buffer(this.handle);
var data = new Uint8ClampedArray(Module.HEAP8.buffer, bufferPointer, this.width * this.height * 4);
var buffer = new Uint8ClampedArray(data);
reply('frame', this.reqId, frameNo, buffer);
} catch(e) {
console.error('Render error:', e);
this.dead = true;
this.pause();
}
};
RLottieItem.prototype.destroy = function() {
this.dead = true;
//Module._free(this.handle);
//console.warn('lottie will destroy:', this.handle);
RLottieWorker.Api.destroy(this.handle);
/* var data = new Uint8ClampedArray(Module.HEAP8.buffer, this.handle - 4, 20);
console.warn('lottie after destroy:', this.reqId, 'string:', data); */
};
var RLottieWorker = (function() {
var worker = {};
worker.Api = {};
//worker.lottieHandle = null;
function initApi() {
/* worker.Api = {
init: Module.cwrap('lottie_init', '', []),
destroy: Module.cwrap('lottie_destroy', '', ['number']),
resize: Module.cwrap('lottie_resize', '', ['number', 'number', 'number']),
buffer: Module.cwrap('lottie_buffer', 'number', ['number']),
frameCount: Module.cwrap('lottie_frame_count', 'number', ['number']),
render: Module.cwrap('lottie_render', '', ['number', 'number']),
loadFromData: Module.cwrap('lottie_load_from_data', 'number', ['number', 'number']),
}; */
worker.Api = {
init: Module.cwrap('lottie_init', '', []),
destroy: Module.cwrap('lottie_destroy', '', ['number']),
resize: Module.cwrap('lottie_resize', '', ['number', 'number', 'number']),
buffer: Module.cwrap('lottie_buffer', 'number', ['number']),
render: Module.cwrap('lottie_render', '', ['number', 'number']),
loadFromData: Module.cwrap('lottie_load_from_data', 'number', ['number', 'number']),
};
}
worker.init = function() {
initApi();
reply('ready');
};
/* worker.loadSticker = function(url, callback) {
getUrlContent(url, function(err, data) {
if (err) {
return console.warn('Can\'t fetch file ' + url, err);
}
try {
var json = pako.inflate(data, {to: 'string'});
var json_parsed = JSON.parse(json);
if (!json_parsed.tgs) {
throw new Error('Invalid file');
}
} catch (e) {
return console.warn('Invalid file ' + url);
}
callback(json, json_parsed.fr);
});
} */
return worker;
}());
Module.onRuntimeInitialized = function() {
RLottieWorker.init();
};
var items = {};
var queryableFunctions = {
/* loadSticker: function(reqId, url, width, height) {
RLottieWorker.loadSticker(url, function(json, fr) {
var frames = RLottieWorker.renderFrames(reqId, json, width, height, fr);
});
}, */
loadFromData: function(reqId, jsString, width, height) {
try {
var json_parsed = jsString;//JSON.parse(jsString);
if(!json_parsed.tgs) {
throw new Error('Invalid file');
}
items[reqId] = new RLottieItem(reqId, JSON.stringify(jsString), width, height, json_parsed.fr);
} catch(e) {}
},
destroy: function(reqId) {
items[reqId].destroy();
delete items[reqId];
},
renderFrame: function(reqId, frameNo) {
items[reqId].render(frameNo);
}
/* renderFrames: function(reqId, jsString, width, height) {
try {
var json_parsed = jsString;//JSON.parse(jsString);
if(!json_parsed.tgs) {
throw new Error('Invalid file');
}
RLottieWorker.renderFrames(reqId, JSON.stringify(jsString), width, height, json_parsed.fr);
// reply('result', reqId, width, height, frames);
} catch(e) {}
} */
};
function defaultReply(message) {
// your default PUBLIC function executed only when main page calls the queryableWorker.postMessage() method directly
// do something
}
function reply() {
if(arguments.length < 1) {
throw new TypeError('reply - not enough arguments');
}
var transfer = [], args = Array.prototype.slice.call(arguments, 1);
for(var i = 0; i < args.length; i++) {
if(args[i] instanceof ArrayBuffer) {
transfer.push(args[i]);
}
if(args[i].buffer && args[i].buffer instanceof ArrayBuffer) {
transfer.push(args[i].buffer);
}
}
postMessage({ 'queryMethodListener': arguments[0], 'queryMethodArguments': args }, transfer);
}
onmessage = function(oEvent) {
if(oEvent.data instanceof Object && oEvent.data.hasOwnProperty('queryMethod') && oEvent.data.hasOwnProperty('queryMethodArguments')) {
queryableFunctions[oEvent.data.queryMethod].apply(self, oEvent.data.queryMethodArguments);
} else {
defaultReply(oEvent.data);
}
};
/* function getUrlContent(path, callback) {
try {
var xhr = new XMLHttpRequest();
xhr.open('GET', path, true);
if ('responseType' in xhr) {
xhr.responseType = 'arraybuffer';
}
if (xhr.overrideMimeType) {
xhr.overrideMimeType('text/plain; charset=x-user-defined');
}
xhr.onreadystatechange = function (event) {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 0) {
callback(null, xhr.response || xhr.responseText);
} else {
callback(new Error('Ajax error: ' + this.status + ' ' + this.statusText));
}
}
};
xhr.send();
} catch (e) {
callback(new Error(e));
}
}; */

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

View File

@ -4,7 +4,7 @@ import { putPreloader } from "./misc";
import { AppSelectPeers } from "./appSelectPeers";
class AppForward {
private container = document.getElementById('forward-container') as HTMLDivElement;
public container = document.getElementById('forward-container') as HTMLDivElement;
private closeBtn = this.container.querySelector('.sidebar-close-button') as HTMLButtonElement;
private sendBtn = this.container.querySelector('.btn-circle') as HTMLButtonElement;
@ -12,10 +12,7 @@ class AppForward {
private msgIDs: number[] = [];
constructor() {
this.closeBtn.addEventListener('click', () => {
this.cleanup();
this.container.classList.remove('active');
});
this.closeBtn.addEventListener('click', this.close.bind(this));
this.sendBtn.addEventListener('click', () => {
let peerIDs = this.selector.getSelected();
@ -45,6 +42,11 @@ class AppForward {
});
}
public close() {
this.cleanup();
this.container.classList.remove('active');
}
public cleanup() {
if(this.selector) {
this.selector.container.remove();

View File

@ -1,5 +1,5 @@
import Scrollable from "./scrollable_new";
import appMessagesManager from "../lib/appManagers/appMessagesManager";
import appMessagesManager, { Dialog } from "../lib/appManagers/appMessagesManager";
import { $rootScope, cancelEvent, findUpTag, findUpClassName } from "../lib/utils";
import appDialogsManager from "../lib/appManagers/appDialogsManager";
import appChatsManager from "../lib/appManagers/appChatsManager";
@ -24,7 +24,7 @@ export class AppSelectPeers {
private myID = $rootScope.myID;
private offsetIndex = 0;
private promise: Promise<number[]>;
private promise: Promise<any>;
private query = '';
private cachedContacts: number[];
@ -47,6 +47,7 @@ export class AppSelectPeers {
this.chatsContainer.classList.add('chats-container');
this.chatsContainer.append(this.list);
this.scrollable = new Scrollable(this.chatsContainer);
this.scrollable.setVirtualContainer(this.list);
this.list.addEventListener('click', (e) => {
let target = e.target as HTMLElement;
@ -89,9 +90,11 @@ export class AppSelectPeers {
if(this.query != value) {
if(this.peerType == 'contacts') {
this.cachedContacts = null;
this.promise = null;
} else {
this.offsetIndex = 0;
}
this.promise = null;
this.list.innerHTML = '';
this.query = value;
@ -115,26 +118,35 @@ export class AppSelectPeers {
}
}
private getMoreDialogs() {
private async getMoreDialogs() {
if(this.promise) return this.promise;
// в десктопе - сначала без группы, потом архивные, потом контакты без сообщений
let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0;
return appMessagesManager.getConversations(this.offsetIndex, pageCount, 0).then(value => {
let dialogs = value.dialogs;
let newOffsetIndex = dialogs[value.dialogs.length - 1].index || 0;
const pageCount = appPhotosManager.windowH / 72 * 1.25 | 0;
dialogs = dialogs.filter(d => d.peerID != this.myID);
if(!this.offsetIndex) {
dialogs.unshift({
peerID: this.myID,
pFlags: {}
} as any);
}
this.promise = appMessagesManager.getConversations(this.query, this.offsetIndex, pageCount, 0);
const value = await this.promise;
this.offsetIndex = newOffsetIndex;
let dialogs = value.dialogs as Dialog[];
if(!dialogs.length) {
return;
}
const newOffsetIndex = dialogs[dialogs.length - 1].index || 0;
this.renderResults(dialogs.map(dialog => dialog.peerID));
});
dialogs = dialogs.filter(d => d.peerID != this.myID);
if(!this.offsetIndex) {
dialogs.unshift({
peerID: this.myID,
pFlags: {}
} as any);
}
this.offsetIndex = newOffsetIndex;
this.renderResults(dialogs.map(dialog => dialog.peerID));
this.promise = null;
}
private async getMoreContacts() {
@ -148,8 +160,8 @@ export class AppSelectPeers {
}
if(this.cachedContacts.length) {
let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0;
let arr = this.cachedContacts.splice(0, pageCount);
const pageCount = appPhotosManager.windowH / 72 * 1.25 | 0;
const arr = this.cachedContacts.splice(0, pageCount);
this.renderResults(arr);
}
}
@ -163,8 +175,9 @@ export class AppSelectPeers {
}
private renderResults(peerIDs: number[]) {
console.log('will renderResults:', peerIDs);
peerIDs.forEach(peerID => {
let {dom} = appDialogsManager.addDialog(peerID, this.list, false, false);
const {dom} = appDialogsManager.addDialog(peerID, this.scrollable, false, false);
dom.containerEl.insertAdjacentHTML('afterbegin', '<div class="checkbox"><label><input type="checkbox"><span></span></label></div>');
let subtitle = '';
@ -184,14 +197,14 @@ export class AppSelectPeers {
}
private add(peerID: number) {
let div = document.createElement('div');
const div = document.createElement('div');
div.classList.add('selector-user', 'scale-in');
div.dataset.peerID = '' + peerID;
this.selected[peerID] = div;
let title = appPeersManager.getPeerTitle(peerID, false, true);
const title = appPeersManager.getPeerTitle(peerID, false, true);
let avatarEl = document.createElement('avatar-element');
const avatarEl = document.createElement('avatar-element');
avatarEl.classList.add('selector-user-avatar', 'tgico');
avatarEl.setAttribute('dialog', '1');
avatarEl.setAttribute('peer', '' + peerID);
@ -205,7 +218,7 @@ export class AppSelectPeers {
}
private remove(peerID: number) {
let div = this.selected[peerID];
const div = this.selected[peerID];
div.classList.remove('scale-in');
void div.offsetWidth;
div.classList.add('scale-out');

View File

@ -254,7 +254,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
stickersDiv.classList.add('stickers-categories');
contentStickersDiv.append(stickersDiv);
stickersDiv.addEventListener('mouseover', (e) => {
/* stickersDiv.addEventListener('mouseover', (e) => {
let target = e.target as HTMLElement;
if(target.tagName == 'CANVAS') { // turn on sticker
@ -269,7 +269,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
}
}
}
});
}); */
stickersDiv.addEventListener('click', onMediaClick);
@ -397,7 +397,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
const text = e.srcElement.result;
let json = await apiManager.gzipUncompress<string>(text, true);
let animation = await lottieLoader.loadAnimation({
let animation = await lottieLoader.loadAnimationWorker({
container: li,
loop: true,
autoplay: false,

View File

@ -341,10 +341,10 @@ export function formatPhoneNumber(str: string) {
return {formatted: str, country};
}
export function parseMenuButtonsTo(to: {[name: string]: HTMLButtonElement}, elements: HTMLCollection) {
export function parseMenuButtonsTo(to: {[name: string]: HTMLElement}, elements: HTMLCollection | NodeListOf<HTMLElement>) {
Array.from(elements).forEach(el => {
let name = el.className.match(/ menu-(.+?) /)[1];
to[name] = el as HTMLButtonElement;
let name = el.className.match(/(?:^|\s)menu-(.+?)(?:$|\s)/)[1];
to[name] = el as HTMLElement;
});
}

View File

@ -697,7 +697,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
let stickerType = doc.sticker;
if(stickerType == 2 && !LottieLoader.loaded) {
LottieLoader.loadLottie();
//LottieLoader.loadLottie();
LottieLoader.loadLottieWorkers();
}
if(!stickerType) {
@ -759,7 +760,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
div.append(img);
}
lazyLoadQueue && !downloaded ? lazyLoadQueue.push({div, load}) : load();
lazyLoadQueue && !downloaded ? lazyLoadQueue.push({div, load, wasSeen: group == 'chat'}) : load();
}
}
@ -779,7 +780,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
let downloaded = doc.downloaded;
let load = () => appDocsManager.downloadDoc(doc.id).then(blob => {
//console.log('loaded sticker:', blob, div);
//console.log('loaded sticker:', doc, div);
if(middleware && !middleware()) return;
//return;
@ -796,21 +797,26 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
//console.timeEnd('decompress sticker' + doc.id);
let animation = await LottieLoader.loadAnimation({
/* if(doc.id == '1860749763008266301') {
console.log('loaded sticker:', doc, div);
} */
let animation = await LottieLoader.loadAnimationWorker/* loadAnimation */({
container: div,
loop: false,
autoplay: false,
animationData: JSON.parse(json),
renderer: 'svg'
loop: !emoji,
autoplay: true,
animationData: JSON.parse(json)
}, group, toneIndex);
animation.addListener('ready', () => {
if(div.firstElementChild && div.firstElementChild.tagName == 'IMG') {
div.firstElementChild.remove();
}
});
//console.timeEnd('render sticker' + doc.id);
if(div.firstElementChild && div.firstElementChild.tagName == 'IMG') {
div.firstElementChild.remove();
}
div.addEventListener('mouseover', (e) => {
/* div.addEventListener('mouseover', (e) => {
let animation = LottieLoader.getAnimation(div, group);
if(animation) {
@ -833,9 +839,9 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
}
});
if(play) {
if(play && false) {
animation.play();
}
} */
});
reader.readAsArrayBuffer(blob);
@ -862,7 +868,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
}
});
return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load, wasSeen: group == 'chat'}), Promise.resolve()) : load();
return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load, wasSeen: group == 'chat' && stickerType != 2}), Promise.resolve()) : load();
}
export function wrapReply(title: string, subtitle: string, message?: any) {

View File

@ -1,9 +1,10 @@
import { $rootScope, isObject, SearchIndexManager, safeReplaceObject, copy, numberWithCommas } from "../utils";
import { $rootScope, isObject, safeReplaceObject, copy, numberWithCommas } from "../utils";
import { RichTextProcessor } from "../richtextprocessor";
import appUsersManager from "./appUsersManager";
import apiManager from '../mtproto/mtprotoworker';
import apiUpdatesManager from "./apiUpdatesManager";
import appProfileManager from "./appProfileManager";
import searchIndexManager from "../searchIndexManager";
type Channel = {
_: 'channel',
@ -72,7 +73,7 @@ export class AppChatsManager {
let oldChat = this.chats[apiChat.id];
let titleWords = SearchIndexManager.cleanSearchText(apiChat.title || '', false).split(' ');
let titleWords = searchIndexManager.cleanSearchText(apiChat.title || '', false).split(' ');
let firstWord = titleWords.shift();
let lastWord = titleWords.pop();
apiChat.initials = firstWord.charAt(0) + (lastWord ? lastWord.charAt(0) : '');
@ -95,7 +96,7 @@ export class AppChatsManager {
}
if(apiChat.username) {
let searchUsername = SearchIndexManager.cleanUsername(apiChat.username);
let searchUsername = searchIndexManager.cleanUsername(apiChat.username);
this.usernames[searchUsername] = apiChat.id;
}
@ -297,10 +298,10 @@ export class AppChatsManager {
}
public getChatMembersString(id: number) {
let chat = this.getChat(id);
const chat = this.getChat(id);
let isChannel = this.isChannel(id) && !this.isMegagroup(id);
let participants_count = chat.participants_count || chat.participants.participants.length;
const isChannel = this.isBroadcast(id);
const participants_count = chat.participants_count || chat.participants?.participants.length || 0;
return numberWithCommas(participants_count) + ' ' + (isChannel ? 'subscribers' : 'members');
}

View File

@ -489,7 +489,7 @@ export class AppDialogsManager {
console.time('getDialogs time');
let loadCount = 50/*this.chatsLoadCount */;
this.loadDialogsPromise = appMessagesManager.getConversations(offsetIndex, loadCount, +archived);
this.loadDialogsPromise = appMessagesManager.getConversations('', offsetIndex, loadCount, +archived);
let result = await this.loadDialogsPromise;
@ -792,7 +792,7 @@ export class AppDialogsManager {
return this.doms[peerID] || this.domsArchived[peerID];
}
public addDialog(_dialog: Dialog | number, container?: HTMLUListElement, drawStatus = true, rippleEnabled = true, onlyFirstName = false) {
public addDialog(_dialog: Dialog | number, container?: HTMLUListElement | Scrollable, drawStatus = true, rippleEnabled = true, onlyFirstName = false) {
let dialog: Dialog;
if(typeof(_dialog) === 'number') {

View File

@ -260,13 +260,6 @@ export class AppImManager {
private scrolledAllDown: boolean;
public contextMenu = new ChatContextMenu(this.bubblesContainer);
private popupDeleteMessage: {
popupEl?: HTMLDivElement,
deleteBothBtn?: HTMLButtonElement,
deleteMeBtn?: HTMLButtonElement,
cancelBtn?: HTMLButtonElement
} = {};
private setPeerPromise: Promise<boolean> = null;
@ -288,9 +281,12 @@ export class AppImManager {
private peerChanged: boolean;
private firstUnreadBubble: HTMLDivElement = null;
private attachedUnreadBubble: boolean;
private stickyIntersector: StickyIntersector = null;
private cleanupID = 0;
constructor() {
/* if(!lottieLoader.loaded) {
lottieLoader.loadLottie();
@ -301,12 +297,7 @@ export class AppImManager {
this.chatInputC = new ChatInput();
this.preloader = new ProgressivePreloader(null, false);
this.popupDeleteMessage.popupEl = this.pageEl.querySelector('.popup-delete-message') as HTMLDivElement;
this.popupDeleteMessage.deleteBothBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-delete-both') as HTMLButtonElement;
this.popupDeleteMessage.deleteMeBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-delete-me') as HTMLButtonElement;
this.popupDeleteMessage.cancelBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-close') as HTMLButtonElement;
apiManager.getUserID().then((id) => {
this.myID = $rootScope.myID = id;
});
@ -657,8 +648,16 @@ export class AppImManager {
return;
}
if(e.key == 'Escape' && this.peerID != 0) { // hide current dialog
this.setPeer(0);
if(e.key == 'Escape') {
if(appMediaViewer.wholeDiv.classList.contains('active')) {
appMediaViewer.buttons.close.click();
} else if(appForward.container.classList.contains('active')) {
appForward.close();
} else if(this.chatInputC.replyElements.container.classList.contains('active')) {
this.chatInputC.replyElements.cancelBtn.click();
} else if(this.peerID != 0) { // hide current dialog
this.setPeer(0);
}
} else if(e.key == 'Meta' || e.key == 'Control') {
return;
} else if(e.key == 'c' && (e.ctrlKey || e.metaKey) && target.tagName != 'INPUT') {
@ -672,16 +671,6 @@ export class AppImManager {
};
document.body.addEventListener('keydown', onKeyDown);
this.popupDeleteMessage.deleteBothBtn.addEventListener('click', () => {
appMessagesManager.deleteMessages([this.contextMenu.msgID], true);
this.popupDeleteMessage.cancelBtn.click();
});
this.popupDeleteMessage.deleteMeBtn.addEventListener('click', () => {
appMessagesManager.deleteMessages([this.contextMenu.msgID], false);
this.popupDeleteMessage.cancelBtn.click();
});
this.goDownBtn.addEventListener('click', () => {
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
@ -939,6 +928,7 @@ export class AppImManager {
this.peerChanged = false;
this.firstUnreadBubble = null;
this.attachedUnreadBubble = false;
this.messagesQueue.length = 0;
this.messagesQueuePromise = null;
@ -954,6 +944,8 @@ export class AppImManager {
this.loadedTopTimes = this.loadedBottomTimes = 0;
this.cleanupID++;
////console.timeEnd('appImManager cleanup');
}
@ -1094,7 +1086,8 @@ export class AppImManager {
this.log('scrolledAllDown:', this.scrolledAllDown);
if(!this.unreaded.length && dialog) { // lol
//if(!this.unreaded.length && dialog) { // lol
if(this.scrolledAllDown && dialog) { // lol
appMessagesManager.readHistory(peerID, dialog.top_message);
}
@ -1401,6 +1394,13 @@ export class AppImManager {
});
}
}
private getMiddleware() {
let cleanupID = this.cleanupID;
return () => {
return this.cleanupID == cleanupID;
};
}
// reverse means top
public renderMessage(message: any, reverse = false, multipleRender = false, bubble: HTMLDivElement = null, updatePosition = true) {
@ -1766,16 +1766,12 @@ export class AppImManager {
wrapAlbum({
groupID: message.grouped_id,
attachmentDiv,
middleware: () => {
return this.peerID == peerID;
},
middleware: this.getMiddleware(),
isOut: our,
lazyLoadQueue: this.lazyLoadQueue
});
} else {
wrapPhoto(photo.id, message, attachmentDiv, undefined, undefined, true, isOut, this.lazyLoadQueue, () => {
return this.peerID == peerID;
});
wrapPhoto(photo.id, message, attachmentDiv, undefined, undefined, true, isOut, this.lazyLoadQueue, this.getMiddleware());
}
break;
@ -1827,9 +1823,7 @@ export class AppImManager {
boxWidth: 380,
boxHeight: 300,
lazyLoadQueue: this.lazyLoadQueue,
middleware: () => {
return this.peerID == peerID;
},
middleware: this.getMiddleware(),
isOut
});
//}
@ -1841,9 +1835,14 @@ export class AppImManager {
if(webpage.photo && !doc) {
bubble.classList.add('photo');
wrapPhoto(webpage.photo.id, message, preview, 380, 300, false, null, this.lazyLoadQueue, () => {
return this.peerID == peerID;
});
const size = webpage.photo.sizes[webpage.photo.sizes.length - 1];
if(size.w == size.h) {
bubble.classList.add('is-square-photo');
} else if(size.h > size.w) {
bubble.classList.add('is-vertical-photo');
}
wrapPhoto(webpage.photo.id, message, preview, 380, 300, false, null, this.lazyLoadQueue, this.getMiddleware());
}
if(preview) {
@ -1861,8 +1860,13 @@ export class AppImManager {
if(webpage.title) {
titleDiv.innerHTML = RichTextProcessor.wrapRichText(webpage.title);
}
let quoteTextDiv = document.createElement('div');
quoteTextDiv.classList.add('quote-text');
quoteTextDiv.append(nameEl, titleDiv, textDiv);
quote.append(quoteTextDiv);
quote.append(nameEl, titleDiv, textDiv);
box.append(quote);
//bubble.prepend(box);
@ -1894,14 +1898,7 @@ export class AppImManager {
wrapSticker({
doc,
div: attachmentDiv,
middleware: () => {
if(this.peerID != peerID) {
this.log.warn('peer changed, canceling sticker attach');
return false;
}
return true;
},
middleware: this.getMiddleware(),
lazyLoadQueue: this.lazyLoadQueue,
group: 'chat',
play: !!message.pending || !multipleRender,
@ -1923,9 +1920,7 @@ export class AppImManager {
wrapAlbum({
groupID: message.grouped_id,
attachmentDiv,
middleware: () => {
return this.peerID == peerID;
},
middleware: this.getMiddleware(),
isOut: our,
lazyLoadQueue: this.lazyLoadQueue
});
@ -1939,9 +1934,7 @@ export class AppImManager {
withTail: doc.type != 'round',
isOut: isOut,
lazyLoadQueue: this.lazyLoadQueue,
middleware: () => {
return this.peerID == peerID;
}
middleware: this.getMiddleware()
});
}
@ -2325,6 +2318,10 @@ export class AppImManager {
}
public setUnreadDelimiter() {
if(this.attachedUnreadBubble) {
return;
}
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
if(!dialog?.unread_count) return;
@ -2343,6 +2340,7 @@ export class AppImManager {
}
this.firstUnreadBubble = bubble;
this.attachedUnreadBubble = true;
}
}

View File

@ -4,35 +4,27 @@ import appMessagesManager from "./appMessagesManager";
import { RichTextProcessor } from "../richtextprocessor";
import { logger } from "../polyfill";
import ProgressivePreloader from "../../components/preloader";
import { findUpClassName, $rootScope, generatePathData, fillPropertyValue } from "../utils";
import { findUpClassName, $rootScope, generatePathData, fillPropertyValue, cancelEvent } from "../utils";
import appDocsManager from "./appDocsManager";
import VideoPlayer from "../mediaPlayer";
import { renderImageFromUrl } from "../../components/misc";
import { renderImageFromUrl, parseMenuButtonsTo } from "../../components/misc";
import AvatarElement from "../../components/avatar";
import LazyLoadQueue from "../../components/lazyLoadQueue";
import appForward from "../../components/appForward";
export class AppMediaViewer {
private overlaysDiv = document.querySelector('.overlays') as HTMLDivElement;
private mediaViewerDiv = this.overlaysDiv.firstElementChild as HTMLDivElement;
public wholeDiv = document.querySelector('.media-viewer-whole') as HTMLDivElement;
private overlaysDiv = this.wholeDiv.firstElementChild as HTMLDivElement;
private author = {
avatarEl: this.overlaysDiv.querySelector('.media-viewer-userpic') as AvatarElement,
nameEl: this.overlaysDiv.querySelector('.media-viewer-name') as HTMLDivElement,
date: this.overlaysDiv.querySelector('.media-viewer-date') as HTMLDivElement
};
private buttons = {
delete: this.overlaysDiv.querySelector('.media-viewer-delete-button') as HTMLDivElement,
forward: this.overlaysDiv.querySelector('.media-viewer-forward-button') as HTMLDivElement,
download: this.overlaysDiv.querySelector('.media-viewer-download-button') as HTMLDivElement,
close: this.overlaysDiv.querySelector('.media-viewer-close-button') as HTMLDivElement,
prev: this.overlaysDiv.querySelector('.media-viewer-switcher-left') as HTMLDivElement,
next: this.overlaysDiv.querySelector('.media-viewer-switcher-right') as HTMLDivElement,
};
private content = {
container: this.overlaysDiv.querySelector('.media-viewer-media') as HTMLDivElement,
caption: this.overlaysDiv.querySelector('.media-viewer-caption') as HTMLDivElement,
//mover: this.overlaysDiv.querySelector('.media-viewer-mover') as HTMLDivElement
mover: document.querySelector('.media-viewer-mover') as HTMLDivElement
public buttons: {[k in 'delete' | 'forward' | 'download' | 'close' | 'prev' | 'next']: HTMLElement} = {} as any;
private content: {[k in 'container' | 'caption' | 'mover']: HTMLDivElement} = {
container: this.overlaysDiv.querySelector('.media-viewer-media'),
caption: this.overlaysDiv.querySelector('.media-viewer-caption'),
mover: null
};
public currentMessageID = 0;
@ -71,9 +63,12 @@ export class AppMediaViewer {
this.preloader = new ProgressivePreloader();
this.lazyLoadQueue = new LazyLoadQueue(5, false);
parseMenuButtonsTo(this.buttons, this.wholeDiv.querySelectorAll(`[class*='menu']`) as NodeListOf<HTMLElement>);
this.onKeyDownBinded = this.onKeyDown.bind(this);
this.buttons.close.addEventListener('click', () => {
this.buttons.close.addEventListener('click', (e) => {
cancelEvent(e);
//this.overlaysDiv.classList.remove('active');
this.content.container.innerHTML = '';
if(this.content.container.firstElementChild) {
@ -92,10 +87,13 @@ export class AppMediaViewer {
this.loadedAllMediaUp = this.loadedAllMediaDown = false;
this.loadMediaPromiseUp = this.loadMediaPromiseDown = null;
appForward.close();
window.removeEventListener('keydown', this.onKeyDownBinded);
});
this.buttons.prev.addEventListener('click', () => {
this.buttons.prev.addEventListener('click', (e) => {
cancelEvent(e);
if(this.setMoverPromise) return;
let target = this.prevTargets.pop();
@ -107,7 +105,8 @@ export class AppMediaViewer {
}
});
this.buttons.next.addEventListener('click', () => {
this.buttons.next.addEventListener('click', (e) => {
cancelEvent(e);
if(this.setMoverPromise) return;
let target = this.nextTargets.shift();
@ -141,6 +140,7 @@ export class AppMediaViewer {
});
this.onClickBinded = (e: MouseEvent) => {
cancelEvent(e);
let target = e.target as HTMLElement;
let mover: HTMLDivElement = null;
@ -156,9 +156,10 @@ export class AppMediaViewer {
}
};
this.overlaysDiv.addEventListener('click', this.onClickBinded);
this.content.mover.addEventListener('click', this.onClickBinded);
this.wholeDiv.addEventListener('click', this.onClickBinded);
//this.content.mover.addEventListener('click', this.onClickBinded);
//this.content.mover.append(this.buttons.prev, this.buttons.next);
this.setNewMover();
}
public onKeyDown(e: KeyboardEvent) {
@ -176,7 +177,7 @@ export class AppMediaViewer {
if(!closing) {
mover.innerHTML = '';
mover.append(this.buttons.prev, this.buttons.next);
//mover.append(this.buttons.prev, this.buttons.next);
}
let wasActive = fromRight !== 0;
@ -357,7 +358,7 @@ export class AppMediaViewer {
mediaElement.src = src;
}
});
} else if(mediaElement instanceof HTMLVideoElement && mediaElement.firstElementChild && (mediaElement.firstElementChild as HTMLSourceElement).src) {
} else if(mediaElement instanceof HTMLVideoElement && mediaElement.firstElementChild && ((mediaElement.firstElementChild as HTMLSourceElement).src || src)) {
await new Promise((resolve, reject) => {
mediaElement.addEventListener('loadeddata', resolve);
@ -386,7 +387,7 @@ export class AppMediaViewer {
}
setTimeout(() => {
this.overlaysDiv.classList.remove('active');
this.wholeDiv.classList.remove('active');
}, 0);
setTimeout(() => {
@ -409,6 +410,8 @@ export class AppMediaViewer {
//await new Promise((resolve) => setTimeout(resolve, 0));
await new Promise((resolve) => window.requestAnimationFrame(resolve));
//throw '';
mover.style.transform = `translate(${containerRect.left}px,${containerRect.top}px) scale(1,1)`;
if(aspecter) {
@ -427,10 +430,12 @@ export class AppMediaViewer {
mover.classList.remove('moving');
if(aspecter) { // всё из-за видео, элементы управления скейлятся, так бы можно было этого не делать
mover.classList.remove('active');
//aspecter.style.cssText = '';
void mover.offsetLeft; // reflow
if(mover.querySelector('video')) {
mover.classList.remove('active');
aspecter.style.cssText = '';
void mover.offsetLeft; // reflow
}
aspecter.classList.remove('disable-hover');
}
@ -453,9 +458,9 @@ export class AppMediaViewer {
}
let {width, height} = rect;
if(proportion == 1) {
/* if(proportion == 1) {
aspecter.style.cssText = '';
} else {
} else { */
if(proportion > 0) {
width = height * proportion;
} else {
@ -465,7 +470,7 @@ export class AppMediaViewer {
//this.log('will set style aspecter:', `width: ${width}px; height: ${height}px; transform: scale(${containerRect.width / width}, ${containerRect.height / height});`);
aspecter.style.cssText = `width: ${width}px; height: ${height}px; transform: scale(${containerRect.width / width}, ${containerRect.height / height});`;
}
//}
}
public sizeTailPath(path: SVGPathElement, rect: DOMRect, scaleX: number, delay: number, upscale: boolean, isOut: boolean, borderRadius: string) {
@ -528,8 +533,12 @@ export class AppMediaViewer {
let newMover = document.createElement('div');
newMover.classList.add('media-viewer-mover');
let oldMover = this.content.mover;
oldMover.parentElement.append(newMover);
if(this.content.mover) {
let oldMover = this.content.mover;
oldMover.parentElement.append(newMover);
} else {
this.wholeDiv.append(newMover);
}
newMover.addEventListener('click', this.onClickBinded);
@ -716,7 +725,7 @@ export class AppMediaViewer {
this.setNewMover();
} else {
window.addEventListener('keydown', this.onKeyDownBinded);
this.overlaysDiv.classList.add('active');
this.wholeDiv.classList.add('active');
}
////////this.log('wasActive:', wasActive);

View File

@ -22,6 +22,7 @@ import apiManager from '../mtproto/mtprotoworker';
import appWebPagesManager from "./appWebPagesManager";
import { CancellablePromise, deferredPromise } from "../polyfill";
import appPollsManager from "./appPollsManager";
import searchIndexManager from '../searchIndexManager';
const APITIMEOUT = 0;
@ -107,6 +108,17 @@ export class AppMessagesManager {
public loaded: Promise<any> = null;
private dialogsIndex = searchIndexManager.createIndex();
private cachedResults: {
query: string,
count: number,
dialogs: Dialog[]
} = {
query: '',
count: 0,
dialogs: []
};
constructor() {
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
let update: any = e.detail;
@ -1226,9 +1238,33 @@ export class AppMessagesManager {
return false;
}
public getConversations(offsetIndex?: number, limit = 20, folderID = 0) {
public getConversations(query = '', offsetIndex?: number, limit = 20, folderID = 0) {
let curDialogStorage = this.dialogsStorage[folderID] ?? (this.dialogsStorage[folderID] = []);
if(query) {
if(!limit || this.cachedResults.query !== query) {
this.cachedResults.query = query
const results = searchIndexManager.search(query, this.dialogsIndex);
this.cachedResults.dialogs = [];
for(const folderID in this.dialogsStorage) {
const dialogs = this.dialogsStorage[folderID];
dialogs.forEach(dialog => {
if(results[dialog.peerID]) {
this.cachedResults.dialogs.push(dialog);
}
});
}
this.cachedResults.count = this.cachedResults.dialogs.length;
}
curDialogStorage = this.cachedResults.dialogs;
} else {
this.cachedResults.query = '';
}
let offset = 0;
if(offsetIndex > 0) {
for(; offset < curDialogStorage.length; offset++) {
@ -1238,15 +1274,15 @@ export class AppMessagesManager {
}
}
if(this.allDialogsLoaded[folderID] || curDialogStorage.length >= offset + limit) {
if(query || this.allDialogsLoaded[folderID] || curDialogStorage.length >= offset + limit) {
return Promise.resolve({
dialogs: curDialogStorage.slice(offset, offset + limit),
count: curDialogStorage.length
});
}
return this.getTopMessages(limit, folderID).then(count => {
let curDialogStorage = this.dialogsStorage[folderID];
return this.getTopMessages(limit, folderID).then(totalCount => {
//const curDialogStorage = this.dialogsStorage[folderID];
offset = 0;
if(offsetIndex > 0) {
@ -1261,7 +1297,7 @@ export class AppMessagesManager {
return {
dialogs: curDialogStorage.slice(offset, offset + limit),
count: count
count: totalCount
};
});
}
@ -1277,15 +1313,13 @@ export class AppMessagesManager {
if(this.dialogsOffsetDate[folderID]) {
offsetDate = this.dialogsOffsetDate[folderID] + serverTimeManager.serverTimeOffset;
offsetIndex = this.dialogsOffsetDate[folderID] * 0x10000;
flags |= 1;
//flags |= 1; // means pinned already loaded
}
if(folderID > 0) {
flags |= 1;
//if(folderID > 0) {
//flags |= 1;
flags |= 2;
}
let hash = 0;
//}
return apiManager.invokeApi('messages.getDialogs', {
flags: flags,
@ -1294,7 +1328,7 @@ export class AppMessagesManager {
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
offset_peer: appPeersManager.getInputPeerByID(offsetPeerID),
limit: limit,
hash: hash
hash: 0
}, {
timeout: APITIMEOUT
}).then((dialogsResult: any) => {
@ -1828,9 +1862,11 @@ export class AppMessagesManager {
let messageText = '';
if(message.media) {
switch(message.media._) {
if(message.grouped_id) {
messageText += '<i>Album' + (message.message ? ', ' : '') + '</i>';
} else switch(message.media._) {
case 'messageMediaPhoto':
messageText += '<i>' + (message.grouped_id ? 'Album' : 'Photo') + (message.message ? ', ' : '') + '</i>';
messageText += '<i>Photo' + (message.message ? ', ' : '') + '</i>';
break;
case 'messageMediaGeo':
messageText += '<i>Geolocation</i>';
@ -1869,26 +1905,33 @@ export class AppMessagesManager {
if(message.action) {
let action = message.action;
console.log('message action:', action);
let suffix = '';
let _ = action._;
if(_ == "messageActionPhoneCall") {
_ += '.' + action.type;
let duration = action.duration;
if(duration) {
let d = [];
d.push(duration % 60 + ' s');
if(duration >= 60) d.push((duration / 60 | 0) + ' min');
//if(duration >= 3600) d.push((duration / 3600 | 0) + ' h');
suffix = ' (' + d.reverse().join(' ') + ')';
let str = '';
if(action.message) {
str = RichTextProcessor.wrapRichText(action.message, {noLinebreaks: true});
} else {
let suffix = '';
let _ = action._;
if(_ == "messageActionPhoneCall") {
_ += '.' + action.type;
let duration = action.duration;
if(duration) {
let d = [];
d.push(duration % 60 + ' s');
if(duration >= 60) d.push((duration / 60 | 0) + ' min');
//if(duration >= 3600) d.push((duration / 3600 | 0) + ' h');
suffix = ' (' + d.reverse().join(' ') + ')';
}
}
// @ts-ignore
str = langPack[_] + suffix;
}
// @ts-ignore
messageText = '<i>' + langPack[_] + suffix + '</i>';
console.log('message action:', action);
messageText = '<i>' + str + '</i>';
}
let messageWrapped = '';
@ -2098,18 +2141,21 @@ export class AppMessagesManager {
}
public saveConversation(dialog: Dialog) {
var peerID = appPeersManager.getPeerID(dialog.peer);
const peerID = appPeersManager.getPeerID(dialog.peer);
if(!peerID) {
return false;
}
var channelID = appPeersManager.isChannel(peerID) ? -peerID : 0;
const channelID = appPeersManager.isChannel(peerID) ? -peerID : 0;
const peerText = appPeersManager.getPeerSearchText(peerID);
searchIndexManager.indexObject(peerID, peerText, this.dialogsIndex);
let mid: number, message;
if(dialog.top_message) {
var mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID);
var message = this.getMessage(mid);
mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID);
message = this.getMessage(mid);
} else {
var mid = this.tempID--;
var message: any = {
mid = this.tempID--;
message = {
_: 'message',
id: mid,
mid: mid,
@ -2120,14 +2166,14 @@ export class AppMessagesManager {
pFlags: {unread: false, out: true},
date: 0,
message: ''
}
};
this.saveMessages([message]);
}
if(!channelID && peerID < 0) {
var chat = appChatsManager.getChat(-peerID)
const chat = appChatsManager.getChat(-peerID);
if(chat && chat.migrated_to && chat.pFlags.deactivated) {
var migratedToPeer = appPeersManager.getPeerID(chat.migrated_to)
const migratedToPeer = appPeersManager.getPeerID(chat.migrated_to);
this.migratedFromTo[peerID] = migratedToPeer;
this.migratedToFrom[migratedToPeer] = peerID;
return;
@ -2151,7 +2197,7 @@ export class AppMessagesManager {
}
if(this.historiesStorage[peerID] === undefined/* && !message.deleted */) { // warning
let historyStorage: HistoryStorage = {count: null, history: [], pending: []};
const historyStorage: HistoryStorage = {count: null, history: [], pending: []};
historyStorage[mid > 0 ? 'history' : 'pending'].push(mid);
if(mid < 0 && message.pFlags.unread) {
dialog.unread_count++;

View File

@ -148,4 +148,6 @@ class AppPollsManager {
}
}
export default new AppPollsManager();
const appPollsManager = new AppPollsManager();
(window as any).appPollsManager = appPollsManager;
export default appPollsManager;

View File

@ -15,6 +15,7 @@ import LazyLoadQueue from "../../components/lazyLoadQueue";
import { wrapDocument, wrapAudio } from "../../components/wrappers";
import AppSearch, { SearchGroup } from "../../components/appSearch";
import AvatarElement from "../../components/avatar";
import appForward from "../../components/appForward";
const testScroll = false;
@ -250,6 +251,11 @@ class AppSidebarRight {
(item.element.firstElementChild as HTMLElement).style.display = '';
}
if(enable == false || (this.sidebarEl.classList.contains('active') && enable == undefined)) {
appForward.close();
this.searchCloseBtn.click();
}
resolve();
}, 200);
});

View File

@ -1,10 +1,11 @@
import { SearchIndexManager, safeReplaceObject, isObject, tsNow, copy, $rootScope } from "../utils";
import { safeReplaceObject, isObject, tsNow, copy, $rootScope } from "../utils";
import { RichTextProcessor } from "../richtextprocessor";
import appChatsManager from "./appChatsManager";
//import apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker';
import serverTimeManager from "../mtproto/serverTimeManager";
import { formatPhoneNumber } from "../../components/misc";
import searchIndexManager from "../searchIndexManager";
export type User = {
_: 'user',
@ -39,7 +40,7 @@ export class AppUsersManager {
public usernames: {[username: string]: number} = {};
public userAccess: {[userID: number]: string} = {};
public cachedPhotoLocations: any = {};
public contactsIndex = SearchIndexManager.createIndex();
public contactsIndex = searchIndexManager.createIndex();
public contactsFillPromise: Promise<number[]>;
public contactsList: number[];
public myID: number;
@ -118,14 +119,14 @@ export class AppUsersManager {
return this.contactsFillPromise = apiManager.invokeApi('contacts.getContacts', {
hash: 0
}).then((result: any) => {
var userID;
let userID: number;
this.contactsList = [];
this.saveApiUsers(result.users);
result.contacts.forEach((contact: any) => {
userID = contact.user_id;
this.contactsList.push(userID);
SearchIndexManager.indexObject(userID, this.getUserSearchText(userID), this.contactsIndex);
searchIndexManager.indexObject(userID, this.getUserSearchText(userID), this.contactsIndex);
});
return this.contactsList;
@ -133,15 +134,12 @@ export class AppUsersManager {
}
public getUserSearchText(id: number) {
var user = this.users[id];
const user = this.users[id];
if(!user) {
return false;
}
var serviceText = '';
if(user.pFlags.self) {
serviceText = 'user_name_saved_msgs_raw';
return '';
}
const serviceText = user.pFlags.self ? 'user_name_saved_msgs_raw' : '';
return (user.first_name || '') +
' ' + (user.last_name || '') +
' ' + (user.phone || '') +
@ -152,7 +150,7 @@ export class AppUsersManager {
public getContacts(query?: string) {
return this.fillContacts().then(contactsList => {
if(query) {
const results: any = SearchIndexManager.search(query, this.contactsIndex);
const results: any = searchIndexManager.search(query, this.contactsIndex);
const filteredContactsList = contactsList.filter(id => !!results[id]);
contactsList = filteredContactsList;
@ -219,11 +217,11 @@ export class AppUsersManager {
}
if(apiUser.username) {
var searchUsername = SearchIndexManager.cleanUsername(apiUser.username);
var searchUsername = searchIndexManager.cleanUsername(apiUser.username);
this.usernames[searchUsername] = userID;
}
apiUser.sortName = apiUser.pFlags.deleted ? '' : SearchIndexManager.cleanSearchText(apiUser.first_name + ' ' + (apiUser.last_name || ''), false);
apiUser.sortName = apiUser.pFlags.deleted ? '' : searchIndexManager.cleanSearchText(apiUser.first_name + ' ' + (apiUser.last_name || ''), false);
var nameWords = apiUser.sortName.split(' ');
var firstWord = nameWords.shift();
@ -544,7 +542,7 @@ export class AppUsersManager {
if(isContact != curIsContact) {
if(isContact) {
this.contactsList.push(userID)
SearchIndexManager.indexObject(userID, this.getUserSearchText(userID), this.contactsIndex);
searchIndexManager.indexObject(userID, this.getUserSearchText(userID), this.contactsIndex);
} else {
this.contactsList.splice(curPos, 1);
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,461 @@
import { isInDOM } from "./utils";
let convert = (value: number) => {
return Math.round(Math.min(Math.max(value, 0), 1) * 255);
};
type RLottiePlayerListeners = 'firstFrame' | 'enterFrame';
export class RLottiePlayer {
public static reqId = 0;
public reqId = 0;
public curFrame: number;
public worker: QueryableWorker;
public el: HTMLElement;
public width: number;
public height: number;
public listeners: Partial<{
[k in RLottiePlayerListeners]: (res: any) => void
}> = {};
public listenerResults: Partial<{
[k in RLottiePlayerListeners]: any
}> = {};
public canvas: HTMLCanvasElement;
public context: CanvasRenderingContext2D;
public paused = false;
public direction = 1;
public speed = 1;
public autoplay = true;
constructor({el, width, height, worker}: {
el: HTMLElement,
width: number,
height: number,
worker: QueryableWorker
}) {
this.reqId = ++RLottiePlayer['reqId'];
this.el = el;
this.width = width;
this.height = height;
this.worker = worker;
}
public addListener(name: RLottiePlayerListeners, callback: (res?: any) => void) {
if(this.listenerResults.hasOwnProperty(name)) return Promise.resolve(this.listenerResults[name]);
this.listeners[name] = callback;
}
public setListenerResult(name: RLottiePlayerListeners, value?: any) {
this.listenerResults[name] = value;
if(this.listeners[name]) {
this.listeners[name](value);
}
}
private sendQuery(methodName: string, ...args: any[]) {
this.worker.sendQuery(methodName, this.reqId, ...args);
}
public loadFromData(json: any) {
this.sendQuery('loadFromData', json, this.width, this.height, {
paused: this.paused,
direction: this.direction,
speed: this.speed
});
}
public play() {
this.sendQuery('play');
this.paused = false;
}
public pause() {
this.sendQuery('pause');
this.paused = true;
}
public stop() {
this.sendQuery('stop');
this.paused = true;
}
public restart() {
this.sendQuery('restart');
}
public setSpeed(speed: number) {
this.sendQuery('setSpeed', speed);
}
public setDirection(direction: number) {
this.direction = direction;
this.sendQuery('setDirection', direction);
}
public destroy() {
lottieLoader.onDestroy(this.reqId);
this.sendQuery('destroy');
}
private attachPlayer() {
this.canvas = document.createElement('canvas');
this.canvas.width = this.width;
this.canvas.height = this.height;
//this.el.appendChild(this.canvas);
this.context = this.canvas.getContext('2d');
}
public renderFrame(frame: Uint8ClampedArray, frameNo: number) {
if(!this.listenerResults.hasOwnProperty('firstFrame')) {
this.attachPlayer();
this.el.appendChild(this.canvas);
this.setListenerResult('firstFrame');
}
this.context.putImageData(new ImageData(frame, this.width, this.height), 0, 0);
this.setListenerResult('enterFrame', frameNo);
}
}
class QueryableWorker {
private worker: Worker;
private listeners: {[name: string]: (...args: any[]) => void} = {};
constructor(url: string, private defaultListener: (data: any) => void = () => {}, onError?: (error: any) => void) {
this.worker = new Worker(url);
if(onError) {
this.worker.onerror = onError;
}
this.worker.onmessage = (event) => {
if(event.data instanceof Object &&
event.data.hasOwnProperty('queryMethodListener') &&
event.data.hasOwnProperty('queryMethodArguments')) {
this.listeners[event.data.queryMethodListener].apply(this, event.data.queryMethodArguments);
} else {
this.defaultListener.call(this, event.data);
}
}
}
public postMessage(message: any) {
this.worker.postMessage(message);
}
public terminate() {
this.worker.terminate();
}
public addListener(name: string, listener: (...args: any[]) => void) {
this.listeners[name] = listener;
}
public removeListener(name: string) {
delete this.listeners[name];
}
public sendQuery(queryMethod: string, ...args: any[]) {
this.worker.postMessage({
'queryMethod': queryMethod,
'queryMethodArguments': args
});
}
}
class LottieLoader {
public loadPromise: Promise<void>;
public loaded = false;
private static COLORREPLACEMENTS = [
[
[0xf77e41, 0xca907a],
[0xffb139, 0xedc5a5],
[0xffd140, 0xf7e3c3],
[0xffdf79, 0xfbefd6],
],
[
[0xf77e41, 0xaa7c60],
[0xffb139, 0xc8a987],
[0xffd140, 0xddc89f],
[0xffdf79, 0xe6d6b2],
],
[
[0xf77e41, 0x8c6148],
[0xffb139, 0xad8562],
[0xffd140, 0xc49e76],
[0xffdf79, 0xd4b188],
],
[
[0xf77e41, 0x6e3c2c],
[0xffb139, 0x925a34],
[0xffd140, 0xa16e46],
[0xffdf79, 0xac7a52],
]
];
private workersLimit = 4;
private players: {[reqId: number]: RLottiePlayer} = {};
private byGroups: {[group: string]: RLottiePlayer[]} = {};
private workers: QueryableWorker[] = [];
private curWorkerNum = 0;
private observer: IntersectionObserver;
private visible: Set<RLottiePlayer> = new Set();
private debug = true;
constructor() {
this.observer = new IntersectionObserver((entries) => {
for(const entry of entries) {
const target = entry.target;
for(const group in this.byGroups) {
const player = this.byGroups[group].find(p => p.el == target);
if(player) {
if(entry.isIntersecting) {
this.visible.add(player);
if(player.paused) {
player.play();
}
} else {
this.visible.delete(player);
if(!player.paused) {
player.pause();
}
}
break;
}
}
}
});
}
public loadLottieWorkers() {
if(this.loadPromise) return this.loadPromise;
const onFrame = this.onFrame.bind(this);
return this.loadPromise = new Promise((resolve, reject) => {
let remain = this.workersLimit;
for(let i = 0; i < this.workersLimit; ++i) {
const worker = this.workers[i] = new QueryableWorker('rlottie.worker.js');
worker.addListener('ready', () => {
console.log('worker #' + i + ' ready');
worker.addListener('frame', onFrame);
--remain;
if(!remain) {
console.log('workers ready');
resolve();
this.loaded = true;
}
});
}
});
}
private applyReplacements(object: any, toneIndex: number) {
const replacements = LottieLoader.COLORREPLACEMENTS[toneIndex - 2];
const iterateIt = (it: any) => {
for(let smth of it) {
switch(smth.ty) {
case 'st':
case 'fl':
let k = smth.c.k;
let color = convert(k[2]) | (convert(k[1]) << 8) | (convert(k[0]) << 16);
let foundReplacement = replacements.find(p => p[0] == color);
if(foundReplacement) {
k[0] = ((foundReplacement[1] >> 16) & 255) / 255;
k[1] = ((foundReplacement[1] >> 8) & 255) / 255;
k[2] = (foundReplacement[1] & 255) / 255;
}
//console.log('foundReplacement!', foundReplacement, color.toString(16), k);
break;
}
if(smth.hasOwnProperty('it')) {
iterateIt(smth.it);
}
}
};
for(let layer of object.layers) {
if(!layer.shapes) continue;
for(let shape of layer.shapes) {
iterateIt(shape.it);
}
}
}
public async loadAnimationWorker(params: {
container: HTMLElement,
autoplay?: boolean,
animationData: any,
loop?: boolean,
renderer?: string,
width?: number,
height?: number
}, group = '', toneIndex = -1) {
//params.autoplay = false;
if(toneIndex >= 1 && toneIndex <= 5) {
this.applyReplacements(params.animationData, toneIndex);
}
if(!this.loaded) {
await this.loadLottieWorkers();
}
this.observer.observe(params.container);
const width = params.width || parseInt(params.container.style.width);
const height = params.height || parseInt(params.container.style.height);
const player = this.initPlayer(params.container, params.animationData, width, height);
for(let i in params) {
// @ts-ignore
if(player.hasOwnProperty(i)) {
// @ts-ignore
player[i] = params[i];
}
}
(this.byGroups[group] ?? (this.byGroups[group] = [])).push(player);
return player;
}
public checkAnimations(blurred?: boolean, group?: string, destroy = false) {
const groups = group && false ? [group] : Object.keys(this.byGroups);
if(group && !this.byGroups[group]) {
console.warn('no animation group:', group);
this.byGroups[group] = [];
//return;
}
for(const group of groups) {
const animations = this.byGroups[group];
const length = animations.length;
for(let i = length - 1; i >= 0; --i) {
const player = animations[i];
if(destroy || (!isInDOM(player.el) && player.listenerResults.hasOwnProperty('firstFrame'))) {
//console.log('destroy animation');
player.destroy();
continue;
}
if(blurred) {
if(!player.paused) {
this.debug && console.log('pause animation', player);
player.pause();
}
} else if(player.paused && this.visible.has(player)) {
this.debug && console.log('play animation', player);
player.play();
}
/* if(canvas) {
let c = container.firstElementChild as HTMLCanvasElement;
if(!c) {
console.warn('no canvas element for check!', container, animations[i]);
continue;
}
if(!c.height && !c.width && isElementInViewport(container)) {
//console.log('lottie need resize');
animation.resize();
}
} */
//if(!autoplay) continue;
/* if(blurred || !isElementInViewport(container)) {
if(!paused) {
this.debug && console.log('pause animation', isElementInViewport(container), container);
animation.pause();
animations[i].paused = true;
}
} else if(paused) {
this.debug && console.log('play animation', container);
animation.play();
animations[i].paused = false;
} */
}
}
}
private onFrame(reqId: number, frameNo: number, frame: Uint8ClampedArray, width: number, height: number) {
const rlPlayer = this.players[reqId];
if(!rlPlayer) {
this.debug && console.warn('onFrame on destroyed player:', reqId, frameNo);
return;
}
rlPlayer.renderFrame(frame, frameNo);
}
public onDestroy(reqId: number) {
let player = this.players[reqId];
for(let group in this.byGroups) {
this.byGroups[group].findAndSplice(p => p == player);
}
delete this.players[player.reqId];
this.observer.unobserve(player.el);
this.visible.delete(player);
}
public destroyWorkers() {
this.workers.forEach((worker, idx) => {
worker.terminate();
console.log('worker #' + idx + ' terminated');
});
console.log('workers destroyed');
this.workers.length = 0;
}
private initPlayer(el: HTMLElement, json: any, width: number, height: number) {
const rlPlayer = new RLottiePlayer({
el,
width,
height,
worker: this.workers[this.curWorkerNum++]
});
this.players[rlPlayer.reqId] = rlPlayer;
if(this.curWorkerNum >= this.workers.length) {
this.curWorkerNum = 0;
}
rlPlayer.loadFromData(json);
return rlPlayer;
}
}
const lottieLoader = new LottieLoader();
(window as any).LottieLoader = lottieLoader;
export default lottieLoader;

View File

@ -1,25 +1,253 @@
//import { isInDOM } from "./utils";
import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web/build/player/lottie.d";
import { isInDOM } from "./utils";
let convert = (value: number) => {
return Math.round(Math.min(Math.max(value, 0), 1) * 255);
};
type RLottiePlayerListeners = 'enterFrame' | 'ready';
export class RLottiePlayer {
public static reqId = 0;
public reqId = 0;
public curFrame: number;
public frameCount: number;
public fps: number;
public worker: QueryableWorker;
public width: number;
public height: number;
public listeners: Partial<{
[k in RLottiePlayerListeners]: (res: any) => void
}> = {};
public listenerResults: Partial<{
[k in RLottiePlayerListeners]: any
}> = {};
public el: HTMLElement;
public canvas: HTMLCanvasElement;
public context: CanvasRenderingContext2D;
public paused = true;
public direction = 1;
public speed = 1;
public autoplay = true;
public loop = true;
private frInterval: number;
private frThen: number;
private rafId: number;
private playedTimes = 0;
constructor({el, width, height, worker}: {
el: HTMLElement,
width: number,
height: number,
worker: QueryableWorker
}) {
this.reqId = ++RLottiePlayer['reqId'];
this.el = el;
this.width = width;
this.height = height;
this.worker = worker;
this.canvas = document.createElement('canvas');
this.canvas.width = this.width;
this.canvas.height = this.height;
this.context = this.canvas.getContext('2d');
}
public addListener(name: RLottiePlayerListeners, callback: (res?: any) => void) {
if(this.listenerResults.hasOwnProperty(name)) return Promise.resolve(this.listenerResults[name]);
this.listeners[name] = callback;
}
public setListenerResult(name: RLottiePlayerListeners, value?: any) {
this.listenerResults[name] = value;
if(this.listeners[name]) {
this.listeners[name](value);
}
}
public sendQuery(methodName: string, ...args: any[]) {
this.worker.sendQuery(methodName, this.reqId, ...args);
}
public loadFromData(json: any) {
this.sendQuery('loadFromData', json, this.width, this.height, {
paused: this.paused,
direction: this.direction,
speed: this.speed
});
}
public play() {
if(!this.paused) return;
this.paused = false;
this.setMainLoop();
}
public pause() {
if(this.paused) return;
this.paused = true;
window.cancelAnimationFrame(this.rafId);
}
public stop() {
this.pause();
this.curFrame = this.direction == 1 ? 0 : this.frameCount;
this.sendQuery('renderFrame', this.curFrame);
}
public restart() {
this.stop();
this.play();
}
public setSpeed(speed: number) {
this.speed = speed;
if(!this.paused) {
this.setMainLoop();
}
}
public setDirection(direction: number) {
this.direction = direction;
if(!this.paused) {
this.setMainLoop();
}
}
public destroy() {
lottieLoader.onDestroy(this.reqId);
this.pause();
this.sendQuery('destroy');
}
public renderFrame(frame: Uint8ClampedArray, frameNo: number) {
this.context.putImageData(new ImageData(frame, this.width, this.height), 0, 0);
this.setListenerResult('enterFrame', frameNo);
}
private mainLoop(method: RLottiePlayer['mainLoopForwards'] | RLottiePlayer['mainLoopBackwards']) {
let r = () => {
if(this.paused) {
return;
}
const now = Date.now(), delta = now - this.frThen;
if(delta > this.frInterval) {
this.frThen = now - (delta % this.frInterval);
const canContinue = method();
if(!canContinue && !this.loop && this.autoplay) {
this.autoplay = false;
}
}
this.rafId = window.requestAnimationFrame(r);
};
//this.rafId = window.requestAnimationFrame(r);
r();
}
private mainLoopForwards() {
this.sendQuery('renderFrame', this.curFrame++);
if(this.curFrame >= this.frameCount) {
this.playedTimes++;
if(!this.loop) return false;
this.curFrame = 0;
}
return true;
};
private mainLoopBackwards() {
this.sendQuery('renderFrame', this.curFrame--);
if(this.curFrame < 0) {
this.playedTimes++;
if(!this.loop) return false;
this.curFrame = this.frameCount - 1;
}
return true;
};
public setMainLoop() {
window.cancelAnimationFrame(this.rafId);
this.frInterval = 1000 / this.fps / this.speed;
this.frThen = Date.now();
//console.trace('setMainLoop', this.frInterval, this.direction, this);
const method = (this.direction == 1 ? this.mainLoopForwards : this.mainLoopBackwards).bind(this);
this.mainLoop(method);
};
}
class QueryableWorker {
private worker: Worker;
private listeners: {[name: string]: (...args: any[]) => void} = {};
constructor(url: string, private defaultListener: (data: any) => void = () => {}, onError?: (error: any) => void) {
this.worker = new Worker(url);
if(onError) {
this.worker.onerror = onError;
}
this.worker.onmessage = (event) => {
if(event.data instanceof Object &&
event.data.hasOwnProperty('queryMethodListener') &&
event.data.hasOwnProperty('queryMethodArguments')) {
this.listeners[event.data.queryMethodListener].apply(this, event.data.queryMethodArguments);
} else {
this.defaultListener.call(this, event.data);
}
}
}
public postMessage(message: any) {
this.worker.postMessage(message);
}
public terminate() {
this.worker.terminate();
}
public addListener(name: string, listener: (...args: any[]) => void) {
this.listeners[name] = listener;
}
public removeListener(name: string) {
delete this.listeners[name];
}
public sendQuery(queryMethod: string, ...args: any[]) {
this.worker.postMessage({
'queryMethod': queryMethod,
'queryMethodArguments': args
});
}
}
class LottieLoader {
public lottie: /* any */ typeof LottiePlayer = null;
private animations: {
[group: string]: {
animation: /* any */AnimationItem,
container: HTMLDivElement,
paused: boolean,
autoplay: boolean,
canvas: boolean
}[]
} = {};
private debug = false;
public loaded: Promise<void>;
private lastTimeLoad = 0;
private waitingTimeouts = 0;
public loadPromise: Promise<void>;
public loaded = false;
private static COLORREPLACEMENTS = [
[
[0xf77e41, 0xca907a],
@ -48,78 +276,69 @@ class LottieLoader {
[0xffd140, 0xa16e46],
[0xffdf79, 0xac7a52],
]
];
];
public loadLottie() {
if(this.loaded) return this.loaded;
private workersLimit = 4;
private players: {[reqId: number]: RLottiePlayer} = {};
private byGroups: {[group: string]: RLottiePlayer[]} = {};
return 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);
private workers: QueryableWorker[] = [];
private curWorkerNum = 0;
private observer: IntersectionObserver;
private visible: Set<RLottiePlayer> = new Set();
private debug = true;
constructor() {
this.observer = new IntersectionObserver((entries) => {
for(const entry of entries) {
const target = entry.target;
for(const group in this.byGroups) {
const player = this.byGroups[group].find(p => p.el == target);
if(player) {
if(entry.isIntersecting) {
this.visible.add(player);
this.checkAnimation(player, false);
} else {
this.visible.delete(player);
this.checkAnimation(player, true);
}
break;
}
}
}
});
}
public checkAnimations(blurred?: boolean, group?: string, destroy = false) {
let groups = group ? [group] : Object.keys(this.animations);
public loadLottieWorkers() {
if(this.loadPromise) return this.loadPromise;
if(group && !this.animations[group]) {
console.warn('no animation group:', group);
this.animations[group] = [];
//return;
}
const onFrame = this.onFrame.bind(this);
const onPlayerLoaded = this.onPlayerLoaded.bind(this);
for(let group of groups) {
let animations = this.animations[group];
return this.loadPromise = new Promise((resolve, reject) => {
let remain = this.workersLimit;
for(let i = 0; i < this.workersLimit; ++i) {
const worker = this.workers[i] = new QueryableWorker('rlottie.worker.js');
let length = animations.length;
for(let i = length - 1; i >= 0; --i) {
let {animation, container, paused, autoplay, canvas} = animations[i];
worker.addListener('ready', () => {
console.log('worker #' + i + ' ready');
if(destroy && !container.parentElement/* !isInDOM(container) */) {
this.debug && console.log('destroy animation');
animation.destroy();
animations.splice(i, 1);
continue;
}
worker.addListener('frame', onFrame);
worker.addListener('loaded', onPlayerLoaded);
/* if(canvas) {
let c = container.firstElementChild as HTMLCanvasElement;
if(!c) {
console.warn('no canvas element for check!', container, animations[i]);
continue;
--remain;
if(!remain) {
console.log('workers ready');
resolve();
this.loaded = true;
}
if(!c.height && !c.width && isElementInViewport(container)) {
//console.log('lottie need resize');
animation.resize();
}
} */
if(!autoplay) continue;
/* if(blurred || !isElementInViewport(container)) {
if(!paused) {
this.debug && console.log('pause animation', isElementInViewport(container), container);
animation.pause();
animations[i].paused = true;
}
} else if(paused) {
this.debug && console.log('play animation', container);
animation.play();
animations[i].paused = false;
} */
});
}
}
});
}
private applyReplacements(object: any, toneIndex: number) {
@ -159,81 +378,156 @@ class LottieLoader {
}
}
public async loadAnimation(params: /* any */AnimationConfigWithPath & AnimationConfigWithData, group = '', toneIndex = -1) {
//params.autoplay = false;
//if(group != 'auth') {
//params.renderer = 'canvas';
params.renderer = 'svg';
//}
public async loadAnimationWorker(params: {
container: HTMLElement,
autoplay?: boolean,
animationData: any,
loop?: boolean,
width?: number,
height?: number
}, group = '', toneIndex = -1) {
params.autoplay = true;
if(toneIndex >= 1 && toneIndex <= 5) {
this.applyReplacements(params.animationData, toneIndex);
}
let rendererSettings = {
//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.
hideOnTransparent: true, //Boolean, only svg renderer, hides elements when opacity reaches 0 (defaults to true),
};
if(params.rendererSettings) {
params.rendererSettings = Object.assign(params.rendererSettings, rendererSettings);
} else {
params.rendererSettings = rendererSettings;
if(!this.loaded) {
await this.loadLottieWorkers();
}
if(!this.lottie) {
if(!this.loaded) this.loadLottie();
await this.loaded;
const width = params.width || parseInt(params.container.style.width);
const height = params.height || parseInt(params.container.style.height);
this.lottie.setQuality('low');
//this.lottie.setQuality(10);
const player = this.initPlayer(params.container, params.animationData, width, height);
for(let i in params) {
// @ts-ignore
if(player.hasOwnProperty(i)) {
// @ts-ignore
player[i] = params[i];
}
}
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--;
}
(this.byGroups[group] ?? (this.byGroups[group] = [])).push(player);
let animation = this.lottie.loadAnimation(params);
this.lastTimeLoad = Date.now();
if(!this.animations[group]) this.animations[group] = [];
this.animations[group].push({
animation,
container: params.container as HTMLDivElement,
paused: !params.autoplay,
autoplay: params.autoplay,
canvas: false//params.renderer == 'canvas'
});
if(params.autoplay) {
this.checkAnimations();
}
return animation;
return player;
}
public getAnimation(el: HTMLElement, group = '') {
let groups = group ? [group] : Object.keys(this.animations);
//console.log('getAnimation', groups, this.animations);
for(let group of groups) {
let animations = this.animations[group];
public checkAnimations(blurred?: boolean, group?: string, destroy = false) {
const groups = group && false ? [group] : Object.keys(this.byGroups);
let animation = animations.find(a => a.container === el);
if(animation) return animation.animation;
if(group && !this.byGroups[group]) {
console.warn('no animation group:', group);
this.byGroups[group] = [];
//return;
}
return null;
for(const group of groups) {
const animations = this.byGroups[group];
animations.forEach(player => {
this.checkAnimation(player, blurred, destroy);
//if(!autoplay) continue;
/* if(blurred || !isElementInViewport(container)) {
if(!paused) {
this.debug && console.log('pause animation', isElementInViewport(container), container);
animation.pause();
animations[i].paused = true;
}
} else if(paused) {
this.debug && console.log('play animation', container);
animation.play();
animations[i].paused = false;
} */
});
}
}
public checkAnimation(player: RLottiePlayer, blurred = false, destroy = false) {
if(destroy || (!isInDOM(player.el) && player.listenerResults.hasOwnProperty('ready'))) {
//console.log('destroy animation');
player.destroy();
return;
}
if(blurred) {
if(!player.paused) {
this.debug && console.log('pause animation', player);
player.pause();
}
} else if(player.paused && this.visible.has(player) && player.autoplay) {
this.debug && console.log('play animation', player);
player.play();
}
}
private onPlayerLoaded(reqId: number, frameCount: number, fps: number) {
const rlPlayer = this.players[reqId];
if(!rlPlayer) {
this.debug && console.warn('onPlayerLoaded on destroyed player:', reqId, frameCount);
return;
}
rlPlayer.el.appendChild(rlPlayer.canvas);
rlPlayer.curFrame = rlPlayer.direction == 1 ? 0 : frameCount - 1;
rlPlayer.frameCount = frameCount;
rlPlayer.fps = fps;
rlPlayer.sendQuery('renderFrame', 0);
rlPlayer.setListenerResult('ready');
this.observer.observe(rlPlayer.el);
}
private onFrame(reqId: number, frameNo: number, frame: Uint8ClampedArray) {
const rlPlayer = this.players[reqId];
if(!rlPlayer) {
this.debug && console.warn('onFrame on destroyed player:', reqId, frameNo);
return;
}
rlPlayer.renderFrame(frame, frameNo);
}
public onDestroy(reqId: number) {
let player = this.players[reqId];
for(let group in this.byGroups) {
this.byGroups[group].findAndSplice(p => p == player);
}
delete this.players[player.reqId];
this.observer.unobserve(player.el);
this.visible.delete(player);
}
public destroyWorkers() {
this.workers.forEach((worker, idx) => {
worker.terminate();
console.log('worker #' + idx + ' terminated');
});
console.log('workers destroyed');
this.workers.length = 0;
}
private initPlayer(el: HTMLElement, json: any, width: number, height: number) {
const rlPlayer = new RLottiePlayer({
el,
width,
height,
worker: this.workers[this.curWorkerNum++]
});
this.players[rlPlayer.reqId] = rlPlayer;
if(this.curWorkerNum >= this.workers.length) {
this.curWorkerNum = 0;
}
rlPlayer.loadFromData(json);
return rlPlayer;
}
}

View File

@ -0,0 +1,110 @@
import Config from './config';
class SearchIndexManager {
public static badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<]+/g;
public static trimRe = /^\s+|\s$/g;
public createIndex() {
return {
shortIndexes: {},
fullTexts: {}
};
}
public cleanSearchText(text: string, latinize = true) {
const hasTag = text.charAt(0) == '%';
text = text.replace(SearchIndexManager['badCharsRe'], '').replace(SearchIndexManager['trimRe'], '');
if(latinize) {
text = text.replace(/[^A-Za-z0-9]/g, (ch) => {
const latinizeCh = Config.LatinizeMap[ch];
return latinizeCh !== undefined ? latinizeCh : ch;
});
}
text = text.toLowerCase();
if(hasTag) {
text = '%' + text;
}
return text;
}
public cleanUsername(username: string) {
return username && username.toLowerCase() || '';
}
public indexObject(id: number, searchText: string, searchIndex: any) {
if(searchIndex.fullTexts[id] !== undefined) {
return false;
}
searchText = this.cleanSearchText(searchText);
if(!searchText.length) {
return false;
}
const shortIndexes = searchIndex.shortIndexes;
searchIndex.fullTexts[id] = searchText;
searchText.split(' ').forEach((searchWord) => {
let len = Math.min(searchWord.length, 3),
wordPart, i;
for(i = 1; i <= len; i++) {
wordPart = searchWord.substr(0, i);
if(shortIndexes[wordPart] === undefined) {
shortIndexes[wordPart] = [id];
} else {
shortIndexes[wordPart].push(id);
}
}
});
}
public search(query: string, searchIndex: any) {
const shortIndexes = searchIndex.shortIndexes;
const fullTexts = searchIndex.fullTexts;
query = this.cleanSearchText(query);
const queryWords = query.split(' ');
let foundObjs: any = false,
newFoundObjs: any, i: number;
let j: number, searchText: string;
let found: boolean;
for(i = 0; i < queryWords.length; i++) {
newFoundObjs = shortIndexes[queryWords[i].substr(0, 3)];
if(!newFoundObjs) {
foundObjs = [];
break;
}
if(foundObjs === false || foundObjs.length > newFoundObjs.length) {
foundObjs = newFoundObjs;
}
}
newFoundObjs = {};
for(j = 0; j < foundObjs.length; j++) {
found = true;
searchText = fullTexts[foundObjs[j]];
for(i = 0; i < queryWords.length; i++) {
if(searchText.indexOf(queryWords[i]) == -1) {
found = false;
break;
}
}
if(found) {
newFoundObjs[foundObjs[j]] = true;
}
}
return newFoundObjs;
}
}
export default new SearchIndexManager();

View File

@ -9,18 +9,6 @@ export function dT () {
return '[' + ((Date.now() - _logTimer) / 1000).toFixed(3) + ']';
}
export function checkClick(e, noprevent) {
if(e.which == 1 && (e.ctrlKey || e.metaKey) || e.which == 2) {
return true;
}
if(!noprevent) {
e.preventDefault();
}
return false;
}
export function isInDOM(element, parentNode) {
if(!element) {
return false;
@ -62,60 +50,6 @@ export function cancelEvent (event) {
return false;
}
export function setFieldSelection (field, from, to) {
field = $(field)[0]
try {
field.focus()
if (from === undefined || from === false) {
from = field.value.length
}
if (to === undefined || to === false) {
to = from
}
if (field.createTextRange) {
var range = field.createTextRange()
range.collapse(true)
range.moveEnd('character', to)
range.moveStart('character', from)
range.select()
}
else if (field.setSelectionRange) {
field.setSelectionRange(from, to)
}
} catch(e) {}
}
export function getFieldSelection (field) {
if (field.selectionStart) {
return field.selectionStart
}
else if (!document.selection) {
return 0
}
var c = '\x01'
var sel = document.selection.createRange()
var txt = sel.text
var dup = sel.duplicate()
var len = 0
try {
dup.moveToElementText(field)
} catch(e) {
return 0
}
sel.text = txt + c
len = dup.text.indexOf(c)
sel.moveStart('character', -1)
sel.text = ''
// if (browser.msie && len == -1) {
// return field.value.length
// }
return len
}
export function getRichValue (field) {
if (!field) {
return ''
@ -152,42 +86,6 @@ export function placeCaretAtEnd(el) {
}
}
export function getRichValueWithCaret (field) {
if (!field) {
return []
}
var lines = []
var line = []
var sel = window.getSelection ? window.getSelection() : false
var selNode
var selOffset
if (sel && sel.rangeCount) {
var range = sel.getRangeAt(0)
/* if (range.startContainer &&
range.startContainer == range.endContainer &&
range.startOffset == range.endOffset) { */
selNode = range.startContainer
selOffset = range.startOffset
//}
}
getRichElementValue(field, lines, line, selNode, selOffset)
if (line.length) {
lines.push(line.join(''))
}
var value = lines.join('\n')
var caretPos = value.indexOf('\x01')
if (caretPos != -1) {
value = value.substr(0, caretPos) + value.substr(caretPos + 1)
}
value = value.replace(/\u00A0/g, ' ')
return [value, caretPos]
}
export function getRichElementValue (node, lines, line, selNode, selOffset) {
if (node.nodeType == 3) { // TEXT
if (selNode === node) {
@ -230,50 +128,6 @@ export function getRichElementValue (node, lines, line, selNode, selOffset) {
}
}
export function setRichFocus (field, selectNode, noCollapse) {
field.focus()
if (selectNode &&
selectNode.parentNode == field &&
!selectNode.nextSibling &&
!noCollapse) {
field.removeChild(selectNode)
selectNode = null
}
if (window.getSelection && document.createRange) {
var range = document.createRange()
if (selectNode) {
range.selectNode(selectNode)
} else {
range.selectNodeContents(field)
}
if (!noCollapse) {
range.collapse(false)
}
var sel = window.getSelection()
sel.removeAllRanges()
sel.addRange(range)
}
else if (document.body.createTextRange !== undefined) {
var textRange = document.body.createTextRange()
textRange.moveToElementText(selectNode || field)
if (!noCollapse) {
textRange.collapse(false)
}
textRange.select()
}
}
export function getSelectedText() {
var sel = (
window.getSelection && window.getSelection() ||
document.getSelection && document.getSelection() ||
document.selection && document.selection.createRange().text || ''
).toString().replace(/^\s+|\s+$/g, '')
return sel
}
/* if (Config.Modes.animations &&
typeof window.requestAnimationFrame == 'function') {
window.onAnimationFrameCallback = function (cb) {
@ -528,20 +382,6 @@ export function listMergeSorted (list1, list2) {
return result
}
export function listUniqSorted (list) {
list = list || []
var resultList = []
var prev = false
for (var i = 0; i < list.length; i++) {
if (list[i] !== prev) {
resultList.push(list[i])
}
prev = list[i]
}
return resultList
}
// credits to https://github.com/sindresorhus/escape-string-regexp/blob/master/index.js
export function escapeRegExp(str) {
return str
@ -657,119 +497,3 @@ export function getEmojiToneIndex(input) {
let match = input.match(/[\uDFFB-\uDFFF]/);
return match ? 5 - (57343 - match[0].charCodeAt(0)) : 0;
}
//var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g,
var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<]+/g,
trimRe = /^\s+|\s$/g
function createIndex () {
return {
shortIndexes: {},
fullTexts: {}
}
}
function cleanSearchText(text, latinize = true) {
var hasTag = text.charAt(0) == '%';
text = text.replace(badCharsRe, '').replace(trimRe, '');
if(latinize) {
text = text.replace(/[^A-Za-z0-9]/g, (ch) => {
var latinizeCh = Config.LatinizeMap[ch];
return latinizeCh !== undefined ? latinizeCh : ch;
});
}
text = text.toLowerCase();
if(hasTag) {
text = '%' + text;
}
return text;
}
function cleanUsername(username) {
return username && username.toLowerCase() || '';
}
function indexObject(id, searchText, searchIndex) {
if(searchIndex.fullTexts[id] !== undefined) {
return false;
}
searchText = cleanSearchText(searchText);
if(!searchText.length) {
return false;
}
var shortIndexes = searchIndex.shortIndexes;
searchIndex.fullTexts[id] = searchText;
searchText.split(' ').forEach((searchWord) => {
var len = Math.min(searchWord.length, 3),
wordPart, i;
for(i = 1; i <= len; i++) {
wordPart = searchWord.substr(0, i);
if(shortIndexes[wordPart] === undefined) {
shortIndexes[wordPart] = [id];
} else {
shortIndexes[wordPart].push(id);
}
}
});
}
function search(query, searchIndex) {
var shortIndexes = searchIndex.shortIndexes;
var fullTexts = searchIndex.fullTexts;
query = cleanSearchText(query);
var queryWords = query.split(' ');
var foundObjs = false,
newFoundObjs, i;
var j, searchText;
var found;
for(i = 0; i < queryWords.length; i++) {
newFoundObjs = shortIndexes[queryWords[i].substr(0, 3)];
if(!newFoundObjs) {
foundObjs = [];
break;
}
if(foundObjs === false || foundObjs.length > newFoundObjs.length) {
foundObjs = newFoundObjs;
}
}
newFoundObjs = {};
for(j = 0; j < foundObjs.length; j++) {
found = true;
searchText = fullTexts[foundObjs[j]];
for(i = 0; i < queryWords.length; i++) {
if(searchText.indexOf(queryWords[i]) == -1) {
found = false;
break;
}
}
if(found) {
newFoundObjs[foundObjs[j]] = true;
}
}
return newFoundObjs;
}
let SearchIndexManager = {
createIndex: createIndex,
indexObject: indexObject,
cleanSearchText: cleanSearchText,
cleanUsername: cleanUsername,
search: search
};
//window.SearchIndexManager = SearchIndexManager;
export {SearchIndexManager};
//})(window)

View File

@ -2,7 +2,7 @@ import pageSignIn from './pageSignIn';
import pageSignUp from './pageSignUp';
import pageIm from './pageIm';
import pagePassword from './pagePassword';
import LottieLoader from '../lib/lottieLoader';
import LottieLoader, { RLottiePlayer } from '../lib/lottieLoader';
//import CryptoWorker from '../lib/crypto/cryptoworker';
//import apiManager from '../lib/mtproto/apiManager';
import apiManager from '../lib/mtproto/mtprotoworker';
@ -29,11 +29,8 @@ let sentTypeElement: HTMLParagraphElement = null;
let onFirstMount = (): Promise<any> => {
let needFrame = 0, lastLength = 0;
let animation: /* AnimationItem */any;
let idleAnimation: any;
let mTrackingSvg: SVGSVGElement;
let mIdleSvg: SVGSVGElement;
let animation: RLottiePlayer;
let idleAnimation: RLottiePlayer;
const CODELENGTH = authCode.type.length;
@ -41,6 +38,8 @@ let onFirstMount = (): Promise<any> => {
const codeInputLabel = codeInput.nextElementSibling as HTMLLabelElement;
const editButton = page.pageEl.querySelector('.phone-edit') as HTMLElement;
codeInput.focus();
if(EDITONSAMEPAGE) {
let editable = false;
let changePhonePromise: Promise<unknown>;
@ -209,17 +208,17 @@ let onFirstMount = (): Promise<any> => {
let frame: number;
if(length) {
frame = Math.round((length > max ? max : length) * (165 / max) + 11.33);
frame = Math.round(Math.min(max, length) * (165 / max) + 11.33);
mIdleSvg.style.display = 'none';
mTrackingSvg.style.display = '';
idleAnimation.canvas.style.display = 'none';
animation.canvas.style.display = '';
} else {
frame = 0;
}
//animation.playSegments([1, 2]);
let direction = needFrame > frame ? -1 : 1;
//console.log('keydown', length, frame, direction);
console.log('keydown', length, frame, direction);
animation.setDirection(direction);
if(needFrame != 0 && frame == 0) {
@ -235,69 +234,64 @@ let onFirstMount = (): Promise<any> => {
//animation.goToAndStop(length / max * );
});
let imageDiv = page.pageEl.querySelector('.auth-image');
let imageDiv = page.pageEl.querySelector('.auth-image') as HTMLDivElement;
return Promise.all([
LottieLoader.loadLottie(),
LottieLoader.loadLottieWorkers(),
fetch('assets/img/TwoFactorSetupMonkeyIdle.tgs')
.then(res => res.arrayBuffer())
.then(data => apiManager.gzipUncompress<string>(data, true))
.then(str => LottieLoader.loadAnimation({
.then(str => LottieLoader.loadAnimationWorker({
container: imageDiv,
renderer: 'svg',
loop: true,
autoplay: true,
animationData: JSON.parse(str),
rendererSettings: {
className: 'monkey-idle'
}
width: 166,
height: 166
}))
.then(_animation => {
idleAnimation = _animation;
mIdleSvg = imageDiv.querySelector('.monkey-idle');
.then(animation => {
idleAnimation = animation;
}),
fetch('assets/img/TwoFactorSetupMonkeyTracking.tgs')
/* false && */fetch('assets/img/TwoFactorSetupMonkeyTracking.tgs')
.then(res => res.arrayBuffer())
.then(data => apiManager.gzipUncompress<string>(data, true))
.then(str => LottieLoader.loadAnimation({
.then(str => LottieLoader.loadAnimationWorker({
container: imageDiv,
renderer: 'svg',
loop: false,
autoplay: false,
animationData: JSON.parse(str),
rendererSettings: {
className: 'monkey-tracking'
}
width: 166,
height: 166
}))
.then(_animation => {
animation = _animation;
animation.setSpeed(1);
//console.log(animation.getDuration(), animation.getDuration(true));
mTrackingSvg = imageDiv.querySelector('.monkey-tracking');
if(!codeInput.value.length) {
mTrackingSvg.style.display = 'none';
animation.canvas.style.display = 'none';
}
animation.addEventListener('enterFrame', (e: any) => {
//console.log('enterFrame', e, needFrame);
let currentFrame = Math.round(e.currentTime);
animation.addListener('enterFrame', currentFrame => {
console.log('enterFrame', currentFrame, needFrame);
//let currentFrame = Math.round(e.currentTime);
if((e.direction == 1 && currentFrame >= needFrame) ||
(e.direction == -1 && currentFrame <= needFrame)) {
if((animation.direction == 1 && currentFrame >= needFrame) ||
(animation.direction == -1 && currentFrame <= needFrame)) {
animation.setSpeed(1);
animation.pause();
}
if(currentFrame == 0 && needFrame == 0 && mIdleSvg) {
mTrackingSvg.style.display = 'none';
mIdleSvg.style.display = '';
idleAnimation.stop();
idleAnimation.play();
if(currentFrame == 0 && needFrame == 0) {
animation.curFrame = 0;
if(idleAnimation) {
animation.canvas.style.display = 'none';
idleAnimation.canvas.style.display = '';
idleAnimation.restart();
}
}
});
//console.log(animation.getDuration(), animation.getDuration(true));
})
]);
};

View File

@ -3,14 +3,14 @@ import pageIm from './pageIm';
//import apiManager from '../lib/mtproto/apiManager';
import { putPreloader } from '../components/misc';
import LottieLoader from '../lib/lottieLoader';
import LottieLoader, { RLottiePlayer } from '../lib/lottieLoader';
//import passwordManager from '../lib/mtproto/passwordManager';
import apiManager from '../lib/mtproto/mtprotoworker';
import Page from './page';
let onFirstMount = (): Promise<any> => {
let needFrame = 0;
let animation: /* AnimationItem */any = undefined;
let animation: RLottiePlayer;
let passwordVisible = false;
@ -91,29 +91,29 @@ let onFirstMount = (): Promise<any> => {
}); */
return Promise.all([
LottieLoader.loadLottie(),
LottieLoader.loadLottieWorkers(),
fetch('assets/img/TwoFactorSetupMonkeyClose.tgs')
.then(res => res.arrayBuffer())
.then(data => apiManager.gzipUncompress<string>(data, true))
.then(str => LottieLoader.loadAnimation({
.then(str => LottieLoader.loadAnimationWorker({
container: page.pageEl.querySelector('.auth-image'),
renderer: 'svg',
loop: false,
autoplay: false,
animationData: JSON.parse(str)
animationData: JSON.parse(str),
width: 166,
height: 166
}))
.then(_animation => {
animation = _animation;
animation.addEventListener('enterFrame', (e: any) => {
animation.addListener('enterFrame', currentFrame => {
//console.log('enterFrame', e, needFrame);
let currentFrame = Math.round(e.currentTime);
if((e.direction == 1 && currentFrame >= needFrame) ||
(e.direction == -1 && currentFrame <= needFrame)) {
if((animation.direction == 1 && currentFrame >= needFrame) ||
(animation.direction == -1 && currentFrame <= needFrame)) {
animation.setSpeed(1);
animation.pause();
}
}
});
needFrame = 49;

View File

@ -56,6 +56,7 @@ let onFirstMount = async() => {
page.pageEl.querySelector('.a-qr').addEventListener('click', () => {
pageSignIn.mount();
stop = true;
});
const results = await Promise.all([
@ -66,98 +67,112 @@ let onFirstMount = async() => {
let stop = false;
document.addEventListener('user_auth', () => {
stop = true;
cachedPromise = null;
}, {once: true});
let options: {dcID?: number} = {};
let options: {dcID?: number, ignoreErrors: true} = {ignoreErrors: true};
let prevToken: Uint8Array;
do {
if(stop) {
break;
}
return async() => {
stop = false;
try {
let loginToken: LoginToken | LoginTokenMigrateTo | LoginTokenSuccess = await apiManager.invokeApi('auth.exportLoginToken', {
api_id: App.id,
api_hash: App.hash,
except_ids: []
}/* , options */);
if(loginToken._ == 'auth.loginTokenMigrateTo') {
if(!options.dcID) {
options.dcID = loginToken.dc_id;
apiManager.setBaseDcID(loginToken.dc_id);
//continue;
}
loginToken = await apiManager.invokeApi('auth.importLoginToken', {
token: loginToken.token
}, options) as LoginToken;
}
if(loginToken._ == 'auth.loginTokenSuccess') {
let authorization = loginToken.authorization;
apiManager.setUserAuth({
id: authorization.user.id
});
pageIm.mount();
do {
if(stop) {
break;
}
/* // to base64
var decoder = new TextDecoder('utf8');
var b64encoded = btoa(String.fromCharCode.apply(null, [...loginToken.token])); */
if(!prevToken || !bytesCmp(prevToken, loginToken.token)) {
prevToken = loginToken.token;
let encoded = bytesToBase64(loginToken.token);
let url = "tg://login?token=" + encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=+$/, "");
imageDiv.innerHTML = '';
const qrCode = new QRCodeStyling({
width: 166,
height: 166,
data: url,
image: "assets/img/logo_padded.svg",
dotsOptions: {
color: "#000000",
type: "rounded"
},
imageOptions: {
imageSize: .75
},
backgroundOptions: {
color: "#ffffff"
},
qrOptions: {
errorCorrectionLevel: "L"
try {
let loginToken: LoginToken | LoginTokenMigrateTo | LoginTokenSuccess = await apiManager.invokeApi('auth.exportLoginToken', {
api_id: App.id,
api_hash: App.hash,
except_ids: []
}, {ignoreErrors: true});
if(loginToken._ == 'auth.loginTokenMigrateTo') {
if(!options.dcID) {
options.dcID = loginToken.dc_id;
apiManager.setBaseDcID(loginToken.dc_id);
//continue;
}
});
qrCode.append(imageDiv);
}
let timestamp = Date.now() / 1000;
let diff = loginToken.expires - timestamp - serverTimeManager.serverTimeOffset;
await new Promise((resolve, reject) => setTimeout(resolve, diff > 5 ? 5e3 : 1e3 * diff | 0));
} catch(err) {
switch(err.type) {
case 'SESSION_PASSWORD_NEEDED':
console.warn('pageSignQR: SESSION_PASSWORD_NEEDED');
err.handled = true;
pagePassword.mount();
break;
default:
console.error('pageSignQR: default error:', err);
loginToken = await apiManager.invokeApi('auth.importLoginToken', {
token: loginToken.token
}, options) as LoginToken;
}
if(loginToken._ == 'auth.loginTokenSuccess') {
let authorization = loginToken.authorization;
apiManager.setUserAuth({
id: authorization.user.id
});
pageIm.mount();
break;
}
/* // to base64
var decoder = new TextDecoder('utf8');
var b64encoded = btoa(String.fromCharCode.apply(null, [...loginToken.token])); */
if(!prevToken || !bytesCmp(prevToken, loginToken.token)) {
prevToken = loginToken.token;
let encoded = bytesToBase64(loginToken.token);
let url = "tg://login?token=" + encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=+$/, "");
imageDiv.innerHTML = '';
const qrCode = new QRCodeStyling({
width: 166,
height: 166,
data: url,
image: "assets/img/logo_padded.svg",
dotsOptions: {
color: "#000000",
type: "rounded"
},
imageOptions: {
imageSize: .75
},
backgroundOptions: {
color: "#ffffff"
},
qrOptions: {
errorCorrectionLevel: "L"
}
});
qrCode.append(imageDiv);
}
let timestamp = Date.now() / 1000;
let diff = loginToken.expires - timestamp - serverTimeManager.serverTimeOffset;
await new Promise((resolve, reject) => setTimeout(resolve, diff > 5 ? 5e3 : 1e3 * diff | 0));
} catch(err) {
switch(err.type) {
case 'SESSION_PASSWORD_NEEDED':
console.warn('pageSignQR: SESSION_PASSWORD_NEEDED');
err.handled = true;
pagePassword.mount();
stop = true;
cachedPromise = null;
break;
default:
console.error('pageSignQR: default error:', err);
break;
}
}
}
} while(true);
} while(true);
};
};
let cachedPromise: Promise<() => Promise<void>>;
const page = new Page('page-signQR', true, () => {
onFirstMount();
return cachedPromise;
}, () => {
console.log('onMount');
if(!cachedPromise) cachedPromise = onFirstMount();
cachedPromise.then(func => {
func();
});
});
export default page;

View File

@ -23,12 +23,8 @@ class PagesManager {
this.selectTab(id);
// это нужно чтобы ресайзнуть канвас (из-за скрытого рендера будет 0х0)
if(this.pageID != -1) {
lottieLoader.loadLottie().then(() => {
// @ts-ignore
lottieLoader.lottie.resize();
});
if(this.pageID != -1 && id > 1) {
lottieLoader.loadLottieWorkers();
}
this.pageID = id;

View File

@ -433,10 +433,10 @@
max-width: 451px;
max-height: none;
> div {
.album-item {
background-color: #000;
background-size: cover;
background-position: center center;
/* background-position: center center; */
/* flex: 1 0 auto; */
max-width: 100%;
cursor: pointer;
@ -555,6 +555,38 @@
}
}
&.is-square-photo {
.bubble__container {
width: fit-content;
}
.box.web {
.quote {
display: flex;
}
.preview {
-webkit-box-ordinal-group: 3;
order: 2;
flex-shrink: 0;
max-width: 5rem;
max-height: 5rem;
}
.quote-text {
-webkit-box-ordinal-group: 2;
order: 1;
padding-right: 1rem;
}
}
}
&.is-vertical-photo {
.bubble__container {
width: fit-content;
}
}
.reply {
max-width: 300px;
margin-bottom: 6px;
@ -908,7 +940,7 @@
}
&:not(.sticker):not(.emoji-big) {
&.hide-name, &:not(.is-group-first) {
&.hide-name, &:not(.is-group-first), &.is-out {
.reply {
margin-top: 6px;
}

View File

@ -33,6 +33,7 @@
video {
max-height: none;
max-width: none;
object-fit: contain;
}
}

View File

@ -209,4 +209,52 @@ $move-duration: .35s;
&-mover.active &-aspecter {
transition: $open-duration all;
}
&-whole {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: fixed!important;
display: block;
z-index: 4;
visibility: hidden;
transition: visibility 0s $open-duration;
&.active {
visibility: visible;
transition-delay: 0s;
.overlays {
opacity: 1;
visibility: visible;
-webkit-transition: opacity $open-duration 0s, visibility 0s 0s;
-moz-transition: opacity $open-duration 0s, visibility 0s 0s;
transition: opacity $open-duration 0s, visibility 0s 0s;
}
}
}
&-switchers {
position: relative;
width: $large-screen;
height: 100%;
margin: 0 auto;
}
}
.overlays {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: fixed!important;
background-color: rgba(0, 0, 0, .2);
z-index: 4;
//display: none;
opacity: 0;
visibility: hidden;
-webkit-transition: opacity $open-duration 0s, visibility 0s $open-duration;
-moz-transition: opacity $open-duration 0s, visibility 0s $open-duration;
transition: opacity $open-duration 0s, visibility 0s $open-duration;
}

View File

@ -1336,30 +1336,6 @@ img.emoji {
}
}
.overlays {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: fixed!important;
background-color: rgba(0, 0, 0, .2);
z-index: 4;
//display: none;
opacity: 0;
visibility: hidden;
-webkit-transition: opacity 0.2s 0s, visibility 0s 0.2s;
-moz-transition: opacity 0.2s 0s, visibility 0s 0.2s;
transition: opacity 0.2s 0s, visibility 0s 0.2s;
&.active {
opacity: 1;
visibility: visible;
-webkit-transition: opacity 0.2s 0s, visibility 0s 0s;
-moz-transition: opacity 0.2s 0s, visibility 0s 0s;
transition: opacity 0.2s 0s, visibility 0s 0s;
}
}
[contenteditable] {
-webkit-user-select: text;
user-select: text;

View File

@ -1,7 +1,7 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
let allowedIPs = ['195.66.140.39', '192.168.31.144', '127.0.0.1', '192.168.31.1', '192.168.31.192', '192.168.0.111', '192.168.0.105'];
let allowedIPs = ['195.66.140.39', '192.168.31.144', '127.0.0.1', '192.168.31.1', '192.168.31.192', '192.168.0.111', '192.168.0.105', '192.168.0.108'];
const opts = {
MTPROTO_WORKER: true,
@ -66,8 +66,8 @@ module.exports = {
//entry: './src/index.ts',
entry: {
index: './src/index.ts',
webp: './src/lib/webp.ts',
lottie: './src/lib/lottie.ts'
webp: './src/lib/webp.ts'/* ,
lottie: './src/lib/lottie.ts' */
},
/* entry: {
index: './src/index.ts',
@ -129,7 +129,7 @@ module.exports = {
minifyURLs: true
}, */
chunks: "all",
excludeChunks: ['npm.webp-hero', 'npm.lottie-web']
excludeChunks: ['npm.webp-hero'/* , 'npm.lottie-web' */]
})
],
};

View File

@ -59,7 +59,13 @@ module.exports = merge(common, {
files.forEach(file => {
//console.log('to unlink 1:', file);
if(file.includes('mitm.') || file.includes('sw.js') || file.includes('.xml') || file.includes('.webmanifest')) return;
if(file.includes('mitm.')
|| file.includes('sw.js')
|| file.includes('.xml')
|| file.includes('.webmanifest')
|| file.includes('.wasm')
|| file.includes('rlottie')
|| file.includes('pako')) return;
let p = path.resolve(buildDir + file);
if(!newlyCreatedAssets[file] && ['.gz', '.js'].find(ext => file.endsWith(ext)) !== undefined) {