Shared media as component
This commit is contained in:
parent
9628db6a2b
commit
2a6a699bc5
709
src/components/appSearchSuper..ts
Normal file
709
src/components/appSearchSuper..ts
Normal file
@ -0,0 +1,709 @@
|
|||||||
|
import { limitSymbols } from "../helpers/string";
|
||||||
|
import appMessagesManager, { MyInputMessagesFilter } from "../lib/appManagers/appMessagesManager";
|
||||||
|
import appPhotosManager from "../lib/appManagers/appPhotosManager";
|
||||||
|
import RichTextProcessor from "../lib/richtextprocessor";
|
||||||
|
import AppMediaViewer from "./appMediaViewer";
|
||||||
|
import { horizontalMenu } from "./horizontalMenu";
|
||||||
|
import LazyLoadQueue from "./lazyLoadQueue";
|
||||||
|
import { renderImageFromUrl, putPreloader } from "./misc";
|
||||||
|
import { ripple } from "./ripple";
|
||||||
|
import Scrollable from "./scrollable";
|
||||||
|
import { wrapDocument } from "./wrappers";
|
||||||
|
|
||||||
|
const testScroll = false;
|
||||||
|
|
||||||
|
export default class AppSearchSuper {
|
||||||
|
public tabs: {[t in MyInputMessagesFilter]: HTMLDivElement} = {} as any;
|
||||||
|
|
||||||
|
public type: MyInputMessagesFilter;
|
||||||
|
public tabSelected: HTMLElement;
|
||||||
|
|
||||||
|
public container: HTMLElement;
|
||||||
|
private tabsContainer: HTMLElement;
|
||||||
|
private tabsMenu: HTMLUListElement;
|
||||||
|
private prevTabId = -1;
|
||||||
|
|
||||||
|
public mediaDivsByIds: {[mid: number]: HTMLDivElement} = {};
|
||||||
|
|
||||||
|
private lazyLoadQueue = new LazyLoadQueue();
|
||||||
|
|
||||||
|
public historyStorage: Partial<{[type in MyInputMessagesFilter]: number[]}> = {};
|
||||||
|
public usedFromHistory: Partial<{[type in MyInputMessagesFilter]: number}> = {};
|
||||||
|
public urlsToRevoke: string[] = [];
|
||||||
|
|
||||||
|
private peerId = 0;
|
||||||
|
private threadId = 0;
|
||||||
|
private query = '';
|
||||||
|
public loadMutex: Promise<any> = Promise.resolve();
|
||||||
|
|
||||||
|
private loadPromises: Partial<{[type in MyInputMessagesFilter]: Promise<void>}> = {};
|
||||||
|
private loaded: Partial<{[type in MyInputMessagesFilter]: boolean}> = {};
|
||||||
|
|
||||||
|
constructor(public types: {inputFilter: MyInputMessagesFilter, name: string}[], public scrollable: Scrollable) {
|
||||||
|
this.container = document.createElement('div');
|
||||||
|
this.container.classList.add('search-super');
|
||||||
|
|
||||||
|
const nav = document.createElement('nav');
|
||||||
|
nav.classList.add('search-super-tabs', 'menu-horizontal');
|
||||||
|
this.tabsMenu = document.createElement('ul');
|
||||||
|
nav.append(this.tabsMenu);
|
||||||
|
|
||||||
|
for(const type of types) {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
const span = document.createElement('span');
|
||||||
|
const i = document.createElement('i');
|
||||||
|
|
||||||
|
span.innerText = type.name;
|
||||||
|
span.append(i);
|
||||||
|
|
||||||
|
li.append(span);
|
||||||
|
|
||||||
|
ripple(li);
|
||||||
|
|
||||||
|
this.tabsMenu.append(li);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tabsContainer = document.createElement('div');
|
||||||
|
this.tabsContainer.classList.add('search-super-tabs-container', 'tabs-container');
|
||||||
|
|
||||||
|
for(const type of types) {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.classList.add('search-super-container-' + type.name.toLowerCase());
|
||||||
|
|
||||||
|
const content = document.createElement('div');
|
||||||
|
content.classList.add('search-super-content-' + type.name.toLowerCase());
|
||||||
|
|
||||||
|
container.append(content);
|
||||||
|
|
||||||
|
this.tabsContainer.append(container);
|
||||||
|
|
||||||
|
this.tabs[type.inputFilter] = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.container.append(nav, this.tabsContainer);
|
||||||
|
|
||||||
|
// * construct end
|
||||||
|
|
||||||
|
this.scrollable.onScrolledBottom = () => {
|
||||||
|
if(this.tabSelected && this.tabSelected.childElementCount/* && false */) {
|
||||||
|
//this.log('onScrolledBottom will load media');
|
||||||
|
this.load(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//this.scroll.attachSentinels(undefined, 400);
|
||||||
|
|
||||||
|
horizontalMenu(this.tabsMenu, this.tabsContainer, (id, tabContent) => {
|
||||||
|
if(this.prevTabId == id) return;
|
||||||
|
|
||||||
|
if(this.prevTabId != -1) {
|
||||||
|
this.onTransitionStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.type = this.types[id].inputFilter;
|
||||||
|
this.tabSelected = tabContent.firstElementChild as HTMLDivElement;
|
||||||
|
|
||||||
|
if(this.prevTabId != -1 && nav.offsetTop) {
|
||||||
|
this.scrollable.scrollTop -= nav.offsetTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this.log('setVirtualContainer', id, this.sharedMediaSelected, this.sharedMediaSelected.childElementCount);
|
||||||
|
this.scroll.setVirtualContainer(this.sharedMediaSelected); */
|
||||||
|
|
||||||
|
if(this.prevTabId != -1 && !this.tabSelected.childElementCount) { // quick brown fix
|
||||||
|
//this.contentContainer.classList.remove('loaded');
|
||||||
|
this.load(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prevTabId = id;
|
||||||
|
}, () => {
|
||||||
|
this.scrollable.onScroll();
|
||||||
|
this.onTransitionEnd();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tabs.inputMessagesFilterPhotoVideo.addEventListener('click', (e) => {
|
||||||
|
const target = e.target as HTMLDivElement;
|
||||||
|
|
||||||
|
const messageId = +target.dataset.mid;
|
||||||
|
if(!messageId) {
|
||||||
|
console.warn('no messageId by click on target:', target);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = appMessagesManager.getMessageByPeer(this.peerId, messageId);
|
||||||
|
|
||||||
|
const ids = Object.keys(this.mediaDivsByIds).map(k => +k).sort((a, b) => a - b);
|
||||||
|
const idx = ids.findIndex(i => i == messageId);
|
||||||
|
|
||||||
|
const targets = ids.map(id => {
|
||||||
|
const element = this.mediaDivsByIds[id] as HTMLElement;
|
||||||
|
//element = element.querySelector('img') || element;
|
||||||
|
return {element, mid: id};
|
||||||
|
});
|
||||||
|
|
||||||
|
new AppMediaViewer().openMedia(message, target, false, targets.slice(idx + 1).reverse(), targets.slice(0, idx).reverse(), true/* , this.threadId */);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private onTransitionStart = () => {
|
||||||
|
// Jolly Cobra's // Workaround for scrollable content flickering during animation.
|
||||||
|
const container = this.scrollable.container;
|
||||||
|
if(container.style.overflowY !== 'hidden') {
|
||||||
|
const scrollBarWidth = container.offsetWidth - container.clientWidth;
|
||||||
|
container.style.overflowY = 'hidden';
|
||||||
|
container.style.paddingRight = `${scrollBarWidth}px`;
|
||||||
|
this.container.classList.add('sliding');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private onTransitionEnd = () => {
|
||||||
|
// Jolly Cobra's // Workaround for scrollable content flickering during animation.
|
||||||
|
const container = this.scrollable.container;
|
||||||
|
container.style.overflowY = '';
|
||||||
|
container.style.paddingRight = '0';
|
||||||
|
this.container.classList.remove('sliding');
|
||||||
|
};
|
||||||
|
|
||||||
|
public filterMessagesByType(ids: number[], type: MyInputMessagesFilter) {
|
||||||
|
let messages: any[] = [];
|
||||||
|
|
||||||
|
if(type != 'inputMessagesFilterUrl') {
|
||||||
|
for(let mid of ids) {
|
||||||
|
let message = appMessagesManager.getMessageByPeer(this.peerId, mid);
|
||||||
|
if(message.media) messages.push(message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
messages = ids.slice().map(mid => appMessagesManager.getMessageByPeer(this.peerId, mid));
|
||||||
|
}
|
||||||
|
|
||||||
|
let filtered: any[] = [];
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case 'inputMessagesFilterPhotoVideo': {
|
||||||
|
for(let message of messages) {
|
||||||
|
let media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document);
|
||||||
|
if(!media) {
|
||||||
|
//this.log('no media!', message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(media._ == 'document' && media.type != 'video'/* && media.type != 'gif' */) {
|
||||||
|
//this.log('broken video', media);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'inputMessagesFilterDocument': {
|
||||||
|
for(let message of messages) {
|
||||||
|
if(!message.media.document || ['voice', 'audio', 'gif', 'sticker', 'round'].includes(message.media.document.type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered.push(message);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'inputMessagesFilterUrl': {
|
||||||
|
console.log('inputMessagesFilterUrl', messages);
|
||||||
|
for(let message of messages) {
|
||||||
|
//if((message.media.webpage && message.media.webpage._ != 'webPageEmpty')) {
|
||||||
|
filtered.push(message);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'inputMessagesFilterMusic': {
|
||||||
|
for(let message of messages) {
|
||||||
|
if(!message.media.document || message.media.document.type != 'audio') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async performSearchResult(messages: any[], type: MyInputMessagesFilter, append = true) {
|
||||||
|
const peerId = this.peerId;
|
||||||
|
const threadId = this.threadId;
|
||||||
|
const elemsToAppend: HTMLElement[] = [];
|
||||||
|
const promises: Promise<any>[] = [];
|
||||||
|
const sharedMediaDiv = this.tabs[type];
|
||||||
|
|
||||||
|
/* for(let contentType in contentToSharedMap) {
|
||||||
|
if(contentToSharedMap[contentType as ContentType] == type) {
|
||||||
|
sharedMediaDiv = this.sharedMedia[contentType as ContentType];
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
|
||||||
|
// https://core.telegram.org/type/MessagesFilter
|
||||||
|
switch(type) {
|
||||||
|
case 'inputMessagesFilterPhotoVideo': {
|
||||||
|
for(const message of messages) {
|
||||||
|
const media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document);
|
||||||
|
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.classList.add('grid-item');
|
||||||
|
div.dataset.mid = '' + message.mid;
|
||||||
|
//console.log(message, photo);
|
||||||
|
|
||||||
|
const isPhoto = media._ == 'photo';
|
||||||
|
|
||||||
|
const photo = isPhoto ? appPhotosManager.getPhoto(media.id) : null;
|
||||||
|
let isDownloaded: boolean;
|
||||||
|
if(photo) {
|
||||||
|
isDownloaded = photo.downloaded > 0;
|
||||||
|
} else {
|
||||||
|
const cachedThumb = appPhotosManager.getDocumentCachedThumb(media.id);
|
||||||
|
isDownloaded = cachedThumb?.downloaded > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//this.log('inputMessagesFilterPhotoVideo', message, media);
|
||||||
|
|
||||||
|
if(!isPhoto) {
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.classList.add('video-time');
|
||||||
|
div.append(span);
|
||||||
|
|
||||||
|
if(media.type != 'gif') {
|
||||||
|
span.innerText = (media.duration + '').toHHMMSS(false);
|
||||||
|
|
||||||
|
/* const spanPlay = document.createElement('span');
|
||||||
|
spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
|
||||||
|
div.append(spanPlay); */
|
||||||
|
} else {
|
||||||
|
span.innerText = 'GIF';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const load = () => appPhotosManager.preloadPhoto(isPhoto ? media.id : media, appPhotosManager.choosePhotoSize(media, 200, 200))
|
||||||
|
.then(() => {
|
||||||
|
if(this.peerId != peerId || this.threadId != threadId) {
|
||||||
|
console.warn('peer changed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = (photo && photo.url) || appPhotosManager.getDocumentCachedThumb(media.id).url;
|
||||||
|
if(url) {
|
||||||
|
//if(needBlur) return;
|
||||||
|
|
||||||
|
const needBlurCallback = needBlur ? () => {
|
||||||
|
//void img.offsetLeft; // reflow
|
||||||
|
img.style.opacity = '';
|
||||||
|
|
||||||
|
if(thumb) {
|
||||||
|
window.setTimeout(() => {
|
||||||
|
thumb.remove();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
} : undefined;
|
||||||
|
renderImageFromUrl(img, url, needBlurCallback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let thumb: HTMLImageElement;
|
||||||
|
const sizes = media.sizes || media.thumbs;
|
||||||
|
|
||||||
|
const willHaveThumb = !isDownloaded && sizes && sizes[0].bytes;
|
||||||
|
if(willHaveThumb) {
|
||||||
|
thumb = new Image();
|
||||||
|
thumb.classList.add('grid-item-media', 'thumbnail');
|
||||||
|
thumb.dataset.mid = '' + message.mid;
|
||||||
|
appPhotosManager.setAttachmentPreview(sizes[0].bytes, thumb, false, false);
|
||||||
|
div.append(thumb);
|
||||||
|
}
|
||||||
|
|
||||||
|
const needBlur = !isDownloaded || !willHaveThumb;
|
||||||
|
const img = new Image();
|
||||||
|
img.dataset.mid = '' + message.mid;
|
||||||
|
img.classList.add('grid-item-media');
|
||||||
|
if(needBlur) img.style.opacity = '0';
|
||||||
|
div.append(img);
|
||||||
|
|
||||||
|
if(isDownloaded || willHaveThumb) {
|
||||||
|
const promise = new Promise<void>((resolve, reject) => {
|
||||||
|
(thumb || img).addEventListener('load', () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
//console.log('didn\'t load', thumb, media, isDownloaded, sizes);
|
||||||
|
reject();
|
||||||
|
}, 1e3);
|
||||||
|
});
|
||||||
|
|
||||||
|
promises.push(promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sizes?.length) {
|
||||||
|
if(isDownloaded) load();
|
||||||
|
else this.lazyLoadQueue.push({div, load});
|
||||||
|
}
|
||||||
|
|
||||||
|
elemsToAppend.push(div);
|
||||||
|
this.mediaDivsByIds[message.mid] = div;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'inputMessagesFilterMusic':
|
||||||
|
case 'inputMessagesFilterDocument': {
|
||||||
|
for(const message of messages) {
|
||||||
|
const div = wrapDocument({
|
||||||
|
message,
|
||||||
|
withTime: true,
|
||||||
|
fontWeight: 400
|
||||||
|
});
|
||||||
|
if(message.media.document.type === 'audio') {
|
||||||
|
div.classList.add('audio-48');
|
||||||
|
}
|
||||||
|
div.dataset.mid = '' + message.mid;
|
||||||
|
elemsToAppend.push(div);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'inputMessagesFilterUrl': {
|
||||||
|
for(let message of messages) {
|
||||||
|
let webpage: any;
|
||||||
|
|
||||||
|
if(message.media?.webpage && message.media.webpage._ != 'webPageEmpty') {
|
||||||
|
webpage = message.media.webpage;
|
||||||
|
} else {
|
||||||
|
const entity = message.totalEntities ? message.totalEntities.find((e: any) => e._ == 'messageEntityUrl' || e._ == 'messageEntityTextUrl') : null;
|
||||||
|
let url: string, display_url: string, sliced: string;
|
||||||
|
|
||||||
|
if(!entity) {
|
||||||
|
//this.log.error('NO ENTITY:', message);
|
||||||
|
const match = RichTextProcessor.matchUrl(message.message);
|
||||||
|
if(!match) {
|
||||||
|
//this.log.error('NO ENTITY AND NO MATCH:', message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
url = match[0];
|
||||||
|
} else {
|
||||||
|
sliced = message.message.slice(entity.offset, entity.offset + entity.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entity?._ == 'messageEntityTextUrl') {
|
||||||
|
url = entity.url;
|
||||||
|
//display_url = sliced;
|
||||||
|
} else {
|
||||||
|
url = url || sliced;
|
||||||
|
}
|
||||||
|
|
||||||
|
display_url = url;
|
||||||
|
|
||||||
|
const same = message.message == url;
|
||||||
|
if(!url.match(/^(ftp|http|https):\/\//)) {
|
||||||
|
display_url = 'https://' + url;
|
||||||
|
url = url.includes('@') ? url : 'https://' + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
display_url = new URL(display_url).hostname;
|
||||||
|
|
||||||
|
webpage = {
|
||||||
|
url,
|
||||||
|
display_url
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!same) {
|
||||||
|
webpage.description = message.message;
|
||||||
|
webpage.rDescription = RichTextProcessor.wrapRichText(limitSymbols(message.message, 150, 180));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let div = document.createElement('div');
|
||||||
|
div.dataset.mid = '' + message.mid;
|
||||||
|
|
||||||
|
let previewDiv = document.createElement('div');
|
||||||
|
previewDiv.classList.add('preview');
|
||||||
|
|
||||||
|
//this.log('wrapping webpage', webpage);
|
||||||
|
|
||||||
|
previewDiv.innerHTML = RichTextProcessor.getAbbreviation(webpage.title || webpage.display_url || webpage.description || webpage.url, true);
|
||||||
|
previewDiv.classList.add('empty');
|
||||||
|
if(webpage.photo) {
|
||||||
|
let load = () => appPhotosManager.preloadPhoto(webpage.photo.id, appPhotosManager.choosePhotoSize(webpage.photo, 60, 60))
|
||||||
|
.then(() => {
|
||||||
|
if(this.peerId != peerId || this.threadId != threadId) {
|
||||||
|
console.warn('peer changed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
previewDiv.classList.remove('empty');
|
||||||
|
|
||||||
|
previewDiv.innerText = '';
|
||||||
|
renderImageFromUrl(previewDiv, webpage.photo.url);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.lazyLoadQueue.push({div: previewDiv, load});
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = webpage.rTitle || '';
|
||||||
|
let subtitle = webpage.rDescription || '';
|
||||||
|
let url = RichTextProcessor.wrapRichText(webpage.url || '');
|
||||||
|
|
||||||
|
if(!title) {
|
||||||
|
//title = new URL(webpage.url).hostname;
|
||||||
|
title = RichTextProcessor.wrapPlainText(webpage.display_url.split('/', 1)[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if(webpage.description?.includes('Еще в начале')) {
|
||||||
|
console.error('FROM THE START', webpage);
|
||||||
|
} */
|
||||||
|
|
||||||
|
div.append(previewDiv);
|
||||||
|
div.insertAdjacentHTML('beforeend', `
|
||||||
|
<div class="title">${title}</button>
|
||||||
|
<div class="subtitle">${subtitle}</div>
|
||||||
|
<div class="url">${url}</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
if(div.innerText.trim().length) {
|
||||||
|
elemsToAppend.push(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
//console.warn('death is my friend', messages);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.loadMutex) {
|
||||||
|
promises.push(this.loadMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(promises.length) {
|
||||||
|
await Promise.all(promises);
|
||||||
|
if(this.peerId !== peerId || this.threadId !== threadId) {
|
||||||
|
console.warn('peer changed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(elemsToAppend.length) {
|
||||||
|
sharedMediaDiv[append ? 'append' : 'prepend'](...elemsToAppend);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sharedMediaDiv) {
|
||||||
|
const parent = sharedMediaDiv.parentElement;
|
||||||
|
Array.from(parent.children).slice(1).forEach(child => {
|
||||||
|
child.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
//this.contentContainer.classList.add('loaded');
|
||||||
|
|
||||||
|
if(!messages.length && !sharedMediaDiv.childElementCount) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.innerText = 'Nothing interesting here yet...';
|
||||||
|
div.classList.add('position-center', 'text-center', 'content-empty', 'no-select');
|
||||||
|
|
||||||
|
parent.append(div);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public load(single = false, justLoad = false) {
|
||||||
|
if(testScroll/* || 1 == 1 */) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('loadSidebarMedia', single, this.peerId, this.loadPromises);
|
||||||
|
|
||||||
|
const peerId = this.peerId;
|
||||||
|
const threadId = this.threadId;
|
||||||
|
|
||||||
|
let typesToLoad = single ? [this.type] : this.types.filter(t => t.inputFilter !== this.type && t.inputFilter !== 'inputMessagesFilterEmpty').map(t => t.inputFilter);
|
||||||
|
typesToLoad = typesToLoad.filter(type => !this.loaded[type]
|
||||||
|
|| this.usedFromHistory[type] < this.historyStorage[type].length);
|
||||||
|
|
||||||
|
if(!typesToLoad.length) return;
|
||||||
|
|
||||||
|
const loadCount = justLoad ? 50 : Math.round((appPhotosManager.windowH / 130 | 0) * 3 * 1.25); // that's good for all types
|
||||||
|
|
||||||
|
const historyStorage = this.historyStorage ?? (this.historyStorage = {});
|
||||||
|
|
||||||
|
const promises = typesToLoad.map(type => {
|
||||||
|
if(this.loadPromises[type]) return this.loadPromises[type];
|
||||||
|
|
||||||
|
const history = historyStorage[type] ?? (historyStorage[type] = []);
|
||||||
|
|
||||||
|
const logStr = 'loadSidebarMedia [' + type + ']: ';
|
||||||
|
|
||||||
|
// render from cache
|
||||||
|
if(history.length && this.usedFromHistory[type] < history.length && !justLoad) {
|
||||||
|
let messages: any[] = [];
|
||||||
|
let used = Math.max(0, this.usedFromHistory[type]);
|
||||||
|
let slicedLength = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
let ids = history.slice(used, used + loadCount);
|
||||||
|
console.log(logStr + 'will render from cache', used, history, ids, loadCount);
|
||||||
|
used += ids.length;
|
||||||
|
slicedLength += ids.length;
|
||||||
|
|
||||||
|
messages.push(...this.filterMessagesByType(ids, type));
|
||||||
|
} while(slicedLength < loadCount && used < history.length);
|
||||||
|
|
||||||
|
// если перебор
|
||||||
|
/* if(slicedLength > loadCount) {
|
||||||
|
let diff = messages.length - loadCount;
|
||||||
|
messages = messages.slice(0, messages.length - diff);
|
||||||
|
used -= diff;
|
||||||
|
} */
|
||||||
|
|
||||||
|
this.usedFromHistory[type] = used;
|
||||||
|
//if(messages.length) {
|
||||||
|
return this.performSearchResult(messages, type);
|
||||||
|
//}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
let maxId = history[history.length - 1] || 0;
|
||||||
|
|
||||||
|
console.log(logStr + 'search house of glass pre', type, maxId);
|
||||||
|
|
||||||
|
//let loadCount = history.length ? 50 : 15;
|
||||||
|
return this.loadPromises[type] = appMessagesManager.getSearch(peerId, '', {_: type}, maxId, loadCount, undefined, undefined/* , this.threadId */)
|
||||||
|
.then(value => {
|
||||||
|
const mids = value.history.map(message => message.mid);
|
||||||
|
history.push(...mids);
|
||||||
|
|
||||||
|
console.log(logStr + 'search house of glass', type, value);
|
||||||
|
|
||||||
|
if(this.peerId !== peerId || this.threadId !== threadId) {
|
||||||
|
//console.warn('peer changed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ! Фикс случая, когда не загружаются документы при открытой панели разработчиков (происходит из-за того, что не совпадают критерии отбора документов в getSearch)
|
||||||
|
if(value.history.length < loadCount) {
|
||||||
|
//if((value.count || history.length == value.count) && history.length >= value.count) {
|
||||||
|
console.log(logStr + 'loaded all media', value, loadCount);
|
||||||
|
this.loaded[type] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(justLoad) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.usedFromHistory[type] = history.length;
|
||||||
|
|
||||||
|
if(!this.loaded[type]) {
|
||||||
|
this.loadPromises[type].then(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('will preload more');
|
||||||
|
const promise = this.load(true, true);
|
||||||
|
if(promise) {
|
||||||
|
promise.then(() => {
|
||||||
|
console.log('preloaded more');
|
||||||
|
this.scrollable.checkForTriggers();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//if(value.history.length) {
|
||||||
|
return this.performSearchResult(this.filterMessagesByType(mids, type), type);
|
||||||
|
//}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('load error:', err);
|
||||||
|
}).finally(() => {
|
||||||
|
this.loadPromises[type] = null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(promises).catch(err => {
|
||||||
|
console.error('Load error all promises:', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public cleanup() {
|
||||||
|
this.loadPromises = {};
|
||||||
|
this.loaded = {};
|
||||||
|
|
||||||
|
this.prevTabId = -1;
|
||||||
|
this.mediaDivsByIds = {};
|
||||||
|
this.lazyLoadQueue.clear();
|
||||||
|
|
||||||
|
this.types.forEach(type => {
|
||||||
|
this.usedFromHistory[type.inputFilter] = -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.type = 'inputMessagesFilterPhotoVideo';
|
||||||
|
}
|
||||||
|
|
||||||
|
public cleanupHTML() {
|
||||||
|
if(this.urlsToRevoke.length) {
|
||||||
|
this.urlsToRevoke.forEach(url => {
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
});
|
||||||
|
this.urlsToRevoke.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
(Object.keys(this.tabs) as MyInputMessagesFilter[]).forEach(sharedMediaType => {
|
||||||
|
this.tabs[sharedMediaType].innerHTML = '';
|
||||||
|
|
||||||
|
if(!this.historyStorage || !this.historyStorage[sharedMediaType]) {
|
||||||
|
const parent = this.tabs[sharedMediaType].parentElement;
|
||||||
|
if(!testScroll) {
|
||||||
|
if(!parent.querySelector('.preloader')) {
|
||||||
|
putPreloader(parent, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const empty = parent.querySelector('.content-empty');
|
||||||
|
if(empty) {
|
||||||
|
empty.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(testScroll) {
|
||||||
|
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('grid-item');
|
||||||
|
div.dataset.id = '' + (i / 3 | 0);
|
||||||
|
//div.innerText = '' + (i / 3 | 0);
|
||||||
|
this.tabs.inputMessagesFilterPhotoVideo.append(div);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(this.tabsMenu.firstElementChild as HTMLLIElement).click(); // set media
|
||||||
|
}
|
||||||
|
|
||||||
|
public setQuery(peerId: number, query: string, threadId: number, historyStorage: AppSearchSuper['historyStorage'] = {}) {
|
||||||
|
this.peerId = peerId;
|
||||||
|
this.query = query;
|
||||||
|
this.threadId = threadId;
|
||||||
|
this.historyStorage = historyStorage;
|
||||||
|
|
||||||
|
this.cleanup();
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,15 @@
|
|||||||
import { limitSymbols } from "../../../helpers/string";
|
|
||||||
import appImManager from "../../../lib/appManagers/appImManager";
|
import appImManager from "../../../lib/appManagers/appImManager";
|
||||||
import appMessagesManager from "../../../lib/appManagers/appMessagesManager";
|
import appMessagesManager, { MyInputMessagesFilter } from "../../../lib/appManagers/appMessagesManager";
|
||||||
import appPeersManager from "../../../lib/appManagers/appPeersManager";
|
import appPeersManager from "../../../lib/appManagers/appPeersManager";
|
||||||
import appPhotosManager from "../../../lib/appManagers/appPhotosManager";
|
|
||||||
import appProfileManager from "../../../lib/appManagers/appProfileManager";
|
import appProfileManager from "../../../lib/appManagers/appProfileManager";
|
||||||
import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
||||||
import { logger } from "../../../lib/logger";
|
import { logger } from "../../../lib/logger";
|
||||||
import { RichTextProcessor } from "../../../lib/richtextprocessor";
|
import { RichTextProcessor } from "../../../lib/richtextprocessor";
|
||||||
import rootScope from "../../../lib/rootScope";
|
import rootScope from "../../../lib/rootScope";
|
||||||
import AppMediaViewer from "../../appMediaViewer";
|
import AppSearchSuper from "../../appSearchSuper.";
|
||||||
import AvatarElement from "../../avatar";
|
import AvatarElement from "../../avatar";
|
||||||
import { horizontalMenu } from "../../horizontalMenu";
|
|
||||||
import LazyLoadQueue from "../../lazyLoadQueue";
|
|
||||||
import { putPreloader, renderImageFromUrl } from "../../misc";
|
|
||||||
import Scrollable from "../../scrollable";
|
import Scrollable from "../../scrollable";
|
||||||
import { SliderTab } from "../../slider";
|
import { SliderTab } from "../../slider";
|
||||||
import { wrapDocument } from "../../wrappers";
|
|
||||||
|
|
||||||
const testScroll = false;
|
|
||||||
|
|
||||||
let setText = (text: string, el: HTMLDivElement) => {
|
let setText = (text: string, el: HTMLDivElement) => {
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
@ -33,10 +25,7 @@ let setText = (text: string, el: HTMLDivElement) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
type SharedMediaType = /* 'inputMessagesFilterContacts' | */'inputMessagesFilterEmpty' | 'inputMessagesFilterPhotoVideo' | 'inputMessagesFilterDocument' | 'inputMessagesFilterUrl' | 'inputMessagesFilterMusic';
|
|
||||||
|
|
||||||
// TODO: отредактированное сообщение не изменится
|
// TODO: отредактированное сообщение не изменится
|
||||||
|
|
||||||
export default class AppSharedMediaTab implements SliderTab {
|
export default class AppSharedMediaTab implements SliderTab {
|
||||||
public container: HTMLElement;
|
public container: HTMLElement;
|
||||||
public closeBtn: HTMLElement;
|
public closeBtn: HTMLElement;
|
||||||
@ -45,7 +34,6 @@ export default class AppSharedMediaTab implements SliderTab {
|
|||||||
private threadId = 0;
|
private threadId = 0;
|
||||||
|
|
||||||
public profileContentEl: HTMLDivElement;
|
public profileContentEl: HTMLDivElement;
|
||||||
public contentContainer: HTMLDivElement;
|
|
||||||
public profileElements: {
|
public profileElements: {
|
||||||
avatar: AvatarElement,
|
avatar: AvatarElement,
|
||||||
name: HTMLDivElement,
|
name: HTMLDivElement,
|
||||||
@ -57,59 +45,25 @@ export default class AppSharedMediaTab implements SliderTab {
|
|||||||
notificationsCheckbox: HTMLInputElement,
|
notificationsCheckbox: HTMLInputElement,
|
||||||
notificationsStatus: HTMLParagraphElement
|
notificationsStatus: HTMLParagraphElement
|
||||||
} = {} as any;
|
} = {} as any;
|
||||||
public sharedMedia: {
|
|
||||||
[t in SharedMediaType]: HTMLDivElement
|
|
||||||
} = {} as any;
|
|
||||||
|
|
||||||
private loadSidebarMediaPromises: {[type: string]: Promise<void>} = {};
|
|
||||||
private loadedAllMedia: {[type: string]: boolean} = {};
|
|
||||||
|
|
||||||
public sharedMediaTypes: SharedMediaType[] = [
|
|
||||||
//'members',
|
|
||||||
'inputMessagesFilterEmpty',
|
|
||||||
//'inputMessagesFilterContacts',
|
|
||||||
'inputMessagesFilterPhotoVideo',
|
|
||||||
'inputMessagesFilterDocument',
|
|
||||||
'inputMessagesFilterUrl',
|
|
||||||
'inputMessagesFilterMusic'
|
|
||||||
];
|
|
||||||
public sharedMediaType: SharedMediaType = 'inputMessagesFilterPhotoVideo';
|
|
||||||
private sharedMediaSelected: HTMLDivElement = null;
|
|
||||||
|
|
||||||
private lazyLoadQueue = new LazyLoadQueue();
|
|
||||||
|
|
||||||
public historiesStorage: {
|
public historiesStorage: {
|
||||||
[peerId: number]: Partial<{
|
[peerId: number]: Partial<{
|
||||||
[type in SharedMediaType]: number[]
|
[type in MyInputMessagesFilter]: number[]
|
||||||
}>
|
}>
|
||||||
} = {};
|
} = {};
|
||||||
public usedFromHistory: Partial<{
|
|
||||||
[type in SharedMediaType]: number
|
|
||||||
}> = {};
|
|
||||||
|
|
||||||
public scroll: Scrollable = null;
|
public scroll: Scrollable = null;
|
||||||
|
|
||||||
private profileTabs: HTMLUListElement;
|
|
||||||
private prevTabId = -1;
|
|
||||||
|
|
||||||
private mediaDivsByIds: {
|
|
||||||
[mid: number]: HTMLDivElement
|
|
||||||
} = {};
|
|
||||||
|
|
||||||
public urlsToRevoke: string[] = [];
|
|
||||||
|
|
||||||
private loadMutex: Promise<any> = Promise.resolve();
|
|
||||||
|
|
||||||
private log = logger('SM'/* , LogLevels.error */);
|
private log = logger('SM'/* , LogLevels.error */);
|
||||||
setPeerStatusInterval: number;
|
setPeerStatusInterval: number;
|
||||||
cleaned: boolean;
|
cleaned: boolean;
|
||||||
|
searchSuper: AppSearchSuper;
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
this.container = document.getElementById('shared-media-container');
|
this.container = document.getElementById('shared-media-container');
|
||||||
this.closeBtn = this.container.querySelector('.sidebar-close-button');
|
this.closeBtn = this.container.querySelector('.sidebar-close-button');
|
||||||
|
|
||||||
this.profileContentEl = this.container.querySelector('.profile-content');
|
this.profileContentEl = this.container.querySelector('.profile-content');
|
||||||
this.contentContainer = this.container.querySelector('.content-container');
|
|
||||||
this.profileElements = {
|
this.profileElements = {
|
||||||
avatar: this.profileContentEl.querySelector('.profile-avatar'),
|
avatar: this.profileContentEl.querySelector('.profile-avatar'),
|
||||||
name: this.profileContentEl.querySelector('.profile-name'),
|
name: this.profileContentEl.querySelector('.profile-name'),
|
||||||
@ -122,77 +76,7 @@ export default class AppSharedMediaTab implements SliderTab {
|
|||||||
notificationsStatus: this.profileContentEl.querySelector('.profile-row-notifications > p')
|
notificationsStatus: this.profileContentEl.querySelector('.profile-row-notifications > p')
|
||||||
};
|
};
|
||||||
|
|
||||||
this.sharedMedia = {
|
|
||||||
//contentMembers: this.profileContentEl.querySelector('#content-members'),
|
|
||||||
//inputMessagesFilterEmpty: null,
|
|
||||||
inputMessagesFilterPhotoVideo: this.profileContentEl.querySelector('#content-media'),
|
|
||||||
inputMessagesFilterDocument: this.profileContentEl.querySelector('#content-docs'),
|
|
||||||
inputMessagesFilterUrl: this.profileContentEl.querySelector('#content-links'),
|
|
||||||
inputMessagesFilterMusic: this.profileContentEl.querySelector('#content-audio'),
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
let container = this.profileContentEl.querySelector('.content-container .tabs-container') as HTMLDivElement;
|
|
||||||
this.profileTabs = this.profileContentEl.querySelector('.profile-tabs');
|
|
||||||
|
|
||||||
this.scroll = new Scrollable(this.container, 'SR', 400);
|
this.scroll = new Scrollable(this.container, 'SR', 400);
|
||||||
this.scroll.onScrolledBottom = () => {
|
|
||||||
if(this.sharedMediaSelected && this.sharedMediaSelected.childElementCount/* && false */) {
|
|
||||||
//this.log('onScrolledBottom will load media');
|
|
||||||
this.loadSidebarMedia(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//this.scroll.attachSentinels(undefined, 400);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if(this.prevTabId != -1 && this.profileTabs.offsetTop) {
|
|
||||||
this.scroll.scrollTop -= this.profileTabs.offsetTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this.log('setVirtualContainer', id, this.sharedMediaSelected, this.sharedMediaSelected.childElementCount);
|
|
||||||
this.scroll.setVirtualContainer(this.sharedMediaSelected); */
|
|
||||||
|
|
||||||
if(this.prevTabId != -1 && !this.sharedMediaSelected.childElementCount) { // quick brown fix
|
|
||||||
//this.contentContainer.classList.remove('loaded');
|
|
||||||
this.loadSidebarMedia(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.prevTabId = id;
|
|
||||||
}, () => {
|
|
||||||
this.scroll.onScroll();
|
|
||||||
this.onTransitionEnd();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.sharedMedia.inputMessagesFilterPhotoVideo.addEventListener('click', (e) => {
|
|
||||||
const target = e.target as HTMLDivElement;
|
|
||||||
|
|
||||||
const messageId = +target.dataset.mid;
|
|
||||||
if(!messageId) {
|
|
||||||
this.log.warn('no messageId by click on target:', target);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const message = appMessagesManager.getMessageByPeer(this.peerId, messageId);
|
|
||||||
|
|
||||||
const ids = Object.keys(this.mediaDivsByIds).map(k => +k).sort((a, b) => a - b);
|
|
||||||
const idx = ids.findIndex(i => i == messageId);
|
|
||||||
|
|
||||||
const targets = ids.map(id => {
|
|
||||||
const element = this.mediaDivsByIds[id] as HTMLElement;
|
|
||||||
//element = element.querySelector('img') || element;
|
|
||||||
return {element, mid: id};
|
|
||||||
});
|
|
||||||
|
|
||||||
new AppMediaViewer().openMedia(message, target, false, targets.slice(idx + 1).reverse(), targets.slice(0, idx).reverse(), true/* , this.threadId */);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.profileElements.notificationsCheckbox.addEventListener('change', () => {
|
this.profileElements.notificationsCheckbox.addEventListener('change', () => {
|
||||||
//let checked = this.profileElements.notificationsCheckbox.checked;
|
//let checked = this.profileElements.notificationsCheckbox.checked;
|
||||||
@ -228,6 +112,22 @@ export default class AppSharedMediaTab implements SliderTab {
|
|||||||
/* this.closeBtn.addEventListener('click', () => {
|
/* this.closeBtn.addEventListener('click', () => {
|
||||||
this.toggleSidebar(false);
|
this.toggleSidebar(false);
|
||||||
}); */
|
}); */
|
||||||
|
|
||||||
|
this.searchSuper = new AppSearchSuper([{
|
||||||
|
inputFilter: 'inputMessagesFilterPhotoVideo',
|
||||||
|
name: 'Media'
|
||||||
|
}, {
|
||||||
|
inputFilter: 'inputMessagesFilterDocument',
|
||||||
|
name: 'Docs'
|
||||||
|
}, {
|
||||||
|
inputFilter: 'inputMessagesFilterUrl',
|
||||||
|
name: 'Links'
|
||||||
|
}, {
|
||||||
|
inputFilter: 'inputMessagesFilterMusic',
|
||||||
|
name: 'Audio'
|
||||||
|
}], this.scroll);
|
||||||
|
|
||||||
|
this.profileContentEl.append(this.searchSuper.container);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setPeerStatus = (needClear = false) => {
|
public setPeerStatus = (needClear = false) => {
|
||||||
@ -253,16 +153,17 @@ export default class AppSharedMediaTab implements SliderTab {
|
|||||||
if(!this.historiesStorage[peerId]) return;
|
if(!this.historiesStorage[peerId]) return;
|
||||||
|
|
||||||
mids = mids.slice().reverse(); // ! because it will be ascend sorted array
|
mids = mids.slice().reverse(); // ! because it will be ascend sorted array
|
||||||
for(const sharedMediaType of this.sharedMediaTypes) {
|
for(const type of this.searchSuper.types) {
|
||||||
const filtered = this.filterMessagesByType(mids, sharedMediaType);
|
const inputFilter = type.inputFilter;
|
||||||
|
const filtered = this.searchSuper.filterMessagesByType(mids, inputFilter);
|
||||||
if(filtered.length) {
|
if(filtered.length) {
|
||||||
if(this.historiesStorage[peerId][sharedMediaType]) {
|
if(this.historiesStorage[peerId][inputFilter]) {
|
||||||
this.historiesStorage[peerId][sharedMediaType].unshift(...mids);
|
this.historiesStorage[peerId][inputFilter].unshift(...mids);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.peerId == peerId && this.usedFromHistory[sharedMediaType] !== -1) {
|
if(this.peerId == peerId && this.searchSuper.usedFromHistory[inputFilter] !== -1) {
|
||||||
this.usedFromHistory[sharedMediaType] += filtered.length;
|
this.searchSuper.usedFromHistory[inputFilter] += filtered.length;
|
||||||
this.performSearchResult(filtered, sharedMediaType, false);
|
this.searchSuper.performSearchResult(filtered, inputFilter, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -276,27 +177,29 @@ export default class AppSharedMediaTab implements SliderTab {
|
|||||||
if(!this.historiesStorage[peerId]) return;
|
if(!this.historiesStorage[peerId]) return;
|
||||||
|
|
||||||
for(const mid of mids) {
|
for(const mid of mids) {
|
||||||
for(const sharedMediaType of this.sharedMediaTypes) {
|
for(const type of this.searchSuper.types) {
|
||||||
if(!this.historiesStorage[peerId][sharedMediaType]) continue;
|
const inputFilter = type.inputFilter;
|
||||||
|
|
||||||
const history = this.historiesStorage[peerId][sharedMediaType];
|
if(!this.historiesStorage[peerId][inputFilter]) continue;
|
||||||
|
|
||||||
|
const history = this.historiesStorage[peerId][inputFilter];
|
||||||
const idx = history.findIndex(m => m == mid);
|
const idx = history.findIndex(m => m == mid);
|
||||||
if(idx !== -1) {
|
if(idx !== -1) {
|
||||||
history.splice(idx, 1);
|
history.splice(idx, 1);
|
||||||
|
|
||||||
if(this.peerId == peerId) {
|
if(this.peerId == peerId) {
|
||||||
const container = this.sharedMedia[sharedMediaType];
|
const container = this.searchSuper.tabs[inputFilter];
|
||||||
const div = container.querySelector(`div[data-mid="${mid}"]`);
|
const div = container.querySelector(`div[data-mid="${mid}"]`);
|
||||||
if(div) {
|
if(div) {
|
||||||
if(sharedMediaType == 'inputMessagesFilterPhotoVideo') {
|
if(inputFilter == 'inputMessagesFilterPhotoVideo') {
|
||||||
delete this.mediaDivsByIds[mid];
|
delete this.searchSuper.mediaDivsByIds[mid];
|
||||||
}
|
}
|
||||||
|
|
||||||
div.remove();
|
div.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.usedFromHistory[sharedMediaType] >= (idx + 1)) {
|
if(this.searchSuper.usedFromHistory[inputFilter] >= (idx + 1)) {
|
||||||
this.usedFromHistory[sharedMediaType]--;
|
this.searchSuper.usedFromHistory[inputFilter]--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,526 +211,7 @@ export default class AppSharedMediaTab implements SliderTab {
|
|||||||
this.scroll.onScroll();
|
this.scroll.onScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
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`;
|
|
||||||
this.contentContainer.classList.add('sliding');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private onTransitionEnd = () => {
|
|
||||||
// Jolly Cobra's // Workaround for scrollable content flickering during animation.
|
|
||||||
const container = this.scroll.container;
|
|
||||||
container.style.overflowY = '';
|
|
||||||
container.style.paddingRight = '0';
|
|
||||||
this.contentContainer.classList.remove('sliding');
|
|
||||||
};
|
|
||||||
|
|
||||||
public filterMessagesByType(ids: number[], type: SharedMediaType) {
|
|
||||||
let messages: any[] = [];
|
|
||||||
|
|
||||||
if(type != 'inputMessagesFilterUrl') {
|
|
||||||
for(let mid of ids) {
|
|
||||||
let message = appMessagesManager.getMessageByPeer(this.peerId, mid);
|
|
||||||
if(message.media) messages.push(message);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
messages = ids.slice().map(mid => appMessagesManager.getMessageByPeer(this.peerId, mid));
|
|
||||||
}
|
|
||||||
|
|
||||||
let filtered: any[] = [];
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case 'inputMessagesFilterPhotoVideo': {
|
|
||||||
for(let message of messages) {
|
|
||||||
let media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document);
|
|
||||||
if(!media) {
|
|
||||||
//this.log('no media!', message);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(media._ == 'document' && media.type != 'video'/* && media.type != 'gif' */) {
|
|
||||||
//this.log('broken video', media);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
filtered.push(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'inputMessagesFilterDocument': {
|
|
||||||
for(let message of messages) {
|
|
||||||
if(!message.media.document || ['voice', 'audio', 'gif', 'sticker', 'round'].includes(message.media.document.type)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
filtered.push(message);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'inputMessagesFilterUrl': {
|
|
||||||
this.log('inputMessagesFilterUrl', messages);
|
|
||||||
for(let message of messages) {
|
|
||||||
//if((message.media.webpage && message.media.webpage._ != 'webPageEmpty')) {
|
|
||||||
filtered.push(message);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'inputMessagesFilterMusic': {
|
|
||||||
for(let message of messages) {
|
|
||||||
if(!message.media.document || message.media.document.type != 'audio') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
filtered.push(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return filtered;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async performSearchResult(messages: any[], type: SharedMediaType, append = true) {
|
|
||||||
const peerId = this.peerId;
|
|
||||||
const threadId = this.threadId;
|
|
||||||
const elemsToAppend: HTMLElement[] = [];
|
|
||||||
const promises: Promise<any>[] = [];
|
|
||||||
const sharedMediaDiv = this.sharedMedia[type];
|
|
||||||
|
|
||||||
/* for(let contentType in contentToSharedMap) {
|
|
||||||
if(contentToSharedMap[contentType as ContentType] == type) {
|
|
||||||
sharedMediaDiv = this.sharedMedia[contentType as ContentType];
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
// https://core.telegram.org/type/MessagesFilter
|
|
||||||
switch(type) {
|
|
||||||
case 'inputMessagesFilterPhotoVideo': {
|
|
||||||
for(const message of messages) {
|
|
||||||
const media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document);
|
|
||||||
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.classList.add('grid-item');
|
|
||||||
div.dataset.mid = '' + message.mid;
|
|
||||||
//console.log(message, photo);
|
|
||||||
|
|
||||||
const isPhoto = media._ == 'photo';
|
|
||||||
|
|
||||||
const photo = isPhoto ? appPhotosManager.getPhoto(media.id) : null;
|
|
||||||
let isDownloaded: boolean;
|
|
||||||
if(photo) {
|
|
||||||
isDownloaded = photo.downloaded > 0;
|
|
||||||
} else {
|
|
||||||
const cachedThumb = appPhotosManager.getDocumentCachedThumb(media.id);
|
|
||||||
isDownloaded = cachedThumb?.downloaded > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//this.log('inputMessagesFilterPhotoVideo', message, media);
|
|
||||||
|
|
||||||
if(!isPhoto) {
|
|
||||||
const span = document.createElement('span');
|
|
||||||
span.classList.add('video-time');
|
|
||||||
div.append(span);
|
|
||||||
|
|
||||||
if(media.type != 'gif') {
|
|
||||||
span.innerText = (media.duration + '').toHHMMSS(false);
|
|
||||||
|
|
||||||
/* const spanPlay = document.createElement('span');
|
|
||||||
spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
|
|
||||||
div.append(spanPlay); */
|
|
||||||
} else {
|
|
||||||
span.innerText = 'GIF';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const load = () => appPhotosManager.preloadPhoto(isPhoto ? media.id : media, appPhotosManager.choosePhotoSize(media, 200, 200))
|
|
||||||
.then(() => {
|
|
||||||
if(this.peerId != peerId || this.threadId != threadId) {
|
|
||||||
this.log.warn('peer changed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = (photo && photo.url) || appPhotosManager.getDocumentCachedThumb(media.id).url;
|
|
||||||
if(url) {
|
|
||||||
//if(needBlur) return;
|
|
||||||
|
|
||||||
const needBlurCallback = needBlur ? () => {
|
|
||||||
//void img.offsetLeft; // reflow
|
|
||||||
img.style.opacity = '';
|
|
||||||
|
|
||||||
if(thumb) {
|
|
||||||
window.setTimeout(() => {
|
|
||||||
thumb.remove();
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
} : undefined;
|
|
||||||
renderImageFromUrl(img, url, needBlurCallback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let thumb: HTMLImageElement;
|
|
||||||
const sizes = media.sizes || media.thumbs;
|
|
||||||
|
|
||||||
const willHaveThumb = !isDownloaded && sizes && sizes[0].bytes;
|
|
||||||
if(willHaveThumb) {
|
|
||||||
thumb = new Image();
|
|
||||||
thumb.classList.add('grid-item-media', 'thumbnail');
|
|
||||||
thumb.dataset.mid = '' + message.mid;
|
|
||||||
appPhotosManager.setAttachmentPreview(sizes[0].bytes, thumb, false, false);
|
|
||||||
div.append(thumb);
|
|
||||||
}
|
|
||||||
|
|
||||||
const needBlur = !isDownloaded || !willHaveThumb;
|
|
||||||
const img = new Image();
|
|
||||||
img.dataset.mid = '' + message.mid;
|
|
||||||
img.classList.add('grid-item-media');
|
|
||||||
if(needBlur) img.style.opacity = '0';
|
|
||||||
div.append(img);
|
|
||||||
|
|
||||||
if(isDownloaded || willHaveThumb) {
|
|
||||||
const promise = new Promise<void>((resolve, reject) => {
|
|
||||||
(thumb || img).addEventListener('load', () => {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
this.log('didn\'t load', thumb, media, isDownloaded, sizes);
|
|
||||||
reject();
|
|
||||||
}, 1e3);
|
|
||||||
});
|
|
||||||
|
|
||||||
promises.push(promise);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sizes?.length) {
|
|
||||||
if(isDownloaded) load();
|
|
||||||
else this.lazyLoadQueue.push({div, load});
|
|
||||||
}
|
|
||||||
|
|
||||||
elemsToAppend.push(div);
|
|
||||||
this.mediaDivsByIds[message.mid] = div;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'inputMessagesFilterMusic':
|
|
||||||
case 'inputMessagesFilterDocument': {
|
|
||||||
for(const message of messages) {
|
|
||||||
const div = wrapDocument({
|
|
||||||
message,
|
|
||||||
withTime: true,
|
|
||||||
fontWeight: 400
|
|
||||||
});
|
|
||||||
if(message.media.document.type === 'audio') {
|
|
||||||
div.classList.add('audio-48');
|
|
||||||
}
|
|
||||||
div.dataset.mid = '' + message.mid;
|
|
||||||
elemsToAppend.push(div);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'inputMessagesFilterUrl': {
|
|
||||||
for(let message of messages) {
|
|
||||||
let webpage: any;
|
|
||||||
|
|
||||||
if(message.media?.webpage && message.media.webpage._ != 'webPageEmpty') {
|
|
||||||
webpage = message.media.webpage;
|
|
||||||
} else {
|
|
||||||
const entity = message.totalEntities ? message.totalEntities.find((e: any) => e._ == 'messageEntityUrl' || e._ == 'messageEntityTextUrl') : null;
|
|
||||||
let url: string, display_url: string, sliced: string;
|
|
||||||
|
|
||||||
if(!entity) {
|
|
||||||
//this.log.error('NO ENTITY:', message);
|
|
||||||
const match = RichTextProcessor.matchUrl(message.message);
|
|
||||||
if(!match) {
|
|
||||||
//this.log.error('NO ENTITY AND NO MATCH:', message);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
url = match[0];
|
|
||||||
} else {
|
|
||||||
sliced = message.message.slice(entity.offset, entity.offset + entity.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(entity?._ == 'messageEntityTextUrl') {
|
|
||||||
url = entity.url;
|
|
||||||
//display_url = sliced;
|
|
||||||
} else {
|
|
||||||
url = url || sliced;
|
|
||||||
}
|
|
||||||
|
|
||||||
display_url = url;
|
|
||||||
|
|
||||||
const same = message.message == url;
|
|
||||||
if(!url.match(/^(ftp|http|https):\/\//)) {
|
|
||||||
display_url = 'https://' + url;
|
|
||||||
url = url.includes('@') ? url : 'https://' + url;
|
|
||||||
}
|
|
||||||
|
|
||||||
display_url = new URL(display_url).hostname;
|
|
||||||
|
|
||||||
webpage = {
|
|
||||||
url,
|
|
||||||
display_url
|
|
||||||
};
|
|
||||||
|
|
||||||
if(!same) {
|
|
||||||
webpage.description = message.message;
|
|
||||||
webpage.rDescription = RichTextProcessor.wrapRichText(limitSymbols(message.message, 150, 180));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let div = document.createElement('div');
|
|
||||||
div.dataset.mid = '' + message.mid;
|
|
||||||
|
|
||||||
let previewDiv = document.createElement('div');
|
|
||||||
previewDiv.classList.add('preview');
|
|
||||||
|
|
||||||
//this.log('wrapping webpage', webpage);
|
|
||||||
|
|
||||||
previewDiv.innerHTML = RichTextProcessor.getAbbreviation(webpage.title || webpage.display_url || webpage.description || webpage.url, true);
|
|
||||||
previewDiv.classList.add('empty');
|
|
||||||
if(webpage.photo) {
|
|
||||||
let load = () => appPhotosManager.preloadPhoto(webpage.photo.id, appPhotosManager.choosePhotoSize(webpage.photo, 60, 60))
|
|
||||||
.then(() => {
|
|
||||||
if(this.peerId != peerId || this.threadId != threadId) {
|
|
||||||
this.log.warn('peer changed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
previewDiv.classList.remove('empty');
|
|
||||||
|
|
||||||
previewDiv.innerText = '';
|
|
||||||
renderImageFromUrl(previewDiv, webpage.photo.url);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.lazyLoadQueue.push({div: previewDiv, load});
|
|
||||||
}
|
|
||||||
|
|
||||||
let title = webpage.rTitle || '';
|
|
||||||
let subtitle = webpage.rDescription || '';
|
|
||||||
let url = RichTextProcessor.wrapRichText(webpage.url || '');
|
|
||||||
|
|
||||||
if(!title) {
|
|
||||||
//title = new URL(webpage.url).hostname;
|
|
||||||
title = RichTextProcessor.wrapPlainText(webpage.display_url.split('/', 1)[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(webpage.description?.includes('Еще в начале')) {
|
|
||||||
this.log.error('FROM THE START', webpage);
|
|
||||||
}
|
|
||||||
|
|
||||||
div.append(previewDiv);
|
|
||||||
div.insertAdjacentHTML('beforeend', `
|
|
||||||
<div class="title">${title}</button>
|
|
||||||
<div class="subtitle">${subtitle}</div>
|
|
||||||
<div class="url">${url}</div>
|
|
||||||
`);
|
|
||||||
|
|
||||||
if(div.innerText.trim().length) {
|
|
||||||
elemsToAppend.push(div);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
//console.warn('death is my friend', messages);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.loadMutex) {
|
|
||||||
promises.push(this.loadMutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(promises.length) {
|
|
||||||
await Promise.all(promises);
|
|
||||||
if(this.peerId !== peerId || this.threadId !== threadId) {
|
|
||||||
this.log.warn('peer changed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(elemsToAppend.length) {
|
|
||||||
sharedMediaDiv[append ? 'append' : 'prepend'](...elemsToAppend);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sharedMediaDiv) {
|
|
||||||
const parent = sharedMediaDiv.parentElement;
|
|
||||||
Array.from(parent.children).slice(1).forEach(child => {
|
|
||||||
child.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
//this.contentContainer.classList.add('loaded');
|
|
||||||
|
|
||||||
if(!messages.length && !sharedMediaDiv.childElementCount) {
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.innerText = 'Nothing interesting here yet...';
|
|
||||||
div.classList.add('position-center', 'text-center', 'content-empty', 'no-select');
|
|
||||||
|
|
||||||
parent.append(div);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public loadSidebarMedia(single = false, justLoad = false) {
|
|
||||||
if(testScroll/* || 1 == 1 */) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log('loadSidebarMedia', single, this.peerId, this.loadSidebarMediaPromises);
|
|
||||||
|
|
||||||
const peerId = this.peerId;
|
|
||||||
const threadId = this.threadId;
|
|
||||||
|
|
||||||
let typesToLoad = single ? [this.sharedMediaType] : this.sharedMediaTypes.filter(t => t !== this.sharedMediaType && t !== 'inputMessagesFilterEmpty');
|
|
||||||
typesToLoad = typesToLoad.filter(type => !this.loadedAllMedia[type]
|
|
||||||
|| this.usedFromHistory[type] < this.historiesStorage[peerId][type].length);
|
|
||||||
|
|
||||||
if(!typesToLoad.length) return;
|
|
||||||
|
|
||||||
const loadCount = justLoad ? 50 : Math.round((appPhotosManager.windowH / 130 | 0) * 3 * 1.25); // that's good for all types
|
|
||||||
|
|
||||||
const historyStorage = this.historiesStorage[peerId] ?? (this.historiesStorage[peerId] = {});
|
|
||||||
|
|
||||||
const promises = typesToLoad.map(type => {
|
|
||||||
if(this.loadSidebarMediaPromises[type]) return this.loadSidebarMediaPromises[type];
|
|
||||||
|
|
||||||
const history = historyStorage[type] ?? (historyStorage[type] = []);
|
|
||||||
|
|
||||||
const logStr = 'loadSidebarMedia [' + type + ']: ';
|
|
||||||
|
|
||||||
// render from cache
|
|
||||||
if(history.length && this.usedFromHistory[type] < history.length && !justLoad) {
|
|
||||||
let messages: any[] = [];
|
|
||||||
let used = Math.max(0, this.usedFromHistory[type]);
|
|
||||||
let slicedLength = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
let ids = history.slice(used, used + loadCount);
|
|
||||||
this.log(logStr + 'will render from cache', used, history, ids, loadCount);
|
|
||||||
used += ids.length;
|
|
||||||
slicedLength += ids.length;
|
|
||||||
|
|
||||||
messages.push(...this.filterMessagesByType(ids, type));
|
|
||||||
} while(slicedLength < loadCount && used < history.length);
|
|
||||||
|
|
||||||
// если перебор
|
|
||||||
/* if(slicedLength > loadCount) {
|
|
||||||
let diff = messages.length - loadCount;
|
|
||||||
messages = messages.slice(0, messages.length - diff);
|
|
||||||
used -= diff;
|
|
||||||
} */
|
|
||||||
|
|
||||||
this.usedFromHistory[type] = used;
|
|
||||||
//if(messages.length) {
|
|
||||||
return this.performSearchResult(messages, type);
|
|
||||||
//}
|
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
// заливать новую картинку сюда только после полной отправки!
|
|
||||||
let maxId = history[history.length - 1] || 0;
|
|
||||||
|
|
||||||
this.log(logStr + 'search house of glass pre', type, maxId);
|
|
||||||
|
|
||||||
//let loadCount = history.length ? 50 : 15;
|
|
||||||
return this.loadSidebarMediaPromises[type] = appMessagesManager.getSearch(peerId, '', {_: type}, maxId, loadCount, undefined, undefined/* , this.threadId */)
|
|
||||||
.then(value => {
|
|
||||||
const mids = value.history.map(message => message.mid);
|
|
||||||
history.push(...mids);
|
|
||||||
|
|
||||||
this.log(logStr + 'search house of glass', type, value);
|
|
||||||
|
|
||||||
if(this.peerId !== peerId || this.threadId !== threadId) {
|
|
||||||
this.log.warn('peer changed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ! Фикс случая, когда не загружаются документы при открытой панели разработчиков (происходит из-за того, что не совпадают критерии отбора документов в getSearch)
|
|
||||||
if(value.history.length < loadCount) {
|
|
||||||
//if((value.count || history.length == value.count) && history.length >= value.count) {
|
|
||||||
this.log(logStr + 'loaded all media', value, loadCount);
|
|
||||||
this.loadedAllMedia[type] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(justLoad) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.usedFromHistory[type] = history.length;
|
|
||||||
|
|
||||||
if(!this.loadedAllMedia[type]) {
|
|
||||||
this.loadSidebarMediaPromises[type].then(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.log('will preload more');
|
|
||||||
const promise = this.loadSidebarMedia(true, true);
|
|
||||||
if(promise) {
|
|
||||||
promise.then(() => {
|
|
||||||
this.log('preloaded more');
|
|
||||||
this.scroll.checkForTriggers();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//if(value.history.length) {
|
|
||||||
return this.performSearchResult(this.filterMessagesByType(mids, type), type);
|
|
||||||
//}
|
|
||||||
}).catch(err => {
|
|
||||||
this.log.error('load error:', err);
|
|
||||||
}).finally(() => {
|
|
||||||
this.loadSidebarMediaPromises[type] = null;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all(promises).catch(err => {
|
|
||||||
this.log.error('Load error all promises:', err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public cleanup() {
|
|
||||||
this.loadSidebarMediaPromises = {};
|
|
||||||
this.loadedAllMedia = {};
|
|
||||||
|
|
||||||
this.prevTabId = -1;
|
|
||||||
this.mediaDivsByIds = {};
|
|
||||||
this.lazyLoadQueue.clear();
|
|
||||||
|
|
||||||
this.sharedMediaTypes.forEach(type => {
|
|
||||||
this.usedFromHistory[type] = -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.sharedMediaType = 'inputMessagesFilterPhotoVideo';
|
|
||||||
this.cleaned = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public cleanupHTML() {
|
public cleanupHTML() {
|
||||||
//this.contentContainer.classList.remove('loaded');
|
|
||||||
|
|
||||||
//this.profileContentEl.parentElement.scrollTop = 0;
|
|
||||||
this.profileElements.bio.style.display = 'none';
|
this.profileElements.bio.style.display = 'none';
|
||||||
this.profileElements.phone.style.display = 'none';
|
this.profileElements.phone.style.display = 'none';
|
||||||
this.profileElements.username.style.display = 'none';
|
this.profileElements.username.style.display = 'none';
|
||||||
@ -835,47 +219,11 @@ export default class AppSharedMediaTab implements SliderTab {
|
|||||||
this.profileElements.notificationsCheckbox.checked = true;
|
this.profileElements.notificationsCheckbox.checked = true;
|
||||||
this.profileElements.notificationsStatus.innerText = 'Enabled';
|
this.profileElements.notificationsStatus.innerText = 'Enabled';
|
||||||
|
|
||||||
if(this.urlsToRevoke.length) {
|
this.searchSuper.cleanupHTML();
|
||||||
this.urlsToRevoke.forEach(url => {
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
});
|
|
||||||
this.urlsToRevoke.length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Object.keys(this.sharedMedia) as SharedMediaType[]).forEach(sharedMediaType => {
|
|
||||||
this.sharedMedia[sharedMediaType].innerHTML = '';
|
|
||||||
|
|
||||||
if(!this.historiesStorage[this.peerId] || !this.historiesStorage[this.peerId][sharedMediaType]) {
|
|
||||||
const parent = this.sharedMedia[sharedMediaType].parentElement;
|
|
||||||
if(!testScroll) {
|
|
||||||
if(!parent.querySelector('.preloader')) {
|
|
||||||
putPreloader(parent, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const empty = parent.querySelector('.content-empty');
|
|
||||||
if(empty) {
|
|
||||||
empty.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(testScroll) {
|
|
||||||
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('grid-item');
|
|
||||||
div.dataset.id = '' + (i / 3 | 0);
|
|
||||||
//div.innerText = '' + (i / 3 | 0);
|
|
||||||
this.sharedMedia.inputMessagesFilterPhotoVideo.append(div);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(this.profileTabs.firstElementChild.children[1] as HTMLLIElement).click(); // set media
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setLoadMutex(promise: Promise<any>) {
|
public setLoadMutex(promise: Promise<any>) {
|
||||||
this.loadMutex = promise;
|
this.searchSuper.loadMutex = promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setPeer(peerId: number, threadId = 0) {
|
public setPeer(peerId: number, threadId = 0) {
|
||||||
@ -888,7 +236,12 @@ export default class AppSharedMediaTab implements SliderTab {
|
|||||||
|
|
||||||
this.peerId = peerId;
|
this.peerId = peerId;
|
||||||
this.threadId = threadId;
|
this.threadId = threadId;
|
||||||
this.cleanup();
|
this.searchSuper.setQuery(peerId, '', threadId, this.historiesStorage[peerId] ?? (this.historiesStorage[peerId] = {}));
|
||||||
|
this.cleaned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public loadSidebarMedia(single: boolean) {
|
||||||
|
this.searchSuper.load(single);
|
||||||
}
|
}
|
||||||
|
|
||||||
public fillProfileElements() {
|
public fillProfileElements() {
|
||||||
|
@ -373,24 +373,6 @@
|
|||||||
<p class="profile-row-label">Enabled</p>
|
<p class="profile-row-label">Enabled</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-container">
|
|
||||||
<nav class="profile-tabs menu-horizontal">
|
|
||||||
<ul>
|
|
||||||
<li class="profile-tabs-members rp" style="display: none;"><span>Members<i></i></span></li>
|
|
||||||
<li class="profile-tabs-media rp"><span>Media<i></i></span></li>
|
|
||||||
<li class="profile-tabs-docs rp"><span>Docs<i></i></span></li>
|
|
||||||
<li class="profile-tabs-links rp"><span>Links<i></i></span></li>
|
|
||||||
<li class="profile-tabs-audio rp"><span>Audio<i></i></span></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<div class="profile-tabs-content tabs-container">
|
|
||||||
<div class="content-members-container"><div id="content-members"></div></div>
|
|
||||||
<div class="content-media-container"><div id="content-media"></div></div>
|
|
||||||
<div class="content-docs-container"><div id="content-docs"></div></div>
|
|
||||||
<div class="content-links-container"><div id="content-links"></div></div>
|
|
||||||
<div class="content-audio-container"><div id="content-audio"></div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-slider-item chatlist-container" id="search-private-container">
|
<div class="sidebar-slider-item chatlist-container" id="search-private-container">
|
||||||
|
@ -62,7 +62,7 @@ export type HistoryResult = {
|
|||||||
export type Dialog = MTDialog.dialog;
|
export type Dialog = MTDialog.dialog;
|
||||||
|
|
||||||
export type MyMessage = Message.message | Message.messageService;
|
export type MyMessage = Message.message | Message.messageService;
|
||||||
type MyInputMessagesFilter = 'inputMessagesFilterEmpty'
|
export type MyInputMessagesFilter = 'inputMessagesFilterEmpty'
|
||||||
| 'inputMessagesFilterPhotos'
|
| 'inputMessagesFilterPhotos'
|
||||||
| 'inputMessagesFilterPhotoVideo'
|
| 'inputMessagesFilterPhotoVideo'
|
||||||
| 'inputMessagesFilterVideo'
|
| 'inputMessagesFilterVideo'
|
||||||
|
@ -110,23 +110,6 @@
|
|||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
//overflow: hidden;
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
//min-height: 100vh; /* Fallback for browsers that do not support Custom Properties */
|
|
||||||
//min-height: calc(var(--vh, 1vh) * 100);
|
|
||||||
min-height: calc((var(--vh, 1vh) * 100) - 100% - 60px);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
&.sliding {
|
|
||||||
max-height: calc((var(--vh, 1vh) * 100) - 100% - 60px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&-container {
|
&-container {
|
||||||
@ -245,6 +228,23 @@
|
|||||||
&-name, &-subtitle, &-row, &-avatar {
|
&-name, &-subtitle, &-row, &-avatar {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-super {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
//overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
//min-height: 100vh; /* Fallback for browsers that do not support Custom Properties */
|
||||||
|
//min-height: calc(var(--vh, 1vh) * 100);
|
||||||
|
min-height: calc((var(--vh, 1vh) * 100) - 100% - 56px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
&.sliding {
|
||||||
|
max-height: calc((var(--vh, 1vh) * 100) - 100% - 56px);
|
||||||
|
}
|
||||||
|
|
||||||
&-tabs {
|
&-tabs {
|
||||||
//margin-top: 36px;
|
//margin-top: 36px;
|
||||||
@ -258,8 +258,9 @@
|
|||||||
padding-right: 1.5rem !important;
|
padding-right: 1.5rem !important;
|
||||||
margin-left: -.75rem !important;
|
margin-left: -.75rem !important;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-content {
|
&-tabs-container {
|
||||||
//min-height: 100%;
|
//min-height: 100%;
|
||||||
min-height: calc(100% - 49px);
|
min-height: calc(100% - 49px);
|
||||||
grid-template-rows: 100%;
|
grid-template-rows: 100%;
|
||||||
@ -301,8 +302,9 @@
|
|||||||
width: 50px;
|
width: 50px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#content-media {
|
&-content-media {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 7.5px;
|
padding: 7.5px;
|
||||||
|
|
||||||
@ -351,7 +353,7 @@
|
|||||||
} */
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
#content-docs {
|
&-content-docs {
|
||||||
padding: 7px 20px;
|
padding: 7px 20px;
|
||||||
|
|
||||||
.document {
|
.document {
|
||||||
@ -380,7 +382,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#content-links {
|
&-content-links {
|
||||||
padding: 0 30px 15px 15px;
|
padding: 0 30px 15px 15px;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
@ -436,7 +438,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#content-audio {
|
&-content-audio {
|
||||||
padding: 20px 15px 15px 20px;
|
padding: 20px 15px 15px 20px;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
@ -494,8 +496,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#stickers-container {
|
#stickers-container {
|
||||||
.sticker-sets {
|
.sticker-sets {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user