A lot of changes:
Right sidebar animation Fix animations speed with translate3d Folders tabs scroll Fix ripple animation Right sidebar translateZ blink fix Misc
This commit is contained in:
parent
ae193a10db
commit
f041d1bd69
|
@ -11,7 +11,8 @@
|
|||
"build:dev": "webpack --config webpack.dev.js",
|
||||
"test": "jest --config=jest.config.js",
|
||||
"profile": "webpack --profile --json > stats.json --config webpack.prod.js",
|
||||
"profile:dev": "webpack --profile --json > stats.json --config webpack.dev.js"
|
||||
"profile:dev": "webpack --profile --json > stats.json --config webpack.dev.js",
|
||||
"whybundled": "npm run profile; whybundled stats.json"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
<svg width="9" height="20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><filter x="-50%" y="-14.7%" width="200%" height="141.2%" filterUnits="objectBoundingBox" id="a"><feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0" in="shadowBlurOuter1"/></filter><path d="M3 17h6V0c-.193 2.84-.876 5.767-2.05 8.782-.904 2.325-2.446 4.485-4.625 6.48A1 1 0 003 17z" id="b"/></defs><g fill="none" fill-rule="evenodd"><use fill="#000" filter="url(#a)" xlink:href="#b"/><use fill="#FFF" xlink:href="#b"/></g></svg>
|
After Width: | Height: | Size: 730 B |
|
@ -0,0 +1 @@
|
|||
<svg width='9' height='20' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'><defs><filter x='-50%' y='-14.7%' width='200%' height='141.2%' filterUnits='objectBoundingBox' id='a'><feOffset dy='1' in='SourceAlpha' result='shadowOffsetOuter1'/><feGaussianBlur stdDeviation='1' in='shadowOffsetOuter1' result='shadowBlurOuter1'/><feColorMatrix values='0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0' in='shadowBlurOuter1'/></filter><path d='M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z' id='b'/></defs><g fill='none' fill-rule='evenodd'><use fill='#000' filter='url(#a)' xlink:href='#b'/><use fill='#EEFFDE' xlink:href='#b'/></g></svg>
|
After Width: | Height: | Size: 730 B |
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
public/sw.js.gz
BIN
public/sw.js.gz
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
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.
|
@ -0,0 +1,182 @@
|
|||
importScripts('rlottie-wasm.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);
|
||||
|
||||
reply('loaded', this.reqId, this.frameCount, this.fps);
|
||||
}
|
||||
|
||||
RLottieItem.prototype.init = function(jsString) {
|
||||
try {
|
||||
this.handle = RLottieWorker.Api.init();
|
||||
|
||||
this.stringOnWasmHeap = allocate(intArrayFromString(jsString), 'i8', 0);
|
||||
|
||||
this.frameCount = RLottieWorker.Api.loadFromData(this.handle, this.stringOnWasmHeap);
|
||||
|
||||
RLottieWorker.Api.resize(this.handle, this.width, this.height);
|
||||
} catch(e) {
|
||||
console.error('init RLottieItem error:', e);
|
||||
}
|
||||
};
|
||||
|
||||
RLottieItem.prototype.render = function(frameNo, clamped) {
|
||||
if(this.dead) return;
|
||||
//return;
|
||||
|
||||
if(this.frameCount < frameNo || frameNo < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
RLottieWorker.Api.render(this.handle, frameNo);
|
||||
|
||||
var bufferPointer = RLottieWorker.Api.buffer(this.handle);
|
||||
|
||||
var data = Module.HEAPU8.subarray(bufferPointer, bufferPointer + (this.width * this.height * 4));
|
||||
|
||||
if(!clamped) {
|
||||
clamped = new Uint8ClampedArray(data);
|
||||
} else {
|
||||
clamped.set(data);
|
||||
}
|
||||
|
||||
reply('frame', this.reqId, frameNo, clamped);
|
||||
} catch(e) {
|
||||
console.error('Render error:', e);
|
||||
this.dead = true;
|
||||
}
|
||||
};
|
||||
|
||||
RLottieItem.prototype.destroy = function() {
|
||||
this.dead = true;
|
||||
|
||||
RLottieWorker.Api.destroy(this.handle);
|
||||
};
|
||||
|
||||
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']),
|
||||
render: Module.cwrap('lottie_render', '', ['number', 'number']),
|
||||
loadFromData: Module.cwrap('lottie_load_from_data', 'number', ['number', 'number']),
|
||||
};
|
||||
}
|
||||
|
||||
worker.init = function() {
|
||||
initApi();
|
||||
reply('ready');
|
||||
};
|
||||
|
||||
return worker;
|
||||
}());
|
||||
|
||||
Module.onRuntimeInitialized = function() {
|
||||
RLottieWorker.init();
|
||||
};
|
||||
|
||||
var items = {};
|
||||
var queryableFunctions = {
|
||||
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, clamped) {
|
||||
//console.log('worker renderFrame', reqId, frameNo, clamped);
|
||||
items[reqId].render(frameNo, clamped);
|
||||
}
|
||||
};
|
||||
|
||||
function defaultReply(message) {
|
||||
// your default PUBLIC function executed only when main page calls the queryableWorker.postMessage() method directly
|
||||
// do something
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when run in WebKit derived browsers.
|
||||
* This is used as a workaround for a memory leak in Safari caused by using Transferable objects to
|
||||
* transfer data between WebWorkers and the main thread.
|
||||
* https://github.com/mapbox/mapbox-gl-js/issues/8771
|
||||
*
|
||||
* This should be removed once the underlying Safari issue is fixed.
|
||||
*
|
||||
* @private
|
||||
* @param scope {WindowOrWorkerGlobalScope} Since this function is used both on the main thread and WebWorker context,
|
||||
* let the calling scope pass in the global scope object.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
var _isSafari = null;
|
||||
function isSafari(scope) {
|
||||
if(_isSafari == null) {
|
||||
var userAgent = scope.navigator ? scope.navigator.userAgent : null;
|
||||
_isSafari = !!scope.safari ||
|
||||
!!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || (!!userAgent.match('Safari') && !userAgent.match('Chrome'))));
|
||||
}
|
||||
return _isSafari;
|
||||
}
|
||||
|
||||
function reply() {
|
||||
if(arguments.length < 1) {
|
||||
throw new TypeError('reply - not enough arguments');
|
||||
}
|
||||
|
||||
//if(arguments[0] == 'frame') return;
|
||||
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if(isSafari(self)) {
|
||||
postMessage({ 'queryMethodListener': arguments[0], 'queryMethodArguments': args });
|
||||
} else {
|
||||
var transfer = [];
|
||||
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);
|
||||
//args[i] = args[i].buffer;
|
||||
}
|
||||
}
|
||||
|
||||
postMessage({ 'queryMethodListener': arguments[0], 'queryMethodArguments': args }, transfer);
|
||||
}
|
||||
|
||||
//postMessage({ 'queryMethodListener': arguments[0], 'queryMethodArguments': Array.prototype.slice.call(arguments, 1) });
|
||||
//console.error(transfer, args);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.WaveWorker=t():e.WaveWorker=t()}("undefined"!=typeof self?self:this,(function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var s=t[n]={i:n,l:!1,exports:{}};return e[n].call(s.exports,s,s.exports,r),s.l=!0,s.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)r.d(n,s,function(t){return e[t]}.bind(null,s));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";(function(t){var r;t.onmessage=function(e){switch(e.data.command){case"encode":r&&r.record(e.data.buffers);break;case"done":r&&(r.requestData(),r=null);break;case"close":t.close();break;case"init":r=new n(e.data),t.postMessage({message:"ready"})}};var n=function(e){if(!(e=Object.assign({wavBitDepth:16},e)).wavSampleRate)throw new Error("wavSampleRate value is required to record. NOTE: Audio is not resampled!");if(-1===[8,16,24,32].indexOf(e.wavBitDepth))throw new Error("Only 8, 16, 24 and 32 bits per sample are supported");this.bitDepth=e.wavBitDepth,this.sampleRate=e.wavSampleRate,this.recordedBuffers=[],this.bytesPerSample=this.bitDepth/8};n.prototype.record=function(e){this.numberOfChannels=this.numberOfChannels||e.length;for(var t=e[0].length,r=new Uint8Array(t*this.numberOfChannels*this.bytesPerSample),n=0;n<t;n++)for(var s=0;s<this.numberOfChannels;s++){var a=(n*this.numberOfChannels+s)*this.bytesPerSample,i=Math.max(-1,Math.min(1,e[s][n]));switch(this.bytesPerSample){case 4:i=2147483647.5*i-.5,r[a]=i,r[a+1]=i>>8,r[a+2]=i>>16,r[a+3]=i>>24;break;case 3:i=8388607.5*i-.5,r[a]=i,r[a+1]=i>>8,r[a+2]=i>>16;break;case 2:i=32767.5*i-.5,r[a]=i,r[a+1]=i>>8;break;case 1:r[a]=127.5*(i+1);break;default:throw new Error("Only 8, 16, 24 and 32 bits per sample are supported")}}this.recordedBuffers.push(r)},n.prototype.requestData=function(){var e=this.recordedBuffers[0].length,r=this.recordedBuffers.length*e,n=new Uint8Array(44+r),s=new DataView(n.buffer);s.setUint32(0,1380533830,!1),s.setUint32(4,36+r,!0),s.setUint32(8,1463899717,!1),s.setUint32(12,1718449184,!1),s.setUint32(16,16,!0),s.setUint16(20,1,!0),s.setUint16(22,this.numberOfChannels,!0),s.setUint32(24,this.sampleRate,!0),s.setUint32(28,this.sampleRate*this.bytesPerSample*this.numberOfChannels,!0),s.setUint16(32,this.bytesPerSample*this.numberOfChannels,!0),s.setUint16(34,this.bitDepth,!0),s.setUint32(36,1684108385,!1),s.setUint32(40,r,!0);for(var a=0;a<this.recordedBuffers.length;a++)n.set(this.recordedBuffers[a],a*e+44);t.postMessage({message:"page",page:n},[n.buffer]),t.postMessage({message:"done"})},e.exports=n}).call(this,r(1))},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r}])}));
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
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.
|
@ -0,0 +1,182 @@
|
|||
importScripts('rlottie-wasm.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);
|
||||
|
||||
reply('loaded', this.reqId, this.frameCount, this.fps);
|
||||
}
|
||||
|
||||
RLottieItem.prototype.init = function(jsString) {
|
||||
try {
|
||||
this.handle = RLottieWorker.Api.init();
|
||||
|
||||
this.stringOnWasmHeap = allocate(intArrayFromString(jsString), 'i8', 0);
|
||||
|
||||
this.frameCount = RLottieWorker.Api.loadFromData(this.handle, this.stringOnWasmHeap);
|
||||
|
||||
RLottieWorker.Api.resize(this.handle, this.width, this.height);
|
||||
} catch(e) {
|
||||
console.error('init RLottieItem error:', e);
|
||||
}
|
||||
};
|
||||
|
||||
RLottieItem.prototype.render = function(frameNo, clamped) {
|
||||
if(this.dead) return;
|
||||
//return;
|
||||
|
||||
if(this.frameCount < frameNo || frameNo < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
RLottieWorker.Api.render(this.handle, frameNo);
|
||||
|
||||
var bufferPointer = RLottieWorker.Api.buffer(this.handle);
|
||||
|
||||
var data = Module.HEAPU8.subarray(bufferPointer, bufferPointer + (this.width * this.height * 4));
|
||||
|
||||
if(!clamped) {
|
||||
clamped = new Uint8ClampedArray(data);
|
||||
} else {
|
||||
clamped.set(data);
|
||||
}
|
||||
|
||||
reply('frame', this.reqId, frameNo, clamped);
|
||||
} catch(e) {
|
||||
console.error('Render error:', e);
|
||||
this.dead = true;
|
||||
}
|
||||
};
|
||||
|
||||
RLottieItem.prototype.destroy = function() {
|
||||
this.dead = true;
|
||||
|
||||
RLottieWorker.Api.destroy(this.handle);
|
||||
};
|
||||
|
||||
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']),
|
||||
render: Module.cwrap('lottie_render', '', ['number', 'number']),
|
||||
loadFromData: Module.cwrap('lottie_load_from_data', 'number', ['number', 'number']),
|
||||
};
|
||||
}
|
||||
|
||||
worker.init = function() {
|
||||
initApi();
|
||||
reply('ready');
|
||||
};
|
||||
|
||||
return worker;
|
||||
}());
|
||||
|
||||
Module.onRuntimeInitialized = function() {
|
||||
RLottieWorker.init();
|
||||
};
|
||||
|
||||
var items = {};
|
||||
var queryableFunctions = {
|
||||
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, clamped) {
|
||||
//console.log('worker renderFrame', reqId, frameNo, clamped);
|
||||
items[reqId].render(frameNo, clamped);
|
||||
}
|
||||
};
|
||||
|
||||
function defaultReply(message) {
|
||||
// your default PUBLIC function executed only when main page calls the queryableWorker.postMessage() method directly
|
||||
// do something
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when run in WebKit derived browsers.
|
||||
* This is used as a workaround for a memory leak in Safari caused by using Transferable objects to
|
||||
* transfer data between WebWorkers and the main thread.
|
||||
* https://github.com/mapbox/mapbox-gl-js/issues/8771
|
||||
*
|
||||
* This should be removed once the underlying Safari issue is fixed.
|
||||
*
|
||||
* @private
|
||||
* @param scope {WindowOrWorkerGlobalScope} Since this function is used both on the main thread and WebWorker context,
|
||||
* let the calling scope pass in the global scope object.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
var _isSafari = null;
|
||||
function isSafari(scope) {
|
||||
if(_isSafari == null) {
|
||||
var userAgent = scope.navigator ? scope.navigator.userAgent : null;
|
||||
_isSafari = !!scope.safari ||
|
||||
!!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || (!!userAgent.match('Safari') && !userAgent.match('Chrome'))));
|
||||
}
|
||||
return _isSafari;
|
||||
}
|
||||
|
||||
function reply() {
|
||||
if(arguments.length < 1) {
|
||||
throw new TypeError('reply - not enough arguments');
|
||||
}
|
||||
|
||||
//if(arguments[0] == 'frame') return;
|
||||
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if(isSafari(self)) {
|
||||
postMessage({ 'queryMethodListener': arguments[0], 'queryMethodArguments': args });
|
||||
} else {
|
||||
var transfer = [];
|
||||
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);
|
||||
//args[i] = args[i].buffer;
|
||||
}
|
||||
}
|
||||
|
||||
postMessage({ 'queryMethodListener': arguments[0], 'queryMethodArguments': args }, transfer);
|
||||
}
|
||||
|
||||
//postMessage({ 'queryMethodListener': arguments[0], 'queryMethodArguments': Array.prototype.slice.call(arguments, 1) });
|
||||
//console.error(transfer, args);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.WaveWorker=t():e.WaveWorker=t()}("undefined"!=typeof self?self:this,(function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var s=t[n]={i:n,l:!1,exports:{}};return e[n].call(s.exports,s,s.exports,r),s.l=!0,s.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)r.d(n,s,function(t){return e[t]}.bind(null,s));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";(function(t){var r;t.onmessage=function(e){switch(e.data.command){case"encode":r&&r.record(e.data.buffers);break;case"done":r&&(r.requestData(),r=null);break;case"close":t.close();break;case"init":r=new n(e.data),t.postMessage({message:"ready"})}};var n=function(e){if(!(e=Object.assign({wavBitDepth:16},e)).wavSampleRate)throw new Error("wavSampleRate value is required to record. NOTE: Audio is not resampled!");if(-1===[8,16,24,32].indexOf(e.wavBitDepth))throw new Error("Only 8, 16, 24 and 32 bits per sample are supported");this.bitDepth=e.wavBitDepth,this.sampleRate=e.wavSampleRate,this.recordedBuffers=[],this.bytesPerSample=this.bitDepth/8};n.prototype.record=function(e){this.numberOfChannels=this.numberOfChannels||e.length;for(var t=e[0].length,r=new Uint8Array(t*this.numberOfChannels*this.bytesPerSample),n=0;n<t;n++)for(var s=0;s<this.numberOfChannels;s++){var a=(n*this.numberOfChannels+s)*this.bytesPerSample,i=Math.max(-1,Math.min(1,e[s][n]));switch(this.bytesPerSample){case 4:i=2147483647.5*i-.5,r[a]=i,r[a+1]=i>>8,r[a+2]=i>>16,r[a+3]=i>>24;break;case 3:i=8388607.5*i-.5,r[a]=i,r[a+1]=i>>8,r[a+2]=i>>16;break;case 2:i=32767.5*i-.5,r[a]=i,r[a+1]=i>>8;break;case 1:r[a]=127.5*(i+1);break;default:throw new Error("Only 8, 16, 24 and 32 bits per sample are supported")}}this.recordedBuffers.push(r)},n.prototype.requestData=function(){var e=this.recordedBuffers[0].length,r=this.recordedBuffers.length*e,n=new Uint8Array(44+r),s=new DataView(n.buffer);s.setUint32(0,1380533830,!1),s.setUint32(4,36+r,!0),s.setUint32(8,1463899717,!1),s.setUint32(12,1718449184,!1),s.setUint32(16,16,!0),s.setUint16(20,1,!0),s.setUint16(22,this.numberOfChannels,!0),s.setUint32(24,this.sampleRate,!0),s.setUint32(28,this.sampleRate*this.bytesPerSample*this.numberOfChannels,!0),s.setUint16(32,this.bytesPerSample*this.numberOfChannels,!0),s.setUint16(34,this.bitDepth,!0),s.setUint32(36,1684108385,!1),s.setUint32(40,r,!0);for(var a=0;a<this.recordedBuffers.length;a++)n.set(this.recordedBuffers[a],a*e+44);t.postMessage({message:"page",page:n},[n.buffer]),t.postMessage({message:"done"})},e.exports=n}).call(this,r(1))},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r}])}));
|
|
@ -1,8 +1,8 @@
|
|||
import { $rootScope } from "../lib/utils";
|
||||
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
||||
import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
|
||||
import { isSafari } from "../lib/config";
|
||||
import { CancellablePromise, deferredPromise } from "../lib/polyfill";
|
||||
import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise";
|
||||
import { isSafari } from "../helpers/userAgent";
|
||||
|
||||
// TODO: если удалить сообщение, и при этом аудио будет играть - оно не остановится, и можно будет по нему перейти вникуда
|
||||
|
||||
|
|
|
@ -307,7 +307,7 @@ export class AppSelectPeers {
|
|||
|
||||
this.selectedContainer.insertBefore(div, this.input);
|
||||
//this.selectedScrollable.scrollTop = this.selectedScrollable.scrollHeight;
|
||||
this.selectedScrollable.scrollTo(this.selectedScrollable.scrollHeight, true, true);
|
||||
this.selectedScrollable.scrollTo(this.selectedScrollable.scrollHeight, 'top', true, true);
|
||||
this.onChange && this.onChange(this.selected.size);
|
||||
|
||||
return div;
|
||||
|
|
|
@ -5,8 +5,9 @@ import ProgressivePreloader from "./preloader";
|
|||
import { MediaProgressLine } from "../lib/mediaPlayer";
|
||||
import appMediaPlaybackController from "./appMediaPlaybackController";
|
||||
import { DocumentAttribute } from "../layer";
|
||||
import { mediaSizes, isSafari } from "../lib/config";
|
||||
import { Download } from "../lib/appManagers/appDownloadManager";
|
||||
import mediaSizes from "../helpers/mediaSizes";
|
||||
import { isSafari } from "../helpers/userAgent";
|
||||
|
||||
// https://github.com/LonamiWebs/Telethon/blob/4393ec0b83d511b6a20d8a20334138730f084375/telethon/utils.py#L1285
|
||||
export function decodeWaveform(waveform: Uint8Array | number[]) {
|
||||
|
@ -121,7 +122,7 @@ function wrapVoiceMessage(doc: MyDocument, audioEl: AudioElement) {
|
|||
|
||||
let start = () => {
|
||||
clearInterval(interval);
|
||||
interval = setInterval(() => {
|
||||
interval = window.setInterval(() => {
|
||||
if(lastIndex > svg.childElementCount || isNaN(audio.duration) || audio.paused) {
|
||||
clearInterval(interval);
|
||||
return;
|
||||
|
|
|
@ -34,11 +34,16 @@ export class ChatAudio {
|
|||
|
||||
this.close.addEventListener('click', (e) => {
|
||||
cancelEvent(e);
|
||||
const scrollTop = appImManager.scrollable.scrollTop;
|
||||
this.container.style.display = 'none';
|
||||
this.container.parentElement.classList.remove('is-audio-shown');
|
||||
appImManager.topbar.classList.remove('is-audio-shown');
|
||||
if(this.toggle.classList.contains('flip-icon')) {
|
||||
appMediaPlaybackController.toggle();
|
||||
}
|
||||
|
||||
if(!appImManager.topbar.classList.contains('is-pinned-shown')) {
|
||||
appImManager.scrollable.scrollTop = scrollTop - height;
|
||||
}
|
||||
});
|
||||
|
||||
this.toggle.addEventListener('click', (e) => {
|
||||
|
@ -46,6 +51,8 @@ export class ChatAudio {
|
|||
appMediaPlaybackController.toggle();
|
||||
});
|
||||
|
||||
const height = 52;
|
||||
|
||||
$rootScope.$on('audio_play', (e) => {
|
||||
const {doc, mid} = e.detail;
|
||||
|
||||
|
@ -68,8 +75,11 @@ export class ChatAudio {
|
|||
if(this.container.style.display) {
|
||||
const scrollTop = appImManager.scrollable.scrollTop;
|
||||
this.container.style.display = '';
|
||||
this.container.parentElement.classList.add('is-audio-shown');
|
||||
appImManager.scrollable.scrollTop = scrollTop;
|
||||
appImManager.topbar.classList.add('is-audio-shown');
|
||||
|
||||
if(!appImManager.topbar.classList.contains('is-pinned-shown')) {
|
||||
appImManager.scrollable.scrollTop = scrollTop + height;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { horizontalMenu } from "../horizontalMenu";
|
|||
import animationIntersector from "../animationIntersector";
|
||||
import appSidebarRight from "../../lib/appManagers/appSidebarRight";
|
||||
import appImManager from "../../lib/appManagers/appImManager";
|
||||
import Scrollable from "../scrollable_new";
|
||||
import Scrollable, { ScrollableX } from "../scrollable_new";
|
||||
import EmojiTab from "./tabs/emoji";
|
||||
import StickersTab from "./tabs/stickers";
|
||||
import StickyIntersector from "../stickyIntersector";
|
||||
|
@ -240,7 +240,7 @@ export class EmoticonsDropdown {
|
|||
//animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP);
|
||||
};
|
||||
|
||||
public static menuOnClick = (menu: HTMLUListElement, scroll: Scrollable, menuScroll?: Scrollable) => {
|
||||
public static menuOnClick = (menu: HTMLUListElement, scroll: Scrollable, menuScroll?: ScrollableX) => {
|
||||
let prevId = 0;
|
||||
let jumpedTo = -1;
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ export default class EmojiTab implements EmoticonsTab {
|
|||
//console.timeEnd('emojiParse');
|
||||
|
||||
const menu = this.content.previousElementSibling.firstElementChild as HTMLUListElement;
|
||||
const emojiScroll = this.scroll = new Scrollable(this.content, 'y', 'EMOJI', null);
|
||||
const emojiScroll = this.scroll = new Scrollable(this.content, 'EMOJI', null);
|
||||
|
||||
//emojiScroll.setVirtualContainer(emojiScroll.container);
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ export default class GifsTab implements EmoticonsTab {
|
|||
const gifsContainer = this.content.firstElementChild as HTMLDivElement;
|
||||
gifsContainer.addEventListener('click', EmoticonsDropdown.onMediaClick);
|
||||
|
||||
const scroll = new Scrollable(this.content, 'y', 'GIFS', null);
|
||||
const scroll = new Scrollable(this.content, 'GIFS', null);
|
||||
const masonry = new GifsMasonry(gifsContainer, EMOTICONSSTICKERGROUP, scroll);
|
||||
const preloader = putPreloader(this.content, true);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import emoticonsDropdown, { EmoticonsTab, EMOTICONSSTICKERGROUP, EmoticonsDropdown } from "..";
|
||||
import { StickerSet } from "../../../layer";
|
||||
import Scrollable from "../../scrollable_new";
|
||||
import Scrollable, { ScrollableX } from "../../scrollable_new";
|
||||
import { wrapSticker } from "../../wrappers";
|
||||
import appStickersManager from "../../../lib/appManagers/appStickersManager";
|
||||
import appDownloadManager from "../../../lib/appManagers/appDownloadManager";
|
||||
|
@ -230,7 +230,7 @@ export default class StickersTab implements EmoticonsTab {
|
|||
let menuWrapper = this.content.previousElementSibling as HTMLDivElement;
|
||||
this.menu = menuWrapper.firstElementChild.firstElementChild as HTMLUListElement;
|
||||
|
||||
let menuScroll = new Scrollable(menuWrapper, 'x');
|
||||
let menuScroll = new ScrollableX(menuWrapper);
|
||||
|
||||
let stickersDiv = document.createElement('div');
|
||||
stickersDiv.classList.add('stickers-categories');
|
||||
|
@ -274,7 +274,7 @@ export default class StickersTab implements EmoticonsTab {
|
|||
|
||||
stickersDiv.addEventListener('click', EmoticonsDropdown.onMediaClick);
|
||||
|
||||
this.scroll = new Scrollable(this.content, 'y', 'STICKERS', undefined, undefined, 2);
|
||||
this.scroll = new Scrollable(this.content, 'STICKERS', undefined, undefined, 2);
|
||||
this.scroll.setVirtualContainer(stickersDiv);
|
||||
|
||||
this.stickyIntersector = EmoticonsDropdown.menuOnClick(this.menu, this.scroll, menuScroll);
|
||||
|
|
|
@ -3,9 +3,9 @@ import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
|
|||
import { wrapVideo } from "./wrappers";
|
||||
import { renderImageFromUrl } from "./misc";
|
||||
import { LazyLoadQueueRepeat2 } from "./lazyLoadQueue";
|
||||
import { CancellablePromise, deferredPromise } from "../lib/polyfill";
|
||||
import animationIntersector from "./animationIntersector";
|
||||
import Scrollable from "./scrollable_new";
|
||||
import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise";
|
||||
|
||||
const width = 400;
|
||||
const maxSingleWidth = width - 100;
|
||||
|
|
|
@ -10,14 +10,15 @@ function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, t
|
|||
tabContent.style.transform = `translateX(-25%)`;
|
||||
prevTabContent.style.transform = `translateX(20%)`;
|
||||
} */
|
||||
const width = prevTabContent.getBoundingClientRect().width;
|
||||
if(toRight) {
|
||||
prevTabContent.style.filter = `brightness(80%)`;
|
||||
prevTabContent.style.transform = `translateX(-25%)`;
|
||||
tabContent.style.transform = `translateX(100%)`;
|
||||
prevTabContent.style.transform = `translate3d(${-width * .25}px, 0, 0)`;
|
||||
tabContent.style.transform = `translate3d(${width}px, 0, 0)`;
|
||||
} else {
|
||||
tabContent.style.filter = `brightness(80%)`;
|
||||
tabContent.style.transform = `translateX(-25%)`;
|
||||
prevTabContent.style.transform = `translateX(100%)`;
|
||||
tabContent.style.transform = `translate3d(${-width * .25}px, 0, 0)`;
|
||||
prevTabContent.style.transform = `translate3d(${width}px, 0, 0)`;
|
||||
}
|
||||
|
||||
tabContent.classList.add('active');
|
||||
|
@ -28,12 +29,13 @@ function slideNavigation(tabContent: HTMLElement, prevTabContent: HTMLElement, t
|
|||
}
|
||||
|
||||
function slideTabs(tabContent: HTMLElement, prevTabContent: HTMLElement, toRight: boolean) {
|
||||
const width = prevTabContent.getBoundingClientRect().width;
|
||||
if(toRight) {
|
||||
tabContent.style.transform = `translateX(100%)`;
|
||||
prevTabContent.style.transform = `translateX(-100%)`;
|
||||
tabContent.style.transform = `translate3d(${width}px, 0, 0)`;
|
||||
prevTabContent.style.transform = `translate3d(${-width}px, 0, 0)`;
|
||||
} else {
|
||||
tabContent.style.transform = `translateX(-100%)`;
|
||||
prevTabContent.style.transform = `translateX(100%)`;
|
||||
tabContent.style.transform = `translate3d(${-width}px, 0, 0)`;
|
||||
prevTabContent.style.transform = `translate3d(${width}px, 0, 0)`;
|
||||
}
|
||||
|
||||
tabContent.classList.add('active');
|
||||
|
@ -83,7 +85,7 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
|
|||
const _prevId = prevId;
|
||||
if(hideTimeouts.hasOwnProperty(id)) clearTimeout(hideTimeouts[id]);
|
||||
if(p/* && false */) {
|
||||
hideTimeouts[_prevId] = setTimeout(() => {
|
||||
hideTimeouts[_prevId] = window.setTimeout(() => {
|
||||
p.style.transform = '';
|
||||
p.style.filter = '';
|
||||
p.classList.remove('active');
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import Config, { touchSupport, isApple, mediaSizes } from "../lib/config";
|
||||
import mediaSizes from "../helpers/mediaSizes";
|
||||
import { isApple } from "../helpers/userAgent";
|
||||
import Config, { touchSupport } from "../lib/config";
|
||||
|
||||
export const loadedURLs: {[url: string]: boolean} = {};
|
||||
const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import appPollsManager, { PollResults, Poll } from "../lib/appManagers/appPollsManager";
|
||||
import { RichTextProcessor } from "../lib/richtextprocessor";
|
||||
import { findUpClassName, $rootScope, cancelEvent } from "../lib/utils";
|
||||
import { mediaSizes, touchSupport } from "../lib/config";
|
||||
import { touchSupport } from "../lib/config";
|
||||
import appSidebarRight from "../lib/appManagers/appSidebarRight";
|
||||
import appImManager from "../lib/appManagers/appImManager";
|
||||
import serverTimeManager from "../lib/mtproto/serverTimeManager";
|
||||
import { ripple } from "./ripple";
|
||||
import mediaSizes from "../helpers/mediaSizes";
|
||||
|
||||
let lineTotalLength = 0;
|
||||
const tailLength = 9;
|
||||
|
|
|
@ -46,7 +46,7 @@ export default class PopupCreatePoll extends PopupElement {
|
|||
|
||||
this.confirmBtn.addEventListener('click', this.onSubmitClick);
|
||||
|
||||
this.scrollable = new Scrollable(this.body, 'y', undefined);
|
||||
this.scrollable = new Scrollable(this.body);
|
||||
this.appendMoreField();
|
||||
}
|
||||
|
||||
|
@ -131,6 +131,6 @@ export default class PopupCreatePoll extends PopupElement {
|
|||
|
||||
this.questions.append(questionField);
|
||||
|
||||
this.scrollable.scrollTo(this.scrollable.scrollHeight, true, true);
|
||||
this.scrollable.scrollTo(this.scrollable.scrollHeight, 'top', true, true);
|
||||
}
|
||||
}
|
|
@ -64,7 +64,7 @@ export default class PopupStickers extends PopupElement {
|
|||
this.stickersFooter.innerText = 'Loading...';
|
||||
|
||||
this.body.append(div);
|
||||
const scrollable = new Scrollable(this.body, 'y', undefined);
|
||||
const scrollable = new Scrollable(this.body);
|
||||
this.body.append(this.stickersFooter);
|
||||
|
||||
// const editButton = document.createElement('button');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { isInDOM, cancelEvent } from "../lib/utils";
|
||||
import { CancellablePromise } from "../lib/polyfill";
|
||||
import { CancellablePromise } from "../helpers/cancellablePromise";
|
||||
|
||||
export default class ProgressivePreloader {
|
||||
public preloader: HTMLDivElement;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { touchSupport } from "../lib/config";
|
||||
import { findUpClassName } from "../lib/utils";
|
||||
|
||||
let rippleClickID = 0;
|
||||
export function ripple(elem: HTMLElement, callback: (id: number) => Promise<boolean | void> = () => Promise.resolve(), onEnd: (id: number) => void = null) {
|
||||
|
@ -28,6 +29,7 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
|
|||
//console.log('ripple drawRipple');
|
||||
|
||||
handler = () => {
|
||||
//return;
|
||||
let elapsedTime = Date.now() - startTime;
|
||||
if(elapsedTime < duration) {
|
||||
let delay = Math.max(duration - elapsedTime, duration / 2);
|
||||
|
@ -66,8 +68,8 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
|
|||
} */
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
span.classList.add('c-ripple__circle');
|
||||
let rect = r.getBoundingClientRect();
|
||||
span.classList.add('c-ripple__circle');
|
||||
|
||||
let clickX = clientX - rect.left;
|
||||
let clickY = clientY - rect.top;
|
||||
|
@ -110,7 +112,7 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
|
|||
|
||||
elem.addEventListener('touchstart', (e) => {
|
||||
//console.log('ripple touchstart', e);
|
||||
if(e.touches.length > 1 || ((e.target as HTMLElement).tagName == 'BUTTON' && e.target != elem)) {
|
||||
if(e.touches.length > 1 || ((e.target as HTMLElement).tagName == 'BUTTON' && e.target != elem) || findUpClassName(e.target as HTMLElement, 'c-ripple') != r) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -130,7 +132,9 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
|
|||
}, {passive: true});
|
||||
} else {
|
||||
elem.addEventListener('mousedown', (e) => {
|
||||
if(elem.dataset.ripple == '0') {
|
||||
//console.log('ripple mousedown', e, e.target, findUpClassName(e.target as HTMLElement, 'c-ripple') == r);
|
||||
|
||||
if(elem.dataset.ripple == '0' || findUpClassName(e.target as HTMLElement, 'c-ripple') != r) {
|
||||
return false;
|
||||
} else if(touchStartFired) {
|
||||
touchStartFired = false;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { logger, LogLevels } from "../lib/logger";
|
||||
import smoothscroll from '../vendor/smoothscroll';
|
||||
import { touchSupport, isSafari, mediaSizes } from "../lib/config";
|
||||
import { CancellablePromise, deferredPromise } from "../lib/polyfill";
|
||||
import smoothscroll, { SCROLL_TIME, SmoothScrollToOptions } from '../vendor/smoothscroll';
|
||||
import { touchSupport } from "../lib/config";
|
||||
//import { CancellablePromise, deferredPromise } from "../lib/polyfill";
|
||||
//import { isInDOM } from "../lib/utils";
|
||||
(window as any).__forceSmoothScrollPolyfill__ = true;
|
||||
smoothscroll.polyfill();
|
||||
smoothscroll();
|
||||
/*
|
||||
var el = $0;
|
||||
var height = 0;
|
||||
|
@ -48,10 +48,88 @@ const scrollsIntersector = new IntersectionObserver(entries => {
|
|||
}
|
||||
}); */
|
||||
|
||||
export default class Scrollable {
|
||||
//public container: HTMLDivElement;
|
||||
public overflowContainer: HTMLElement;
|
||||
export class ScrollableBase {
|
||||
protected log: ReturnType<typeof logger>;
|
||||
|
||||
protected onScroll: () => void;
|
||||
public getScrollValue: () => number;
|
||||
|
||||
public scrollLocked = 0;
|
||||
|
||||
constructor(public el: HTMLElement, logPrefix = '', public appendTo = el, public container: HTMLElement = document.createElement('div')) {
|
||||
this.container.classList.add('scrollable');
|
||||
|
||||
if(!appendTo) {
|
||||
this.appendTo = this.container;
|
||||
}
|
||||
|
||||
this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : ''), LogLevels.error);
|
||||
|
||||
if(el) {
|
||||
Array.from(el.children).forEach(c => this.container.append(c));
|
||||
|
||||
el.append(this.container);
|
||||
}
|
||||
//this.onScroll();
|
||||
}
|
||||
|
||||
protected setListeners() {
|
||||
window.addEventListener('resize', this.onScroll);
|
||||
this.container.addEventListener('scroll', this.onScroll, {passive: true, capture: true});
|
||||
}
|
||||
|
||||
public prepend(element: HTMLElement) {
|
||||
this.appendTo.prepend(element);
|
||||
}
|
||||
|
||||
public append(element: HTMLElement) {
|
||||
this.appendTo.append(element);
|
||||
}
|
||||
|
||||
public contains(element: Element) {
|
||||
return !!element.parentElement;
|
||||
}
|
||||
|
||||
public removeElement(element: Element) {
|
||||
element.remove();
|
||||
}
|
||||
|
||||
public scrollTo(value: number, side: 'top' | 'left', smooth = true, important = false, scrollTime = SCROLL_TIME) {
|
||||
if(this.scrollLocked && !important) return;
|
||||
|
||||
const scrollValue = this.getScrollValue();
|
||||
if(scrollValue == Math.floor(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.scrollLocked) clearTimeout(this.scrollLocked);
|
||||
/* else {
|
||||
this.scrollLockedPromise = deferredPromise<void>();
|
||||
} */
|
||||
|
||||
this.scrollLocked = window.setTimeout(() => {
|
||||
this.scrollLocked = 0;
|
||||
//this.scrollLockedPromise.resolve();
|
||||
//this.onScroll();
|
||||
this.container.dispatchEvent(new CustomEvent('scroll'));
|
||||
}, scrollTime);
|
||||
|
||||
const options: SmoothScrollToOptions = {
|
||||
behavior: smooth ? 'smooth' : 'auto',
|
||||
scrollTime
|
||||
};
|
||||
|
||||
options[side] = value;
|
||||
|
||||
this.container.scrollTo(options as any);
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.appendTo.childElementCount;
|
||||
}
|
||||
}
|
||||
|
||||
export default class Scrollable extends ScrollableBase {
|
||||
public splitUp: HTMLElement;
|
||||
|
||||
public onScrolledTop: () => void = null;
|
||||
|
@ -63,8 +141,6 @@ export default class Scrollable {
|
|||
|
||||
private disableHoverTimeout: number = 0;
|
||||
|
||||
private log: ReturnType<typeof logger>;
|
||||
|
||||
/* private sentinelsObserver: IntersectionObserver;
|
||||
private topSentinel: HTMLDivElement;
|
||||
private bottomSentinel: HTMLDivElement; */
|
||||
|
@ -80,8 +156,7 @@ export default class Scrollable {
|
|||
/* private onScrolledTopFired = false;
|
||||
private onScrolledBottomFired = false; */
|
||||
|
||||
public scrollLocked = 0;
|
||||
public scrollLockedPromise: CancellablePromise<void> = Promise.resolve();
|
||||
//public scrollLockedPromise: CancellablePromise<void> = Promise.resolve();
|
||||
public isVisible = false;
|
||||
|
||||
private reorderTimeout: number;
|
||||
|
@ -102,8 +177,8 @@ export default class Scrollable {
|
|||
this.visible.delete(element);
|
||||
}
|
||||
|
||||
constructor(public el: HTMLElement, axis: 'y' | 'x' = 'y', logPrefix = '', public appendTo = el, public onScrollOffset = 300, public splitCount = 15, public container: HTMLElement = document.createElement('div')) {
|
||||
this.container.classList.add('scrollable');
|
||||
constructor(el: HTMLElement, logPrefix = '', appendTo = el, public onScrollOffset = 300, public splitCount = 15, container: HTMLElement = document.createElement('div')) {
|
||||
super(el, logPrefix, appendTo, container);
|
||||
|
||||
this.visible = new Set();
|
||||
this.observer = new IntersectionObserver(entries => {
|
||||
|
@ -173,63 +248,11 @@ export default class Scrollable {
|
|||
}
|
||||
});
|
||||
|
||||
if(!appendTo) {
|
||||
this.appendTo = this.container;
|
||||
}
|
||||
|
||||
this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : ''), LogLevels.error);
|
||||
this.container.classList.add('scrollable-y');
|
||||
|
||||
if(axis == 'x') {
|
||||
this.container.classList.add('scrollable-x');
|
||||
|
||||
if(!touchSupport) {
|
||||
const scrollHorizontally = (e: any) => {
|
||||
e = window.event || e;
|
||||
if(e.which == 1) {
|
||||
// maybe horizontal scroll is natively supports, works on macbook
|
||||
return;
|
||||
}
|
||||
|
||||
const delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
|
||||
this.container.scrollLeft -= (delta * 20);
|
||||
e.preventDefault();
|
||||
};
|
||||
if(this.container.addEventListener) {
|
||||
// IE9, Chrome, Safari, Opera
|
||||
this.container.addEventListener("mousewheel", scrollHorizontally, false);
|
||||
// Firefox
|
||||
this.container.addEventListener("DOMMouseScroll", scrollHorizontally, false);
|
||||
} else {
|
||||
// IE 6/7/8
|
||||
// @ts-ignore
|
||||
this.container.attachEvent("onmousewheel", scrollHorizontally);
|
||||
}
|
||||
}
|
||||
} else if(axis == 'y') {
|
||||
this.container.classList.add('scrollable-y');
|
||||
} else {
|
||||
throw new Error('no side for scroll');
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
this.overflowContainer = mediaSizes.isMobile && false ? document.documentElement : this.container;
|
||||
this.onScroll();
|
||||
});
|
||||
this.container.addEventListener('scroll', this.onScroll, {passive: true, capture: true});
|
||||
//document.documentElement.addEventListener('scroll', binded, {passive: true, capture: true});
|
||||
//window.addEventListener('scroll', binded, {passive: true, capture: true});
|
||||
|
||||
if(el) {
|
||||
Array.from(el.children).forEach(c => this.container.append(c));
|
||||
|
||||
el.append(this.container);
|
||||
}
|
||||
//this.onScroll();
|
||||
|
||||
this.overflowContainer = mediaSizes.isMobile && false ? document.documentElement : this.container;
|
||||
|
||||
/* scrollables.set(this.container, this);
|
||||
scrollsIntersector.observe(this.container); */
|
||||
this.setListeners();
|
||||
}
|
||||
|
||||
// public attachSentinels(container = this.container, offset = this.onScrollOffset) {
|
||||
|
@ -329,12 +352,12 @@ export default class Scrollable {
|
|||
this.onScrollMeasure = window.requestAnimationFrame(() => {
|
||||
//if(!this.isVisible) return;
|
||||
|
||||
this.checkForTriggers(this.overflowContainer);
|
||||
this.checkForTriggers();
|
||||
|
||||
this.onScrollMeasure = 0;
|
||||
if(!this.splitUp) return;
|
||||
|
||||
const scrollTop = this.overflowContainer.scrollTop;
|
||||
const scrollTop = this.scrollTop;
|
||||
if(this.lastScrollTop != scrollTop) {
|
||||
this.lastScrollDirection = this.lastScrollTop < scrollTop ? 1 : -1;
|
||||
this.lastScrollTop = scrollTop;
|
||||
|
@ -344,9 +367,10 @@ export default class Scrollable {
|
|||
});
|
||||
};
|
||||
|
||||
public checkForTriggers(container: HTMLElement) {
|
||||
public checkForTriggers() {
|
||||
if(this.scrollLocked || (!this.onScrolledTop && !this.onScrolledBottom)) return;
|
||||
|
||||
const container = this.container;
|
||||
const scrollHeight = container.scrollHeight;
|
||||
if(!scrollHeight) { // незачем вызывать триггеры если блок пустой или не виден
|
||||
return;
|
||||
|
@ -439,11 +463,11 @@ export default class Scrollable {
|
|||
if(element.parentElement && !this.scrollLocked) {
|
||||
const isFirstUnread = element.classList.contains('is-first-unread');
|
||||
|
||||
let offsetTop = element.getBoundingClientRect().top - this.container.getBoundingClientRect().top;
|
||||
offsetTop = this.container.scrollTop + offsetTop;
|
||||
let offset = element.getBoundingClientRect().top - this.container.getBoundingClientRect().top;
|
||||
offset = this.scrollTop + offset;
|
||||
|
||||
if(!smooth && isFirstUnread) {
|
||||
this.scrollTo(offsetTop, false);
|
||||
this.scrollTo(offset, 'top', false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -451,39 +475,20 @@ export default class Scrollable {
|
|||
const height = element.scrollHeight;
|
||||
|
||||
const d = (clientHeight - height) / 2;
|
||||
offsetTop -= d;
|
||||
offset -= d;
|
||||
|
||||
this.scrollTo(offsetTop, smooth);
|
||||
this.scrollTo(offset, 'top', smooth);
|
||||
}
|
||||
}
|
||||
|
||||
public scrollTo(top: number, smooth = true, important = false) {
|
||||
if(this.scrollLocked && !important) return;
|
||||
|
||||
const scrollTop = this.scrollTop;
|
||||
if(scrollTop == Math.floor(top)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.scrollLocked) clearTimeout(this.scrollLocked);
|
||||
else {
|
||||
this.scrollLockedPromise = deferredPromise<void>();
|
||||
}
|
||||
|
||||
this.scrollLocked = window.setTimeout(() => {
|
||||
this.scrollLocked = 0;
|
||||
this.scrollLockedPromise.resolve();
|
||||
//this.onScroll();
|
||||
this.container.dispatchEvent(new CustomEvent('scroll'));
|
||||
}, 468);
|
||||
|
||||
this.container.scrollTo({behavior: smooth ? 'smooth' : 'auto', top});
|
||||
}
|
||||
|
||||
public removeElement(element: Element) {
|
||||
element.remove();
|
||||
}
|
||||
|
||||
public getScrollValue = () => {
|
||||
return this.scrollTop;
|
||||
};
|
||||
|
||||
set scrollTop(y: number) {
|
||||
this.container.scrollTop = y;
|
||||
}
|
||||
|
@ -496,8 +501,57 @@ export default class Scrollable {
|
|||
get scrollHeight() {
|
||||
return this.container.scrollHeight;
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.appendTo.childElementCount;
|
||||
}
|
||||
}
|
||||
|
||||
export class ScrollableX extends ScrollableBase {
|
||||
constructor(public el: HTMLElement, logPrefix = '', public appendTo = el, public onScrollOffset = 300, public splitCount = 15, public container: HTMLElement = document.createElement('div')) {
|
||||
super(el, logPrefix, appendTo, container);
|
||||
|
||||
this.container.classList.add('scrollable-x');
|
||||
|
||||
if(!touchSupport) {
|
||||
const scrollHorizontally = (e: any) => {
|
||||
e = window.event || e;
|
||||
if(e.which == 1) {
|
||||
// maybe horizontal scroll is natively supports, works on macbook
|
||||
return;
|
||||
}
|
||||
|
||||
const delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
|
||||
this.container.scrollLeft -= (delta * 20);
|
||||
e.preventDefault();
|
||||
};
|
||||
if(this.container.addEventListener) {
|
||||
// IE9, Chrome, Safari, Opera
|
||||
this.container.addEventListener("mousewheel", scrollHorizontally, false);
|
||||
// Firefox
|
||||
this.container.addEventListener("DOMMouseScroll", scrollHorizontally, false);
|
||||
} else {
|
||||
// IE 6/7/8
|
||||
// @ts-ignore
|
||||
this.container.attachEvent("onmousewheel", scrollHorizontally);
|
||||
}
|
||||
}
|
||||
|
||||
this.setListeners();
|
||||
}
|
||||
|
||||
public scrollIntoView(element: HTMLElement, smooth = true, scrollTime?: number) {
|
||||
if(element.parentElement && !this.scrollLocked) {
|
||||
let offset = element.getBoundingClientRect().left - this.container.getBoundingClientRect().left;
|
||||
offset = this.getScrollValue() + offset;
|
||||
|
||||
const clientWidth = this.container.clientWidth;
|
||||
const width = element.scrollWidth;
|
||||
|
||||
const d = (clientWidth - width) / 2;
|
||||
offset -= d;
|
||||
|
||||
this.scrollTo(offset, 'left', smooth, undefined, scrollTime);
|
||||
}
|
||||
}
|
||||
|
||||
public getScrollValue = () => {
|
||||
return this.container.scrollLeft;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ export default class AppEditProfileTab implements SliderTab {
|
|||
});
|
||||
});
|
||||
|
||||
let scrollable = new Scrollable(this.scrollWrapper as HTMLElement, 'y');
|
||||
let scrollable = new Scrollable(this.scrollWrapper as HTMLElement);
|
||||
}
|
||||
|
||||
public fillElements() {
|
||||
|
|
|
@ -31,7 +31,7 @@ export default class AppGifsTab implements SliderTab {
|
|||
private searchPromise: ReturnType<AppInlineBotsManager['getInlineResults']>;
|
||||
|
||||
constructor() {
|
||||
this.scrollable = new Scrollable(this.contentDiv, 'y', ANIMATIONGROUP, undefined, undefined, 2);
|
||||
this.scrollable = new Scrollable(this.contentDiv, ANIMATIONGROUP, undefined, undefined, 2);
|
||||
this.scrollable.setVirtualContainer(this.gifsDiv);
|
||||
|
||||
this.masonry = new GifsMasonry(this.gifsDiv, ANIMATIONGROUP, this.scrollable);
|
||||
|
|
|
@ -17,7 +17,7 @@ export default class AppPollResultsTab implements SliderTab {
|
|||
private mid: number;
|
||||
|
||||
constructor() {
|
||||
this.scrollable = new Scrollable(this.contentDiv, 'y', 'POLL-RESULTS', undefined, undefined, 2);
|
||||
this.scrollable = new Scrollable(this.contentDiv, 'POLL-RESULTS', undefined, undefined, 2);
|
||||
}
|
||||
|
||||
public cleanup() {
|
||||
|
|
|
@ -23,7 +23,7 @@ export default class AppStickersTab implements SliderTab {
|
|||
private lazyLoadQueue: LazyLoadQueue;
|
||||
|
||||
constructor() {
|
||||
this.scrollable = new Scrollable(this.contentDiv, 'y', 'STICKERS-SEARCH', undefined, undefined, 2);
|
||||
this.scrollable = new Scrollable(this.contentDiv, 'STICKERS-SEARCH', undefined, undefined, 2);
|
||||
this.scrollable.setVirtualContainer(this.setsDiv);
|
||||
|
||||
this.lazyLoadQueue = new LazyLoadQueue();
|
||||
|
|
|
@ -10,7 +10,6 @@ import { renderImageFromUrl } from './misc';
|
|||
import appMessagesManager from '../lib/appManagers/appMessagesManager';
|
||||
import { Layouter, RectPart } from './groupedLayout';
|
||||
import PollElement from './poll';
|
||||
import { mediaSizes, isSafari } from '../lib/config';
|
||||
import animationIntersector from './animationIntersector';
|
||||
import AudioElement from './audio';
|
||||
import { DownloadBlob } from '../lib/appManagers/appDownloadManager';
|
||||
|
@ -18,7 +17,9 @@ import webpWorkerController from '../lib/webp/webpWorkerController';
|
|||
import { readBlobAsText } from '../helpers/blob';
|
||||
import appMediaPlaybackController from './appMediaPlaybackController';
|
||||
import { PhotoSize } from '../layer';
|
||||
import { deferredPromise } from '../lib/polyfill';
|
||||
import { deferredPromise } from '../helpers/cancellablePromise';
|
||||
import mediaSizes from '../helpers/mediaSizes';
|
||||
import { isSafari } from '../helpers/userAgent';
|
||||
|
||||
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: {
|
||||
doc: MyDocument,
|
||||
|
@ -704,6 +705,14 @@ export function wrapReply(title: string, subtitle: string, message?: any, isPinn
|
|||
|
||||
const replySubtitle = document.createElement('div');
|
||||
replySubtitle.classList.add(prefix + '-subtitle');
|
||||
|
||||
if(title.length > 150) {
|
||||
title = title.substr(0, 140) + '...';
|
||||
}
|
||||
|
||||
if(subtitle.length > 150) {
|
||||
subtitle = subtitle.substr(0, 140) + '...';
|
||||
}
|
||||
|
||||
replyTitle.innerHTML = title ? RichTextProcessor.wrapEmojiText(title) : '';
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
export interface CancellablePromise<T> extends Promise<T> {
|
||||
resolve?: (...args: any[]) => void,
|
||||
reject?: (...args: any[]) => void,
|
||||
cancel?: () => void,
|
||||
|
||||
notify?: (...args: any[]) => void,
|
||||
notifyAll?: (...args: any[]) => void,
|
||||
lastNotify?: any,
|
||||
listeners?: Array<(...args: any[]) => void>,
|
||||
addNotifyListener?: (callback: (...args: any[]) => void) => void,
|
||||
|
||||
isFulfilled?: boolean,
|
||||
isRejected?: boolean
|
||||
}
|
||||
|
||||
export function deferredPromise<T>() {
|
||||
let deferredHelper: any = {
|
||||
isFulfilled: false,
|
||||
isRejected: false,
|
||||
|
||||
notify: () => {},
|
||||
notifyAll: (...args: any[]) => {
|
||||
deferredHelper.lastNotify = args;
|
||||
deferredHelper.listeners.forEach((callback: any) => callback(...args));
|
||||
},
|
||||
|
||||
lastNotify: undefined,
|
||||
listeners: [],
|
||||
addNotifyListener: (callback: (...args: any[]) => void) => {
|
||||
if(deferredHelper.lastNotify) {
|
||||
callback(...deferredHelper.lastNotify);
|
||||
}
|
||||
|
||||
deferredHelper.listeners.push(callback);
|
||||
}
|
||||
};
|
||||
|
||||
let deferred: CancellablePromise<T> = new Promise<T>((resolve, reject) => {
|
||||
deferredHelper.resolve = (value: T) => {
|
||||
if(deferred.isFulfilled) return;
|
||||
|
||||
deferred.isFulfilled = true;
|
||||
resolve(value);
|
||||
};
|
||||
|
||||
deferredHelper.reject = (...args: any[]) => {
|
||||
if(deferred.isRejected) return;
|
||||
|
||||
deferred.isRejected = true;
|
||||
reject(...args);
|
||||
};
|
||||
});
|
||||
|
||||
deferred.finally(() => {
|
||||
deferred.notify = null;
|
||||
deferred.listeners.length = 0;
|
||||
deferred.lastNotify = null;
|
||||
});
|
||||
|
||||
Object.assign(deferred, deferredHelper);
|
||||
|
||||
return deferred;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import type { ArgumentTypes } from "../types";
|
||||
|
||||
export default class EventListenerBase<Listeners extends {[name: string]: Function}> {
|
||||
protected listeners: Partial<{
|
||||
[k in keyof Listeners]: Array<{callback: Listeners[k], once?: true}>
|
||||
}> = {};
|
||||
protected listenerResults: Partial<{
|
||||
[k in keyof Listeners]: ArgumentTypes<Listeners[k]>
|
||||
}> = {};
|
||||
|
||||
constructor(private reuseResults?: true) {
|
||||
|
||||
}
|
||||
|
||||
public addListener(name: keyof Listeners, callback: Listeners[typeof name], once?: true) {
|
||||
(this.listeners[name] ?? (this.listeners[name] = [])).push({callback, once});
|
||||
|
||||
if(this.listenerResults.hasOwnProperty(name)) {
|
||||
callback(this.listenerResults[name]);
|
||||
|
||||
if(once) {
|
||||
this.removeListener(name, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public removeListener(name: keyof Listeners, callback: Listeners[typeof name]) {
|
||||
if(this.listeners[name]) {
|
||||
this.listeners[name].findAndSplice(l => l.callback == callback);
|
||||
}
|
||||
}
|
||||
|
||||
protected setListenerResult(name: keyof Listeners, ...args: ArgumentTypes<Listeners[typeof name]>) {
|
||||
if(this.reuseResults) {
|
||||
this.listenerResults[name] = args;
|
||||
}
|
||||
|
||||
if(this.listeners[name]) {
|
||||
this.listeners[name].forEach(listener => {
|
||||
listener.callback(...args);
|
||||
|
||||
if(listener.once) {
|
||||
this.removeListener(name, listener.callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
import EventListenerBase from "./eventListenerBase";
|
||||
|
||||
type Size = {width: number, height: number};
|
||||
type Sizes = {
|
||||
regular: Size,
|
||||
webpage: Size,
|
||||
album: Size
|
||||
};
|
||||
|
||||
export enum ScreenSize {
|
||||
mobile,
|
||||
medium,
|
||||
large
|
||||
}
|
||||
|
||||
const MOBILE_SIZE = 896;
|
||||
const MEDIUM_SIZE = 1275;
|
||||
const LARGE_SIZE = 1680;
|
||||
|
||||
class MediaSizes extends EventListenerBase<{
|
||||
changeScreen: (from: ScreenSize, to: ScreenSize) => void
|
||||
}> {
|
||||
private screenSizes: {key: ScreenSize, value: number}[] = [
|
||||
{key: ScreenSize.mobile, value: MOBILE_SIZE - 1},
|
||||
{key: ScreenSize.medium, value: MEDIUM_SIZE},
|
||||
{key: ScreenSize.large, value: LARGE_SIZE}
|
||||
];
|
||||
|
||||
private sizes: {[k in 'desktop' | 'handhelds']: Sizes} = {
|
||||
handhelds: {
|
||||
regular: {
|
||||
width: 293,
|
||||
height: 293
|
||||
},
|
||||
webpage: {
|
||||
width: 293,
|
||||
height: 213
|
||||
},
|
||||
album: {
|
||||
width: 293,
|
||||
height: 0
|
||||
}
|
||||
},
|
||||
desktop: {
|
||||
regular: {
|
||||
width: 480,
|
||||
height: 480
|
||||
},
|
||||
webpage: {
|
||||
width: 480,
|
||||
height: 400
|
||||
},
|
||||
album: {
|
||||
width: 451,
|
||||
height: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public isMobile = false;
|
||||
public active: Sizes;
|
||||
public activeScreen: ScreenSize;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
this.handleResize();
|
||||
}
|
||||
|
||||
private handleResize = () => {
|
||||
const innerWidth = window.innerWidth;
|
||||
//this.isMobile = innerWidth <= 720;
|
||||
|
||||
let activeScreen = this.screenSizes[0].key;
|
||||
for(let i = this.screenSizes.length - 1; i >= 0; --i) {
|
||||
if(this.screenSizes[i].value < innerWidth) {
|
||||
activeScreen = (this.screenSizes[i + 1] || this.screenSizes[i]).key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(this.activeScreen != activeScreen) {
|
||||
//console.log('changeScreen', this.activeScreen, activeScreen);
|
||||
this.setListenerResult('changeScreen', this.activeScreen, activeScreen);
|
||||
}
|
||||
|
||||
this.activeScreen = activeScreen;
|
||||
|
||||
this.isMobile = this.activeScreen == ScreenSize.mobile;
|
||||
|
||||
this.active = this.isMobile ? this.sizes.handhelds : this.sizes.desktop;
|
||||
|
||||
/* if(this.isMobile) {
|
||||
for(let i in this.active) {
|
||||
// @ts-ignore
|
||||
let size = this.active[i];
|
||||
size.width = innerWidth
|
||||
}
|
||||
} */
|
||||
};
|
||||
}
|
||||
|
||||
const mediaSizes = new MediaSizes();
|
||||
export default mediaSizes;
|
|
@ -22,7 +22,7 @@
|
|||
{{/ each }}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body class="animation-level-2">
|
||||
<!--[if IE]>
|
||||
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="https://browsehappy.com/">upgrade your browser</a> to improve your experience and security.</p>
|
||||
<![endif]-->
|
||||
|
@ -212,7 +212,7 @@
|
|||
<div class="sidebar-content">
|
||||
<div id="chats-container">
|
||||
<div class="folders-tabs-scrollable hide">
|
||||
<nav class="menu-horizontal no-stripe" id="folders-tabs">
|
||||
<nav class="menu-horizontal" id="folders-tabs">
|
||||
<ul>
|
||||
<li class="rp"><span>All<span class="unread-count"></span><i></i></span></li>
|
||||
</ul>
|
||||
|
@ -237,7 +237,7 @@
|
|||
</div>
|
||||
<div class="sidebar-slider-item">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title">Archived Chats</div>
|
||||
</div>
|
||||
<div class="sidebar-content">
|
||||
|
@ -248,7 +248,7 @@
|
|||
</div>
|
||||
<div class="sidebar-slider-item" id="contacts-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
</div>
|
||||
<div class="sidebar-content">
|
||||
<div>
|
||||
|
@ -258,7 +258,7 @@
|
|||
</div>
|
||||
<div class="sidebar-slider-item new-channel-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title">New Channel</div>
|
||||
</div>
|
||||
<div class="sidebar-content">
|
||||
|
@ -282,7 +282,7 @@
|
|||
</div>
|
||||
<div class="sidebar-slider-item addmembers-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title">Add Members</div>
|
||||
</div>
|
||||
<div class="sidebar-content">
|
||||
|
@ -291,7 +291,7 @@
|
|||
</div>
|
||||
<div class="sidebar-slider-item new-group-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title">New Group</div>
|
||||
</div>
|
||||
<div class="sidebar-content">
|
||||
|
@ -310,7 +310,7 @@
|
|||
</div>
|
||||
<div class="sidebar-slider-item settings-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title">Settings</div>
|
||||
<div class="btn-icon tgico-more rp btn-menu-toggle">
|
||||
<div class="btn-menu bottom-left">
|
||||
|
@ -336,7 +336,7 @@
|
|||
</div>
|
||||
<div class="sidebar-slider-item edit-profile-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title">Edit Profile</div>
|
||||
</div>
|
||||
<div class="sidebar-content">
|
||||
|
@ -377,7 +377,7 @@
|
|||
</div>
|
||||
<div class="sidebar-slider-item chat-folders-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title">Chat Folders</div>
|
||||
</div>
|
||||
<div class="sidebar-content">
|
||||
|
@ -401,7 +401,7 @@
|
|||
</div>
|
||||
<div class="sidebar-slider-item edit-folder-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title"></div>
|
||||
<div class="btn-icon tgico-check1 btn-confirm rp hide"></div>
|
||||
<div class="btn-icon btn-menu-toggle rp tgico-more hide">
|
||||
|
@ -445,7 +445,7 @@
|
|||
</div>
|
||||
<div class="sidebar-slider-item included-chats-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title"></div>
|
||||
<div class="btn-icon tgico-check1 btn-confirm rp" style="display: none;"></div>
|
||||
</div>
|
||||
|
@ -460,9 +460,10 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="chat-container main-column" id="column-center">
|
||||
<canvas id="chat-background-canvas"></canvas>
|
||||
{{!-- <canvas id="chat-background-canvas"></canvas> --}}
|
||||
<div class="chat-background"></div>
|
||||
<div id="topbar" style="display: none;" class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-back sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
<div class="chat-info">
|
||||
<div class="person">
|
||||
<avatar-element id="im-avatar" dialog="1"></avatar-element>
|
||||
|
@ -476,21 +477,23 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn-primary rp chat-join hide">SUBSCRIBE</button>
|
||||
<div class="btn-icon rp chat-mute-button hide"></div>
|
||||
<div class="btn-icon rp tgico-search chat-search-button"></div>
|
||||
<div class="btn-icon btn-menu-toggle rp tgico-more chat-more-button">
|
||||
<div class="btn-menu bottom-left">
|
||||
<div class="btn-menu-item menu-search tgico-search rp">Search</div>
|
||||
<div class="btn-menu-item menu-mute rp">Mute</div>
|
||||
<div class="btn-menu-item menu-delete tgico-delete danger rp btn-disabled">Delete and Leave</div>
|
||||
<div class="chat-utils">
|
||||
<button class="btn-primary rp chat-join hide">SUBSCRIBE</button>
|
||||
<div class="btn-icon rp chat-mute-button hide"></div>
|
||||
<div class="btn-icon rp tgico-search chat-search-button"></div>
|
||||
<div class="btn-icon btn-menu-toggle rp tgico-more chat-more-button">
|
||||
<div class="btn-menu bottom-left">
|
||||
<div class="btn-menu-item menu-search tgico-search rp">Search</div>
|
||||
<div class="btn-menu-item menu-mute rp">Mute</div>
|
||||
<div class="btn-menu-item menu-delete tgico-delete danger rp btn-disabled">Delete and Leave</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="bubbles" class="scrolled-down">
|
||||
<div id="bubbles-inner"></div>
|
||||
<div id="bubbles-go-down" class="tgico-down z-depth-1 rp" style="display: none;"></div>
|
||||
</div>
|
||||
<div id="bubbles-go-down" class="tgico-down btn-corner z-depth-1 rp hide"></div>
|
||||
<div class="btn-menu" id="bubble-contextmenu">
|
||||
<div class="btn-menu-item menu-reply tgico-reply rp">Reply</div>
|
||||
<div class="btn-menu-item menu-edit tgico-edit rp">Edit</div>
|
||||
|
@ -587,7 +590,7 @@
|
|||
<div class="sidebar-content sidebar-slider tabs-container">
|
||||
<div class="sidebar-slider-item profile-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title">Info</div>
|
||||
|
||||
<!-- <button class="btn-icon rp tgico-edit sidebar-edit-button"></button> -->
|
||||
|
@ -648,33 +651,33 @@
|
|||
</div>
|
||||
<div class="sidebar-slider-item sidebar-search chats-container" id="search-private-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-close sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-close sidebar-close-button"></button>
|
||||
</div>
|
||||
<div class="chats-container"></div>
|
||||
</div>
|
||||
<div class="sidebar-slider-item sidebar-search" id="forward-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-close sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-close sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title">Forward</div>
|
||||
</div>
|
||||
<button class="btn-primary btn-circle btn-icon rp btn-corner tgico-send"></button>
|
||||
</div>
|
||||
<div class="sidebar-slider-item sidebar-search chats-container" id="stickers-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-close sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-close sidebar-close-button"></button>
|
||||
</div>
|
||||
<div class="sidebar-content"><div class="sticker-sets"></div></div>
|
||||
</div>
|
||||
<div class="sidebar-slider-item chats-container" id="poll-results-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-close sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-close sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title">Results</div>
|
||||
</div>
|
||||
<div class="sidebar-content"><div class="poll-results"></div></div>
|
||||
</div>
|
||||
<div class="sidebar-slider-item sidebar-search chats-container" id="search-gifs-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon rp tgico-close sidebar-close-button"></button>
|
||||
<button class="btn-icon tgico-close sidebar-close-button"></button>
|
||||
</div>
|
||||
<div class="sidebar-content"><div class="gifs-masonry"></div></div>
|
||||
</div>
|
||||
|
|
|
@ -6,16 +6,17 @@ import appUsersManager, { User } from "./appUsersManager";
|
|||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
import { putPreloader, positionMenu, openBtnMenu, parseMenuButtonsTo, attachContextMenuListener } from "../../components/misc";
|
||||
//import Scrollable from "../../components/scrollable";
|
||||
import Scrollable from "../../components/scrollable_new";
|
||||
import Scrollable, { ScrollableX } from "../../components/scrollable_new";
|
||||
import { logger, LogLevels } from "../logger";
|
||||
import appChatsManager from "./appChatsManager";
|
||||
import AvatarElement from "../../components/avatar";
|
||||
import { PopupButton, PopupPeer } from "../../components/popup";
|
||||
import { SliderTab } from "../../components/slider";
|
||||
import appStateManager from "./appStateManager";
|
||||
import { touchSupport, isSafari } from "../config";
|
||||
import { touchSupport } from "../config";
|
||||
import { horizontalMenu } from "../../components/horizontalMenu";
|
||||
import { ripple } from "../../components/ripple";
|
||||
import { isSafari } from "../../helpers/userAgent";
|
||||
|
||||
type DialogDom = {
|
||||
avatarEl: AvatarElement,
|
||||
|
@ -275,7 +276,7 @@ export class AppArchivedTab implements SliderTab {
|
|||
public wasFilterID: number;
|
||||
|
||||
init() {
|
||||
this.scroll = new Scrollable(this.container, 'y', 'CLA', this.chatList, 500);
|
||||
this.scroll = new Scrollable(this.container, 'CLA', this.chatList, 500);
|
||||
this.scroll.setVirtualContainer(this.chatList);
|
||||
this.scroll.onScrolledBottom = appDialogsManager.onChatsScroll;
|
||||
///this.scroll.attachSentinels();
|
||||
|
@ -379,7 +380,7 @@ export class AppDialogsManager {
|
|||
|
||||
this.folders.menuScrollContainer = this.folders.menu.parentElement;
|
||||
|
||||
this.scroll = this._scroll = new Scrollable(this.chatsContainer, 'y', 'CL', this.chatList, 500);
|
||||
this.scroll = this._scroll = new Scrollable(this.chatsContainer, 'CL', this.chatList, 500);
|
||||
this.scroll.onScrolledBottom = this.onChatsScroll;
|
||||
this.scroll.setVirtualContainer(this.chatList);
|
||||
//this.scroll.attachSentinels();
|
||||
|
@ -588,13 +589,15 @@ export class AppDialogsManager {
|
|||
}
|
||||
}); */
|
||||
|
||||
new Scrollable(this.folders.menuScrollContainer, 'x');
|
||||
const foldersScrollable = new ScrollableX(this.folders.menuScrollContainer);
|
||||
this.chatsContainer.prepend(this.folders.menuScrollContainer);
|
||||
const selectTab = horizontalMenu(this.folders.menu, this.folders.container, (id, tabContent) => {
|
||||
/* if(id != 0) {
|
||||
id += 1;
|
||||
} */
|
||||
|
||||
foldersScrollable.scrollIntoView(this.folders.menu.firstElementChild.children[id] as HTMLElement, true, 250);
|
||||
|
||||
id = +tabContent.dataset.filterID || 0;
|
||||
|
||||
if(this.filterID == id) return;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { $rootScope } from "../utils";
|
||||
import apiManager from "../mtproto/mtprotoworker";
|
||||
import { deferredPromise, CancellablePromise } from "../polyfill";
|
||||
import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePromise";
|
||||
import type { DownloadOptions } from "../mtproto/apiFileManager";
|
||||
import { getFileNameByLocation } from "../bin_utils";
|
||||
import { InputFile } from "../../layer";
|
||||
|
|
|
@ -28,7 +28,7 @@ import appStickersManager from './appStickersManager';
|
|||
import AvatarElement from '../../components/avatar';
|
||||
import appInlineBotsManager from './AppInlineBotsManager';
|
||||
import StickyIntersector from '../../components/stickyIntersector';
|
||||
import { mediaSizes, touchSupport, isAndroid, isApple } from '../config';
|
||||
import { touchSupport } from '../config';
|
||||
import animationIntersector from '../../components/animationIntersector';
|
||||
import PopupStickers from '../../components/popupStickers';
|
||||
import PopupDatePicker from '../../components/popupDatepicker';
|
||||
|
@ -40,6 +40,8 @@ import { InputNotifyPeer, InputPeerNotifySettings } from '../../layer';
|
|||
import { ChatAudio } from '../../components/chat/audio';
|
||||
import { ChatContextMenu } from '../../components/chat/contextMenu';
|
||||
import { ChatSearch } from '../../components/chat/search';
|
||||
import mediaSizes from '../../helpers/mediaSizes';
|
||||
import { isAndroid, isApple } from '../../helpers/userAgent';
|
||||
|
||||
//console.log('appImManager included33!');
|
||||
|
||||
|
@ -148,7 +150,7 @@ export class AppImManager {
|
|||
parseMenuButtonsTo(this.menuButtons, this.columnEl.querySelector('.chat-more-button').firstElementChild.children);
|
||||
|
||||
this.chatAudio = new ChatAudio();
|
||||
this.topbar.insertBefore(this.chatAudio.container, this.chatInfo.nextElementSibling);
|
||||
this.chatInfo.nextElementSibling.prepend(this.chatAudio.container);
|
||||
|
||||
apiManager.getUserID().then((id) => {
|
||||
this.myID = $rootScope.myID = id;
|
||||
|
@ -738,7 +740,7 @@ export class AppImManager {
|
|||
}, {once: true});
|
||||
newPinned.append(close);
|
||||
|
||||
this.topbar.insertBefore(newPinned, this.btnJoin);
|
||||
this.btnJoin.parentElement.insertBefore(newPinned, this.btnJoin);
|
||||
this.topbar.classList.add('is-pinned-shown');
|
||||
|
||||
if(this.pinnedMessageContainer) {
|
||||
|
@ -833,7 +835,7 @@ export class AppImManager {
|
|||
}
|
||||
|
||||
public setScroll() {
|
||||
this.scrollable = new Scrollable(this.bubblesContainer, 'y', 'IM', this.chatInner, 300);
|
||||
this.scrollable = new Scrollable(this.bubblesContainer, 'IM', this.chatInner, 300);
|
||||
|
||||
/* const getScrollOffset = () => {
|
||||
//return Math.round(Math.max(300, appPhotosManager.windowH / 1.5));
|
||||
|
@ -847,8 +849,6 @@ export class AppImManager {
|
|||
this.scrollable = new Scrollable(this.bubblesContainer, 'y', 'IM', this.chatInner, getScrollOffset()); */
|
||||
this.scroll = this.scrollable.container;
|
||||
|
||||
this.bubblesContainer.append(this.goDownBtn);
|
||||
|
||||
this.scrollable.onScrolledTop = () => this.loadMoreHistory(true);
|
||||
this.scrollable.onScrolledBottom = () => this.loadMoreHistory(false);
|
||||
//this.scrollable.attachSentinels(undefined, 300);
|
||||
|
@ -1004,7 +1004,8 @@ export class AppImManager {
|
|||
////console.time('appImManager: pre render start');
|
||||
if(peerID == 0) {
|
||||
appSidebarRight.toggleSidebar(false);
|
||||
this.topbar.style.display = this.chatInput.style.display = this.goDownBtn.style.display = 'none';
|
||||
this.topbar.style.display = this.chatInput.style.display = 'none';
|
||||
this.goDownBtn.classList.add('hide');
|
||||
this.cleanup(true);
|
||||
this.peerID = $rootScope.selectedPeerID = 0;
|
||||
$rootScope.$broadcast('peer_changed', this.peerID);
|
||||
|
@ -1253,7 +1254,7 @@ export class AppImManager {
|
|||
else title = appPeersManager.getPeerTitle(this.peerID);
|
||||
this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = title;
|
||||
|
||||
this.goDownBtn.style.display = '';
|
||||
this.goDownBtn.classList.remove('hide');
|
||||
|
||||
this.setPeerStatus(true);
|
||||
});
|
||||
|
@ -1320,10 +1321,10 @@ export class AppImManager {
|
|||
|
||||
//if(scrolledDown) this.scrollable.scrollTop = this.scrollable.scrollHeight;
|
||||
if(this.messagesQueuePromise && scrolledDown) {
|
||||
this.scrollable.scrollTo(this.scrollable.scrollHeight - 1, false, true);
|
||||
this.scrollable.scrollTo(this.scrollable.scrollHeight - 1, 'top', false, true);
|
||||
this.messagesQueuePromise.then(() => {
|
||||
this.log('messagesQueuePromise after:', this.chatInner.childElementCount, this.scrollable.scrollHeight);
|
||||
this.scrollable.scrollTo(this.scrollable.scrollHeight, true, true);
|
||||
this.scrollable.scrollTo(this.scrollable.scrollHeight, 'top', true, true);
|
||||
|
||||
setTimeout(() => {
|
||||
this.log('messagesQueuePromise afterafter:', this.chatInner.childElementCount, this.scrollable.scrollHeight);
|
||||
|
@ -1974,17 +1975,17 @@ export class AppImManager {
|
|||
quoteTextDiv.append(nameEl);
|
||||
}
|
||||
|
||||
if(webpage.title) {
|
||||
if(webpage.rTitle) {
|
||||
let titleDiv = document.createElement('div');
|
||||
titleDiv.classList.add('title');
|
||||
titleDiv.innerHTML = RichTextProcessor.wrapRichText(webpage.title);
|
||||
titleDiv.innerHTML = webpage.rTitle;
|
||||
quoteTextDiv.append(titleDiv);
|
||||
}
|
||||
|
||||
if(webpage.description) {
|
||||
if(webpage.rDescription) {
|
||||
let textDiv = document.createElement('div');
|
||||
textDiv.classList.add('text');
|
||||
textDiv.innerHTML = RichTextProcessor.wrapRichText(webpage.description);
|
||||
textDiv.innerHTML = webpage.rDescription;
|
||||
quoteTextDiv.append(textDiv);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,11 +9,13 @@ import appDocsManager, {MyDocument} from "./appDocsManager";
|
|||
import VideoPlayer from "../mediaPlayer";
|
||||
import { renderImageFromUrl, parseMenuButtonsTo } from "../../components/misc";
|
||||
import AvatarElement from "../../components/avatar";
|
||||
import LazyLoadQueue, { LazyLoadQueueBase } from "../../components/lazyLoadQueue";
|
||||
import { LazyLoadQueueBase } from "../../components/lazyLoadQueue";
|
||||
import appForward from "../../components/appForward";
|
||||
import { isSafari, mediaSizes, touchSupport } from "../config";
|
||||
import { deferredPromise } from "../polyfill";
|
||||
import { touchSupport } from "../config";
|
||||
import appMediaPlaybackController from "../../components/appMediaPlaybackController";
|
||||
import { deferredPromise } from "../../helpers/cancellablePromise";
|
||||
import mediaSizes from "../../helpers/mediaSizes";
|
||||
import { isSafari } from "../../helpers/userAgent";
|
||||
|
||||
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
|
||||
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
|
||||
|
@ -355,7 +357,7 @@ export class AppMediaViewer {
|
|||
top = rect.top;
|
||||
}
|
||||
|
||||
transform += `translate(${left}px,${top}px) `;
|
||||
transform += `translate3d(${left}px,${top}px,0) `;
|
||||
|
||||
/* if(wasActive) {
|
||||
left = fromRight === 1 ? appPhotosManager.windowW / 2 : -(containerRect.width + appPhotosManager.windowW / 2);
|
||||
|
@ -585,7 +587,7 @@ export class AppMediaViewer {
|
|||
// чтобы проверить установленную позицию - раскомментировать
|
||||
//throw '';
|
||||
|
||||
mover.style.transform = `translate(${containerRect.left}px,${containerRect.top}px) scale(1,1)`;
|
||||
mover.style.transform = `translate3d(${containerRect.left}px,${containerRect.top}px,0) scale(1,1)`;
|
||||
//mover.style.transform = `translate(-50%,-50%) scale(1,1)`;
|
||||
|
||||
if(aspecter) {
|
||||
|
@ -691,7 +693,7 @@ export class AppMediaViewer {
|
|||
if(mover.classList.contains('center')) {
|
||||
//const rect = mover.getBoundingClientRect();
|
||||
const rect = this.content.container.getBoundingClientRect();
|
||||
mover.style.transform = `translate(${rect.left}px,${rect.top}px)`;
|
||||
mover.style.transform = `translate3d(${rect.left}px,${rect.top}px,0)`;
|
||||
mover.classList.remove('center');
|
||||
void mover.offsetLeft; // reflow
|
||||
mover.classList.remove('no-transition');
|
||||
|
@ -712,7 +714,7 @@ export class AppMediaViewer {
|
|||
|
||||
const rect = mover.getBoundingClientRect();
|
||||
|
||||
const newTransform = mover.style.transform.replace(/translate\((.+?),/, (match, p1) => {
|
||||
const newTransform = mover.style.transform.replace(/translate3d\((.+?),/, (match, p1) => {
|
||||
const x = toLeft ? -rect.width : windowW;
|
||||
//const x = toLeft ? -(rect.right + (rect.width / 2)) : windowW / 2;
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import serverTimeManager from "../mtproto/serverTimeManager";
|
|||
//import apiManager from '../mtproto/apiManager';
|
||||
import apiManager from '../mtproto/mtprotoworker';
|
||||
import appWebPagesManager from "./appWebPagesManager";
|
||||
import { CancellablePromise, deferredPromise } from "../polyfill";
|
||||
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
|
||||
import appPollsManager from "./appPollsManager";
|
||||
import searchIndexManager from '../searchIndexManager';
|
||||
import { Modify } from "../../types";
|
||||
|
@ -2471,6 +2471,10 @@ export class AppMessagesManager {
|
|||
|
||||
let messageWrapped = '';
|
||||
if(text) {
|
||||
if(text.length > 40) {
|
||||
text = text.substr(0, 35) + '...';
|
||||
}
|
||||
|
||||
let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' '), {noLinebreakers: true});
|
||||
|
||||
messageWrapped = RichTextProcessor.wrapRichText(text, {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { calcImageInBox, isObject } from "../utils";
|
||||
import { bytesFromHex, getFileNameByLocation } from "../bin_utils";
|
||||
import appDownloadManager from "./appDownloadManager";
|
||||
import { CancellablePromise } from "../polyfill";
|
||||
import { isSafari } from "../../helpers/userAgent";
|
||||
import { FileLocation, InputFileLocation, Photo, PhotoSize } from "../../layer";
|
||||
import { MyDocument } from "./appDocsManager";
|
||||
import { CancellablePromise } from "../../helpers/cancellablePromise";
|
||||
|
||||
export type MyPhoto = Photo.photo;
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//import { logger } from "../polyfill";
|
||||
import appDialogsManager, { AppArchivedTab, archivedTab } from "./appDialogsManager";
|
||||
import { $rootScope, findUpTag, findUpClassName } from "../utils";
|
||||
import { $rootScope, findUpTag, findUpClassName, formatNumber } from "../utils";
|
||||
import appImManager from "./appImManager";
|
||||
import AppSearch, { SearchGroup } from "../../components/appSearch";
|
||||
import { parseMenuButtonsTo } from "../../components/misc";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
import Scrollable from "../../components/scrollable_new";
|
||||
import Scrollable, { ScrollableX } from "../../components/scrollable_new";
|
||||
import appPeersManager from "../appManagers/appPeersManager";
|
||||
import AvatarElement from "../../components/avatar";
|
||||
import AppNewChannelTab from "../../components/sidebarLeft/newChannel";
|
||||
|
@ -175,7 +175,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
|||
peopleContainer.classList.add('search-group-scrollable');
|
||||
peopleContainer.append(this.searchGroups.people.list);
|
||||
this.searchGroups.people.container.append(peopleContainer);
|
||||
let peopleScrollable = new Scrollable(peopleContainer, 'x');
|
||||
let peopleScrollable = new ScrollableX(peopleContainer);
|
||||
|
||||
parseMenuButtonsTo(this.buttons, this.menuEl.children);
|
||||
parseMenuButtonsTo(this.newButtons, this.newBtnMenu.firstElementChild.children);
|
||||
|
@ -251,7 +251,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
|||
});
|
||||
|
||||
$rootScope.$on('dialogs_archived_unread', (e) => {
|
||||
this.archivedCount.innerText = '' + e.detail.count;
|
||||
this.archivedCount.innerText = '' + formatNumber(e.detail.count, 1);
|
||||
});
|
||||
|
||||
appUsersManager.getTopPeers().then(peers => {
|
||||
|
|
|
@ -16,16 +16,19 @@ import { wrapDocument, wrapAudio } from "../../components/wrappers";
|
|||
import AppSearch, { SearchGroup } from "../../components/appSearch";
|
||||
import AvatarElement from "../../components/avatar";
|
||||
import appForward from "../../components/appForward";
|
||||
import { mediaSizes } from "../config";
|
||||
import SidebarSlider from "../../components/slider";
|
||||
import SearchInput from "../../components/searchInput";
|
||||
import { horizontalMenu } from "../../components/horizontalMenu";
|
||||
import AppStickersTab from "../../components/sidebarRight/stickers";
|
||||
import AppPollResultsTab from "../../components/sidebarRight/pollResults";
|
||||
import AppGifsTab from "../../components/sidebarRight/gifs";
|
||||
import mediaSizes, { ScreenSize } from "../../helpers/mediaSizes";
|
||||
import { isSafari } from "../../helpers/userAgent";
|
||||
|
||||
const testScroll = false;
|
||||
|
||||
const COLUMN_ACTIVE_CLASSNAME = 'is-right-column-shown';
|
||||
|
||||
let setText = (text: string, el: HTMLDivElement) => {
|
||||
window.requestAnimationFrame(() => {
|
||||
if(el.childElementCount > 1) {
|
||||
|
@ -183,7 +186,7 @@ export class AppSidebarRight extends SidebarSlider {
|
|||
let container = this.profileContentEl.querySelector('.content-container .tabs-container') as HTMLDivElement;
|
||||
this.profileTabs = this.profileContentEl.querySelector('.profile-tabs');
|
||||
|
||||
this.scroll = new Scrollable(this.profileContainer, 'y', 'SR', undefined, 400);
|
||||
this.scroll = new Scrollable(this.profileContainer, 'SR', undefined, 400);
|
||||
this.scroll.onScrolledBottom = () => {
|
||||
if(this.sharedMediaSelected && this.sharedMediaSelected.childElementCount/* && false */) {
|
||||
this.log('onScrolledBottom will load media');
|
||||
|
@ -194,6 +197,10 @@ export class AppSidebarRight extends SidebarSlider {
|
|||
|
||||
horizontalMenu(this.profileTabs, container, (id, tabContent) => {
|
||||
if(this.prevTabID == id) return;
|
||||
|
||||
if(this.prevTabID != -1) {
|
||||
this.onTransitionStart();
|
||||
}
|
||||
|
||||
this.sharedMediaType = this.sharedMediaTypes[id];
|
||||
this.sharedMediaSelected = tabContent.firstElementChild as HTMLDivElement;
|
||||
|
@ -213,6 +220,7 @@ export class AppSidebarRight extends SidebarSlider {
|
|||
this.prevTabID = id;
|
||||
}, () => {
|
||||
this.scroll.onScroll();
|
||||
this.onTransitionEnd();
|
||||
});
|
||||
|
||||
let sidebarCloseBtn = this.sidebarEl.querySelector('.sidebar-close-button') as HTMLButtonElement;
|
||||
|
@ -252,8 +260,31 @@ export class AppSidebarRight extends SidebarSlider {
|
|||
//let checked = this.profileElements.notificationsCheckbox.checked;
|
||||
appImManager.mutePeer(this.peerID);
|
||||
});
|
||||
|
||||
mediaSizes.addListener('changeScreen', (from, to) => {
|
||||
if(from !== undefined && to == ScreenSize.medium) {
|
||||
this.toggleSidebar(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private onTransitionStart = () => {
|
||||
// Jolly Cobra's // Workaround for scrollable content flickering during animation.
|
||||
const container = this.scroll.container;
|
||||
if(container.style.overflowY !== 'hidden') {
|
||||
const scrollBarWidth = container.offsetWidth - container.clientWidth;
|
||||
container.style.overflowY = 'hidden';
|
||||
container.style.paddingRight = `${scrollBarWidth}px`;
|
||||
}
|
||||
};
|
||||
|
||||
private onTransitionEnd = () => {
|
||||
// Jolly Cobra's // Workaround for scrollable content flickering during animation.
|
||||
const container = this.scroll.container;
|
||||
container.style.overflowY = '';
|
||||
container.style.paddingRight = '0';
|
||||
};
|
||||
|
||||
public beginSearch() {
|
||||
this.toggleSidebar(true);
|
||||
this.searchContainer.classList.add('active');
|
||||
|
@ -263,7 +294,7 @@ export class AppSidebarRight extends SidebarSlider {
|
|||
public toggleSidebar(enable?: boolean) {
|
||||
/////this.log('sidebarEl', this.sidebarEl, enable, isElementInViewport(this.sidebarEl));
|
||||
|
||||
const active = this.sidebarEl.classList.contains('active');
|
||||
const active = document.body.classList.contains(COLUMN_ACTIVE_CLASSNAME);
|
||||
let willChange: boolean;
|
||||
if(enable !== undefined) {
|
||||
if(enable) {
|
||||
|
@ -286,9 +317,15 @@ export class AppSidebarRight extends SidebarSlider {
|
|||
}
|
||||
|
||||
const set = () => {
|
||||
this.sidebarEl.classList.toggle('active', enable);
|
||||
document.body.classList.toggle(COLUMN_ACTIVE_CLASSNAME, enable);
|
||||
};
|
||||
|
||||
set();
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, 200);
|
||||
});
|
||||
//return Promise.resolve();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const hidden: {element: HTMLDivElement, height: number}[] = [];
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
|
@ -483,6 +520,12 @@ export class AppSidebarRight extends SidebarSlider {
|
|||
const needBlurCallback = needBlur ? () => {
|
||||
//void img.offsetLeft; // reflow
|
||||
img.style.opacity = '';
|
||||
|
||||
if(thumb) {
|
||||
window.setTimeout(() => {
|
||||
thumb.remove();
|
||||
}, 200);
|
||||
}
|
||||
} : undefined;
|
||||
renderImageFromUrl(img, url, needBlurCallback);
|
||||
}
|
||||
|
@ -515,7 +558,7 @@ export class AppSidebarRight extends SidebarSlider {
|
|||
});
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
this.log('did not loaded', thumb, media, isDownloaded, sizes);
|
||||
this.log('didn\'t load', thumb, media, isDownloaded, sizes);
|
||||
reject();
|
||||
}, 1e3);
|
||||
});
|
||||
|
@ -780,8 +823,10 @@ export class AppSidebarRight extends SidebarSlider {
|
|||
const inputFilter = contentToSharedMap[key];
|
||||
if(!this.historiesStorage[this.peerID] || !this.historiesStorage[this.peerID][inputFilter]) {
|
||||
const parent = this.sharedMedia[key].parentElement;
|
||||
if(!parent.querySelector('.preloader')) {
|
||||
putPreloader(parent, true);
|
||||
if(!testScroll) {
|
||||
if(!parent.querySelector('.preloader')) {
|
||||
putPreloader(parent, true);
|
||||
}
|
||||
}
|
||||
|
||||
const empty = parent.querySelector('.content-empty');
|
||||
|
@ -792,16 +837,14 @@ export class AppSidebarRight extends SidebarSlider {
|
|||
});
|
||||
|
||||
if(testScroll) {
|
||||
for(let i = 0; i < 30; ++i) {
|
||||
//div.insertAdjacentHTML('beforeend', `<div style="background-image: url(assets/img/camomile.jpg);"></div>`);
|
||||
for(let i = 0; i < 1500; ++i) {
|
||||
let div = document.createElement('div');
|
||||
div.insertAdjacentHTML('beforeend', `<img class="media-image" src="assets/img/camomile.jpg">`);
|
||||
div.classList.add('media-item');
|
||||
div.dataset.id = '' + (i / 3 | 0);
|
||||
div.innerText = '' + (i / 3 | 0);
|
||||
//div.innerText = '' + (i / 3 | 0);
|
||||
this.sharedMedia.contentMedia.append(div);
|
||||
}
|
||||
|
||||
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
|
||||
}
|
||||
|
||||
(this.profileTabs.firstElementChild.children[1] as HTMLLIElement).click(); // set media
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
// @ts-ignore
|
||||
import {BigInteger, SecureRandom} from 'jsbn';
|
||||
import { InputFileLocation, FileLocation } from '../layer';
|
||||
import type { InputFileLocation, FileLocation } from '../layer';
|
||||
|
||||
/// #if !MTPROTO_WORKER
|
||||
// @ts-ignore
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
|||
import { convertToArrayBuffer, convertToByteArray } from "../bin_utils";
|
||||
import { InputCheckPasswordSRP } from "../../layer";
|
||||
import type { InputCheckPasswordSRP } from "../../layer";
|
||||
|
||||
export default abstract class CryptoWorkerMethods {
|
||||
abstract performTaskWorker<T>(task: string, ...args: any[]): Promise<T>;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import {dT} from '../bin_utils';
|
||||
import CryptoWorkerMethods from './crypto_methods';
|
||||
|
||||
type Task = {
|
||||
|
@ -24,7 +23,7 @@ class CryptoWorker extends CryptoWorkerMethods {
|
|||
|
||||
constructor() {
|
||||
super();
|
||||
console.log(dT(), 'CW constructor');
|
||||
console.log('CW constructor');
|
||||
|
||||
/// #if MTPROTO_WORKER
|
||||
Promise.all([
|
||||
|
@ -60,7 +59,7 @@ class CryptoWorker extends CryptoWorkerMethods {
|
|||
tmpWorker.onmessage = (e: any) => {
|
||||
if(!this.webWorker) {
|
||||
this.webWorker = tmpWorker;
|
||||
console.info(dT(), 'CW set webWorker');
|
||||
console.info('CW set webWorker');
|
||||
this.releasePending();
|
||||
} else {
|
||||
this.finalizeTask(e.data.taskID, e.data.result);
|
||||
|
@ -76,17 +75,19 @@ class CryptoWorker extends CryptoWorkerMethods {
|
|||
/// #endif
|
||||
}
|
||||
|
||||
/// #if !MTPROTO_WORKER
|
||||
private finalizeTask(taskID: number, result: any) {
|
||||
let deferred = this.awaiting[taskID];
|
||||
if(deferred !== undefined) {
|
||||
this.debug && console.log(dT(), 'CW done', deferred.taskName, result);
|
||||
this.debug && console.log('CW done', deferred.taskName, result);
|
||||
deferred.resolve(result);
|
||||
delete this.awaiting[taskID];
|
||||
}
|
||||
}
|
||||
/// #endif
|
||||
|
||||
public performTaskWorker<T>(task: string, ...args: any[]) {
|
||||
this.debug && console.log(dT(), 'CW start', task, args);
|
||||
this.debug && console.log('CW start', task, args);
|
||||
|
||||
/// #if MTPROTO_WORKER
|
||||
return Promise.resolve<T>(this.utils[task](...args));
|
||||
|
@ -109,6 +110,7 @@ class CryptoWorker extends CryptoWorkerMethods {
|
|||
/// #endif
|
||||
}
|
||||
|
||||
/// #if !MTPROTO_WORKER
|
||||
private releasePending() {
|
||||
if(this.webWorker) {
|
||||
this.pending.forEach(pending => {
|
||||
|
@ -118,6 +120,7 @@ class CryptoWorker extends CryptoWorkerMethods {
|
|||
this.pending.length = 0;
|
||||
}
|
||||
}
|
||||
/// #endif
|
||||
}
|
||||
|
||||
const cryptoWorker = new CryptoWorker();
|
||||
|
|
368
src/lib/idb.ts
368
src/lib/idb.ts
|
@ -1,368 +0,0 @@
|
|||
import {blobConstruct, bytesToBase64, blobSafeMimeType, dataUrlToBlob} from './bin_utils';
|
||||
import FileManager from './filemanager';
|
||||
import { logger } from './logger';
|
||||
|
||||
class IdbFileStorage {
|
||||
public dbName = 'cachedFiles';
|
||||
public dbStoreName = 'files';
|
||||
public dbVersion = 2;
|
||||
public openDbPromise: Promise<IDBDatabase>;
|
||||
public storageIsAvailable = true;
|
||||
public name = 'IndexedDB';
|
||||
|
||||
private log: ReturnType<typeof logger> = logger('IDB');
|
||||
|
||||
constructor() {
|
||||
this.openDatabase(true);
|
||||
}
|
||||
|
||||
public isAvailable() {
|
||||
return this.storageIsAvailable;
|
||||
}
|
||||
|
||||
public openDatabase(createNew = false): Promise<IDBDatabase> {
|
||||
if(this.openDbPromise && !createNew) {
|
||||
return this.openDbPromise;
|
||||
}
|
||||
|
||||
const createObjectStore = (db: IDBDatabase) => {
|
||||
db.createObjectStore(this.dbStoreName);
|
||||
};
|
||||
|
||||
try {
|
||||
var request = indexedDB.open(this.dbName, this.dbVersion);
|
||||
|
||||
if(!request) {
|
||||
throw new Error();
|
||||
}
|
||||
} catch(error) {
|
||||
this.log.error('error opening db', error.message)
|
||||
this.storageIsAvailable = false;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
let finished = false;
|
||||
setTimeout(() => {
|
||||
if(!finished) {
|
||||
request.onerror({type: 'IDB_CREATE_TIMEOUT'} as Event);
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
return this.openDbPromise = new Promise<IDBDatabase>((resolve, reject) => {
|
||||
request.onsuccess = (event) => {
|
||||
finished = true;
|
||||
const db = request.result;
|
||||
let calledNew = false;
|
||||
|
||||
this.log('Opened');
|
||||
|
||||
db.onerror = (error) => {
|
||||
this.storageIsAvailable = false;
|
||||
this.log.error('Error creating/accessing IndexedDB database', error);
|
||||
reject(error);
|
||||
};
|
||||
|
||||
db.onclose = (e) => {
|
||||
this.log.error('closed:', e);
|
||||
!calledNew && this.openDatabase();
|
||||
};
|
||||
|
||||
db.onabort = (e) => {
|
||||
this.log.error('abort:', e);
|
||||
const transaction = e.target as IDBTransaction;
|
||||
|
||||
this.openDatabase(calledNew = true);
|
||||
|
||||
if(transaction.onerror) {
|
||||
transaction.onerror(e);
|
||||
}
|
||||
|
||||
db.close();
|
||||
};
|
||||
|
||||
db.onversionchange = (e) => {
|
||||
this.log.error('onversionchange, lol?');
|
||||
};
|
||||
|
||||
resolve(db);
|
||||
};
|
||||
|
||||
request.onerror = (event) => {
|
||||
finished = true;
|
||||
this.storageIsAvailable = false;
|
||||
this.log.error('Error creating/accessing IndexedDB database', event);
|
||||
reject(event);
|
||||
};
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
finished = true;
|
||||
this.log.warn('performing idb upgrade from', event.oldVersion, 'to', event.newVersion);
|
||||
|
||||
// @ts-ignore
|
||||
var db = event.target.result as IDBDatabase;
|
||||
if(event.oldVersion == 1) {
|
||||
db.deleteObjectStore(this.dbStoreName);
|
||||
}
|
||||
|
||||
createObjectStore(db);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public deleteFile(fileName: string): Promise<void> {
|
||||
//return Promise.resolve();
|
||||
return this.openDatabase().then((db) => {
|
||||
try {
|
||||
this.log('Delete file: `' + fileName + '`');
|
||||
var objectStore = db.transaction([this.dbStoreName], 'readwrite')
|
||||
.objectStore(this.dbStoreName);
|
||||
|
||||
var request = objectStore.delete(fileName);
|
||||
} catch(error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
this.log.error('deleteFile request not finished!', fileName, request);
|
||||
resolve();
|
||||
}, 3000);
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
this.log('deleted file', event);
|
||||
resolve();
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
|
||||
request.onerror = (error) => {
|
||||
reject(error);
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public saveFile(fileName: string, blob: Blob | Uint8Array): Promise<Blob> {
|
||||
return Promise.resolve(blobConstruct([blob]));
|
||||
return this.openDatabase().then((db) => {
|
||||
if(!(blob instanceof Blob)) {
|
||||
blob = blobConstruct([blob]) as Blob;
|
||||
}
|
||||
|
||||
this.log('saveFile:', fileName, blob);
|
||||
|
||||
const handleError = (error: Error) => {
|
||||
this.log.error('saveFile transaction error:', fileName, blob, db, error, error && error.name);
|
||||
if((!error || error.name === 'InvalidStateError')/* && false */) {
|
||||
setTimeout(() => {
|
||||
this.saveFile(fileName, blob);
|
||||
}, 2e3);
|
||||
} else {
|
||||
//console.error('IndexedDB saveFile transaction error:', error, error && error.name);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const transaction = db.transaction([this.dbStoreName], 'readwrite');
|
||||
transaction.onerror = (e) => {
|
||||
handleError(transaction.error);
|
||||
};
|
||||
transaction.oncomplete = (e) => {
|
||||
this.log('saveFile transaction complete:', fileName);
|
||||
};
|
||||
|
||||
/* transaction.addEventListener('abort', (e) => {
|
||||
//handleError();
|
||||
this.log.error('IndexedDB: saveFile transaction abort!', transaction.error);
|
||||
}); */
|
||||
|
||||
const objectStore = transaction.objectStore(this.dbStoreName);
|
||||
var request = objectStore.put(blob, fileName);
|
||||
} catch(error) {
|
||||
handleError(error);
|
||||
return blob;
|
||||
|
||||
/* this.storageIsAvailable = false;
|
||||
throw error; */
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
this.log.error('saveFile request not finished', fileName, request);
|
||||
}, 3000);
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
resolve(blob as Blob);
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
|
||||
request.onerror = (error) => {
|
||||
reject(error);
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public saveFileBase64(db: IDBDatabase, fileName: string, blob: Blob | any): Promise<Blob> {
|
||||
if(this.getBlobSize(blob) > 10 * 1024 * 1024) {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
if(!(blob instanceof Blob)) {
|
||||
var safeMimeType = blobSafeMimeType(blob.type || 'image/jpeg');
|
||||
var address = 'data:' + safeMimeType + ';base64,' + bytesToBase64(blob);
|
||||
return this.storagePutB64String(db, fileName, address).then(() => {
|
||||
return blob;
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
var reader = new FileReader();
|
||||
} catch (e) {
|
||||
this.storageIsAvailable = false;
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
let promise = new Promise<Blob>((resolve, reject) => {
|
||||
reader.onloadend = () => {
|
||||
this.storagePutB64String(db, fileName, reader.result as string).then(() => {
|
||||
resolve(blob);
|
||||
}, reject);
|
||||
}
|
||||
|
||||
reader.onerror = reject;
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
reader.readAsDataURL(blob);
|
||||
} catch (e) {
|
||||
this.storageIsAvailable = false;
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
public storagePutB64String(db: IDBDatabase, fileName: string, b64string: string) {
|
||||
try {
|
||||
var objectStore = db.transaction([this.dbStoreName], 'readwrite')
|
||||
.objectStore(this.dbStoreName);
|
||||
var request = objectStore.put(b64string, fileName);
|
||||
} catch(error) {
|
||||
this.storageIsAvailable = false;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request.onsuccess = function(event) {
|
||||
resolve();
|
||||
};
|
||||
|
||||
request.onerror = reject;
|
||||
});
|
||||
}
|
||||
|
||||
public getBlobSize(blob: any) {
|
||||
return blob.size || blob.byteLength || blob.length;
|
||||
}
|
||||
|
||||
public getFile(fileName: string): Promise<Blob> {
|
||||
//return Promise.reject();
|
||||
return this.openDatabase().then((db) => {
|
||||
this.log('getFile pre:', fileName);
|
||||
|
||||
try {
|
||||
const transaction = db.transaction([this.dbStoreName], 'readonly');
|
||||
transaction.onabort = (e) => {
|
||||
this.log.error('getFile transaction onabort?', e);
|
||||
};
|
||||
const objectStore = transaction.objectStore(this.dbStoreName);
|
||||
var request = objectStore.get(fileName);
|
||||
|
||||
//this.log.log('IDB getFile:', fileName, request);
|
||||
} catch(err) {
|
||||
this.log.error('getFile error:', err, fileName, request, request.error);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
this.log.error('getFile request not finished!', fileName, request);
|
||||
reject();
|
||||
}, 3000);
|
||||
|
||||
request.onsuccess = function(event) {
|
||||
const result = request.result;
|
||||
if(result === undefined) {
|
||||
reject();
|
||||
} else if(typeof result === 'string' &&
|
||||
result.substr(0, 5) === 'data:') {
|
||||
resolve(dataUrlToBlob(result));
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
request.onerror = () => {
|
||||
clearTimeout(timeout);
|
||||
reject();
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getAllKeys(): Promise<Array<string>> {
|
||||
console.time('getAllEntries');
|
||||
return this.openDatabase().then((db) => {
|
||||
var objectStore = db.transaction([this.dbStoreName], 'readonly')
|
||||
.objectStore(this.dbStoreName);
|
||||
var request = objectStore.getAllKeys();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request.onsuccess = function(event) {
|
||||
// @ts-ignore
|
||||
var result = event.target.result;
|
||||
resolve(result);
|
||||
console.timeEnd('getAllEntries');
|
||||
}
|
||||
|
||||
request.onerror = reject;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public isFileExists(fileName: string): Promise<boolean> {
|
||||
console.time('isFileExists');
|
||||
return this.openDatabase().then((db) => {
|
||||
var objectStore = db.transaction([this.dbStoreName], 'readonly')
|
||||
.objectStore(this.dbStoreName);
|
||||
var request = objectStore.openCursor(fileName);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request.onsuccess = function(event) {
|
||||
// @ts-ignore
|
||||
var cursor = event.target.result;
|
||||
resolve(!!cursor);
|
||||
console.timeEnd('isFileExists');
|
||||
}
|
||||
|
||||
request.onerror = reject;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getFileWriter(fileName: string, mimeType: string) {
|
||||
var fakeWriter = FileManager.getFakeFileWriter(mimeType, (blob) => {
|
||||
return this.saveFile(fileName, blob);
|
||||
});
|
||||
|
||||
return Promise.resolve(fakeWriter);
|
||||
}
|
||||
}
|
||||
|
||||
const idbFileStorage = new IdbFileStorage();
|
||||
(window as any).IdbFileStorage = idbFileStorage;
|
||||
export default idbFileStorage;
|
|
@ -1,5 +0,0 @@
|
|||
// @ts-ignore
|
||||
//import LottiePlayer from "lottie-web/build/player/lottie_canvas.min.js";
|
||||
import LottiePlayer from "lottie-web/build/player/lottie_light.min.js";
|
||||
|
||||
(window as any).lottie = LottiePlayer;
|
|
@ -1,8 +1,10 @@
|
|||
import { isApple, mediaSizes, isSafari } from "./config";
|
||||
import { logger, LogLevels } from "./logger";
|
||||
import animationIntersector from "../components/animationIntersector";
|
||||
import apiManager from "./mtproto/mtprotoworker";
|
||||
import { copy } from "./utils";
|
||||
import EventListenerBase from "../helpers/eventListenerBase";
|
||||
import mediaSizes from "../helpers/mediaSizes";
|
||||
import { isApple, isSafari } from "../helpers/userAgent";
|
||||
|
||||
let convert = (value: number) => {
|
||||
return Math.round(Math.min(Math.max(value, 0), 1) * 255);
|
||||
|
@ -21,7 +23,12 @@ type RLottieOptions = {
|
|||
needUpscale?: true
|
||||
};
|
||||
|
||||
export class RLottiePlayer {
|
||||
export class RLottiePlayer extends EventListenerBase<{
|
||||
enterFrame: (frameNo: number) => void,
|
||||
ready: () => void,
|
||||
firstFrame: () => void,
|
||||
cached: () => void
|
||||
}> {
|
||||
public static reqId = 0;
|
||||
|
||||
public reqId = 0;
|
||||
|
@ -34,13 +41,6 @@ export class RLottiePlayer {
|
|||
public width = 0;
|
||||
public height = 0;
|
||||
|
||||
public listeners: Partial<{
|
||||
[k in RLottiePlayerListeners]: Array<{callback: (res: any) => void, once?: true}>
|
||||
}> = {};
|
||||
public listenerResults: Partial<{
|
||||
[k in RLottiePlayerListeners]: any
|
||||
}> = {};
|
||||
|
||||
public el: HTMLElement;
|
||||
public canvas: HTMLCanvasElement;
|
||||
public context: CanvasRenderingContext2D;
|
||||
|
@ -75,6 +75,8 @@ export class RLottiePlayer {
|
|||
worker: QueryableWorker,
|
||||
options: RLottieOptions
|
||||
}) {
|
||||
super(true);
|
||||
|
||||
this.reqId = ++RLottiePlayer['reqId'];
|
||||
this.el = el;
|
||||
this.worker = worker;
|
||||
|
@ -138,37 +140,6 @@ export class RLottiePlayer {
|
|||
this.frames = {};
|
||||
}
|
||||
|
||||
public addListener(name: RLottiePlayerListeners, callback: (res?: any) => void, once?: true) {
|
||||
(this.listeners[name] ?? (this.listeners[name] = [])).push({callback, once});
|
||||
|
||||
if(this.listenerResults.hasOwnProperty(name)) {
|
||||
callback(this.listenerResults[name]);
|
||||
|
||||
if(once) {
|
||||
this.removeListener(name, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public removeListener(name: RLottiePlayerListeners, callback: (res?: any) => void) {
|
||||
if(this.listeners[name]) {
|
||||
this.listeners[name].findAndSplice(l => l.callback == callback);
|
||||
}
|
||||
}
|
||||
|
||||
public setListenerResult(name: RLottiePlayerListeners, value?: any) {
|
||||
this.listenerResults[name] = value;
|
||||
if(this.listeners[name]) {
|
||||
this.listeners[name].forEach(listener => {
|
||||
listener.callback(value);
|
||||
|
||||
if(listener.once) {
|
||||
this.removeListener(name, listener.callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public sendQuery(methodName: string, ...args: any[]) {
|
||||
//console.trace('RLottie sendQuery:', methodName);
|
||||
this.worker.sendQuery(methodName, this.reqId, ...args);
|
||||
|
@ -424,11 +395,12 @@ export class RLottiePlayer {
|
|||
}
|
||||
}
|
||||
|
||||
class QueryableWorker {
|
||||
class QueryableWorker extends EventListenerBase<any> {
|
||||
private worker: Worker;
|
||||
private listeners: {[name: string]: (...args: any[]) => void} = {};
|
||||
|
||||
constructor(url: string, private defaultListener: (data: any) => void = () => {}, onError?: (error: any) => void) {
|
||||
super();
|
||||
|
||||
this.worker = new Worker(url);
|
||||
if(onError) {
|
||||
this.worker.onerror = onError;
|
||||
|
@ -444,7 +416,7 @@ class QueryableWorker {
|
|||
return;
|
||||
} */
|
||||
|
||||
this.listeners[event.data.queryMethodListener](...event.data.queryMethodArguments);
|
||||
this.setListenerResult(event.data.queryMethodListener, ...event.data.queryMethodArguments);
|
||||
} else {
|
||||
this.defaultListener.call(this, event.data);
|
||||
}
|
||||
|
@ -459,14 +431,6 @@ class QueryableWorker {
|
|||
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[]) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if(isSafari) {
|
||||
|
|
|
@ -3,12 +3,12 @@ import { nextRandomInt, getFileNameByLocation } from "../bin_utils";
|
|||
import cacheStorage from "../cacheStorage";
|
||||
import FileManager from "../filemanager";
|
||||
import apiManager from "./apiManager";
|
||||
import { deferredPromise, CancellablePromise } from "../polyfill";
|
||||
import { logger, LogLevels } from "../logger";
|
||||
import { isSafari } from "../../helpers/userAgent";
|
||||
import cryptoWorker from "../crypto/cryptoworker";
|
||||
import { notifySomeone, notifyAll } from "../../helpers/context";
|
||||
import { InputFileLocation, FileLocation, InputFile, UploadFile } from "../../layer";
|
||||
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
|
||||
|
||||
type Delayed = {
|
||||
offset: number,
|
||||
|
|
|
@ -2,9 +2,9 @@ import { isSafari } from '../../helpers/userAgent';
|
|||
import { logger, LogLevels } from '../logger';
|
||||
import type { DownloadOptions } from './apiFileManager';
|
||||
import type { WorkerTaskTemplate } from '../../types';
|
||||
import { deferredPromise, CancellablePromise } from '../polyfill';
|
||||
import { notifySomeone } from '../../helpers/context';
|
||||
import { InputFileLocation, FileLocation, UploadFile } from '../../layer';
|
||||
import type { InputFileLocation, FileLocation, UploadFile } from '../../layer';
|
||||
import { CancellablePromise, deferredPromise } from '../../helpers/cancellablePromise';
|
||||
|
||||
const log = logger('SW', LogLevels.error/* | LogLevels.debug | LogLevels.log */);
|
||||
const ctx = self as any as ServiceWorkerGlobalScope;
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
// just to include
|
||||
import {secureRandom, CancellablePromise} from '../polyfill';
|
||||
import {secureRandom} from '../polyfill';
|
||||
secureRandom;
|
||||
|
||||
import apiManager from "./apiManager";
|
||||
import AppStorage from '../storage';
|
||||
import cryptoWorker from "../crypto/cryptoworker";
|
||||
import networkerFactory from "./networkerFactory";
|
||||
import apiFileManager, { ApiFileManager } from './apiFileManager';
|
||||
import apiFileManager from './apiFileManager';
|
||||
import { logger, LogLevels } from '../logger';
|
||||
import type { ServiceWorkerTask, ServiceWorkerTaskResponse } from './mtproto.service';
|
||||
import { UploadFile } from '../../layer';
|
||||
|
||||
const log = logger('DW', LogLevels.error);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { isSafari } from "./config";
|
||||
import { isSafari } from "../helpers/userAgent";
|
||||
import { logger, LogLevels } from "./logger";
|
||||
|
||||
type Result = {
|
||||
|
@ -132,7 +132,7 @@ export class OpusDecodeController {
|
|||
}, isSafari ? undefined : [task.pages.buffer]);
|
||||
//}, 1e3);
|
||||
|
||||
task.timeout = setTimeout(() => {
|
||||
task.timeout = window.setTimeout(() => {
|
||||
this.log.error('decode timeout'/* , task */);
|
||||
|
||||
this.terminateWorkers(true);
|
||||
|
|
|
@ -4,70 +4,6 @@ import {SecureRandom} from 'jsbn';
|
|||
|
||||
export const secureRandom = new SecureRandom();
|
||||
|
||||
export interface CancellablePromise<T> extends Promise<T> {
|
||||
resolve?: (...args: any[]) => void,
|
||||
reject?: (...args: any[]) => void,
|
||||
cancel?: () => void,
|
||||
|
||||
notify?: (...args: any[]) => void,
|
||||
notifyAll?: (...args: any[]) => void,
|
||||
lastNotify?: any,
|
||||
listeners?: Array<(...args: any[]) => void>,
|
||||
addNotifyListener?: (callback: (...args: any[]) => void) => void,
|
||||
|
||||
isFulfilled?: boolean,
|
||||
isRejected?: boolean
|
||||
}
|
||||
|
||||
export function deferredPromise<T>() {
|
||||
let deferredHelper: any = {
|
||||
isFulfilled: false,
|
||||
isRejected: false,
|
||||
|
||||
notify: () => {},
|
||||
notifyAll: (...args: any[]) => {
|
||||
deferredHelper.lastNotify = args;
|
||||
deferredHelper.listeners.forEach((callback: any) => callback(...args));
|
||||
},
|
||||
|
||||
lastNotify: undefined,
|
||||
listeners: [],
|
||||
addNotifyListener: (callback: (...args: any[]) => void) => {
|
||||
if(deferredHelper.lastNotify) {
|
||||
callback(...deferredHelper.lastNotify);
|
||||
}
|
||||
|
||||
deferredHelper.listeners.push(callback);
|
||||
}
|
||||
};
|
||||
|
||||
let deferred: CancellablePromise<T> = new Promise<T>((resolve, reject) => {
|
||||
deferredHelper.resolve = (value: T) => {
|
||||
if(deferred.isFulfilled) return;
|
||||
|
||||
deferred.isFulfilled = true;
|
||||
resolve(value);
|
||||
};
|
||||
|
||||
deferredHelper.reject = (...args: any[]) => {
|
||||
if(deferred.isRejected) return;
|
||||
|
||||
deferred.isRejected = true;
|
||||
reject(...args);
|
||||
};
|
||||
});
|
||||
|
||||
deferred.finally(() => {
|
||||
deferred.notify = null;
|
||||
deferred.listeners.length = 0;
|
||||
deferred.lastNotify = null;
|
||||
});
|
||||
|
||||
Object.assign(deferred, deferredHelper);
|
||||
|
||||
return deferred;
|
||||
}
|
||||
|
||||
Object.defineProperty(Uint8Array.prototype, 'hex', {
|
||||
get: function(): string {
|
||||
return bytesToHex([...this]);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import WebpWorker from 'worker-loader!./webp.worker';
|
||||
import { CancellablePromise, deferredPromise } from '../polyfill';
|
||||
import { CancellablePromise, deferredPromise } from '../../helpers/cancellablePromise';
|
||||
import apiManagerProxy from '../mtproto/mtprotoworker';
|
||||
|
||||
export type WebpConvertTask = {
|
||||
|
|
|
@ -8,7 +8,7 @@ import LottieLoader, { RLottiePlayer } from '../lib/lottieLoader';
|
|||
import apiManager from '../lib/mtproto/mtprotoworker';
|
||||
import Page from './page';
|
||||
import { App } from '../lib/mtproto/mtproto_config';
|
||||
import { mediaSizes } from '../lib/config';
|
||||
import mediaSizes from '../helpers/mediaSizes';
|
||||
|
||||
let authCode: {
|
||||
_: string, // 'auth.sentCode'
|
||||
|
|
|
@ -7,10 +7,10 @@ import LottieLoader, { RLottiePlayer } from '../lib/lottieLoader';
|
|||
//import passwordManager from '../lib/mtproto/passwordManager';
|
||||
import apiManager from '../lib/mtproto/mtprotoworker';
|
||||
import Page from './page';
|
||||
import { mediaSizes } from '../lib/config';
|
||||
import passwordManager from '../lib/mtproto/passwordManager';
|
||||
import { cancelEvent } from '../lib/utils';
|
||||
import { AccountPassword } from '../layer';
|
||||
import mediaSizes from '../helpers/mediaSizes';
|
||||
|
||||
let onFirstMount = (): Promise<any> => {
|
||||
let needFrame = 0;
|
||||
|
|
|
@ -105,7 +105,7 @@ Utility Classes
|
|||
}
|
||||
|
||||
.position-center {
|
||||
position: absolute;
|
||||
position: absolute !important;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
.audio {
|
||||
position: relative;
|
||||
padding-left: 67px;
|
||||
min-height: 58px;
|
||||
max-width: 244px;
|
||||
overflow: visible!important;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
padding-left: 45px;
|
||||
}
|
||||
|
||||
&-toggle, &-download {
|
||||
border-radius: 50%;
|
||||
background-color: $color-blue;
|
||||
font-size: 2.3rem;
|
||||
align-items: center;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
font-size: 24px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&-download {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&-waveform {
|
||||
height: 23px;
|
||||
|
||||
//overflow: visible!important;
|
||||
|
||||
rect {
|
||||
//overflow: visible!important;
|
||||
fill: #CBCBCB;
|
||||
|
||||
&.active {
|
||||
fill: $color-blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-title {
|
||||
font-size: 1rem;
|
||||
color: #000;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
&-time, &-subtitle {
|
||||
font-size: 14px;
|
||||
color: $color-gray;
|
||||
margin-top: 3px;
|
||||
margin-left: -1px;
|
||||
user-select: none;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
margin-top: 1px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&-title, &:not(.audio-show-progress) &-subtitle {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
&-download {
|
||||
/* background: transparent; */
|
||||
margin-left: 2px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
&.is-voice {
|
||||
.audio-download {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
avatar-element {
|
||||
color: #fff;
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
line-height: 54px;
|
||||
border-radius: 50%;
|
||||
background-color: $color-blue;
|
||||
text-align: center;
|
||||
font-size: 1.25em;
|
||||
/* overflow: hidden; */
|
||||
position: relative;
|
||||
user-select: none;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* kostil */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
user-select: none;
|
||||
|
||||
&.fade-in {
|
||||
animation: fadeIn .2s ease forwards;
|
||||
}
|
||||
}
|
||||
|
||||
&[class*=" tgico-"] {
|
||||
line-height: 52px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
path {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
&.is-online:after {
|
||||
position: absolute;
|
||||
content: " ";
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
border: 2px solid white;
|
||||
background-color: #0ac630;
|
||||
left: 74%;
|
||||
top: 73%;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
&.tgico-avatar_deletedaccount {
|
||||
font-size: 3rem;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue