Global search 'saved messages'
Fix blinking down arrown Lazy load queue fixes GIFs for Chrome
This commit is contained in:
parent
0333ecfcce
commit
e2280112b2
@ -100,6 +100,12 @@ export class AnimationIntersector {
|
|||||||
if((destroy || (!isInDOM(el) && !this.lockedGroups[group]))/* && false */) {
|
if((destroy || (!isInDOM(el) && !this.lockedGroups[group]))/* && false */) {
|
||||||
//console.log('destroy animation');
|
//console.log('destroy animation');
|
||||||
animation.remove();
|
animation.remove();
|
||||||
|
|
||||||
|
if(animation instanceof HTMLVideoElement) {
|
||||||
|
animation.src = '';
|
||||||
|
animation.load();
|
||||||
|
}
|
||||||
|
|
||||||
for(const group in this.byGroups) {
|
for(const group in this.byGroups) {
|
||||||
this.byGroups[group].findAndSplice(p => p == player);
|
this.byGroups[group].findAndSplice(p => p == player);
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,11 @@ import appMessagesIDsManager from "../lib/appManagers/appMessagesIDsManager";
|
|||||||
import appUsersManager from "../lib/appManagers/appUsersManager";
|
import appUsersManager from "../lib/appManagers/appUsersManager";
|
||||||
import appPeersManager from '../lib/appManagers/appPeersManager';
|
import appPeersManager from '../lib/appManagers/appPeersManager';
|
||||||
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
||||||
import { escapeRegExp } from "../lib/utils";
|
import { $rootScope, escapeRegExp } from "../lib/utils";
|
||||||
import { formatPhoneNumber } from "./misc";
|
import { formatPhoneNumber } from "./misc";
|
||||||
import appChatsManager from "../lib/appManagers/appChatsManager";
|
import appChatsManager from "../lib/appManagers/appChatsManager";
|
||||||
import SearchInput from "./searchInput";
|
import SearchInput from "./searchInput";
|
||||||
|
import { Peer } from "../layer";
|
||||||
|
|
||||||
export class SearchGroup {
|
export class SearchGroup {
|
||||||
container: HTMLDivElement;
|
container: HTMLDivElement;
|
||||||
@ -48,6 +49,11 @@ export class SearchGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * Saved будет использована только для вывода одного элемента - избранное
|
||||||
|
*/
|
||||||
|
type SearchGroupType = 'saved' | 'contacts' | 'globalContacts' | 'messages' | string;
|
||||||
|
|
||||||
export default class AppSearch {
|
export default class AppSearch {
|
||||||
private minMsgID = 0;
|
private minMsgID = 0;
|
||||||
private loadedCount = -1;
|
private loadedCount = -1;
|
||||||
@ -66,15 +72,15 @@ export default class AppSearch {
|
|||||||
|
|
||||||
private scrollable: Scrollable;
|
private scrollable: Scrollable;
|
||||||
|
|
||||||
constructor(public container: HTMLElement, public searchInput: SearchInput, public searchGroups: {[group: string]: SearchGroup}, public onSearch?: (count: number) => void) {
|
constructor(public container: HTMLElement, public searchInput: SearchInput, public searchGroups: {[group in SearchGroupType]: SearchGroup}, public onSearch?: (count: number) => void) {
|
||||||
this.scrollable = new Scrollable(this.container);
|
this.scrollable = new Scrollable(this.container);
|
||||||
this.listsContainer = this.scrollable.container as HTMLDivElement;
|
this.listsContainer = this.scrollable.container as HTMLDivElement;
|
||||||
for(let i in this.searchGroups) {
|
for(let i in this.searchGroups) {
|
||||||
this.listsContainer.append(this.searchGroups[i].container);
|
this.listsContainer.append(this.searchGroups[i as SearchGroupType].container);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.searchGroups['messages']) {
|
if(this.searchGroups.messages) {
|
||||||
this.scrollable.setVirtualContainer(this.searchGroups['messages'].list);
|
this.scrollable.setVirtualContainer(this.searchGroups.messages.list);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.searchInput.onChange = (value) => {
|
this.searchInput.onChange = (value) => {
|
||||||
@ -92,7 +98,7 @@ export default class AppSearch {
|
|||||||
if(!this.query.trim()) return;
|
if(!this.query.trim()) return;
|
||||||
|
|
||||||
if(!this.searchTimeout) {
|
if(!this.searchTimeout) {
|
||||||
this.searchTimeout = setTimeout(() => {
|
this.searchTimeout = window.setTimeout(() => {
|
||||||
this.searchMore();
|
this.searchMore();
|
||||||
this.searchTimeout = 0;
|
this.searchTimeout = 0;
|
||||||
}, 0);
|
}, 0);
|
||||||
@ -114,7 +120,7 @@ export default class AppSearch {
|
|||||||
this.loadedContacts = false;
|
this.loadedContacts = false;
|
||||||
|
|
||||||
for(let i in this.searchGroups) {
|
for(let i in this.searchGroups) {
|
||||||
this.searchGroups[i].clear();
|
this.searchGroups[i as SearchGroupType].clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.searchPromise = null;
|
this.searchPromise = null;
|
||||||
@ -127,6 +133,13 @@ export default class AppSearch {
|
|||||||
|
|
||||||
this.searchInput.input.focus();
|
this.searchInput.input.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private renderSaved() {
|
||||||
|
const group = this.searchGroups.contacts;
|
||||||
|
let {dialog, dom} = appDialogsManager.addDialog($rootScope.myID, group.list, false);
|
||||||
|
dom.lastMessageSpan.innerHTML = 'chat with yourself';
|
||||||
|
group.setActive();
|
||||||
|
}
|
||||||
|
|
||||||
public searchMore() {
|
public searchMore() {
|
||||||
if(this.searchPromise) return this.searchPromise;
|
if(this.searchPromise) return this.searchPromise;
|
||||||
@ -145,18 +158,40 @@ export default class AppSearch {
|
|||||||
const maxID = appMessagesIDsManager.getMessageIDInfo(this.minMsgID)[0] || 0;
|
const maxID = appMessagesIDsManager.getMessageIDInfo(this.minMsgID)[0] || 0;
|
||||||
|
|
||||||
if(!this.peerID && !maxID && !this.loadedContacts) {
|
if(!this.peerID && !maxID && !this.loadedContacts) {
|
||||||
appUsersManager.searchContacts(query, 20).then((contacts: any) => {
|
let renderedSaved = false;
|
||||||
|
if('saved messages'.includes(query.toLowerCase())
|
||||||
|
|| appUsersManager.getUser($rootScope.myID).sortName.includes(query.toLowerCase())/* && this.searchGroups.hasOwnProperty('saved') */) {
|
||||||
|
this.renderSaved();
|
||||||
|
renderedSaved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
appUsersManager.searchContacts(query, 20).then((contacts) => {
|
||||||
if(this.searchInput.value != query) {
|
if(this.searchInput.value != query) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadedContacts = true;
|
this.loadedContacts = true;
|
||||||
|
|
||||||
///////this.log('input search contacts result:', contacts);
|
// set saved message as first peer to render
|
||||||
|
const peer = contacts.my_results.findAndSplice(p => (p as Peer.peerUser).user_id == $rootScope.myID);
|
||||||
|
if(peer) {
|
||||||
|
contacts.my_results.unshift(peer);
|
||||||
|
}
|
||||||
|
|
||||||
let setResults = (results: any, group: SearchGroup, showMembersCount = false) => {
|
//console.log('input search contacts result:', contacts);
|
||||||
results.forEach((inputPeer: any) => {
|
|
||||||
|
let setResults = (results: Peer[], group: SearchGroup, showMembersCount = false) => {
|
||||||
|
results.forEach((inputPeer) => {
|
||||||
let peerID = appPeersManager.getPeerID(inputPeer);
|
let peerID = appPeersManager.getPeerID(inputPeer);
|
||||||
|
|
||||||
|
if(peerID == $rootScope.myID) {
|
||||||
|
if(!renderedSaved) {
|
||||||
|
this.renderSaved();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let peer = appPeersManager.getPeer(peerID);
|
let peer = appPeersManager.getPeer(peerID);
|
||||||
let originalDialog = appMessagesManager.getDialogByPeerID(peerID)[0];
|
let originalDialog = appMessagesManager.getDialogByPeerID(peerID)[0];
|
||||||
|
|
||||||
@ -194,7 +229,11 @@ export default class AppSearch {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(results.length) group.setActive();
|
if(results.length) group.setActive();
|
||||||
else group.clear();
|
else if(renderedSaved) { // удалить все пункты снизу
|
||||||
|
Array.from(group.list.children).slice(1).forEach(c => c.remove());
|
||||||
|
} else {
|
||||||
|
group.clear();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
setResults(contacts.my_results, this.searchGroups.contacts, true);
|
setResults(contacts.my_results, this.searchGroups.contacts, true);
|
||||||
@ -209,7 +248,7 @@ export default class AppSearch {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('input search result:', this.peerID, query, null, maxID, 20, res);
|
//console.log('input search result:', this.peerID, query, null, maxID, 20, res);
|
||||||
|
|
||||||
const {count, history, next_rate} = res;
|
const {count, history, next_rate} = res;
|
||||||
|
|
||||||
@ -217,7 +256,7 @@ export default class AppSearch {
|
|||||||
history.shift();
|
history.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchGroup = this.searchGroups['messages'];
|
const searchGroup = this.searchGroups.messages;
|
||||||
searchGroup.setActive();
|
searchGroup.setActive();
|
||||||
|
|
||||||
history.forEach((msgID: number) => {
|
history.forEach((msgID: number) => {
|
||||||
|
@ -13,8 +13,8 @@ export default class GifsTab implements EmoticonsTab {
|
|||||||
const gifsContainer = this.content.firstElementChild as HTMLDivElement;
|
const gifsContainer = this.content.firstElementChild as HTMLDivElement;
|
||||||
gifsContainer.addEventListener('click', EmoticonsDropdown.onMediaClick);
|
gifsContainer.addEventListener('click', EmoticonsDropdown.onMediaClick);
|
||||||
|
|
||||||
const masonry = new GifsMasonry(gifsContainer);
|
|
||||||
const scroll = new Scrollable(this.content, 'y', 'GIFS', null);
|
const scroll = new Scrollable(this.content, 'y', 'GIFS', null);
|
||||||
|
const masonry = new GifsMasonry(gifsContainer, EMOTICONSSTICKERGROUP, scroll);
|
||||||
const preloader = putPreloader(this.content, true);
|
const preloader = putPreloader(this.content, true);
|
||||||
|
|
||||||
apiManager.invokeApi('messages.getSavedGifs', {hash: 0}).then((res) => {
|
apiManager.invokeApi('messages.getSavedGifs', {hash: 0}).then((res) => {
|
||||||
@ -24,7 +24,7 @@ export default class GifsTab implements EmoticonsTab {
|
|||||||
res.gifs.forEach((doc, idx) => {
|
res.gifs.forEach((doc, idx) => {
|
||||||
res.gifs[idx] = doc = appDocsManager.saveDoc(doc);
|
res.gifs[idx] = doc = appDocsManager.saveDoc(doc);
|
||||||
//if(doc._ == 'documentEmpty') return;
|
//if(doc._ == 'documentEmpty') return;
|
||||||
masonry.add(doc as MyDocument, EMOTICONSSTICKERGROUP, EmoticonsDropdown.lazyLoadQueue);
|
//masonry.add(doc as MyDocument);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,30 +319,6 @@ export default class StickersTab implements EmoticonsTab {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/* let closed = true;
|
|
||||||
emoticonsDropdown.events.onClose.push(() => {
|
|
||||||
closed = false;
|
|
||||||
this.lazyLoadQueue.lock();
|
|
||||||
});
|
|
||||||
|
|
||||||
emoticonsDropdown.events.onCloseAfter.push(() => {
|
|
||||||
const divs = this.lazyLoadQueue.intersector.getVisible();
|
|
||||||
|
|
||||||
for(const div of divs) {
|
|
||||||
this.processInvisibleDiv(div);
|
|
||||||
}
|
|
||||||
|
|
||||||
closed = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
emoticonsDropdown.events.onOpenAfter.push(() => {
|
|
||||||
if(closed) {
|
|
||||||
this.lazyLoadQueue.unlockAndRefresh();
|
|
||||||
closed = false;
|
|
||||||
} else {
|
|
||||||
this.lazyLoadQueue.unlock();
|
|
||||||
}
|
|
||||||
}); */
|
|
||||||
emoticonsDropdown.events.onClose.push(() => {
|
emoticonsDropdown.events.onClose.push(() => {
|
||||||
this.lazyLoadQueue.lock();
|
this.lazyLoadQueue.lock();
|
||||||
});
|
});
|
||||||
|
@ -1,19 +1,151 @@
|
|||||||
import { calcImageInBox, findUpClassName } from "../lib/utils";
|
import { calcImageInBox } from "../lib/utils";
|
||||||
import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
|
import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
|
||||||
import { wrapVideo } from "./wrappers";
|
import { wrapVideo } from "./wrappers";
|
||||||
import { renderImageFromUrl } from "./misc";
|
import { renderImageFromUrl } from "./misc";
|
||||||
import LazyLoadQueue from "./lazyLoadQueue";
|
import { LazyLoadQueueRepeat2 } from "./lazyLoadQueue";
|
||||||
|
import { CancellablePromise, deferredPromise } from "../lib/polyfill";
|
||||||
|
import animationIntersector from "./animationIntersector";
|
||||||
|
import Scrollable from "./scrollable_new";
|
||||||
|
|
||||||
const width = 400;
|
const width = 400;
|
||||||
const maxSingleWidth = width - 100;
|
const maxSingleWidth = width - 100;
|
||||||
const height = 100;
|
const height = 100;
|
||||||
|
|
||||||
export default class GifsMasonry {
|
export default class GifsMasonry {
|
||||||
constructor(private element: HTMLElement) {
|
public lazyLoadQueue: LazyLoadQueueRepeat2;
|
||||||
|
private scrollPromise: CancellablePromise<void> = Promise.resolve();
|
||||||
|
|
||||||
|
constructor(private element: HTMLElement, private group: string, private scrollable: Scrollable) {
|
||||||
|
this.lazyLoadQueue = new LazyLoadQueueRepeat2(undefined, (target, visible) => {
|
||||||
|
if(visible) {
|
||||||
|
this.processVisibleDiv(target);
|
||||||
|
} else {
|
||||||
|
this.processInvisibleDiv(target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
const players = animationIntersector.byGroups[group];
|
||||||
|
|
||||||
|
if(players) {
|
||||||
|
console.log(`GIFS RENDERED IN ${group}:`, players.length, players.filter(p => !p.animation.paused).length, this.lazyLoadQueue.intersector.getVisible().length);
|
||||||
|
}
|
||||||
|
}, .25e3);
|
||||||
|
|
||||||
|
let timeout = 0;
|
||||||
|
// memory leak
|
||||||
|
scrollable.container.addEventListener('scroll', () => {
|
||||||
|
if(timeout) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
} else {
|
||||||
|
this.scrollPromise = deferredPromise<void>();
|
||||||
|
//animationIntersector.checkAnimations(true, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout = window.setTimeout(() => {
|
||||||
|
timeout = 0;
|
||||||
|
this.scrollPromise.resolve();
|
||||||
|
//animationIntersector.checkAnimations(false, group);
|
||||||
|
}, 150);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public add(doc: MyDocument, group: string, lazyLoadQueue?: LazyLoadQueue) {
|
private processVisibleDiv = (div: HTMLElement) => {
|
||||||
|
const video = div.querySelector('video');
|
||||||
|
if(video) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const load = () => {
|
||||||
|
const docID = div.dataset.docID;
|
||||||
|
const doc = appDocsManager.getDoc(docID);
|
||||||
|
|
||||||
|
const promise = this.scrollPromise.then(() => {
|
||||||
|
const promise = wrapVideo({
|
||||||
|
doc,
|
||||||
|
container: div as HTMLDivElement,
|
||||||
|
lazyLoadQueue: null,
|
||||||
|
//lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue,
|
||||||
|
group: this.group,
|
||||||
|
noInfo: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
promise.finally(() => {
|
||||||
|
const video = div.querySelector('video');
|
||||||
|
|
||||||
|
div.style.opacity = '';
|
||||||
|
const img = div.querySelector('img');
|
||||||
|
img && img.classList.add('hide');
|
||||||
|
|
||||||
|
if(video && !video.parentElement) {
|
||||||
|
setTimeout(() => {
|
||||||
|
video.src = '';
|
||||||
|
video.load();
|
||||||
|
const animations = animationIntersector.getAnimations(video);
|
||||||
|
animations.forEach(item => {
|
||||||
|
animationIntersector.checkAnimation(item, true, true);
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//clearTimeout(timeout);
|
||||||
|
if(!this.lazyLoadQueue.intersector.isVisible(div)) {
|
||||||
|
this.processInvisibleDiv(div);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
});
|
||||||
|
|
||||||
|
/* let timeout = window.setTimeout(() => {
|
||||||
|
console.error('processVisibleDiv timeout', div, doc);
|
||||||
|
}, 1e3); */
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
//return load();
|
||||||
|
|
||||||
|
this.lazyLoadQueue.push({div, load});
|
||||||
|
};
|
||||||
|
|
||||||
|
private processInvisibleDiv = async(div: HTMLElement) => {
|
||||||
|
return this.scrollPromise.then(async() => {
|
||||||
|
//return;
|
||||||
|
|
||||||
|
if(this.lazyLoadQueue.intersector.isVisible(div)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const video = div.querySelector('video');
|
||||||
|
const img = div.querySelector('img');
|
||||||
|
|
||||||
|
if(img) {
|
||||||
|
img && img.classList.remove('hide');
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
window.requestAnimationFrame(() => window.requestAnimationFrame(resolve));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.lazyLoadQueue.intersector.isVisible(div)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(video) {
|
||||||
|
video.remove();
|
||||||
|
video.src = '';
|
||||||
|
video.load();
|
||||||
|
const animations = animationIntersector.getAnimations(video);
|
||||||
|
animations.forEach(item => {
|
||||||
|
animationIntersector.checkAnimation(item, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
public add(doc: MyDocument) {
|
||||||
let gifWidth = doc.w;
|
let gifWidth = doc.w;
|
||||||
let gifHeight = doc.h;
|
let gifHeight = doc.h;
|
||||||
if(gifHeight < height) {
|
if(gifHeight < height) {
|
||||||
@ -21,8 +153,8 @@ export default class GifsMasonry {
|
|||||||
gifHeight = height;
|
gifHeight = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
let willUseWidth = Math.min(maxSingleWidth, width, gifWidth);
|
const willUseWidth = Math.min(maxSingleWidth, width, gifWidth);
|
||||||
let {w, h} = calcImageInBox(gifWidth, gifHeight, willUseWidth, height);
|
const {w, h} = calcImageInBox(gifWidth, gifHeight, willUseWidth, height);
|
||||||
|
|
||||||
/* wastedWidth += w;
|
/* wastedWidth += w;
|
||||||
|
|
||||||
@ -37,7 +169,7 @@ export default class GifsMasonry {
|
|||||||
|
|
||||||
//console.log('gif:', gif, w, h);
|
//console.log('gif:', gif, w, h);
|
||||||
|
|
||||||
let div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.classList.add('gif', 'fade-in-transition');
|
div.classList.add('gif', 'fade-in-transition');
|
||||||
div.style.width = w + 'px';
|
div.style.width = w + 'px';
|
||||||
div.style.opacity = '0';
|
div.style.opacity = '0';
|
||||||
@ -46,6 +178,9 @@ export default class GifsMasonry {
|
|||||||
|
|
||||||
this.element.append(div);
|
this.element.append(div);
|
||||||
|
|
||||||
|
//this.lazyLoadQueue.observe({div, load: this.processVisibleDiv});
|
||||||
|
this.lazyLoadQueue.observe(div);
|
||||||
|
|
||||||
//let preloader = new ProgressivePreloader(div);
|
//let preloader = new ProgressivePreloader(div);
|
||||||
|
|
||||||
const gotThumb = appDocsManager.getThumb(doc, false);
|
const gotThumb = appDocsManager.getThumb(doc, false);
|
||||||
@ -62,72 +197,11 @@ export default class GifsMasonry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mouseOut = false;
|
|
||||||
const onMouseOver = (/* e: MouseEvent */) => {
|
|
||||||
//console.log('onMouseOver', doc.id);
|
|
||||||
//cancelEvent(e);
|
|
||||||
mouseOut = false;
|
|
||||||
|
|
||||||
wrapVideo({
|
|
||||||
doc,
|
|
||||||
container: div,
|
|
||||||
lazyLoadQueue,
|
|
||||||
//lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue,
|
|
||||||
group,
|
|
||||||
noInfo: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const video = div.querySelector('video');
|
|
||||||
video.addEventListener('canplay', () => {
|
|
||||||
div.style.opacity = '';
|
|
||||||
if(!mouseOut) {
|
|
||||||
img && img.classList.add('hide');
|
|
||||||
} else {
|
|
||||||
img && img.classList.remove('hide');
|
|
||||||
if(div.lastElementChild != img) {
|
|
||||||
div.lastElementChild.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, {once: true});
|
|
||||||
};
|
|
||||||
|
|
||||||
const afterRender = () => {
|
const afterRender = () => {
|
||||||
if(img) {
|
if(img) {
|
||||||
div.append(img);
|
div.append(img);
|
||||||
div.style.opacity = '';
|
div.style.opacity = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(lazyLoadQueue) {
|
|
||||||
onMouseOver();
|
|
||||||
} else {
|
|
||||||
div.addEventListener('mouseover', onMouseOver, {once: true});
|
|
||||||
div.addEventListener('mouseout', (e) => {
|
|
||||||
const toElement = (e as any).toElement as Element;
|
|
||||||
//console.log('onMouseOut', doc.id, e);
|
|
||||||
if(findUpClassName(toElement, 'gif') == div) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//cancelEvent(e);
|
|
||||||
|
|
||||||
mouseOut = true;
|
|
||||||
|
|
||||||
const cb = () => {
|
|
||||||
if(div.lastElementChild != img) {
|
|
||||||
div.lastElementChild.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
div.addEventListener('mouseover', onMouseOver, {once: true});
|
|
||||||
};
|
|
||||||
|
|
||||||
img && img.classList.remove('hide');
|
|
||||||
/* window.requestAnimationFrame(() => {
|
|
||||||
window.requestAnimationFrame();
|
|
||||||
}); */
|
|
||||||
if(img) window.requestAnimationFrame(() => window.requestAnimationFrame(cb));
|
|
||||||
else cb();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
(gotThumb?.thumb?.url ? renderImageFromUrl(img, gotThumb.thumb.url, afterRender) : afterRender());
|
(gotThumb?.thumb?.url ? renderImageFromUrl(img, gotThumb.thumb.url, afterRender) : afterRender());
|
||||||
|
@ -2,16 +2,19 @@ import { logger, LogLevels } from "../lib/logger";
|
|||||||
import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector";
|
import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector";
|
||||||
|
|
||||||
type LazyLoadElementBase = {
|
type LazyLoadElementBase = {
|
||||||
div: HTMLDivElement,
|
load: () => Promise<any>
|
||||||
load: (target?: HTMLDivElement) => Promise<any>
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type LazyLoadElement = LazyLoadElementBase & {
|
type LazyLoadElement = Omit<LazyLoadElementBase, 'load'> & {
|
||||||
wasSeen?: boolean
|
load: (target?: HTMLElement) => Promise<any>,
|
||||||
|
div: HTMLElement
|
||||||
|
wasSeen?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PARALLEL_LIMIT = 5;
|
||||||
|
|
||||||
export class LazyLoadQueueBase {
|
export class LazyLoadQueueBase {
|
||||||
protected lazyLoadMedia: Array<LazyLoadElementBase> = [];
|
protected queue: Array<LazyLoadElementBase> = [];
|
||||||
protected inProcess: Set<LazyLoadElementBase> = new Set();
|
protected inProcess: Set<LazyLoadElementBase> = new Set();
|
||||||
|
|
||||||
protected lockPromise: Promise<void> = null;
|
protected lockPromise: Promise<void> = null;
|
||||||
@ -19,13 +22,13 @@ export class LazyLoadQueueBase {
|
|||||||
|
|
||||||
protected log = logger('LL', LogLevels.error);
|
protected log = logger('LL', LogLevels.error);
|
||||||
|
|
||||||
constructor(protected parallelLimit = 5) {
|
constructor(protected parallelLimit = PARALLEL_LIMIT) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public clear() {
|
public clear() {
|
||||||
this.inProcess.clear(); // ацтеки забьются, будет плохо
|
this.inProcess.clear(); // ацтеки забьются, будет плохо
|
||||||
|
|
||||||
this.lazyLoadMedia.length = 0;
|
this.queue.length = 0;
|
||||||
// unreachable code
|
// unreachable code
|
||||||
/* for(let item of this.inProcess) {
|
/* for(let item of this.inProcess) {
|
||||||
this.lazyLoadMedia.push(item);
|
this.lazyLoadMedia.push(item);
|
||||||
@ -34,34 +37,40 @@ export class LazyLoadQueueBase {
|
|||||||
|
|
||||||
public lock() {
|
public lock() {
|
||||||
if(this.lockPromise) return;
|
if(this.lockPromise) return;
|
||||||
|
|
||||||
|
const perf = performance.now();
|
||||||
this.lockPromise = new Promise((resolve, reject) => {
|
this.lockPromise = new Promise((resolve, reject) => {
|
||||||
this.unlockResolve = resolve;
|
this.unlockResolve = resolve;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.lockPromise.then(() => {
|
||||||
|
this.log('was locked for:', performance.now() - perf);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public unlock() {
|
public unlock() {
|
||||||
if(!this.unlockResolve) return;
|
if(!this.unlockResolve) return;
|
||||||
this.lockPromise = null;
|
|
||||||
this.unlockResolve();
|
this.unlockResolve();
|
||||||
this.unlockResolve = null;
|
this.unlockResolve = this.lockPromise = null;
|
||||||
|
|
||||||
|
this.processQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async processItem(item: LazyLoadElementBase) {
|
public async processItem(item: LazyLoadElementBase) {
|
||||||
|
if(this.lockPromise) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.inProcess.add(item);
|
this.inProcess.add(item);
|
||||||
|
|
||||||
this.log('will load media', this.lockPromise, item);
|
this.log('will load media', this.lockPromise, item);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(this.lockPromise/* && false */) {
|
|
||||||
const perf = performance.now();
|
|
||||||
await this.lockPromise;
|
|
||||||
|
|
||||||
this.log('waited lock:', performance.now() - perf);
|
|
||||||
}
|
|
||||||
|
|
||||||
//await new Promise((resolve) => setTimeout(resolve, 2e3));
|
//await new Promise((resolve) => setTimeout(resolve, 2e3));
|
||||||
//await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
|
//await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
|
||||||
await item.load(item.div);
|
//await item.load(item.div);
|
||||||
|
await this.loadItem(item);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
this.log.error('loadMediaQueue error:', err/* , item */);
|
this.log.error('loadMediaQueue error:', err/* , item */);
|
||||||
}
|
}
|
||||||
@ -70,25 +79,28 @@ export class LazyLoadQueueBase {
|
|||||||
|
|
||||||
this.log('loaded media', item);
|
this.log('loaded media', item);
|
||||||
|
|
||||||
if(this.lazyLoadMedia.length) {
|
this.processQueue();
|
||||||
this.processQueue();
|
}
|
||||||
}
|
|
||||||
|
protected loadItem(item: LazyLoadElementBase) {
|
||||||
|
return item.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getItem() {
|
protected getItem() {
|
||||||
return this.lazyLoadMedia.shift();
|
return this.queue.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected addElement(el: LazyLoadElementBase) {
|
protected addElement(method: 'push' | 'unshift', el: LazyLoadElementBase) {
|
||||||
this.processQueue(el);
|
this.queue[method](el);
|
||||||
|
this.processQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async processQueue(item?: LazyLoadElementBase) {
|
public async processQueue(item?: LazyLoadElementBase) {
|
||||||
if(this.parallelLimit > 0 && this.inProcess.size >= this.parallelLimit) return;
|
if(!this.queue.length || this.lockPromise || (this.parallelLimit > 0 && this.inProcess.size >= this.parallelLimit)) return;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if(item) {
|
if(item) {
|
||||||
this.lazyLoadMedia.findAndSplice(i => i == item);
|
this.queue.findAndSplice(i => i == item);
|
||||||
} else {
|
} else {
|
||||||
item = this.getItem();
|
item = this.getItem();
|
||||||
}
|
}
|
||||||
@ -100,25 +112,26 @@ export class LazyLoadQueueBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
item = null;
|
item = null;
|
||||||
} while(this.inProcess.size < this.parallelLimit && this.lazyLoadMedia.length);
|
} while(this.inProcess.size < this.parallelLimit && this.queue.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public push(el: LazyLoadElementBase) {
|
public push(el: LazyLoadElementBase) {
|
||||||
this.lazyLoadMedia.push(el);
|
this.addElement('push', el);
|
||||||
this.addElement(el);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public unshift(el: LazyLoadElementBase) {
|
public unshift(el: LazyLoadElementBase) {
|
||||||
this.lazyLoadMedia.unshift(el);
|
this.addElement('unshift', el);
|
||||||
this.addElement(el);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
|
export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
|
||||||
|
protected queue: Array<LazyLoadElement> = [];
|
||||||
|
protected inProcess: Set<LazyLoadElement> = new Set();
|
||||||
|
|
||||||
public intersector: VisibilityIntersector;
|
public intersector: VisibilityIntersector;
|
||||||
protected intersectorTimeout: number;
|
protected intersectorTimeout: number;
|
||||||
|
|
||||||
constructor(protected parallelLimit = 5) {
|
constructor(protected parallelLimit = PARALLEL_LIMIT) {
|
||||||
super(parallelLimit);
|
super(parallelLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,6 +159,26 @@ export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
|
|||||||
this.intersector.refresh();
|
this.intersector.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected loadItem(item: LazyLoadElement) {
|
||||||
|
return item.load(item.div);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected addElement(method: 'push' | 'unshift', el: LazyLoadElement) {
|
||||||
|
const item = this.queue.find(i => i.div == el.div);
|
||||||
|
if(item) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
for(const item of this.inProcess) {
|
||||||
|
if(item.div == el.div) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.queue[method](el);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected setProcessQueueTimeout() {
|
protected setProcessQueueTimeout() {
|
||||||
if(!this.intersectorTimeout) {
|
if(!this.intersectorTimeout) {
|
||||||
this.intersectorTimeout = window.setTimeout(() => {
|
this.intersectorTimeout = window.setTimeout(() => {
|
||||||
@ -154,52 +187,6 @@ export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
|
|||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default class LazyLoadQueue extends LazyLoadQueueIntersector {
|
|
||||||
protected lazyLoadMedia: Array<LazyLoadElement> = [];
|
|
||||||
protected inProcess: Set<LazyLoadElement> = new Set();
|
|
||||||
|
|
||||||
constructor(protected parallelLimit = 5) {
|
|
||||||
super(parallelLimit);
|
|
||||||
|
|
||||||
this.intersector = new VisibilityIntersector(this.onVisibilityChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onVisibilityChange = (target: HTMLElement, visible: boolean) => {
|
|
||||||
if(visible) {
|
|
||||||
this.log('isIntersecting', target);
|
|
||||||
|
|
||||||
// need for set element first if scrolled
|
|
||||||
const item = this.lazyLoadMedia.findAndSplice(i => i.div == target);
|
|
||||||
if(item) {
|
|
||||||
item.wasSeen = true;
|
|
||||||
this.lazyLoadMedia.unshift(item);
|
|
||||||
//this.processQueue(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setProcessQueueTimeout();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
protected getItem() {
|
|
||||||
return this.lazyLoadMedia.findAndSplice(item => item.wasSeen);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async processItem(item: LazyLoadElement) {
|
|
||||||
await super.processItem(item);
|
|
||||||
this.intersector.unobserve(item.div);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected addElement(el: LazyLoadElement) {
|
|
||||||
//super.addElement(el);
|
|
||||||
if(el.wasSeen) {
|
|
||||||
super.processQueue(el);
|
|
||||||
} else {
|
|
||||||
el.wasSeen = false;
|
|
||||||
this.intersector.observe(el.div);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public push(el: LazyLoadElement) {
|
public push(el: LazyLoadElement) {
|
||||||
super.push(el);
|
super.push(el);
|
||||||
@ -210,18 +197,66 @@ export default class LazyLoadQueue extends LazyLoadQueueIntersector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
|
export default class LazyLoadQueue extends LazyLoadQueueIntersector {
|
||||||
private _lazyLoadMedia: Map<HTMLElement, LazyLoadElementBase> = new Map();
|
constructor(protected parallelLimit = PARALLEL_LIMIT) {
|
||||||
|
super(parallelLimit);
|
||||||
|
|
||||||
constructor(protected parallelLimit = 5, protected onVisibilityChange?: OnVisibilityChange) {
|
this.intersector = new VisibilityIntersector(this.onVisibilityChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onVisibilityChange = (target: HTMLElement, visible: boolean) => {
|
||||||
|
if(visible) {
|
||||||
|
this.log('isIntersecting', target);
|
||||||
|
|
||||||
|
// need for set element first if scrolled
|
||||||
|
const item = this.queue.findAndSplice(i => i.div == target);
|
||||||
|
if(item) {
|
||||||
|
item.wasSeen = true;
|
||||||
|
this.queue.unshift(item);
|
||||||
|
//this.processQueue(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setProcessQueueTimeout();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected getItem() {
|
||||||
|
return this.queue.findAndSplice(item => item.wasSeen);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async processItem(item: LazyLoadElement) {
|
||||||
|
await super.processItem(item);
|
||||||
|
this.intersector.unobserve(item.div);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected addElement(method: 'push' | 'unshift', el: LazyLoadElement) {
|
||||||
|
const inserted = super.addElement(method, el);
|
||||||
|
|
||||||
|
if(!inserted) return false;
|
||||||
|
|
||||||
|
this.intersector.observe(el.div);
|
||||||
|
if(el.wasSeen) {
|
||||||
|
this.processQueue(el);
|
||||||
|
} else if(!el.hasOwnProperty('wasSeen')) {
|
||||||
|
el.wasSeen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
|
||||||
|
private _queue: Map<HTMLElement, LazyLoadElement> = new Map();
|
||||||
|
|
||||||
|
constructor(protected parallelLimit = PARALLEL_LIMIT, protected onVisibilityChange?: OnVisibilityChange) {
|
||||||
super(parallelLimit);
|
super(parallelLimit);
|
||||||
|
|
||||||
this.intersector = new VisibilityIntersector((target, visible) => {
|
this.intersector = new VisibilityIntersector((target, visible) => {
|
||||||
if(visible) {
|
if(visible) {
|
||||||
const item = this.lazyLoadMedia.findAndSplice(i => i.div == target);
|
const item = this.queue.findAndSplice(i => i.div == target);
|
||||||
this.lazyLoadMedia.unshift(item || this._lazyLoadMedia.get(target));
|
this.queue.unshift(item || this._queue.get(target));
|
||||||
} else {
|
} else {
|
||||||
this.lazyLoadMedia.findAndSplice(i => i.div == target);
|
this.queue.findAndSplice(i => i.div == target);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onVisibilityChange && this.onVisibilityChange(target, visible);
|
this.onVisibilityChange && this.onVisibilityChange(target, visible);
|
||||||
@ -229,6 +264,11 @@ export class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
super.clear();
|
||||||
|
this._queue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/* public async processItem(item: LazyLoadElement) {
|
/* public async processItem(item: LazyLoadElement) {
|
||||||
//await super.processItem(item);
|
//await super.processItem(item);
|
||||||
await LazyLoadQueueBase.prototype.processItem.call(this, item);
|
await LazyLoadQueueBase.prototype.processItem.call(this, item);
|
||||||
@ -238,8 +278,28 @@ export class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
|
|||||||
}
|
}
|
||||||
} */
|
} */
|
||||||
|
|
||||||
public observe(el: LazyLoadElementBase) {
|
public observe(el: LazyLoadElement) {
|
||||||
this._lazyLoadMedia.set(el.div, el);
|
this._queue.set(el.div, el);
|
||||||
this.intersector.observe(el.div);
|
this.intersector.observe(el.div);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class LazyLoadQueueRepeat2 extends LazyLoadQueueIntersector {
|
||||||
|
constructor(protected parallelLimit = PARALLEL_LIMIT, protected onVisibilityChange?: OnVisibilityChange) {
|
||||||
|
super(parallelLimit);
|
||||||
|
|
||||||
|
this.intersector = new VisibilityIntersector((target, visible) => {
|
||||||
|
const item = this.queue.findAndSplice(i => i.div == target);
|
||||||
|
if(visible && item) {
|
||||||
|
this.queue.unshift(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onVisibilityChange && this.onVisibilityChange(target, visible);
|
||||||
|
this.setProcessQueueTimeout();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public observe(el: HTMLElement) {
|
||||||
|
this.intersector.observe(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { logger, LogLevels } from "../lib/logger";
|
import { logger, LogLevels } from "../lib/logger";
|
||||||
import smoothscroll from '../vendor/smoothscroll';
|
import smoothscroll from '../vendor/smoothscroll';
|
||||||
import { touchSupport, isSafari, mediaSizes } from "../lib/config";
|
import { touchSupport, isSafari, mediaSizes } from "../lib/config";
|
||||||
|
import { CancellablePromise, deferredPromise } from "../lib/polyfill";
|
||||||
//import { isInDOM } from "../lib/utils";
|
//import { isInDOM } from "../lib/utils";
|
||||||
(window as any).__forceSmoothScrollPolyfill__ = true;
|
(window as any).__forceSmoothScrollPolyfill__ = true;
|
||||||
smoothscroll.polyfill();
|
smoothscroll.polyfill();
|
||||||
@ -80,6 +81,7 @@ export default class Scrollable {
|
|||||||
private onScrolledBottomFired = false; */
|
private onScrolledBottomFired = false; */
|
||||||
|
|
||||||
public scrollLocked = 0;
|
public scrollLocked = 0;
|
||||||
|
public scrollLockedPromise: CancellablePromise<void> = Promise.resolve();
|
||||||
public isVisible = false;
|
public isVisible = false;
|
||||||
|
|
||||||
private reorderTimeout: number;
|
private reorderTimeout: number;
|
||||||
@ -209,13 +211,11 @@ export default class Scrollable {
|
|||||||
throw new Error('no side for scroll');
|
throw new Error('no side for scroll');
|
||||||
}
|
}
|
||||||
|
|
||||||
const binded = this.onScroll.bind(this);
|
|
||||||
|
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
this.overflowContainer = mediaSizes.isMobile && false ? document.documentElement : this.container;
|
this.overflowContainer = mediaSizes.isMobile && false ? document.documentElement : this.container;
|
||||||
this.onScroll();
|
this.onScroll();
|
||||||
});
|
});
|
||||||
this.container.addEventListener('scroll', binded, {passive: true, capture: true});
|
this.container.addEventListener('scroll', this.onScroll, {passive: true, capture: true});
|
||||||
//document.documentElement.addEventListener('scroll', binded, {passive: true, capture: true});
|
//document.documentElement.addEventListener('scroll', binded, {passive: true, capture: true});
|
||||||
//window.addEventListener('scroll', binded, {passive: true, capture: true});
|
//window.addEventListener('scroll', binded, {passive: true, capture: true});
|
||||||
|
|
||||||
@ -289,7 +289,7 @@ export default class Scrollable {
|
|||||||
this.log('setVirtualContainer:', el, this);
|
this.log('setVirtualContainer:', el, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onScroll() {
|
public onScroll = () => {
|
||||||
/* let scrollTop = this.scrollTop;
|
/* let scrollTop = this.scrollTop;
|
||||||
this.lastScrollDirection = this.lastScrollTop < scrollTop;
|
this.lastScrollDirection = this.lastScrollTop < scrollTop;
|
||||||
this.lastScrollTop = scrollTop;
|
this.lastScrollTop = scrollTop;
|
||||||
@ -313,7 +313,7 @@ export default class Scrollable {
|
|||||||
if(this.splitUp) {
|
if(this.splitUp) {
|
||||||
clearTimeout(this.disableHoverTimeout);
|
clearTimeout(this.disableHoverTimeout);
|
||||||
|
|
||||||
this.disableHoverTimeout = setTimeout(() => {
|
this.disableHoverTimeout = window.setTimeout(() => {
|
||||||
//appendTo.classList.remove('disable-hover');
|
//appendTo.classList.remove('disable-hover');
|
||||||
this.lastScrollDirection = 0;
|
this.lastScrollDirection = 0;
|
||||||
}, 100);
|
}, 100);
|
||||||
@ -342,7 +342,7 @@ export default class Scrollable {
|
|||||||
this.lastScrollDirection = 0;
|
this.lastScrollDirection = 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
public checkForTriggers(container: HTMLElement) {
|
public checkForTriggers(container: HTMLElement) {
|
||||||
if(this.scrollLocked || (!this.onScrolledTop && !this.onScrolledBottom)) return;
|
if(this.scrollLocked || (!this.onScrolledTop && !this.onScrolledBottom)) return;
|
||||||
@ -369,7 +369,7 @@ export default class Scrollable {
|
|||||||
public reorder() {
|
public reorder() {
|
||||||
if(!this.splitUp || this.reorderTimeout) return;
|
if(!this.splitUp || this.reorderTimeout) return;
|
||||||
|
|
||||||
this.reorderTimeout = setTimeout(() => {
|
this.reorderTimeout = window.setTimeout(() => {
|
||||||
this.reorderTimeout = 0;
|
this.reorderTimeout = 0;
|
||||||
|
|
||||||
(Array.from(this.splitUp.children) as HTMLElement[]).forEach((el, idx) => {
|
(Array.from(this.splitUp.children) as HTMLElement[]).forEach((el, idx) => {
|
||||||
@ -466,9 +466,15 @@ export default class Scrollable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(this.scrollLocked) clearTimeout(this.scrollLocked);
|
if(this.scrollLocked) clearTimeout(this.scrollLocked);
|
||||||
this.scrollLocked = setTimeout(() => {
|
else {
|
||||||
|
this.scrollLockedPromise = deferredPromise<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scrollLocked = window.setTimeout(() => {
|
||||||
this.scrollLocked = 0;
|
this.scrollLocked = 0;
|
||||||
this.onScroll();
|
this.scrollLockedPromise.resolve();
|
||||||
|
//this.onScroll();
|
||||||
|
this.container.dispatchEvent(new CustomEvent('scroll'));
|
||||||
}, 468);
|
}, 468);
|
||||||
|
|
||||||
this.container.scrollTo({behavior: smooth ? 'smooth' : 'auto', top});
|
this.container.scrollTo({behavior: smooth ? 'smooth' : 'auto', top});
|
||||||
|
@ -7,6 +7,8 @@ import appSidebarLeft, { AppSidebarLeft } from "../../lib/appManagers/appSidebar
|
|||||||
import { $rootScope } from "../../lib/utils";
|
import { $rootScope } from "../../lib/utils";
|
||||||
import SearchInput from "../searchInput";
|
import SearchInput from "../searchInput";
|
||||||
|
|
||||||
|
// TODO: поиск по людям глобальный, если не нашло в контактах никого
|
||||||
|
|
||||||
export default class AppContactsTab implements SliderTab {
|
export default class AppContactsTab implements SliderTab {
|
||||||
private container = document.getElementById('contacts-container');
|
private container = document.getElementById('contacts-container');
|
||||||
private list = this.container.querySelector('#contacts') as HTMLUListElement;
|
private list = this.container.querySelector('#contacts') as HTMLUListElement;
|
||||||
@ -58,7 +60,13 @@ export default class AppContactsTab implements SliderTab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const contacts = [..._contacts];
|
const contacts = [..._contacts];
|
||||||
contacts.findAndSplice(u => u == $rootScope.myID);
|
|
||||||
|
if(!query) {
|
||||||
|
contacts.findAndSplice(u => u == $rootScope.myID);
|
||||||
|
}
|
||||||
|
/* if(query && 'saved messages'.includes(query.toLowerCase())) {
|
||||||
|
contacts.unshift($rootScope.myID);
|
||||||
|
} */
|
||||||
|
|
||||||
let sorted = contacts
|
let sorted = contacts
|
||||||
.map(userID => {
|
.map(userID => {
|
||||||
|
@ -21,7 +21,6 @@ export default class AppGifsTab implements SliderTab {
|
|||||||
private searchInput: SearchInput;
|
private searchInput: SearchInput;
|
||||||
private gifsDiv = this.contentDiv.firstElementChild as HTMLDivElement;
|
private gifsDiv = this.contentDiv.firstElementChild as HTMLDivElement;
|
||||||
private scrollable: Scrollable;
|
private scrollable: Scrollable;
|
||||||
private lazyLoadQueue: LazyLoadQueue;
|
|
||||||
|
|
||||||
private nextOffset = '';
|
private nextOffset = '';
|
||||||
private loadedAll = false;
|
private loadedAll = false;
|
||||||
@ -35,9 +34,7 @@ export default class AppGifsTab implements SliderTab {
|
|||||||
this.scrollable = new Scrollable(this.contentDiv, 'y', ANIMATIONGROUP, undefined, undefined, 2);
|
this.scrollable = new Scrollable(this.contentDiv, 'y', ANIMATIONGROUP, undefined, undefined, 2);
|
||||||
this.scrollable.setVirtualContainer(this.gifsDiv);
|
this.scrollable.setVirtualContainer(this.gifsDiv);
|
||||||
|
|
||||||
this.masonry = new GifsMasonry(this.gifsDiv);
|
this.masonry = new GifsMasonry(this.gifsDiv, ANIMATIONGROUP, this.scrollable);
|
||||||
|
|
||||||
this.lazyLoadQueue = new LazyLoadQueue();
|
|
||||||
|
|
||||||
this.searchInput = new SearchInput('Search GIFs', (value) => {
|
this.searchInput = new SearchInput('Search GIFs', (value) => {
|
||||||
this.reset();
|
this.reset();
|
||||||
@ -76,7 +73,7 @@ export default class AppGifsTab implements SliderTab {
|
|||||||
this.searchPromise = null;
|
this.searchPromise = null;
|
||||||
this.nextOffset = '';
|
this.nextOffset = '';
|
||||||
this.loadedAll = false;
|
this.loadedAll = false;
|
||||||
this.lazyLoadQueue.clear();
|
this.masonry.lazyLoadQueue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
@ -117,7 +114,7 @@ export default class AppGifsTab implements SliderTab {
|
|||||||
if(results.length) {
|
if(results.length) {
|
||||||
results.forEach((result) => {
|
results.forEach((result) => {
|
||||||
if(result._ === 'botInlineMediaResult' && result.document) {
|
if(result._ === 'botInlineMediaResult' && result.document) {
|
||||||
this.masonry.add(result.document as MyDocument, ANIMATIONGROUP, this.lazyLoadQueue);
|
this.masonry.add(result.document as MyDocument);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -125,7 +122,8 @@ export default class AppGifsTab implements SliderTab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.scrollable.onScroll();
|
this.scrollable.onScroll();
|
||||||
} catch (err) {
|
} catch(err) {
|
||||||
|
this.searchPromise = null;
|
||||||
throw new Error(JSON.stringify(err));
|
throw new Error(JSON.stringify(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,6 +210,8 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
|
|
||||||
//console.log('loaded doc:', doc, doc.url, container);
|
//console.log('loaded doc:', doc, doc.url, container);
|
||||||
|
|
||||||
|
const deferred = deferredPromise<void>();
|
||||||
|
|
||||||
//if(doc.type == 'gif'/* || true */) {
|
//if(doc.type == 'gif'/* || true */) {
|
||||||
video.addEventListener('canplay', () => {
|
video.addEventListener('canplay', () => {
|
||||||
if(img?.parentElement) {
|
if(img?.parentElement) {
|
||||||
@ -222,9 +224,16 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
if(doc.type == 'gif' && group) {
|
if(doc.type == 'gif' && group) {
|
||||||
animationIntersector.addAnimation(video, group);
|
animationIntersector.addAnimation(video, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test lazyLoadQueue
|
||||||
|
//setTimeout(() => {
|
||||||
|
deferred.resolve();
|
||||||
|
//}, 5000);
|
||||||
}, {once: true});
|
}, {once: true});
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
video.addEventListener('error', deferred.reject);
|
||||||
|
|
||||||
//if(doc.type != 'round') {
|
//if(doc.type != 'round') {
|
||||||
renderImageFromUrl(video, doc.url);
|
renderImageFromUrl(video, doc.url);
|
||||||
//}
|
//}
|
||||||
@ -243,6 +252,8 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
video.dataset.overlay = '1';
|
video.dataset.overlay = '1';
|
||||||
new VideoPlayer(video);
|
new VideoPlayer(video);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return deferred;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* if(doc.size >= 20e6 && !doc.downloaded) {
|
/* if(doc.size >= 20e6 && !doc.downloaded) {
|
||||||
@ -263,8 +274,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
return;
|
return;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
/* doc.downloaded || */!lazyLoadQueue/* && false */ ? loadVideo() : lazyLoadQueue.push({div: container, load: loadVideo/* , wasSeen: true */});
|
return /* doc.downloaded || */!lazyLoadQueue/* && false */ ? loadVideo() : (lazyLoadQueue.push({div: container, load: loadVideo/* , wasSeen: true */}), Promise.resolve());
|
||||||
return video;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const formatDate = (timestamp: number, monthShort = false, withYear = true) => {
|
export const formatDate = (timestamp: number, monthShort = false, withYear = true) => {
|
||||||
@ -476,7 +486,7 @@ export function wrapPhoto(photo: MyPhoto | MyDocument, message: any, container:
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return cacheContext.downloaded || !lazyLoadQueue ? load() : lazyLoadQueue.push({div: container, load: load, wasSeen: true});
|
return cacheContext.downloaded || !lazyLoadQueue ? load() : (lazyLoadQueue.push({div: container, load: load, wasSeen: true}), Promise.resolve());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop}: {
|
export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop}: {
|
||||||
|
@ -20,7 +20,7 @@ export class AppInlineBotsManager {
|
|||||||
query: query,
|
query: query,
|
||||||
geo_point: geo && {_: 'inputGeoPoint', lat: geo['lat'], long: geo['long']},
|
geo_point: geo && {_: 'inputGeoPoint', lat: geo['lat'], long: geo['long']},
|
||||||
offset
|
offset
|
||||||
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then(botResults => {
|
}, {/* timeout: 1, */stopTime: -1, noErrorBox: true}).then(botResults => {
|
||||||
const queryID = botResults.query_id;
|
const queryID = botResults.query_id;
|
||||||
/* delete botResults._;
|
/* delete botResults._;
|
||||||
delete botResults.flags;
|
delete botResults.flags;
|
||||||
|
@ -801,7 +801,8 @@ export class AppImManager {
|
|||||||
public onScroll(e: Event) {
|
public onScroll(e: Event) {
|
||||||
if(this.onScrollRAF) window.cancelAnimationFrame(this.onScrollRAF);
|
if(this.onScrollRAF) window.cancelAnimationFrame(this.onScrollRAF);
|
||||||
|
|
||||||
//if(this.scrollable.scrollLocked) return;
|
// * В таком случае, кнопка не будет моргать если чат в самом низу, и правильно отработает случай написания нового сообщения и проскролла вниз
|
||||||
|
if(this.scrollable.scrollLocked && this.scrolledDown) return;
|
||||||
|
|
||||||
this.onScrollRAF = window.requestAnimationFrame(() => {
|
this.onScrollRAF = window.requestAnimationFrame(() => {
|
||||||
//lottieLoader.checkAnimations(false, 'chat');
|
//lottieLoader.checkAnimations(false, 'chat');
|
||||||
|
@ -1073,10 +1073,7 @@ export class AppMediaViewer {
|
|||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.lazyLoadQueue.unshift({
|
this.lazyLoadQueue.unshift({load});
|
||||||
div: null,
|
|
||||||
load
|
|
||||||
});
|
|
||||||
//} else createPlayer();
|
//} else createPlayer();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -1137,10 +1134,7 @@ export class AppMediaViewer {
|
|||||||
return cancellablePromise;
|
return cancellablePromise;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.lazyLoadQueue.unshift({
|
this.lazyLoadQueue.unshift({load});
|
||||||
div: null,
|
|
||||||
load
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,13 +97,11 @@ export class AppPeersManager {
|
|||||||
: appChatsManager.getChat(-peerID)
|
: appChatsManager.getChat(-peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPeerID(peerString: any): number {
|
public getPeerID(peerString: any/* Peer | number | string */): number {
|
||||||
if(typeof(peerString) === 'number') return peerString;
|
if(typeof(peerString) === 'number') return peerString;
|
||||||
else if(isObject(peerString)) {
|
else if(isObject(peerString)) return peerString.user_id ? peerString.user_id : -(peerString.channel_id || peerString.chat_id);
|
||||||
return peerString.user_id
|
else if(!peerString) return 0;
|
||||||
? peerString.user_id
|
|
||||||
: -(peerString.channel_id || peerString.chat_id);
|
|
||||||
} else if(!peerString) return 0;
|
|
||||||
const isUser = peerString.charAt(0) == 'u';
|
const isUser = peerString.charAt(0) == 'u';
|
||||||
const peerParams = peerString.substr(1).split('_');
|
const peerParams = peerString.substr(1).split('_');
|
||||||
|
|
||||||
|
@ -86,6 +86,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
|||||||
//private log = logger('SL');
|
//private log = logger('SL');
|
||||||
|
|
||||||
private searchGroups = {
|
private searchGroups = {
|
||||||
|
//saved: new SearchGroup('', 'contacts'),
|
||||||
contacts: new SearchGroup('Chats', 'contacts'),
|
contacts: new SearchGroup('Chats', 'contacts'),
|
||||||
globalContacts: new SearchGroup('Global Search', 'contacts'),
|
globalContacts: new SearchGroup('Global Search', 'contacts'),
|
||||||
messages: new SearchGroup('Global Search', 'messages'),
|
messages: new SearchGroup('Global Search', 'messages'),
|
||||||
|
@ -179,7 +179,7 @@ export class AppUsersManager {
|
|||||||
return this.fillContacts().then(_contactsList => {
|
return this.fillContacts().then(_contactsList => {
|
||||||
let contactsList = [..._contactsList];
|
let contactsList = [..._contactsList];
|
||||||
if(query) {
|
if(query) {
|
||||||
const results: any = searchIndexManager.search(query, this.contactsIndex);
|
const results = searchIndexManager.search(query, this.contactsIndex);
|
||||||
const filteredContactsList = [...contactsList].filter(id => !!results[id]);
|
const filteredContactsList = [...contactsList].filter(id => !!results[id]);
|
||||||
|
|
||||||
contactsList = filteredContactsList;
|
contactsList = filteredContactsList;
|
||||||
@ -589,7 +589,7 @@ export class AppUsersManager {
|
|||||||
return apiManager.invokeApi('contacts.search', {
|
return apiManager.invokeApi('contacts.search', {
|
||||||
q: query,
|
q: query,
|
||||||
limit
|
limit
|
||||||
}).then((peers: any) => {
|
}).then((peers) => {
|
||||||
//console.log(peers);
|
//console.log(peers);
|
||||||
this.saveApiUsers(peers.users);
|
this.saveApiUsers(peers.users);
|
||||||
appChatsManager.saveApiChats(peers.chats);
|
appChatsManager.saveApiChats(peers.chats);
|
||||||
|
Loading…
Reference in New Issue
Block a user